Search

Top 60 Oracle Blogs

Recent comments

Passing complex data types to Ansible on the command line

Earlier this year I wrote a post about passing JSON files as --extra-vars to ansible-playbook in order to simplify deployments and to make them more flexible. JSON syntax must be used to pass more complex data types to Ansible playbooks, the topic of this post. Unlike last time though I’ll pass the arguments directly to the playbook rather than by means of a JSON file. This should cover both methods of passing extra variables.

A lot of what you are about to read depends on Ansible configuration settings. I have used Ansible on Debian 10. When I installed it earlier today I found it to be version 2.7.7. It’s the distribution’s (stock) Ansible version:

vagrant@debian10:~$ lsb_release -a
No LSB modules are available.
Distributor ID:    Debian
Description:       Debian GNU/Linux 10 (buster)
Release:           10
Codename:          buster
vagrant@debian10:~$ ansible-playbook --version
ansible-playbook 2.7.7
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/vagrant/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible-playbook
  python version = 3.7.3 (default, Dec 20 2019, 18:57:59) [GCC 8.3.0]
vagrant@debian10:~$  

The only change I consciously made was to set the output to debug:

vagrant@debian10:~$ export ANSIBLE_STDOUT_CALLBACK=debug

However, as with all information you find on the Internet – this post explicitly included – your mileage may vary. Don’t blindly copy/paste. Test everything you deem useful on an unimportant, disposable, lower-tier test system and make sure you understand any code before you even think about using it! Vagrant is a pretty good tool for this purpose by the way.

Having said that, let’s go over some examples.

Dictionaries

Let’s assume you’d like to use a dictionary in your playbook, like so:

 ---
- hosts: localhost
  connection: local
  vars:
    dictExample:
      propertyA: propertyA-key
      propertyB: propertyB-key

  tasks:
  - name: dump dictExample
    debug:
      var: dictExample 

Unsurprisingly, when invoking the playbook, the output matches the code exactly:

$ ansible-playbook -i localhost, dict-example.yml 

PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [dump dictExample] ********************************************************
ok: [localhost] => {
    "dictExample": {
        "propertyA": "propertyA-key",
        "propertyB": "propertyB-key"
    }
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0 

Overriding dictExample on the command line requires the use of JSON, while paying attention to shell expansion at the same time. Here is an example:

$ ansible-playbook -i localhost, dict-example.yml --extra-vars "{
>     "dictExample": {
>       "propertyA": "'property A set on the command line'",
>       "propertyB": "'property B set on the command line'"
>     }
> }"
Using /etc/ansible/ansible.cfg as config file

PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [dump dictExample] ********************************************************
ok: [localhost] => {
    "dictExample": {
        "propertyA": "property A set on the command line",
        "propertyB": "property B set on the command line"
    }
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0 

As per the Ansible documentation, variables passed as extra-vars take precedence over those defined in the playbook.

Lists

Similarly it is possible to pass lists to playbooks. Here is an example:

---
- hosts: localhost
  connection: local
  vars:
    listExample:
    - one
    - two
    - three

  tasks:
  - name: dump listExample
    debug:
      var: listExample 

Invoking it with the defaults yields the expected result:

$ ansible-playbook -i localhost, list-example.yml 

PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [dump listExample] ********************************************************
ok: [localhost] => {
    "listExample": [
        "one",
        "two",
        "three"
    ]
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0 

You can override listExample as shown here:

$ ansible-playbook -i localhost, list-example.yml --extra-vars "{
>     "listExample": [
>         "'commandline one'",
>         "'commandline two'",
>         "'commandline three'"
>     ]
> }"

PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [dump listExample] ********************************************************
ok: [localhost] => {
    "listExample": [
        "commandline one",
        "commandline two",
        "commandline three"
    ]
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0 

Combinations

If you worked with perl, you probably used a Hash Of Hashes (HoH) as it’s a very powerful data structure. Something similar is also possible in Ansible. Here is the example playbook:

---
- hosts: localhost
  connection: local
  vars:
    complexExample:
      propertyA:
      - a_one
      - a_two
      - a_three
      propertyB:
      - b_one
      - b_two
      - b_three

  tasks:
  - name: dump complexExample
    debug:
      var: complexExample 

By now you are probably tired of seeing the result of the call to the playbook, so I’ll skip that and move on to an example where I’m overriding the variable:

$ ansible-playbook -i localhost, complex-example.yml --extra-vars "{
    "complexExample": {
        "propertyA": [
            "a_one_changed",
            "a_two_changed",
            "a_three_changed"
        ],
        "propertyB": [
            "b_one_changed",
            "b_two_changed",
            "b_three_changed"
        ]
    }
}"

PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [dump complexExample] *****************************************************
ok: [localhost] => {
    "complexExample": {
        "propertyA": [
            "a_one_changed",
            "a_two_changed",
            "a_three_changed"
        ],
        "propertyB": [
            "b_one_changed",
            "b_two_changed",
            "b_three_changed"
        ]
    }
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0 

Summary

Passing variables to Ansible playbooks is a powerful way of working with automation. Apart from simple variables you could read about in the previous post on the topic, you can pass dictionaries, lists and combinations thereof using JSON notation. Happy automating!