Iterating an ansible task using a dictionary object of variables

Recently we have been writing ansible scripts to deploy one of our applications. In doing so, we came across the need to configure 3 node processes as services. All of these node processes used a similar init script but required different values writing into the script.

We chose to use the template task thinking that with the 4 variables we needed to populate on each occasion we should be able to use the with_items method on the task to do what we wanted. We did plenty of Googling and couldn’t find a way forward and so were all set to revert to writing three separate implementations of the template task, and three associated templates.

Asking a question on the ansible IRC channel eventually yielded an answer and I thought I would share our implementation.

We start with a generic template, service_init_script.j2. The script itself is not important to this blog post, you should note that way in which the ansible ( / jinja2) variables refer to – this is important, and was our initial stumbling block. Each time the task is iterated upon the items within the with_items block are referred to as an item:

 # Provides: {{ item.service_name }}
 # Required-Start: $all
 # Required-Stop: $all
 # Default-Start: 2 3 4 5
 # Default-Stop: 0 1 6
 # Short-Description: {{ item.service_description }}
# Taken loosely from
 prgcmd={{ item.service_exec }} # What gets executed?
 prgname={{ item.service_name }} # What's the name (used to ensure only one instance is running)
 prguser={{ item.service_user }} # Which user should be used
 pidfile=/var/run/{{ item.service_name }}.pid # Where should the pid file be stored?
 logfile=/var/log/{{ item.service_name }}.log
export BJA_CONFIG={{ bja_config }}
start() {
 if [ -f $pidfile ]; then
 pid=`cat $pidfile`
 kill -0 $pid >& /dev/null
 if [ $? -eq 0 ]; then
 echo "{{ item.service_name }} has already been started."
 return 1
echo -n "Starting {{ item.service_name }}"
nohup start-stop-daemon -c $prguser -n $prgname -p $pidfile -m --exec /usr/bin/env --start $prgcmd >> $logfile 2>&1 &
if [ $? -eq 0 ]; then
 echo "."
 return 0
 echo "Failed to start {{ item.service_name }}."
 return 1
stop() {
if [ ! -f $pidfile ]; then
 echo "{{ item.service_name }} not started."
 return 1
echo -n "Stopping {{ item.service_name }}."
start-stop-daemon -p $pidfile --stop
if [ $? -ne 0 ]; then
 echo "Failed to stop {{ item.service_name }}."
 return 1
rm $pidfile
 echo "."
status() {
if [ -f $pidfile ]; then
 pid=`cat $pidfile`
 kill -0 $pid >& /dev/null
 if [ $? -eq 0 ]; then
 echo "{{ item.service_name }} running. (PID: ${pid})"
 return 0
 echo "{{ item.service_name }} might have crashed. (PID: ${pid} file remains)"
 return 1
 echo "{{ item.service_name }} not started."
 return 0
restart() {
 if [ $? -ne 0 ]; then
 return 1
sleep 2
 return $?
case "$1" in
 start | stop | status | restart)
 echo "Usage: $0 {start|stop|status|restart}"
 exit 2
exit $?

We then wrote our ansible script and call the template task thus:

- name: Create the service init scripts
template: src=service_init_script.j2 dest=/etc/init.d/{{ item.service_name }} owner=root group=root mode=755
- { service_name: paymentHandler, service_description: 'Handles payments', service_user: blackjack_attack, service_exec: "{{ application_directory }}/apps/paymentHandler.js" }
- { service_name: tablePool, service_description: 'The pool of tables available', service_user: blackjack_attack, service_exec: "{{ application_directory }}/apps/tablePool.js" }
- { service_name: userManager, service_description: 'Manages users', service_user: blackjack_attack, service_exec: "{{ application_directory }}/apps/userManager.js" }
sudo: yes

So, we are calling the same template task, which in turn refers to the same jinja2 template, passing in a collection of dictionary objects.

The key point to understand here is that when the playbook is run ansible enumerates the items specified referring to each object as ‘item’. This is the reason why we had to prefix the variable names in the template with item.*

I’ve exposed both of these pieces of code as gists here, and here.

Tagged , , , , , , , ,

5 thoughts on “Iterating an ansible task using a dictionary object of variables

  1. Boom looking into ansible myself. I’d take this in a different way to keep it more separate, you can pass parameters to files includes so I’d build a task file for CreateScript and pass on a var file for a specific service.

    pros: readability of the item array, separation of what a service comprises
    cons: abstraction layer, might be over engineering

    #######untested brain to keyboard dump

    include: tasks/create.yml item={{item}}
    – paymentHandler

    – vars:
    include: service/{{item}}.yml
    – tasks:

    name: paymentHandler
    description: …

    • danrough says:

      Thanks Goncalo. It’s interesting to see another approach.

      Instinctively, I’m inclined to agree that it might be an abstraction too far. I think that the readability is compromised a little by the layer. Given the context of the other tasks within the play it feels more appropriate to keep the information in the same place. I take your point about separation though.

      I’ll give your version a go too so that I can get my head around a little more.


  2. Guy Matz says:

    Thanks for saving my keister!!

  3. Freddy says:

    I read a lot of interesting content here.

    Probably you spend a lot of time writing, i know how to save you a lot of time, there is an online tool that creates unique,
    google friendly posts in minutes, just type in google – laranitas free content source

  4. naga says:

    thank you so much, it helped me lot

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: