Saturday, 23 June 2018

Ansible Loops with_items, with_nested and with_subelements example

In this article we will see the usage of Ansible Loops

Ansible Loops are used to execute the task multiple times. Consider a scenario where we want to create multiple users or want to install multiple packages. In any normal programming language, we achieve these using loops. Anisble also provide a similar feature.


Example of Ansible Loops with_items, with_nested and with_subelements example

Using Loops for multiple tasks

By passing a list, Ansible will run the task for all packages listed. This is called Standard Loops. For example

[root@server1 loops]# cat loop3.yml
---
- hosts: ubuntu
  tasks:

  - name: Create Multiple users
    shell: useradd {{ item }}
    with_items:
       - testuser1
       - testuser2

This example playbook will run the useradd command taking each from the list defined under the with_items construct. Each of the names is exposed as item, which is a default variable that Ansible creates. Ansible then assigns a package name to item, based on the iteration it is currently part of.


Once we run the above playbook,

[root@server1 loops]# ansible-playbook loop3.yml

PLAY [ubuntu] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.18.188.24]

TASK: [Create Multiple users] *************************************************
changed: [172.18.188.24] => (item=testuser1)
changed: [172.18.188.24] => (item=testuser2)

PLAY RECAP ********************************************************************

172.18.188.24              : ok=2    changed=1    unreachable=0    failed


From the above example, we can now see that both users are created by using loops


How to use with_items construct using Ansible:


Lets see one more example of using with_items construct with variable substitution.

[root@server1 loops]# cat loop6.yml
---
- hosts: ubuntu
  tasks:

  - name: Installing Packages
    yum: name={{item.name}} state={{item.value}}
    with_items:
       - {name: 'httpd', value: 'present'}

Here in the above example playbook, we have defined a task which installs packages that are defined in the with_items construct. We have defined the package name with name and value variable which are then accessed by the item element. The item does then have the name and value data which will substitute when needed. 

Lets execute the playbook:

[root@server1 loops]# ansible-playbook loop6.yml

PLAY [ubuntu] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.18.188.24]

TASK: [Installing Packages] ***************************************************
changed: [172.18.188.24] => (item={'name': 'httpd', 'value': 'present'})

PLAY RECAP ********************************************************************

172.18.188.24              : ok=2    changed=1    unreachable=0    failed=0  



Nested Loops using with_nested construct

Nested loops comes into picture where we want to perform multiple operations on the same resource. Take a simple example of usecase where we want to set different permission on a file or directory thus defining them using sub elements. As the title explains, Nested loop is a loop within a loop.

[root@server1 loops]# cat nested_loop1.yml
---
- hosts: ubuntu
  tasks:

  - name: Nested Loop / Change File Permissions
    shell: chmod -R {{ item[0] }} {{ item[1] }}
    with_nested:
       - [ '700' ]
       - [ '/tmp/test' ]

Okay, here is the simple example of nested loops. In the above usecase we have defined a task which will change the permission on a file /tmp/test with 700. The values are defined with with_nested construct and can be accessed as an array access (a[0]), while executing the above playbook.

 [root@server1 loops]# ansible-playbook nested_loop1.yml

PLAY [ubuntu] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.18.188.24]

TASK: [Nested Loop / Change File Permissions] *********************************
changed: [172.18.188.24] => (item=['700', '/tmp/test'])

PLAY RECAP ********************************************************************
172.18.188.24              : ok=2    changed=1    unreachable=0    failed=0  


Sub Nested Loops using Ansible with_subelements

Sub Nested Loops come into picture when we want to give data dynamically. uptill now, we have seen loops with static data but what if we want give data dynamically. Consider an example of creating multiple users along with different comments. To deal with such cases, we can use a dictionary to loop over sub-elements. Using such loops, we can specify a set of comments per user. Consider a sample example as,

[root@server1 loops]# cat subnested1.yml
---
- hosts: ubuntu
  vars:
    users:
     - name: testuser
       comments:
         - 'For testing purpose'

     - name: devuser
       comments:
         - 'For Dev purpose' 

  tasks:
   - name: User Creation
     shell: useradd -c "{{ item.1 }}" "{{ item.0.name }}"
     with_subelements:
         - users
         - comments

In the above playbook we defined a task which will create users along with user comments taken from 2 dictionaries users and comments. As we can see both users and comments are defined in the vars element above. Both dictionaries are defined under the with_subelements construct making Ansible to get sub elements from users and comments for running the task. Now lets execute this playbook:

[root@server1 loops]# ansible-playbook subnested1.yml

PLAY [ubuntu] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.18.188.24]

TASK: [User Creation] *********************************************************
changed: [172.18.188.24] => (item=({'name': 'testuser'}, 'For testing purpose'))
changed: [172.18.188.24] => (item=({'name': 'devuser'}, 'For Dev purpose'))

PLAY RECAP ********************************************************************
172.18.188.24              : ok=2    changed=1    unreachable=0    failed=0  


That’s all about Ansible Loops with multiple tasks, with_items, with_nested and with_subelements.