skip to Main Content

I want to minimize my efforts when adding content to my hosts/vars groups.

For example, I have a variable that looks like this:

blue_items:

- name: ocean
  color: blue
- name: sky
  color: blue
- name: umbrella
  color: blue

result with a debug:

root@debian:/opt/ansible# ansible -m "debug msg="{{blue_items}}"" client1 client1 | SUCCESS => {     "msg": [         {             "color": "blue",             "name": "ocean"         },         {             "color": "blue",             "name": "sky"         },         {             "color": "blue",             "name": "umbrella"         }     ] }

I tried to use a "for" parsing into a list:

my host_vars:

list_blue_items: ['ocean', 'sky', 'umbrella']

blue_items:
{% for item in list_blue_items %}

- name: "{{ item }}"
  color: blue
  {% endfor %}

result with a debug:

ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)

Syntax Error while loading YAML.
found character that cannot start any token

The error appears to be in '/opt/ansible/host_vars/client1/nginx.yml': line 4, column 2, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

blue_items:
{% for item in list_blue_items %}
^ here

I also tried adding a pipe in front of the variable like this :

list_blue_items: ['ocean', 'sky', 'umbrella']

blue_items: |
{% for item in list_blue_items %}

- name: "{{ item }}"
  color: blue
  {% endfor %}

But the result isn’t like i want, all the content is written on a single line and don’t return my items:

client1 | SUCCESS => {     "msg": "- name: "ocean"n  color: bluen- name: "sky"n  color: bluen- name: "umbrella"n  color: bluen" }

3

