Search code examples
ansiblejinja2

Ansible when condition in loop


I'm trying to setup an role for rolling out users. I have a list of users with their variables and I would like to only roll out authorized_key when the variable pubkey is set. Here is my code:

provisioning_user:
  - name: ansible
    state: present
    pubkeys:
      - 'ssh-rsa peter-key-1 peter@key1'
      - 'ssh-rsa peter-key-3 peter@key3'
    root: true
    # removes directorys associated with the user
    remove: true
    create_home: true
    comment: Deploy user for ansible
    # If set to true when used with home: , attempt to move the user’s old home
    # directory to the specified directory if it isn’t there already and the
    # old home exists.
    #non_unique: false
    #uid: 11
    #group:
    groups: admin, developer
    # If false, user will only be added to the groups specified in groups,
    # removing them from all other groups.
    append: yes
    password: '!'
    #ssh_public_keyfiles: ['ansible.pub', 'patrick.pub']
    #key: ssh-ed25519 AAAAC3NzetfqeafaC1lZDI1NTE5AAAAIHu28wqv0r4aqoK1obosoLCBP0vqZj8MIlkvpAbXv0LL
    #key_options:
    #key_comment:
    key_exclusive: true
    key_manage_dir: true

  - name: testuser2
    state: present
    # removes directorys associated with the user
    #remove: false
    create_home: yes
    comment: Deploy user for ansible

As you see, the second user has no attribute pubkeys. Here is my Ansible code:

- name: test key
  authorized_key:
    user: "{{ item.name }}"
    key: "{{ '\n'.join(provisioning_user|map(attribute='pubkeys')|flatten) }}"
    comment: "{{ item.key_comment | default('managed by ansible') }}"
    state: "{{ item.state | default('true') }}"
    exclusive: "{{ item.key_exclusive | default('true') }}"
    key_options: "{{ item.key_options | default(omit) }}"
    manage_dir: "{{ item.manage_dir | default('true') }}"
  loop: "{{ provisioning_user }}"
  when: item.pubkeys is defined

Thats what Ansible says:

fatal: [cloud.xxx.xxx]: FAILED! => 
  msg: |-
    The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'pubkeys'
  
    The error appears to be in '/home/xxx/gitlab.com/xxx/ansible/roles/provisioning/tasks/keys.yaml': line 2, column 3, but may
    be elsewhere in the file depending on the exact syntax problem.
  
    The offending line appears to be:
  
    ---
    - name: test key
      ^ here

Can you please help me getting the when condition work? I only want this task to run if a user has pubkeys defined, when not just skipping it.


Solution

  • This doesn't make any sense:

    key: "{{ '\n'.join(provisioning_user|map(attribute='pubkeys')|flatten) }}"
    

    You're looping over the contents of provisioning_user in this task; provisioning_user doesn't have a key pubkeys, rather, each individual item in that list may have a pubkeys value. So you'd want something like:

    key: "{{ '\n'.join(item.pubkeys) }}"
    

    Making the complete task look like:

    - name: test key
      authorized_key:
        user: "{{ item.name }}"
        key: "{{ '\n'.join(item.pubkeys) }}"
        comment: "{{ item.key_comment | default('managed by ansible') }}"
        state: "{{ item.state | default('true') }}"
        exclusive: "{{ item.key_exclusive | default('true') }}"
        key_options: "{{ item.key_options | default(omit) }}"
        manage_dir: "{{ item.manage_dir | default('true') }}"
      loop: "{{ provisioning_user }}"
      when: item.pubkeys is defined
    

    Running the above in a test environment produces:

    TASK [test key] *****************************************************************************************
    changed: [node0] => (item={'name': 'ansible', 'state': 'present', 'pubkeys': ['ssh-rsa peter-key-1 peter@key1', 'ssh-rsa peter-key-3 peter@key3'], 'root': True, 'remove': True, 'create_home': True, 'comment': 'Deploy user for ansible', 'groups': 'admin, developer', 'append': True, 'password': '!', 'key_exclusive': True, 'key_manage_dir': True})
    skipping: [node0] => (item={'name': 'testuser2', 'state': 'present', 'create_home': True, 'comment': 'Deploy user for ansible'})
    

    ...which is I think what you want.