Search code examples
pythonansiblejinja2ciscoasa

Ansible + Jinja2 Loop - Dict object has no attribute


I'm losing my mind here but i just cant seem to see where my problem lies! Im trying to have ansible create me users on a Cisco ASA and im using Jinja 2 templates.

I have a host vars file where i am specifying the users (sensitised data):

users:
  tom:
    sshkey: "xxxx"
    privilegelevel: 15
  dick:
    sshkey: "xxxx"
    privilegelevel: 15
  harry:
    password: "yyyy"
    privilegelevel: 15

My templates for creating the users (users.j2):

{% if users is defined %}
{% for user, value in users.items() %}
username {{ value }} privilege {{ value.privilegelevel }}
{% endfor %}
{% endif %}

and if they have an ssh key (users-ssh.j2):

{% if users and 'sshkey' is defined %}
{% for user, value in users.items() %}
username {{ value }} privilege {{ value.privilegelevel }}
username {{ value }} attributes
  ssh authentication publickey {{ value.sshkey }}
{% endfor %}
{% endif %}

And last but not least the playbook:

- name: create users
  asa_config:
    src: templates/users.j2
    provider: "{{ cli }}"
    save: yes
  tags: users

- name: create ssh pubkey auth
  asa_config:
    src: templates/users-ssh.j2
    provider: "{{ cli }}"
    save: yes
  tags: users

When running the playbook, the user.j2 works fine, but user-ssh.j2 fails with:

fatal: [HOSTNAME_HERE]: FAILED! => {"changed": false, "msg": "'dict object' has no attribute 'sshkey'"}

When calling the dict values with:

- name: Output dict
  debug:
    msg: "User is {{ item.key }} and the ssh key is {{ item.value.sshkey }}"
  loop: "{{ lookup('dict', users) }}"
  tags: users

It gives me the correct values:

ok: [fw01.prd.sc1] => (item={'key': 'tom', 'value': {'sshkey': 'xxxx', 'privilegelevel': 15}}) => {
    "msg": "User is tom and the ssh key is xxxx"
}
ok: [fw01.prd.sc1] => (item={'key': 'dick', 'value': {'sshkey': 'xxxx', 'privilegelevel': 15}}) => {
    "msg": "User is dick and the ssh key is xxxx"
}

Can anyone see where i may be going wrong with my J2 templating as ive a fair few things but getting nowhere!

Many thanks in advance :)

Kris


Solution

  • This loop...

    {% if users and 'sshkey' is defined %}
    {% for user, value in users.items() %}
    username {{ value }} privilege {{ value.privilegelevel }}
    username {{ value }} attributes
      ssh authentication publickey {{ value.sshkey }}
    {% endfor %}
    {% endif %}
    

    ...doesn't check if a user has an sshkey attribute. The expression if users and 'sshkey' is defined is identical to writing if users, because the expressionif 'sshkey' is defined will always be true -- 'sshkey' is a literal string, not a variable.

    Since the loop runs for all users, it fails when you get to harry, because harry doesn't have an sshkey attribute.

    You need to filter your users for those that have an sshkey attribute, for example by using a loop filtering as described in the documentation:

    {% for user, value in users.items() if value.sshkey is defined %}
    username {{ user }} privilege {{ value.privilegelevel }}
    username {{ user }} attributes
      ssh authentication publickey {{ value.sshkey }}
    {% endfor %}
    

    Note that I've also replaced username {{ value }} with username {{ user }}, because I'm reasonably sure the former was not what you meant.


    For example, this playbook:

    - hosts: localhost
      gather_facts: false
      vars:
        users:
          tom:
            sshkey: "xxxx"
            privilegelevel: 15
          dick:
            sshkey: "xxxx"
            privilegelevel: 15
          harry:
            password: "yyyy"
            privilegelevel: 15
    
      tasks:
        - copy:
            dest: users.txt
            content: |
              {% for user, value in users.items() if value.sshkey is defined %}
              username {{ user }} privilege {{ value.privilegelevel }}
              username {{ user }} attributes
                ssh authentication publickey {{ value.sshkey }}
              {% endfor %}
    

    Produces as output in users.txt:

    username tom privilege 15
    username tom attributes
      ssh authentication publickey xxxx
    username dick privilege 15
    username dick attributes
      ssh authentication publickey xxxx