Answers


  1. Chosen as BEST ANSWER

    Thank you for your answer :)

    I tried your first solution but it doesn't seem to work :'(

    I have a more complex exemple:

    In my host_vars file:

    nginx__vhost:
      - name: vhost1.test.com
        file_conf: vhost1.test.com.conf
        name: option1, option2
        tls: /etc/letsencrypt/live/vhost1.test.com.key
    
      - name: vhost2.test.com
        file_conf: vhost2.test.com.conf
        name: option1, option2
        tls: /etc/letsencrypt/live/vhost2.test.com.key
    

    output:

    client1 | SUCCESS => {
        "msg": [
            {
                "file_conf": "vhost1.test.com.conf",
                "name": "option1, option2",
                "tls": "/etc/letsencrypt/live/vhost1.test.com.key"
            },
            {
                "file_conf": "vhost2.test.com.conf",
                "name": "option1, option2",
                "tls": "/etc/letsencrypt/live/vhost2.test.com.key"
            }
        ]
    }
    

    I need to manipulate all of these data like "nginx__vhost.name, nginx__vhost.tls, nginx__vhost.file_conf" and a lot of other...

    I also tried to use "{% filter from_yaml %}"

    nginx__vhost_list: ['vhost1.test.com', 'vhost2.test.com']
    
    nginx__vhost: |
      {% filter from_yaml %}
      {% for vhost in nginx__vhost_list %}
      - {name: "{{vhost}}"
        file_conf: "{{vhost}}.conf"
        name: option1, option2
        tls: "/etc/letsencrypt/live/{{vhost}}.key"}
      {% endfor %}
      {% endfilter %}
    

    but it provides me an error

    client1 | FAILED! => {
        "msg": "An unhandled exception occurred while templating '{% filter from_yaml %}n{% for vhost in nginx__vhost_list %}n- {name: "{{vhost}}"n  file_conf: "{{vhost}}.conf"n  name: option1, option2n  tls: "/etc/letsencrypt/live/{{vhost}}.key"}n{% endfor %}n{% endfilter %}n'. Error was a <class 'yaml.parser.ParserError'>, original message: while parsing a flow mappingn  in "<unicode string>", line 1, column 3:n    - {name: "vhost1.test.com"n      ^nexpected ',' or '}', but got '<scalar>'n  in "<unicode string>", line 2, column 3:n      file_conf: "vhost1.test.com.conf"n      ^"
    }
    

    I think i have to use the "from_yaml" functionnality but idk why the "{% filter from_yaml %}" doesn't work :/


  2. For example, given the data in group_vars

    shell> cat group_vars/all/colors.yml
    data:
      blue: [ocean, sky, umbrella]
      green: [grass, peas, car]
      red: [flag, apple, hat]
    
    • Use Jinja if you want the for loop
    blue_items: |
      {% filter from_yaml %}
      {% for i in data.blue %}
      - {name: {{ i }}, color: blue}
      {% endfor %}
      {% endfilter %}
    

    gives what you want

    blue_items:
      - color: blue
        name: ocean
      - color: blue
        name: sky
      - color: blue
        name: umbrella
    
    • Optionally, you can use the filter product and create a dictionary first
    blue_items: "{{ dict(data.blue|product(['blue'])) }}"
    

    gives

    blue_items:
      ocean: blue
      sky: blue
      umbrella: blue
    

    If you need the list of the dictionaries use the filter dict2items

    blue_items: "{{ dict(data.blue|product(['blue']))|
                    dict2items(key_name='name', value_name='color') }}"
    

    also gives what you want

    blue_items:
      - color: blue
        name: ocean
      - color: blue
        name: sky
      - color: blue
        name: umbrella
    

    Example of a complete project for testing

    shell> cat group_vars/all/colors.yml
    data:
      blue: [ocean, sky, umbrella]
      green: [grass, peas, car]
      red: [flag, apple, hat]
    
    blue_items: "{{ dict(data.blue|product(['blue'])) }}"
    
    blue_item2: "{{ dict(data.blue|product(['blue']))|
                    dict2items(key_name='name', value_name='color') }}"
    
    blue_item3: |
      {% filter from_yaml %}
      {% for i in data.blue %}
      - {name: {{ i }}, color: blue}
      {% endfor %}
      {% endfilter %}
    
    shell> cat pb.yml
    - hosts: all
    
      tasks:
    
        - debug:
            var: blue_items
        - debug:
            var: blue_item2
        - debug:
            var: blue_item3
    


    There are plenty of other options on how to use the data. For example, declare color_items

    color_items: "{{ dict(data[color]|product([color])) }}"
    

    and use it in the tasks

        - debug:
            var: color_items
          vars:
            color: blue
    

    gives

      color_items:
        ocean: blue
        sky: blue
        umbrella: blue
    

    Use the dictionary in a loop

        - debug:
            msg: "{{ item }}"
          loop: "{{ color_items|dict2items }}"
          vars:
            color: blue
    

    gives

    ok: [host_A] => (item={'key': 'ocean', 'value': 'blue'}) => 
      msg:
        key: ocean
        value: blue
    ok: [host_A] => (item={'key': 'sky', 'value': 'blue'}) => 
      msg:
        key: sky
        value: blue
    ok: [host_A] => (item={'key': 'umbrella', 'value': 'blue'}) => 
      msg:
        key: umbrella
        value: blue
    

    If you use item in the declaration instead of color

    color_items: "{{ dict(data[item]|product([item])) }}"
    

    you can iterate the colors

        - debug:
            msg: "{{ color_items }}"
          loop: "{{ data.keys() }}"
    

    gives

    ok: [host_A] => (item=blue) => 
      msg:
        ocean: blue
        sky: blue
        umbrella: blue
    ok: [host_A] => (item=green) => 
      msg:
        car: green
        grass: green
        peas: green
    ok: [host_A] => (item=red) => 
      msg:
        apple: red
        flag: red
        hat: red
    
    Login or Signup to reply.
  3. I finally succeeded in creating a new variable:

    nginx__vhost_list:
      - vhost1.test.com
      - vhost2.test.com
    
    nginx__vhost: |
      {% for vhost in nginx__vhost_list %}
      - name: "{{ vhost }}"
        file_conf: "{{ vhost }}.conf"
        name: option1, option2
        tls: "/etc/letsencrypt/live/{{ vhost }}.key"
      {% endfor %}
    
    nginx__vhost_final:
      "{{ nginx__vhost | from_yaml }}"
    

    Wouldn’t it be possible to keep only my "nginx__vhost" list, I don’t think it’s a good way to create a variable just to filter another one, I’d rather be able to filter directly on the 1st variable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search