Ansible: Merge item variable that's been used multiple times

Viewed 86

I've written an nftables role for ansible that opens me ports for my servers. I would like to have some kind of hierarchical system. I would like to define rules that shall be opened on every host but some rules shall only be applied to a group of hosts.

Inventory

---
myservers:
  hosts:
    server1:
    server2:
    server3:
    server4:
groupa:
  hosts:
    server1:
    server2:
groupb:
  hosts:
    server3:
    server4:

Those are my group_vars: myservers.yml

---
nftablesopen:
  - dport: "22"
    comment: "SSH"
    proto: tcp

groupa.yml

---
nftablesopen:
  - dport: "123"
    comment: "NTP"
    proto: "udp"

groupb.yml

---
nftablesopen:
  - dport: "80"
    comment: "HTTP"
    proto: "udp"

When I run the playbook the varible priorities kick in and only a fragment of what I've defined is being applied. What is the best approach in Ansible to handle this? I'd rather not make plenty of different variables that I merge into a single one because I don't want to update my tasks when I add a new group of hosts for example. Can't I just append items to an already existing list of items, just like it's being done in different programming languages?

1 Answers

I'd recommend to create a dictionary with the groups and let the hosts extract the configuration. For example

shell> cat hosts
[myservers]
test_01
test_02

[groupa]
test_01

[groupb]
test_02
nftablesopen:
  myservers:
    - dport: "22"
      comment: "SSH"
      proto: tcp
  groupa:
    - dport: "123"
      comment: "NTP"
      proto: "udp"
  groupb:
    - dport: "80"
      comment: "HTTP"
      proto: "udp"
- set_fact:
    my_open: "{{ group_names|
                 map('extract',nftablesopen)|
                 list|flatten }}"
- debug:
    var: my_open

give

ok: [test_01] => {
    "my_open": [
        {
            "comment": "NTP",
            "dport": "123",
            "proto": "udp"
        },
        {
            "comment": "SSH",
            "dport": "22",
            "proto": "tcp"
        }
    ]
}
ok: [test_02] => {
    "my_open": [
        {
            "comment": "HTTP",
            "dport": "80",
            "proto": "udp"
        },
        {
            "comment": "SSH",
            "dport": "22",
            "proto": "tcp"
        }
    ]
}

Q: "What if there are group names that I don't specify in the nftabblesopen?"

A: intersect the lists of keys and groups. For example

    - set_fact:
        my_open: "{{ nftablesopen.keys()|
                     list|
                     intersect(group_names)|
                     map('extract',nftablesopen)|
                     list|flatten }}"

Running the playbook with three hosts

shell> cat hosts
[myservers]
test_01
test_02

[groupa]
test_01

[groupb]
test_02

[groupc]
test_03
- hosts: test_01, test_02, test_03

gives

ok: [test_02] => {
    "my_open": [
        {
            "comment": "SSH",
            "dport": "22",
            "proto": "tcp"
        },
        {
            "comment": "HTTP",
            "dport": "80",
            "proto": "udp"
        }
    ]
}
ok: [test_01] => {
    "my_open": [
        {
            "comment": "SSH",
            "dport": "22",
            "proto": "tcp"
        },
        {
            "comment": "NTP",
            "dport": "123",
            "proto": "udp"
        }
    ]
}
ok: [test_03] => {
    "my_open": []
}

Q: "(This way) I need to relate to the group by explicitly naming them instead of just putting the variable into the corresponding group."

A: Put the common variables of a project into a directory, e.g. global_vars

shell> cat global_vars/nftablesopen.yml 
nftablesopen:
  myservers:
    - dport: "22"
      comment: "SSH"
      proto: tcp
  groupa:
    - dport: "123"
      comment: "NTP"
      proto: "udp"
  groupb:
    - dport: "80"
      comment: "HTTP"
      proto: "udp"

Link this directory into each host_vars of the project. For example

shell> tree host_vars/
host_vars/
├── test_01
│   └── global_vars -> ../../global_vars
├── test_02
│   └── global_vars -> ../../global_vars
└── test_03
    └── global_vars -> ../../global_vars

To change the configuration then means a modification of a single file in a directory either way.

Related