Search

Top 60 Oracle Blogs

Recent comments

Ansible tips'n'tricks: defining –extra-vars as JSON

While I’m continuing to learn more about Ansible I noticed a nifty little thing I wanted to share: it is possible to specify –extra-vars for an Ansible playbook in a JSON document in addition to the space-separated list of key=value pairs I have used so often. This can come in handy if you have many parameters in your play and want to test changing them without having to modify your defaults stored in group_vars/*.yml or wherever else you stored them. If you do change your global variables, you can almost be certain that your version control system notifies you about a change in the file and it wants to commit it next time. This might not be exactly what you had in mind.

For later reference, this article was composed using Ubuntu 18.04.4 LTS with all updates up to February 3rd, 2020.

The documentation reference for this article can be found in Docs – User Guide – Working With Playbooks – Using Variables. It links to the “latest” Ansible version though, so you might have to go to your specific Ansible version’s documentation in case stuff changes.

The Playbook

Let’s assume the following, simple playbook:

---
- hosts: localhost
  connection: local
  vars:
    var1: "var1 set in playbook"
    var2: "var2 set in playbook"
    var3: "var3 set in playbook"
    var4: "var4 set in playbook"
    var5: "var5 set in playbook"
    var6: "var6 set in playbook"
    var7: "var7 set in playbook"
    var8: "var8 set in playbook"
    var9: "var9 set in playbook"

  tasks:

  - name: print var1
    debug: var=var1

  - name: print var2
    debug: var=var2

  - name: print var3
    debug: var=var3

  - name: print var4
    debug: var=var4

  - name: print var5
    debug: var=var5

  - name: print var6
    debug: var=var6

  - name: print var7
    debug: var=var7

  - name: print var8
    debug: var=var8

  - name: print var9
    debug: var=var9

I appreciate it isn’t the usual fancy code, spread out nicely into roles and group_vars, but it keeps the discussion simple enough and using a more elaborate coding structure wouldn’t change the end result, so please bear with me…

As you can see I defined 9 variables in the playbook, and each of them is assigned a value. Executing the playbook, the output is what would be expected:

$ ansible-playbook -i 127.0.0.1, -v main.yml
Using /etc/ansible/ansible.cfg as config file

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

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

TASK [print var1] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var1": "var1 set in playbook"
}

TASK [print var2] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var2": "var2 set in playbook"
}

TASK [print var3] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var3": "var3 set in playbook"
}

TASK [print var4] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var4": "var4 set in playbook"
}

TASK [print var5] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var5": "var5 set in playbook"
}

TASK [print var6] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var6": "var6 set in playbook"
}

TASK [print var7] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var7": "var7 set in playbook"
}

TASK [print var8] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var8": "var8 set in playbook"
}

TASK [print var9] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var9": "var9 set in playbook"
}

PLAY RECAP **********************************************************************************************************************************
127.0.0.1                  : ok=10   changed=0    unreachable=0    failed=0  

Not really a surprise.

Overriding variables on the command line

Now let’s assume I want to override var8 and var9 with a custom parameter, and without changing the code. Pretty straight forward, since ansible-playbook allows us to do so.

$ ansible-playbook --help 2>&1 | grep -i extra-vars
  -e EXTRA_VARS, --extra-vars=EXTRA_VARS

With that in mind, let’s change var8 and var9:

$ ansible-playbook -i 127.0.0.1, -v main.yml --extra-vars "var8='var8 set on the command line' var9='var9 set on the command line'"
Using /etc/ansible/ansible.cfg as config file

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

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

TASK [print var1] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var1": "var1 set in playbook"
}

TASK [print var2] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var2": "var2 set in playbook"
}

TASK [print var3] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var3": "var3 set in playbook"
}

TASK [print var4] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var4": "var4 set in playbook"
}

TASK [print var5] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var5": "var5 set in playbook"
}

TASK [print var6] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var6": "var6 set in playbook"
}

TASK [print var7] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var7": "var7 set in playbook"
}

TASK [print var8] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var8": "var8 set on the command line"
}

TASK [print var9] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var9": "var9 set on the command line"
}

PLAY RECAP **********************************************************************************************************************************
127.0.0.1                  : ok=10   changed=0    unreachable=0    failed=0   

Great! But you can probably spot where this is heading … changing lots of parameters on the command line is a real pain and you are almost guaranteed to introduce a typo on the way.

Passing parameters as a JSON file

As per the documentation reference I mentioned earlier, I can use a JSON document to set all parameters. For this example, I’ll use the following file:

$ cat parameters.json 
{
    "var1" : "var1 as defined in the JSON parameter file",
    "var2" : "var2 as defined in the JSON parameter file",
    "var3" : "var3 as defined in the JSON parameter file",
    "var4" : "var4 as defined in the JSON parameter file",
    "var5" : "var5 as defined in the JSON parameter file",
    "var6" : "var6 as defined in the JSON parameter file",
    "var7" : "var7 as defined in the JSON parameter file",
    "var8" : "var8 as defined in the JSON parameter file",
    "var9" : "var9 as defined in the JSON parameter file"
} 

With the file in place, I can easily reference it as shown in the next example:

$ ansible-playbook -i 127.0.0.1, -v main.yml --extra-vars "@parameters.json"

You include the JSON file containing all your parameters using the same command line argument, eg –extra-vars. Note however that this time you use the “at” sign followed immediately by the filename.

The result is exactly what I wanted:

$ ansible-playbook -i 127.0.0.1, -v main.yml --extra-vars "@parameters.json"
Using /etc/ansible/ansible.cfg as config file

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

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

TASK [print var1] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var1": "var1 as defined in the JSON parameter file"
}

TASK [print var2] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var2": "var2 as defined in the JSON parameter file"
}

TASK [print var3] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var3": "var3 as defined in the JSON parameter file"
}

TASK [print var4] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var4": "var4 as defined in the JSON parameter file"
}

TASK [print var5] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var5": "var5 as defined in the JSON parameter file"
}

TASK [print var6] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var6": "var6 as defined in the JSON parameter file"
}

TASK [print var7] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var7": "var7 as defined in the JSON parameter file"
}

TASK [print var8] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var8": "var8 as defined in the JSON parameter file"
}

TASK [print var9] ***************************************************************************************************************************
ok: [127.0.0.1] => {
    "var9": "var9 as defined in the JSON parameter file"
}

PLAY RECAP **********************************************************************************************************************************
127.0.0.1                  : ok=10   changed=0    unreachable=0    failed=0 

Summary

Using a JSON file to pass parameters to an Ansible playbook is a great way of testing (parameter-driven) changes to your code without having to worry about your version control system wanting to check in changes to your variable definitions. I tend to stick with defaults in my playbooks, and use –extra-vars to deviate from them if needed and to deal with edge cases.

The extra benefit to me is that I can add the JSON file containing all the parameters to .gitignore as well.

Happy automating!