Ansible - Pass extra variables to Playbook

Let's talk about passing extra Playbook variables from command line.

First off, why would you want to do it? Maybe you want to execute task only if a variable is defined, which is handy for debugs and tasks with additional processing. Or maybe you have a more generic playbook that can work with more than one version of OS, and you want your user to decide which one OS he wants to run Playbook for.

I will show in this post how we can tackle both of these problems.

Contents


Introduction to extra variables

Before we move on to solutions to our problems, an introduction to passing extra variables is in order.

Ansible syntax for passing extra variables.

We pass extra variables to Playbook using 'extra-var' argument, which can be abbreviated to 'e':

ansible-playbook extra_var_single.yml --extra-var my_var=CoolCmdLineVar

or

ansible-playbook extra_var_single.yml -e my_var=CoolCmdLineVar
Passing multiple variables.

If we want to pass multiple variables, we need to enclose our vars and their values in quotation marks:

ansible-playbook extra_var_multiple.yml --extra-var "my_var1=CoolVar1 my_var2=WarmVar2"
Passing variables containing spaces.

If our variable contains spaces, we need to enclose the entire expression in quotation marks, and additionally value of the variable needs another set of quotation marks. Use single quotation marks if you used double already, or double if single ones were used first:

ansible-playbook extra_var_single.yml -e "my_var='Very Hot Var with spaces'"
Passing integer, bool, list, or any other non-string values.

Ansible treats values of the extra variables as strings. To pass values that are not strings, we need to use JSON format.

To pass extra vars in JSON format we need to enclose JSON in quotation marks:

ansible-playbook extra_var_json.yml -e '{"my_float": 1.618, "my_list": ["HGTTG", 42], "my_string": "Time is an illusion."}'
Passing JSON formatted variables saved in a file

Variables in JSON format can be also loaded from a file, which would be especially useful if we have a large number of variables to pass. Our previous example rewritten, with variables saved to a file:

[przemek@quasar extra_var]$ cat extra_vars.json
{"my_float": 1.618, "my_list": ["HGTTG", 42], "my_string": "Time is an illusion."}

ansible-playbook extra_var_json.yml -e "@extra_vars.json"

This ends our short introduction to passing extra variables. Listings of the playbooks shown in the above examples can be found at the bottom of the page: Playbook listings for examples from Introduction

Execute task when variable is defined

Now that we know how to pass extra variables, and what options are available, it's time to move to some more meaningful examples.

Let's say we have a Playbook that configures ACL on our devices, and once in a while we want to display/save output returned from device for troubleshooting purposes.

To achieve this we will create a task in our Playbook, called 'Debugging output', that will only execute if variable 'debug' is defined. We don't really need to check the value of this variable, it suffices to check if it exists in the namespace.

Playbook:

[przemek@quasar extra_var]$ cat extra_var_debug.yml
---
- name: Working with extra variables - Conditional debug
  hosts: vEOS-01
  connection: local
  gather_facts: no

  tasks:

  - name: Configure a dummy ACL
    eos_config:
      lines:
        - 10 permit ip 10.0.1.42/32 172.16.1.0/24
        - 20 deny ip any any
      parents: ip access-list DUMMY_ACL
      before: no ip access-list DUMMY_ACL
      replace: block
      authorize: yes
    register: debug_out

  - name: Debug output from the device
    debug:
      var: debug_out
    when: debug is defined

First, we run this Playbook without passing 'debug' variable:

[przemek@quasar extra_var]$ ansible-playbook extra_var_debug.yml

PLAY [Working with extra variables - Conditional debug] **************************************************************************

TASK [Configure a dummy ACL] *****************************************************************************************************
changed: [vEOS-01]

TASK [Debug output from the device] **********************************************************************************************
skipping: [vEOS-01]

PLAY RECAP ***********************************************************************************************************************
vEOS-01                    : ok=1    changed=1    unreachable=0    failed=0

Playbook ran just fine but the debug task was skipped, as expected.

Time to pass our extra variable and see what happens:

[przemek@quasar extra_var]$ ansible-playbook extra_var_debug.yml -e "debug=True"

PLAY [Working with extra variables - Conditional debug] **************************************************************************

TASK [Configure a dummy ACL] *****************************************************************************************************
changed: [vEOS-01]

