Problem: you want to run a command in a loop within Ansible, registering the result of each run to a single variable, and then process those results depending on a condition. Specifically, you want to register the output of a command run over all the elements in a dictionary but then only process the elements where the command returned a particular result.
This appears difficult because registered variables aren’t obviously designed to work with lists and you want the task to check the results in one list variable while processing elements from another data structure.
The key is to realise that register absolutely will work with a list or dictionary and that the resulting registered variable (which contains a dictionary) will store each element of the original list alongside the task results.
In this specific context, I wanted to set a number of resource controls in
/etc/projects on a Solaris 11 client. Ansible doesn’t yet have a module
to manipulate these natively, so you need to execute the Solaris
projadd/projmod commands. Annoyingly, you can add a new project or modify
an existing one, but there is no semantic for “create this project if it
doesn’t exist but modify its attributes if it does” (hint to Oracle: add a
flag for ‘create project if not present’ to the projmod command).
Therefore, first of all we need to determine if the project(s) we want to
create already exist. In this case, I have a list of database instances
which each have a dedicated project and a number of associated attributes.
The instances are listed in a YAML dictionary keyed by the name, with specific per-instance resources (such as maximum shared memory) stored as values. E.g.:
1 2 3 4 5
Now we iterate over this dictionary to see if a project exists for each instance (naturally, the projects are named after the instances):
1 2 3 4 5 6
(Note that we need to ignore errors, since we’re expecting that at least
some of the projects may not exist.)
proj variable will contain a list of results from the command, one for
each instance (strictly, each execution). If we display it using the
debug module, we can see that each result also contains a complete copy
of the subject element from the original dictionary, including all the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Now we want to run the projadd command for each instance where the rc value was
non-zero (meaning that the project didn’t already exist, as the
command returned an error.
1 2 3 4
In this case, that means iterating over the list of results (not the original
list of instances; the results reference all the elements of the instance
dictionary anyway), so that we can test the rc value for each one. If it’s
non-zero, the command is executed. Within the command arguments, we can refer
to the instance attributes by utilising the unwieldy but correct names
item.item.key (the key of the original item from the instance dictionary
that is stored in this item of our list) and
item.item.value.valuename (a value associated with that key). (Note
that most of the resource limits are hardcoded in this example, but
there’s no reason they couldn’t be taken from the instances dictionary
with - at the expense of increased verbosity - default values as
For bonus marks, we can code a second pass to execute projmod for instances where the project already exists, to reset the resource attributes. This may mean resetting them to the same value on each run, which is an unfortunate overhead but no harm done. (As an alternative shortcut, we could execute projadd in the initial registered task and then run projmod for all the results where that failed because the project already existed.)
Note that this technique will also work on lists or any other iterative data structure. (Tested on Ansible 2.1, but ought to work on most late releases.)
- This post from the nTh among all blog first tipped me off to the structure of registered variables from loops.