How to pass lists and dicts as extra vars when running an ansible playbook

Posted on April 16, 2021 by Adrian Wyssmann ‐ 4 min read

Passing variables at runtime

As mentioned in the official docu one can pass variables at runtime using --extra-vars or -e. Values passed in using the key=value syntax are interpreted as string

$ ansible-playbook release.yml \
    --extra-vars "version=1.23.45 other_variable=foo"

The above command will define two variables version and other_variable. Now if you want to pass dicts or lists you can define it in a external json or yaml file

ansible-playbook release.yml --extra-vars "@some_file.json"

This still may be cumbersome sometime, especially the extra vars you choose may change. For [bootstrapping my servers](({ ref “2021-04-what-are-tags-in-ansible-and-how-to-use-them” })) I have a playbook which expects to pass a parameter hcloud_server_labels which should assign certain labels to the server. This expects a key-value pair i.e. a dictionary.

example fo labels in hetzner cloud

This can be done using JSON string format. The extra vars for a have to be wrapped in a single quote ', whereas the dictionary itself is wrapped with curly brackets { and }. In case you want to pass a list, you use square brackets [ and ], instead the curly brackets

Let’s make a test

This seems simple, so let’s check with this playbooks:

- name: Test passing different types of extra vars
  hosts: localhost

  tasks:
    - name: Show dictionary values
      debug:
        msg:
          - "Key1: {{ key1 }}"
          - "Key2: {{ key2 }}"
      when: key1 is defined
$ ansible-playbook extra-vars-example.yml \
    -e '{"key1":"value1","key2":"value2"}'
...

TASK [Show dictionary values] **********************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "Key1: value1",
        "Key2: value2"
    ]
}
...

That looks ok, but what if I want to assign the dictionary to a variable? Let’s modify the playbook:

- name: Test passing different types of extra vars
  hosts: localhost

  tasks:
    - name: Show dictionary values
      debug:
        msg:
          - "Key1: {{ key1 }}"
          - "Key2: {{ key2 }}"
      when: key1 is defined
    - block:
      - name: Show dictionary assigned to a variable
        debug:
          msg:
            - "{{ sample_dict | from_yaml_all }}"
            - " is of type '{{ sample_dict | type_debug }}'"
      - name: Show dictionary values
        debug:
          msg:
            - "{{ item.key }}: {{ item.value }}"
        with_dict: "{{ sample_dict }}
      when: sample_dict is defined

I want the dictionary be accessible as sample_dict so I do

$ ansible-playbook extra-vars-example.yml \
    -e sample_dict='{"key1":"value1","key2":"value2"}'
...

TASK [Show dictionary assigned to a variable] ******************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        [
            {
                "key1": "value1",
                "key2": "value2"
            }
        ],
        " is of type 'str'"
    ]
}

TASK [Show dictionary values] **********************************************************************************************************************************************************************************
ok: [localhost] => (item={'key': 'key1', 'value': 'value1'}) => {
    "msg": [
        "key1: value1"
    ]
}
ok: [localhost] => (item={'key': 'key2', 'value': 'value2'}) => {
    "msg": [
        "key2: value2"
    ]
}
PLAY RECAP *****************************************************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Well the variable sample_dict is considered a str instead a dict, however, the iteration seems to work. Still, there must be something wrong. Let’s try this

$ ansible-playbook extra-vars-example.yml \
    -e '{ "sample_dict": {"key1":"value1","key2":"value2"}}'
...

TASK [Show dictionary assigned to a variable] ******************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        {
            "key1": "value1",
            "key2": "value2"
        },
        "is of type 'dict'"
    ]
}

TASK [Show dictionary values] **********************************************************************************************************************************************************************************
ok: [localhost] => (item={'key': 'key1', 'value': 'value1'}) => {
    "msg": [
        "key1: value1"
    ]
}
ok: [localhost] => (item={'key': 'key2', 'value': 'value2'}) => {
    "msg": [
        "key2: value2"
    ]
}

PLAY RECAP *****************************************************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Cool, now the same for a list with the modified playbook

- name: Test passing different types of extra-vars
  hosts: localhost

  tasks:
    - block:
      - name: Show list assigned to a variable
        debug:
          msg:
            - "{{ sample_list | from_yaml_all }}"
            - "is of type '{{ sample_list | type_debug }}'"
      - name: Show list values
        debug:
          msg:
            - "{{ item }}"
        with_items: "{{ sample_list }}"
      when: sample_list is defined
$ ansible-playbook extra-vars-example.yml \
  -e '{ "sample_list": [ "list_value_1", "list_value_2", "list_value_3" ]}'
...

TASK [Show list assigned to a variable] ************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        [
            "list_value_1",
            "list_value_2",
            "list_value_3"
        ],
        "is of type 'list'"
    ]
}

TASK [Show list values] ****************************************************************************************************************************************************************************************
ok: [localhost] => (item=list_value_1) => {
    "msg": [
        "list_value_1"
    ]
}
ok: [localhost] => (item=list_value_2) => {
    "msg": [
        "list_value_2"
    ]
}
ok: [localhost] => (item=list_value_3) => {
    "msg": [
        "list_value_3"
    ]
}

PLAY RECAP *****************************************************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Obviously I can combine these by either using multiple -e parameter:

ansible-playbook extra-vars-example.yml \
  -e '{ "sample_dict": {"key1":"value1","key2":"value2"}}' \
  -e '{ "sample_list": [ "list_value_1", "list_value_2", "list_value_3" ]}' \
  -e 'simple_string_1=test simple_string_2="another test"'

or with a single e parameter

ansible-playbook extra-vars-example.yml \
   -e '{ "sample_dict": {"key1":"value1","key2":"value2"}, "sample_list": [ "list_value_1", "list_value_2", "list_value_3" ], "simple_string_1": "test", "simple_string_2": "another test" }'

You can find the complete playbook extra-vars-example.yml in my ansible-playground

And what about my use case?

Well, for my use case the command I call, would look like this:

ansible-playbook bootstrap-hcloud_servers.yml \
     --tags hcloud-server \
     -e server_names="dev0001" \
     -e '{ "hcloud_server_labels": {"dev":"","k8s":"master" }}'