Standardizing syntax for 'Playbook Loops' documentation (#20863)

pull/19669/merge
Aric Renzo 8 years ago committed by scottb
parent f0491b4261
commit 96538ec158

@ -16,7 +16,10 @@ Standard Loops
To save some typing, repeated tasks can be written in short-hand like so:: To save some typing, repeated tasks can be written in short-hand like so::
- name: add several users - name: add several users
user: name={{ item }} state=present groups=wheel user:
name: "{{ item }}"
state: present
groups: "wheel"
with_items: with_items:
- testuser1 - testuser1
- testuser2 - testuser2
@ -28,9 +31,15 @@ If you have defined a YAML list in a variables file, or the 'vars' section, you
The above would be the equivalent of:: The above would be the equivalent of::
- name: add user testuser1 - name: add user testuser1
user: name=testuser1 state=present groups=wheel user:
name: "testuser1"
state: present
groups: "wheel"
- name: add user testuser2 - name: add user testuser2
user: name=testuser2 state=present groups=wheel user:
name: "testuser2"
state: present
groups: "wheel"
The yum and apt modules use with_items to execute fewer package manager transactions. The yum and apt modules use with_items to execute fewer package manager transactions.
@ -38,7 +47,10 @@ Note that the types of items you iterate over with 'with_items' do not have to b
If you have a list of hashes, you can reference subkeys using things like:: If you have a list of hashes, you can reference subkeys using things like::
- name: add several users - name: add several users
user: name={{ item.name }} state=present groups={{ item.groups }} user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
with_items: with_items:
- { name: 'testuser1', groups: 'wheel' } - { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' } - { name: 'testuser2', groups: 'root' }
@ -55,7 +67,11 @@ Nested Loops
Loops can be nested as well:: Loops can be nested as well::
- name: give users access to multiple databases - name: give users access to multiple databases
mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "foo"
with_nested: with_nested:
- [ 'alice', 'bob' ] - [ 'alice', 'bob' ]
- [ 'clientdb', 'employeedb', 'providerdb' ] - [ 'clientdb', 'employeedb', 'providerdb' ]
@ -63,7 +79,11 @@ Loops can be nested as well::
As with the case of 'with_items' above, you can use previously defined variables.:: As with the case of 'with_items' above, you can use previously defined variables.::
- name: here, 'users' contains the above list of employees - name: here, 'users' contains the above list of employees
mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "foo"
with_nested: with_nested:
- "{{ users }}" - "{{ users }}"
- [ 'clientdb', 'employeedb', 'providerdb' ] - [ 'clientdb', 'employeedb', 'providerdb' ]
@ -90,8 +110,10 @@ And you want to print every user's name and phone number. You can loop through
tasks: tasks:
- name: Print phone records - name: Print phone records
debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})" debug:
with_dict: "{{ users }}" msg: "User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict:
- "{{ users }}"
.. _looping_over_fileglobs: .. _looping_over_fileglobs:
@ -138,12 +160,20 @@ Looping over Fileglobs
tasks: tasks:
# first ensure our target directory exists # first ensure our target directory exists
- file: dest=/etc/fooapp state=directory - name: Ensure target directory exists
file:
dest: "/etc/fooapp"
state: directory
# copy each file over that matches the given pattern # copy each file over that matches the given pattern
- copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600 - name: Copy each file over that matches the given pattern
copy:
src: "{{ item }}"
dest: "/etc/fooapp/"
owner: "root"
mode: 600
with_fileglob: with_fileglob:
- /playbooks/files/fooapp/* - "/playbooks/files/fooapp/*"
.. note:: When using a relative path with ``with_fileglob`` in a role, Ansible resolves the path relative to the `roles/<rolename>/files` directory. .. note:: When using a relative path with ``with_fileglob`` in a role, Ansible resolves the path relative to the `roles/<rolename>/files` directory.
@ -161,7 +191,8 @@ Suppose you have the following variable data was loaded in via somewhere::
And you want the set of '(a, 1)' and '(b, 2)' and so on. Use 'with_together' to get this:: And you want the set of '(a, 1)' and '(b, 2)' and so on. Use 'with_together' to get this::
tasks: tasks:
- debug: msg="{{ item.0 }} and {{ item.1 }}" - debug:
msg: "{{ item.0 }} and {{ item.1 }}"
with_together: with_together:
- "{{ alpha }}" - "{{ alpha }}"
- "{{ numbers }}" - "{{ numbers }}"
@ -203,10 +234,18 @@ How might that be accomplished? Let's assume you had the following defined and
It might happen like so:: It might happen like so::
- user: name={{ item.name }} state=present generate_ssh_key=yes - name: Create User
with_items: "{{ users }}" user:
name: "{{ item.name }}"
state: present
generate_ssh_key: yes
with_items:
- "{{ users }}"
- authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'" - name: Set authorized ssh key
authorized_key:
user: "{{ item.0.name }}"
key: "{{ lookup('file', item.1) }}"
with_subelements: with_subelements:
- "{{ users }}" - "{{ users }}"
- authorized - authorized
@ -214,10 +253,14 @@ It might happen like so::
Given the mysql hosts and privs subkey lists, you can also iterate over a list in a nested subkey:: Given the mysql hosts and privs subkey lists, you can also iterate over a list in a nested subkey::
- name: Setup MySQL users - name: Setup MySQL users
mysql_user: name={{ item.0.name }} password={{ item.0.mysql.password }} host={{ item.1 }} priv={{ item.0.mysql.privs | join('/') }} mysql_user:
name: "{{ item.0.name }}"
password: "{{ item.0.mysql.password }}"
host: "{{ item.1 }}"
priv: "{{ item.0.mysql.privs | join('/') }}"
with_subelements: with_subelements:
- "{{ users }}" - "{{ users }}"
- mysql.hosts - "{{ mysql.hosts }}"
Subelements walks a list of hashes (aka dictionaries) and then traverses a list with a given (nested sub-)key inside of those Subelements walks a list of hashes (aka dictionaries) and then traverses a list with a given (nested sub-)key inside of those
records. records.
@ -249,21 +292,39 @@ Negative numbers are not supported. This works as follows::
tasks: tasks:
# create groups # create groups
- group: name=evens state=present - group:
- group: name=odds state=present name: "evens"
state: present
- group:
name: "odds"
state: present
# create some test users # create some test users
- user: name={{ item }} state=present groups=evens - user:
with_sequence: start=0 end=32 format=testuser%02x name: "{{ item }}"
state: present
groups: "evens"
with_sequence:
- start: 0
- end: 32
- format: testuser%02x
# create a series of directories with even numbers for some reason # create a series of directories with even numbers for some reason
- file: dest=/var/stuff/{{ item }} state=directory - file:
with_sequence: start=4 end=16 stride=2 dest: "/var/stuff/{{ item }}"
state: directory
with_sequence:
- start: 4
- end: 16
- stride: 2
# a simpler way to use the sequence plugin # a simpler way to use the sequence plugin
# create 4 groups # create 4 groups
- group: name=group{{ item }} state=present - group:
with_sequence: count=4 name: "group{{ item }}"
state: present
with_sequence:
count: 4
.. _random_choice: .. _random_choice:
@ -273,7 +334,8 @@ Random Choices
The 'random_choice' feature can be used to pick something at random. While it's not a load balancer (there are modules The 'random_choice' feature can be used to pick something at random. While it's not a load balancer (there are modules
for those), it can somewhat be used as a poor man's load balancer in a MacGyver like situation:: for those), it can somewhat be used as a poor man's load balancer in a MacGyver like situation::
- debug: msg={{ item }} - debug:
msg: "{{ item }}"
with_random_choice: with_random_choice:
- "go through the door" - "go through the door"
- "drink from the goblet" - "drink from the goblet"
@ -293,7 +355,8 @@ Do-Until Loops
Sometimes you would want to retry a task until a certain condition is met. Here's an example:: Sometimes you would want to retry a task until a certain condition is met. Here's an example::
- action: shell /usr/bin/foo - action:
shell /usr/bin/foo
register: result register: result
until: result.stdout.find("all systems go") != -1 until: result.stdout.find("all systems go") != -1
retries: 5 retries: 5
@ -316,7 +379,9 @@ This isn't exactly a loop, but it's close. What if you want to use a reference
that matches a given criteria, and some of the filenames are determined by variable names? Yes, you can do that as follows:: that matches a given criteria, and some of the filenames are determined by variable names? Yes, you can do that as follows::
- name: INTERFACES | Create Ansible header for /etc/network/interfaces - name: INTERFACES | Create Ansible header for /etc/network/interfaces
template: src={{ item }} dest=/etc/foo.conf template:
src: "{{ item }}"
dest: "/etc/foo.conf"
with_first_found: with_first_found:
- "{{ ansible_virtualization_type }}_foo.conf" - "{{ ansible_virtualization_type }}_foo.conf"
- "default_foo.conf" - "default_foo.conf"
@ -324,7 +389,12 @@ that matches a given criteria, and some of the filenames are determined by varia
This tool also has a long form version that allows for configurable search paths. Here's an example:: This tool also has a long form version that allows for configurable search paths. Here's an example::
- name: some configuration template - name: some configuration template
template: src={{ item }} dest=/etc/file.cfg mode=0444 owner=root group=root template:
src: "{{ item }}"
dest: "/etc/file.cfg"
mode: 0444
owner: "root"
group: "root"
with_first_found: with_first_found:
- files: - files:
- "{{ inventory_hostname }}/etc/file.cfg" - "{{ inventory_hostname }}/etc/file.cfg"
@ -348,8 +418,9 @@ Ansible provides a neat way to do that, though you should remember, this is alwa
machine:: machine::
- name: Example of looping over a command result - name: Example of looping over a command result
shell: /usr/bin/frobnicate {{ item }} shell: "/usr/bin/frobnicate {{ item }}"
with_lines: /usr/bin/frobnications_per_host --param {{ inventory_hostname }} with_lines:
- "/usr/bin/frobnications_per_host --param {{ inventory_hostname }}"
Ok, that was a bit arbitrary. In fact, if you're doing something that is inventory related you might just want to write a dynamic Ok, that was a bit arbitrary. In fact, if you're doing something that is inventory related you might just want to write a dynamic
inventory source instead (see :doc:`intro_dynamic_inventory`), but this can be occasionally useful in quick-and-dirty implementations. inventory source instead (see :doc:`intro_dynamic_inventory`), but this can be occasionally useful in quick-and-dirty implementations.
@ -357,12 +428,13 @@ inventory source instead (see :doc:`intro_dynamic_inventory`), but this can be o
Should you ever need to execute a command remotely, you would not use the above method. Instead do this:: Should you ever need to execute a command remotely, you would not use the above method. Instead do this::
- name: Example of looping over a REMOTE command result - name: Example of looping over a REMOTE command result
shell: /usr/bin/something shell: "/usr/bin/something"
register: command_result register: command_result
- name: Do something with each result - name: Do something with each result
shell: /usr/bin/something_else --param {{ item }} shell: "/usr/bin/something_else --param {{ item }}"
with_items: "{{ command_result.stdout_lines }}" with_items:
- "{{ command_result.stdout_lines }}"
.. _indexed_lists: .. _indexed_lists:
@ -377,8 +449,10 @@ If you want to loop over an array and also get the numeric index of where you ar
It's uncommonly used:: It's uncommonly used::
- name: indexed loop demo - name: indexed loop demo
debug: msg="at array position {{ item.0 }} there is a value {{ item.1 }}" debug:
with_indexed_items: "{{ some_list }}" msg: "at array position {{ item.0 }} there is a value {{ item.1 }}"
with_indexed_items:
- "{{ some_list }}"
.. _using_ini_with_a_loop: .. _using_ini_with_a_loop:
@ -400,8 +474,13 @@ The ini plugin can use regexp to retrieve a set of keys. As a consequence, we ca
Here is an example of using ``with_ini``:: Here is an example of using ``with_ini``::
- debug: msg="{{ item }}" - debug:
with_ini: value[1-2] section=section1 file=lookup.ini re=true msg: "{{ item }}"
with_ini:
- value[1-2]
- section: section1
- file: "lookup.ini"
- re: true
And here is the returned value:: And here is the returned value::
@ -451,7 +530,9 @@ a really crazy hypothetical datastructure::
As you can see the formatting of packages in these lists is all over the place. How can we install all of the packages in both lists?:: As you can see the formatting of packages in these lists is all over the place. How can we install all of the packages in both lists?::
- name: flattened loop demo - name: flattened loop demo
yum: name={{ item }} state=installed yum:
name: "{{ item }}"
state: present
with_flattened: with_flattened:
- "{{ packages_base }}" - "{{ packages_base }}"
- "{{ packages_apps }}" - "{{ packages_apps }}"
@ -467,10 +548,10 @@ After using ``register`` with a loop, the data structure placed in the variable
Here is an example of using ``register`` with ``with_items``:: Here is an example of using ``register`` with ``with_items``::
- shell: echo "{{ item }}" - shell: "echo {{ item }}"
with_items: with_items:
- one - "one"
- two - "two"
register: echo register: echo
This differs from the data structure returned when using ``register`` without a loop:: This differs from the data structure returned when using ``register`` without a loop::
@ -540,22 +621,30 @@ If you wish to loop over the inventory, or just a subset of it, there is multipl
One can use a regular ``with_items`` with the ``play_hosts`` or ``groups`` variables, like this:: One can use a regular ``with_items`` with the ``play_hosts`` or ``groups`` variables, like this::
# show all the hosts in the inventory # show all the hosts in the inventory
- debug: msg={{ item }} - debug:
with_items: "{{ groups['all'] }}" msg: "{{ item }}"
with_items:
- "{{ groups['all'] }}"
# show all the hosts in the current play # show all the hosts in the current play
- debug: msg={{ item }} - debug:
with_items: "{{ play_hosts }}" msg: "{{ item }}"
with_items:
- "{{ play_hosts }}"
There is also a specific lookup plugin ``inventory_hostnames`` that can be used like this:: There is also a specific lookup plugin ``inventory_hostnames`` that can be used like this::
# show all the hosts in the inventory # show all the hosts in the inventory
- debug: msg={{ item }} - debug:
with_inventory_hostnames: all msg: "{{ item }}"
with_inventory_hostnames:
- all
# show all the hosts matching the pattern, ie all but the group www # show all the hosts matching the pattern, ie all but the group www
- debug: msg={{ item }} - debug:
with_inventory_hostnames: all:!www msg: "{{ item }}"
with_inventory_hostnames:
- all:!www
More information on the patterns can be found on :doc:`intro_patterns` More information on the patterns can be found on :doc:`intro_patterns`
@ -580,7 +669,8 @@ As of Ansible 2.1, the `loop_control` option can be used to specify the name of
loop_var: outer_item loop_var: outer_item
# inner.yml # inner.yml
- debug: msg="outer item={{ outer_item }} inner item={{ item }}" - debug:
msg: "outer item={{ outer_item }} inner item={{ item }}"
with_items: with_items:
- a - a
- b - b
@ -593,7 +683,9 @@ As of Ansible 2.1, the `loop_control` option can be used to specify the name of
When using complex data structures for looping the display might get a bit too "busy", this is where the C(label) directive comes to help:: When using complex data structures for looping the display might get a bit too "busy", this is where the C(label) directive comes to help::
- name: create servers - name: create servers
digital_ocean: name={{item.name}} state=present .... digital_ocean:
name: "{{ item.name }}"
state: present
with_items: with_items:
- name: server1 - name: server1
disks: 3gb disks: 3gb
@ -613,7 +705,9 @@ Another option to loop control is C(pause), which allows you to control the time
# main.yml # main.yml
- name: create servers, pause 3s before creating next - name: create servers, pause 3s before creating next
digital_ocean: name={{item}} state=present .... digital_ocean:
name: "{{ item }}"
state: present
with_items: with_items:
- server1 - server1
- server2 - server2
@ -672,4 +766,3 @@ information. Each of the above features are implemented as plugins in ansible,
Have a question? Stop by the google group! Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_ `irc.freenode.net <http://irc.freenode.net>`_
#ansible IRC chat channel #ansible IRC chat channel

Loading…
Cancel
Save