TASK [Debug output from the device] **********************************************************************************************
ok: [vEOS-01] => {
    "debug_out": {
        "changed": true,
        "commands": [
            "no ip access-list DUMMY_ACL",
            "ip access-list DUMMY_ACL",
            "10 permit ip 10.0.1.42/32 172.16.1.0/24",
            "20 deny ip any any"
        ],
        "diff": {
            "prepared": "--- system:/running-config\n+++ session:/ansible_1517781081-session-config\n@@ -44,10 +44,6 @@\n interface Management1\n    ip address 10.50.0.2/24\n !\n-ip access-list DUMMY_ACL\n-   10 permit ip host 10.0.1.42 172.16.1.0/24\n-   20 deny ip any any\n-!\n ip routing\n !\n ip multicast-routing"
        },
        "failed": false,
        "session": "ansible_1517781081",
        "updates": [
            "no ip access-list DUMMY_ACL",
            "ip access-list DUMMY_ACL",
            "10 permit ip 10.0.1.42/32 172.16.1.0/24",
            "20 deny ip any any"
        ]
    }
}

PLAY RECAP ***********************************************************************************************************************
vEOS-01                    : ok=2    changed=1    unreachable=0    failed=0

Great, now we can see exactly what commands were sent to the device and what it reported back to us.

Instead of sending this to the screen, you could save the results to a file, ideally recording a timestamp, and keep it as a part of your audit. This would allow you to see exactly what was done when the playbook ran.

Now onto the next example, selecting tasks based on the value of a variable.

Select task based on the value of passed variable

In this example we will create a playbook that executes different tasks depending on the value of the extra variable. This could be used in a generic playbook that accepts different versions of the OS, and configures various features accordingly. Also, in some cases different versions of the OS have different syntax for configuring the same feature, a very common issue facing network engineers.

In this Playbook I'm assuming that I can't dynamically establish the version of OS on the target, and I require operator to pass it in the variable 'eos_ver'.

Playbook:

[przemek@quasar extra_var]$ cat extra_var_os_version.yml
---
- name: Working with extra variables - Pass OS version
  hosts: vEOS-01
  connection: local
  gather_facts: no

  tasks:

  - name: EOS >= 4.16.7 VRF aware SNMP source interface
    set_fact:
      config_lines:
        - snmp-server vrf mgmt source-interface Management1
    when: eos_ver[2:] | float > 16.6

  - name: EOS < 4.16.7 Non-VRF aware SNMP source interface
    set_fact:
      config_lines:
        - snmp-server source-interface Management1
    when: eos_ver[2:] | float < 16.7

  - name: Configure SNMP source interface based on the EOS version
    eos_config:
      lines: "{{ config_lines }}"
      authorize: yes
    register: debug_out

  - name: Debug chosen config line
    debug:
      msg: "{{ debug_out.commands }}"
    when: debug_out.changed

Output when I pass 'eos_ver=4.17.5' to the Playbook:

[przemek@quasar extra_var]$ ansible-playbook extra_var_os_version.yml -e "eos_ver=4.17.5"

PLAY [Working with extra variables - Pass OS version] ****************************************************************************

TASK [EOS >= 4.16.7 VRF aware SNMP source interface] *****************************************************************************
ok: [vEOS-01]

TASK [EOS < 4.16.7 Non-VRF aware SNMP source interface] **************************************************************************
skipping: [vEOS-01]

TASK [Configure SNMP source interface based on the EOS version] ******************************************************************
changed: [vEOS-01]

TASK [Debug chosen config line] **************************************************************************************************
ok: [vEOS-01] => {
    "msg": [
        "snmp-server vrf mgmt source-interface Management1"
    ]
}

PLAY RECAP ***********************************************************************************************************************
vEOS-01                    : ok=3    changed=1    unreachable=0    failed=0
</pre>
Playbook correctly selected task meant for EOS versions >= 4.16.7, and skipped the other task.

Output when I pass 'eos_ver=4.16.2' to the Playbook:
<pre>
[przemek@quasar extra_var]$ ansible-playbook extra_var_os_version.yml -e "eos_ver=4.16.2"

PLAY [Working with extra variables - Pass OS version] ****************************************************************************

TASK [EOS >= 4.16.7 VRF aware SNMP source interface] *****************************************************************************
skipping: [vEOS-01]

TASK [EOS < 4.16.7 Non-VRF aware SNMP source interface] **************************************************************************
ok: [vEOS-01]

TASK [Configure SNMP source interface based on the EOS version] ******************************************************************
changed: [vEOS-01]

TASK [Debug chosen config line] **************************************************************************************************
ok: [vEOS-01] => {
    "msg": [
        "snmp-server source-interface Management1"
    ]
}

PLAY RECAP ***********************************************************************************************************************
vEOS-01                    : ok=3    changed=1    unreachable=0    failed=0

