Ansible with_dict template use

16,821

Using {{ item.value.http_port }} is exactly the right solution.

When you pass with_dict, it loops through the task passing each of the items in your containers dictionary as {{ item }}, where the item has a key and whatever values that dictionary item contains - in your case, key/value pairs where the keys are http_port and the values are those two different integers - but you can pass seriously complex nested dictionaries where it gets even more important to access things with the {{ item.value.http_port }} syntax you came up with.

The thing to be wary of as you get to more complex template usage is how to mix stuff up and set defaults and use if-statements when you have some extra variables to template for one host (or container, or whatever) but not another.

To get to grips on it, read up on Jinja2, the language Ansible interprets templates in. A good example would be something like serving files over SSL on your frontend here, but not the backend. Use syntax like {{ foo | default('bar') }} to avoid Ansible getting angry about you trying to use undefined variables, and if-statements so as to make sure you're only templating the stuff you need.

A rough sketch - say you had:

containers:
  frontend:
    http_port: 8080
    https_port: 8443
    ssl_cert: ./files/keystore
    ssl_pass: "{{ vaulted_vars.ssl_pass }}" 
  backend:
    http_port: 8081

In that case, imagining you'd had a task to copy that keystore over onto the filesystem when needed, you could use something along the lines of:

<Connector port="{{ item.value.http_port }}" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="{{ item.value.https_port | default('8443')" />
           {% if item.value.ssl_cert is defined %} 
           scheme="https" secure="true" SSLEnabled="true"
           keystoreFile="${user.home}/.keystore" keystorePass="{{ item.value.ssl_pass }}"
           clientAuth="false" sslProtocol="TLS"/>
           {% endif %}

Happy templating!

Share:
16,821
user1432403
Author by

user1432403

Updated on June 09, 2022

Comments

  • user1432403
    user1432403 almost 2 years

    I have the following task:

    - name: copy server.xml
      template: src=server.xml dest=/var/containers/{{ item.key }}/conf
      with_dict: containers
    

    And I've also added the containers dictionary in my group_vars

    containers:
      frontend:
        http_port: 8080
      backend:
        http_port: 8081
    

    Finally here is the relevant snippet from server.xml

    <Connector port="{{ http_port }}" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    

    What I want to happen is that the relevant http_port gets used in the template module. But instead I get and error:

    fatal: [localhost] => {'msg': "AnsibleUndefinedVariable: One or more undefined variables: 'http_port' is undefined", 'failed': True}

    Is this possible? How do I leverage an item's values for variable substitution?