Loops¶
Loops are extremely common in playbooks and tasks. It’s a great way to reuse a single task to iterate through a list of values or objects. We can use it to install multiple packages, maybe run a task on multiple objects.
Standard Loops¶
The most common loop you will likely use is the standard loop. Which we use with_items
provided to the task.
- name: Install packages
package: name={{ item }} state=present
with_items:
- httpd
- memcached
This will run the task twice once with httpd
as the package name, and once with memcached
as the package name.
We can also iterate through hashes and reference subkeys.
- name: Add user and assign groups
user:
name: "{{ item.name }}"
group: "{{ item.groups }}"
state: present
with_items:
- name: user1
groups: wheel
- name: user2
groups: root
With items can also have a non-yaml hash
- name: Add user and assign groups
user:
name: "{{ item.name }}"
group: "{{ item.groups }}"
state: present
with_items:
- { name: user1, groups: wheel }
- { name: user2, groups: root }
Nested Loops¶
Loops can also be nested:
vars:
ip_addresses:
- 10.10.20.21
- 10.10.20.22
- 10.10.20.23
ports: ['80','443']
tasks:
- name: give multiple users access to multiple databases
mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "password"
with_nested:
- [ '', 'user2' ]
- [ 'database1', 'database2', 'database3' ]
You can also use a variable with a list.
vars:
users:
- user1
- user2
databases:
- database1
- database2
- database3
tasks:
- name: give multiple users access to multiple databases
mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "password"
with_nested:
- {{ users }}
- {{ databases }}
This could help you reuse the same user list and database list for other tasks too.
Looping with Hashes¶
Lets use a hash of a few datapoints.
vars:
pools:
pool1:
servers:
- { "ip": { "addr": "10.90.130.13", "type": "V4" }}
- { "ip": { "addr": "10.90.130.15", "type": "V4" }}
pool2:
servers:
- { "ip": { "addr": "10.90.132.13", "type": "V4" }}
- { "ip": { "addr": "10.90.132.15", "type": "V4" }}
tasks:
- name: Create the pools
avi_pool:
controller: 10.10.27.90
username: admin
password: AviNetworks123!
tenant: admin
name: "{{ item.key }}"
state: present
enabled: false
health_monitor_refs:
- '/api/healthmonitor?name=System-HTTP'
servers: "{{ item.value.servers }}"
with_dict:
"{{ pools }}"
This would iterate through our list of pools, and with their different servers create 2 new pools via the avi_pool
module.
Looping with Files¶
We can also loop over files for example printing content of a file we have locally. Files are either absolute location or relative.
- tasks:
- debug: msg={{ item }}
with_file:
- file01
- file02
This will print out both file01 and file02 to our screen.
Looping with Fileglobs¶
Using a fileglob we can output all files in a directory that match a pattern, non-recursively.
- tasks:
- name: ensure remote directory exists
file: dest="/opt/mydirectory" state=directory
- name: deploy my files
copy: src="{{ item }}" dest="/opt/mydirectory/"
with_fileglob:
- "mydirectory/*"
Note
When using a relative path with with_fileglob
in a role, Ansible resolves the path relative to the roles/<rolename>/files directory.
Looping with Subelements¶
Lets take an example where we need to setup a set of servers before deploying Avi Controllers to them. We need to create users, and allow their SSH keys to be used. We will define a set a vars and then use those to create our users with associated keys.
users:
- name: user1
ssh_pub_key:
- /tmp/user1/ssh/user1.pub
- /tmp/user1/ssh/otherkey.pub
- name: user2
ssh_pub_key:
- /tmp/user2/ssh/user2.pub
- name: Create User
user:
name: "{{ item.name }}"
state: present
generate_ssh_key: yes
with_items:
- "{{ users }}"
- name: Set authorized ssh key
authorized_key:
user: "{{ item.0.name }}"
key: "{{ lookup('file', item.1) }}"
with_subelements:
- "{{ users }}"
- ssh_pub_key
Registered Variables with Loops¶
When using register
with a loop the results will contain a list of the responses. This can be very useful when registering host creation and getting the servers from the array from the task.
tasks:
- name: Provision a set of instances
ec2:
aws_access_key: "{{ec2_access_key}}"
aws_secret_key: "{{ec2_secret_key}}"
key_name: aws_key
group: server
instance_type: t2.micro
image: ami-123456
wait: true
exact_count: 1
count_tag:
Name: "{{ item }}"
instance_tags:
Name: "{{ item }}"
register: ec2
with_items:
- server1
- server2
- server3
This would then return an array of results, which each would contain IP addresses and other data from the AWS api call. We can then use the returned data to add servers to a group, create an array to submit to a controller as new pool servers as well.
# Value returned from Ansible
Looping over Inventory¶
If you want to loop over a group of inventory hosts or a subset you can use with_items
and the variable play_hosts
or using the group
variable. Play hosts would use the hosts assigned to the current play in the playbook. Group variable usage is groups['groupname']
and would use all of the hosts in the declared group.
This example would give us a list of all the hosts in the current play.
- debug: msg={{ item }}
with_items:
- "{{ play_hosts }}"
This example would give us a list of all the hosts in the group we specified in this case servers
.
- debug: msg={{ item }}
with_items:
- "{{ groups['servers']}}"