Playbook correctly selected task meant for EOS versions < 4.16.7, and skipped the other task.

This shows how simple it is to make our Playbook more generic by reacting to the value of passed extra variable. With added variable validation it is a valuable feature that can be added to our Ansible toolbox.

If you're interested in seeing Playbooks, and their outputs, used in the introduction, then follow to the next section.

Playbook listings for examples from Introduction


extra_var_single.yml

---
- name: Working with extra variables - Single var
  hosts: localhost
  connection: local
  gather_facts: no

  tasks:

  - name: Extra variable test
    debug:
     msg: "{{ my_var }}"
[przemek@quasar extra_var]$ ansible-playbook extra_var_single.yml -e my_var=CoolCmdLineVar

PLAY [Working with extra variables - Single var] *********************************************************************************

TASK [Extra variable test] *******************************************************************************************************
ok: [localhost] => {
    "msg": "CoolCmdLineVar"
}

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

[przemek@quasar extra_var]$ ansible-playbook extra_var_single.yml -e "my_var='Very Hot Var with spaces'"

PLAY [Working with extra variables - Single var] *********************************************************************************

TASK [Extra variable test] *******************************************************************************************************
ok: [localhost] => {
    "msg": "Very Hot Var with spaces"
}

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


extra_var_multiple.yml

---
- name: Working with extra variables - Multiple vars
  hosts: localhost
  connection: local
  gather_facts: no

  tasks:

  - name: Multiple extra variables test
    debug:
     msg: "Var1: {{ my_var1 }}; Var2: {{ my_var2 }}"
[przemek@quasar extra_var]$ ansible-playbook extra_var_multiple.yml --extra-var "my_var1=CoolVar1 my_var2=WarmVar2"

PLAY [Working with extra variables - Multiple vars] ******************************************************************************

TASK [Multiple extra variables test] *********************************************************************************************
ok: [localhost] => {
    "msg": "Var1: CoolVar1; Var2: WarmVar2"
}

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


extra_var_json.yml

[przemek@quasar extra_var]$ cat extra_var_json.yml
---
- name: Working with extra variables - Cmd Line JSON
  hosts: localhost
  connection: local
  gather_facts: no

  tasks:

  - name: CmdLine JSON - string
    debug:
     msg: "{{ my_string }}"

  - name: CmdLine JSON - float
    debug:
     msg: "Passed float: {{ my_float }}; Subtracted from 1.0: {{ 1.0 - my_float }}"

  - name: CmdLine JSON - list
    debug:
     msg: "{{ item }}"
    with_items: "{{ my_list }}"
[przemek@quasar extra_var]$ ansible-playbook extra_var_json.yml -e '{"my_string": "JSON with spaces", "my_float": 1.618, "my_list": ["HGTTG", 42]}'

PLAY [Working with extra variables - Cmd Line JSON] ******************************************************************************

TASK [CmdLine JSON - string] *****************************************************************************************************
ok: [localhost] => {
    "msg": "JSON with spaces"
}

TASK [CmdLine JSON - float] ******************************************************************************************************
ok: [localhost] => {
    "msg": "Passed float: 1.618; Subtracted from 1.0: -0.618"
}

TASK [CmdLine JSON - list] *******************************************************************************************************
ok: [localhost] => (item=HGTTG) => {
    "item": "HGTTG",
    "msg": "HGTTG"
}
ok: [localhost] => (item=42) => {
    "item": 42,
    "msg": 42
}

PLAY RECAP ***********************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0
[przemek@quasar extra_var]$ cat extra_vars.json
{"my_string": "JSON with spaces", "my_float": 1.618, "my_list": ["HGTTG", 42]}

[przemek@quasar extra_var]$ ansible-playbook extra_var_json.yml -e "@extra_vars.json"

PLAY [Working with extra variables - Cmd Line JSON] ******************************************************************************

TASK [CmdLine JSON - string] *****************************************************************************************************
ok: [localhost] => {
    "msg": "JSON with spaces"
}

TASK [CmdLine JSON - float] ******************************************************************************************************
ok: [localhost] => {
    "msg": "Passed float: 1.618; Subtracted from 1.0: -0.618"
}

TASK [CmdLine JSON - list] *******************************************************************************************************
ok: [localhost] => (item=HGTTG) => {
    "item": "HGTTG",
    "msg": "HGTTG"
}
ok: [localhost] => (item=42) => {
    "item": 42,
    "msg": 42
}

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

You can also get full listings of the playbooks from my GitHub repository:
https://github.com/progala/ttl255.com/tree/master/ansible/playbook-extra-variables