Using Loops and Items #
- Some modules enable you to provide a list that needs to be processed.
- Many modules don’t, and in these cases, it makes sense to use a loop mechanism to iterate over a list of items.
- Take, for instance, the yum module. While specifying the names of packages, you can use a list of packages.
- If, however, you want to do something similar for the service module, you find out that this is not possible.
- That is where loops come in.
Working with Loops #
Install software packages using the yum module and then ensures that services installed from these packages are started using the service module:
---
- name: install and start services
hosts: ansible1
tasks:
- name: install packages
yum:
name:
- vsftpd
- httpd
- samba
state: latest
- name: start the services
service:
name: "{{ item }}"
state: started
enabled: yes
loop:
- vsftpd
- httpd
- smb
-
A loop is defined at the same level as the service module.
-
The loop has a list of services in a list (array) statement
-
Items in the loop can be accessed by using the system internal variable item.
-
At no place in the playbook is there a definition of the variable item; the loop takes care of this.
-
When considering whether to use a loop, you should first investigate whether a module offers support for providing lists as values to the keys that are used.
-
If this is the case, just provide a list, as all items in the list can be considered in one run of the module.
-
If not, define the list using loop and provide "{{ item }}" as the value to the key.
-
When using loop, the module is activated again on each iteration.
Using Loops on Variables #
- Although it’s possible to define a loop within a task, it’s not the most elegant way.
- To create a flexible environment where static code is separated from dynamic site-specific parameters, it’s a much better idea to define loops outside the static code, in variables.
- When you define loops within a variable, all the normal rules for working with variables apply: The variables can be defined in the play header, using an include file, or as host/hostgroup variables.
Include the loop from a variable:
---
- name: install and start services
hosts: ansible1
vars:
services:
- vsftpd
- httpd
- smb
tasks:
- name: install packages
yum:
name:
- vsftpd
- httpd
- samba
state: latest
- name: start the services
service:
name: "{{ item }}"
state: started
enabled: yes
loop: "{{ services }}"
Using Loops on Multivalued Variables #
An item can be a simple list, but it can also be presented as a multivalued variable, as long as the multivalued variable is presented as a list.
Use variables that are imported from the file vars/users-list:
users:
- username: linda
homedir: /home/linda
shell: /bin/bash
groups: wheel
- username: lisa
homedir: /home/lisa
shell: /bin/bash
groups: users
- username: anna
homedir: /home/anna
shell: /bin/bash
groups: users
Use the list in a playbook:
---
- name: create users using a loop from a list
hosts: ansible1
vars_files: vars/users-list
tasks:
- name: create users
user:
name: "{{ item.username }}"
state: present
groups: "{{ item.groups }}"
shell: "{{ item.shell }}"
loop: "{{ users }}"
- Working with multivalued variables is possible, but the variables in that case must be presented as a list; using dictionaries is not supported.
- The only way to loop over dictionaries is to use the dict2items filter.
- Use of filters is not included in the RHCE topics and for that reason is not explained further here.
- You can look up “Iterating over a dictionary” in the Ansible documentation for more information.
Understanding with_items #
- Since Ansible 2.5, using loop has been the command way to iterate over the values in a list.
- In earlier versions of Ansible, the with_keyword statement was used instead.
- In this approach, the keyword is replaced with the name of an Ansible look-up plug-in, but the rest of the syntax is very common.
- Will be deprecated in a future version of Ansible.
With_keyword Options Overview with_items
- Used like loop to iterate over values in a list with_file
- Used to iterate over a list of filenames on the control node with_sequence
- Used to generate a list of values based on a numeric sequence
Loop over a list using with_keyword:
---
- name: install and start services
hosts: ansible1
vars:
services:
- vsftpd
- httpd
- smb
tasks:
- name: install packages
yum:
name:
- vsftpd
- httpd
- samba
state: latest
- name: start the services
service:
name: "{{ item }}"
state: started
enabled: yes
with_items: "{{ services }}"
Lab: Working with loop #
1. Use your editor to define a variables file with the name vars/packages and the following contents:
packages:
- name: httpd
state: absent
- name: vsftpd
state: installed
- name: mysql-server
state: latest
2. Use your editor to define a playbook with the name exercise71.yaml and create the play header:
- name: manage packages using a loop from a list
hosts: ansible1
vars_files: vars/packages
tasks:
3. Continue the playbook by adding the yum task that will manage the packages, using the packages variable as defined in the vars/packages variable include file:
- name: manage packages using a loop from a list
hosts: ansible1
vars_files: vars/packages
tasks:
- name: install packages
yum:
name: "{{ item.name }}"
state: "{{ item.state }}"
loop: "{{ packages }}"
4. Run the playbook using ansible-playbook exercise71.yaml, and observe the results. In the results you should see which packages it is trying to manage and in which state it is trying to get the packages.