1

I'm getting started with Ansible.

I have few hosts in my /etc/ansible/hosts. This file and my playbooks are versioned with git. Members of my team are versioned in git like

$cat users/john.json
{
   login: 'john.johnson',
   firstName: 'John',
   lastName : 'Johnson',
   sshPubKey: '....'
}

I want to grant access to some users to some machine like:

server1 [john, alice, bob]
server2 [john, alice]
server3 [john, bob]
server4 [john]
server5 [john, kevin]
server6 [john, albert, alice]
server7 [john, bob, kevin]

I'd also like to have the same approach for open ports like:

server1 [22, 80, 443]
server2 [22, 1234]
server3 [22, 1122]
server4 [22]
server5 [22, 1717]
server6 [22, 80, 443]
server7 [22]

I don't need help with writing the playbook's tasks (I know how to use the user/ufw module) but I can't find a way to cleanly centralise a per-host configuration with ansible.

The only solution I have for now is to have a user.yml and port.yml playbook, edit them every time I want to run them for a specific server and run the playbook with a --limit server1. It doesn't feel clean and prevent me to have a clear snapshot on git of users' access and open ports for each server.

What would be the clean way to organise this in ansible ?

2

Given the dictionary servers (see the abridged version in playbook below) and the JSON files in the directory users let's create the dictionary users in the first play and use it in the next play. For example

- name: Collect data
  hosts: server1:server2:server3

  vars:
    servers:
      server1:
        access: [john, alice, bob]
        ports: [22, 80, 443]
      server2:
        access: [john, alice]
        ports: [22, 1234]
      server3:
        access: [john, bob]
        ports: [22, 1122]

  tasks:

    - block:
        - set_fact:
            servers: "{{ servers }}"
        - set_fact:
            users_list: "{{ servers|json_query('*.access')|flatten|unique }}"
        - set_fact:
            users: "{{ users|default({})|
                       combine({item: lookup('file', 'users/' ~ item ~ '.json')|from_yaml}) }}"
          loop: "{{ users_list }}"
        - debug:
            var: users
      run_once: true

- name: Configure users
  hosts: server1:server2:server3
  tasks:
    - debug:
        msg:
          - "{{ inventory_hostname }}"
          - "{{ item }}"
          - "{{ users[item].sshPubKey }}"
      loop: "{{ servers[inventory_hostname].access }}"

gives

PLAY [Collect data] ***
...
ok: [server1] => 
  users:
    alice:
      firstName: Alice
      lastName: Springs
      login: alice.springs
      sshPubKey: sshPubKey_alice
    bob:
      firstName: Bob
      lastName: Brown
      login: bob.brown
      sshPubKey: sshPubKey_bob
    john:
      firstName: John
      lastName: Johnson
      login: john.johnson
      sshPubKey: sshPubKey_john

PLAY [Configure users] ***
...
ok: [server1] => (item=john) => 
  msg:
  - server1
  - john
  - sshPubKey_john
ok: [server1] => (item=alice) => 
  msg:
  - server1
  - alice
  - sshPubKey_alice
ok: [server1] => (item=bob) => 
  msg:
  - server1
  - bob
  - sshPubKey_bob
ok: [server3] => (item=john) => 
  msg:
  - server3
  - john
  - sshPubKey_john
ok: [server2] => (item=john) => 
  msg:
  - server2
  - john
  - sshPubKey_john
ok: [server3] => (item=bob) => 
  msg:
  - server3
  - bob
  - sshPubKey_bob
ok: [server2] => (item=alice) => 
  msg:
  - server2
  - alice
  - sshPubKey_alice

Change the tasks in the play Configure users to your needs. In a similar way, it's possible to add a next play Configure ports.

1
  • 1
    I think all the variables (what you have in vars) could also be fixed in the group_vars / all.yml file, inside the inventory folder. If we talk about centralizing data in git, then I have a good practice storing inventory data separately from ansible playbook code. This is convenient when there are many different playbooks for the same servers (and even more convenient if there is a strict separation of access rights to the code in git). – TheDESTROS Feb 13 '20 at 8:59
1

There are multiple ways to accomplish this.

Set variables in the inventory:

server1 users='["joe", "jane"]'
server2 users='["jane"]'

Set the variables in a file in the host_vars directory.

Use include_vars. Example file contents:

"users": {
  "server1": [ "joe", "jane" ],
  "server2": [ "jane" ]
}

Load it with include_vars. For your task loop (with_items) over users[ansible_hostname]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.