Alexandre Soares
Alexandre Soares

Reputation: 37

Iterate over hash with lists

I would like to setup several ssh public keys for users, today I have something that can already setup only one key:

users:
  user1:
    comment: "User 1"
    sshkey: "ssh-rsa ******** user1"
    state: present

  user2:
    comment: "User 2"
    sshkey: "ssh-rsa ******** user2"
    state: present

  user3:
    comment: "User 3"
    sshkey: "ssh-rsa ******** user3"
    state: present

and my play is:

- name: set authorized_keys
  ansible.posix.authorized_key:
    user:  "{{ item.key }}"
    state: "{{ item.value.state }}"
    key: "{{ item.value.sshkey }}"
  with_dict: "{{ users }}"
  when: item.value.state == "present" and item.value.sshkey is defined

I would like to be able in my users var to pass several keys for one user, something like:

users:
  user1:
    comment: "User 1"
    sshkeys:
      - ssh-rsa ******** user1.key
    state: present

  user2:
    comment: "User 2"
    sshkeys:
      - ssh-rsa ******** user2.key-a
      - ssh-rsa ******** user2.key-b
    state: present

  user3:
    comment: "User 3"
    sshkeys:
      - ssh-rsa ******** user2.key-a
      - ssh-rsa ******** user2.key-b
      - ssh-rsa ******** user3.key-c
    state: present

But I don't have an idea how I should iterate over this sshkeys list

Upvotes: 2

Views: 740

Answers (1)

Vladimir Botka
Vladimir Botka

Reputation: 68144

In the example, you test the existence of the attribute sshkeys. I assume this is because this attribute might be missing in the dictionary. Let's remove this attribute from user3 for testing

    users:
      user1:
        comment: User 1
        sshkeys:
          - ssh-rsa ******** user1.key
        state: present
      user2:
        comment: User 2
        sshkeys:
          - ssh-rsa ******** user2.key-a
          - ssh-rsa ******** user2.key-b
        state: present
      user3:
        comment: User 3
        state: present

Q: "How should I iterate over this sshkeys list?"

A1: It's not necessary to iterate the list of sshkeys. It's possible to configure them all in one step. Quoting from exclusive:

Multiple keys can be specified in a single key string value by separating them by newlines.

For example

    - debug:
        msg: |-
          user: {{ item.key }}
          state: {{ item.value.state }}
          key: {{ item.value.sshkeys|join('\n') }}
      loop: "{{ users|dict2items }}"
      when:
        - item.value.state == 'present'
        - item.value.sshkeys|d([])|length > 0

gives (abridged)

  msg: |-
    user: user1
    state: present
    key: ssh-rsa ******** user1.key
  msg: |-
    user: user2
    state: present
    key: ssh-rsa ******** user2.key-a\nssh-rsa ******** user2.key-b

If you want to iterate the keys, proceed to the next option.


A2: As a first step add the attribute sshkeys if missing

    - set_fact:
        users: "{{ dict(_keys|zip(_vals_update)) }}"
      vars:
        _keys: "{{ users.keys()|list }}"
        _vals: "{{ users.values()|list }}"
        _vals_update: "{{ [{'sshkeys': []}]|
                          product(_vals)|
                          map('combine')|list }}"

gives

  users:
    user1:
      comment: User 1
      sshkeys:
      - ssh-rsa ******** user1.key
      state: present
    user2:
      comment: User 2
      sshkeys:
      - ssh-rsa ******** user2.key-a
      - ssh-rsa ******** user2.key-b
      state: present
    user3:
      comment: User 3
      sshkeys: []
      state: present

Now, iterate the dictionary with subelements

    - debug:
        msg: >-
          user: {{ item.0.key }}
          state: {{ item.0.value.state }}
          key: {{ item.1 }}
      with_subelements:
        - "{{ users|dict2items }}"
        - value.sshkeys
      when: item.0.value.state == 'present'

gives (abridged)

  msg: 'user: user1 state: present key: ssh-rsa ******** user1.key'
  msg: 'user: user2 state: present key: ssh-rsa ******** user2.key-a'
  msg: 'user: user2 state: present key: ssh-rsa ******** user2.key-b'

Upvotes: 2

Related Questions