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

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

Passing simple strings to an ansible playbook as extra-vars is easy, but what if you want to pass lists or dictionary? Let's see...

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
Labels in hetzner cloud are key-value pairs

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" }}'