PageRenderTime 50ms CodeModel.GetById 27ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/docsite/rst/playbooks_loops.rst

https://github.com/ajanthanm/ansible
ReStructuredText | 432 lines | 312 code | 120 blank | 0 comment | 0 complexity | 52f6cd65883bd50797db6bfc71b90416 MD5 | raw file
  1Loops
  2=====
  3
  4Often you'll want to do many things in one task, such as create a lot of users, install a lot of packages, or
  5repeat a polling step until a certain result is reached.
  6
  7This chapter is all about how to use loops in playbooks.
  8
  9.. contents:: Topics
 10
 11.. _standard_loops:
 12
 13Standard Loops
 14``````````````
 15
 16To save some typing, repeated tasks can be written in short-hand like so::
 17
 18    - name: add several users
 19      user: name={{ item }} state=present groups=wheel
 20      with_items:
 21         - testuser1
 22         - testuser2
 23
 24If you have defined a YAML list in a variables file, or the 'vars' section, you can also do::
 25
 26    with_items: somelist
 27
 28The above would be the equivalent of::
 29
 30    - name: add user testuser1
 31      user: name=testuser1 state=present groups=wheel
 32    - name: add user testuser2
 33      user: name=testuser2 state=present groups=wheel
 34
 35The yum and apt modules use with_items to execute fewer package manager transactions.
 36
 37Note that the types of items you iterate over with 'with_items' do not have to be simple lists of strings.
 38If you have a list of hashes, you can reference subkeys using things like::
 39
 40    - name: add several users
 41      user: name={{ item.name }} state=present groups={{ item.groups }}
 42      with_items:
 43        - { name: 'testuser1', groups: 'wheel' }
 44        - { name: 'testuser2', groups: 'root' }
 45
 46.. _nested_loops:
 47
 48Nested Loops
 49````````````
 50
 51Loops can be nested as well::
 52
 53    - name: give users access to multiple databases
 54      mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
 55      with_nested:
 56        - [ 'alice', 'bob' ]
 57        - [ 'clientdb', 'employeedb', 'providerdb' ]
 58
 59As with the case of 'with_items' above, you can use previously defined variables. Just specify the variable's name without templating it with '{{ }}'::
 60
 61    - name: here, 'users' contains the above list of employees
 62      mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
 63      with_nested:
 64        - users
 65        - [ 'clientdb', 'employeedb', 'providerdb' ]
 66
 67.. _looping_over_hashes:
 68
 69Looping over Hashes
 70```````````````````
 71
 72.. versionadded:: 1.5
 73
 74Suppose you have the following variable::
 75
 76    ---
 77    users:
 78      alice:
 79        name: Alice Appleworth
 80        telephone: 123-456-7890
 81      bob:
 82        name: Bob Bananarama
 83        telephone: 987-654-3210
 84
 85And you want to print every user's name and phone number.  You can loop through the elements of a hash using ``with_dict`` like this::
 86
 87    tasks:
 88      - name: Print phone records
 89        debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
 90        with_dict: users
 91
 92.. _looping_over_fileglobs:
 93
 94Looping over Fileglobs
 95``````````````````````
 96
 97``with_fileglob`` matches all files in a single directory, non-recursively, that match a pattern.  It can
 98be used like this::
 99
100    ---
101    - hosts: all
102
103      tasks:
104
105        # first ensure our target directory exists
106        - file: dest=/etc/fooapp state=directory
107
108        # copy each file over that matches the given pattern
109        - copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600
110          with_fileglob:
111            - /playbooks/files/fooapp/*
112            
113.. note:: When using a relative path with ``with_fileglob`` in a role, Ansible resolves the path relative to the `roles/<rolename>/files` directory.
114
115Looping over Parallel Sets of Data
116``````````````````````````````````
117
118.. note:: This is an uncommon thing to want to do, but we're documenting it for completeness.  You probably won't be reaching for this one often.
119
120Suppose you have the following variable data was loaded in via somewhere::
121
122    ---
123    alpha: [ 'a', 'b', 'c', 'd' ]
124    numbers:  [ 1, 2, 3, 4 ]
125
126And you want the set of '(a, 1)' and '(b, 2)' and so on.   Use 'with_together' to get this::
127
128    tasks:
129        - debug: msg="{{ item.0 }} and {{ item.1 }}"
130          with_together:
131            - alpha
132            - numbers
133
134Looping over Subelements
135````````````````````````
136
137Suppose you want to do something like loop over a list of users, creating them, and allowing them to login by a certain set of
138SSH keys. 
139
140How might that be accomplished?  Let's assume you had the following defined and loaded in via "vars_files" or maybe a "group_vars/all" file::
141
142    ---
143    users:
144      - name: alice
145        authorized: 
146          - /tmp/alice/onekey.pub
147          - /tmp/alice/twokey.pub
148      - name: bob
149        authorized:
150          - /tmp/bob/id_rsa.pub
151
152It might happen like so::
153
154    - user: name={{ item.name }} state=present generate_ssh_key=yes
155      with_items: users
156
157    - authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'"
158      with_subelements:
159         - users
160         - authorized
161
162Subelements walks a list of hashes (aka dictionaries) and then traverses a list with a given key inside of those
163records.
164
165The authorized_key pattern is exactly where it comes up most.
166
167.. _looping_over_integer_sequences:
168
169Looping over Integer Sequences
170``````````````````````````````
171
172``with_sequence`` generates a sequence of items in ascending numerical order. You
173can specify a start, end, and an optional step value.
174
175Arguments should be specified in key=value pairs.  If supplied, the 'format' is a printf style string.
176
177Numerical values can be specified in decimal, hexadecimal (0x3f8) or octal (0600).
178Negative numbers are not supported.  This works as follows::
179
180    ---
181    - hosts: all
182
183      tasks:
184
185        # create groups
186        - group: name=evens state=present
187        - group: name=odds state=present
188
189        # create some test users
190        - user: name={{ item }} state=present groups=evens
191          with_sequence: start=0 end=32 format=testuser%02x
192
193        # create a series of directories with even numbers for some reason
194        - file: dest=/var/stuff/{{ item }} state=directory
195          with_sequence: start=4 end=16 stride=2
196
197        # a simpler way to use the sequence plugin
198        # create 4 groups
199        - group: name=group{{ item }} state=present
200          with_sequence: count=4
201
202.. _random_choice:
203
204Random Choices
205``````````````
206
207The 'random_choice' feature can be used to pick something at random.  While it's not a load balancer (there are modules
208for those), it can somewhat be used as a poor man's loadbalancer in a MacGyver like situation::
209
210    - debug: msg={{ item }}
211      with_random_choice:
212         - "go through the door"
213         - "drink from the goblet"
214         - "press the red button"
215         - "do nothing"
216
217One of the provided strings will be selected at random.  
218
219At a more basic level, they can be used to add chaos and excitement to otherwise predictable automation environments.
220
221.. _do_until_loops:
222
223Do-Until Loops
224``````````````
225
226.. versionadded: 1.4
227
228Sometimes you would want to retry a task until a certain condition is met.  Here's an example::
229   
230    - action: shell /usr/bin/foo
231      register: result
232      until: result.stdout.find("all systems go") != -1
233      retries: 5
234      delay: 10
235
236The above example run the shell module recursively till the module's result has "all systems go" in its stdout or the task has
237been retried for 5 times with a delay of 10 seconds. The default value for "retries" is 3 and "delay" is 5.
238
239The task returns the results returned by the last task run. The results of individual retries can be viewed by -vv option.
240The registered variable will also have a new key "attempts" which will have the number of the retries for the task.
241
242.. _with_first_found:
243
244Finding First Matched Files
245```````````````````````````
246
247.. note:: This is an uncommon thing to want to do, but we're documenting it for completeness.  You probably won't be reaching for this one often.
248
249This isn't exactly a loop, but it's close.  What if you want to use a reference to a file based on the first file found
250that matches a given criteria, and some of the filenames are determined by variable names?  Yes, you can do that as follows::
251
252    - name: INTERFACES | Create Ansible header for /etc/network/interfaces
253      template: src={{ item }} dest=/etc/foo.conf
254      with_first_found:
255        - "{{ansible_virtualization_type}}_foo.conf"
256        - "default_foo.conf"
257
258This tool also has a long form version that allows for configurable search paths.  Here's an example::
259
260    - name: some configuration template
261      template: src={{ item }} dest=/etc/file.cfg mode=0444 owner=root group=root
262      with_first_found:
263        - files:
264           - "{{inventory_hostname}}/etc/file.cfg"
265          paths:
266           - ../../../templates.overwrites
267           - ../../../templates
268        - files:
269            - etc/file.cfg
270          paths:
271            - templates
272
273.. _looping_over_the_results_of_a_program_execution:
274
275Iterating Over The Results of a Program Execution
276`````````````````````````````````````````````````
277
278.. note:: This is an uncommon thing to want to do, but we're documenting it for completeness.  You probably won't be reaching for this one often.
279
280Sometimes you might want to execute a program, and based on the output of that program, loop over the results of that line by line.
281Ansible provides a neat way to do that, though you should remember, this is always executed on the control machine, not the local
282machine::
283
284    - name: Example of looping over a command result
285      shell: /usr/bin/frobnicate {{ item }}
286      with_lines: /usr/bin/frobnications_per_host --param {{ inventory_hostname }}
287
288Ok, that was a bit arbitrary.  In fact, if you're doing something that is inventory related you might just want to write a dynamic
289inventory source instead (see :doc:`intro_dynamic_inventory`), but this can be occasionally useful in quick-and-dirty implementations.
290
291Should you ever need to execute a command remotely, you would not use the above method.  Instead do this::
292
293    - name: Example of looping over a REMOTE command result
294      shell: /usr/bin/something
295      register: command_result
296
297    - name: Do something with each result
298      shell: /usr/bin/something_else --param {{ item }}
299      with_items: command_result.stdout_lines
300
301.. _indexed_lists:
302
303Looping Over A List With An Index
304`````````````````````````````````
305
306.. note:: This is an uncommon thing to want to do, but we're documenting it for completeness.  You probably won't be reaching for this one often.
307
308.. versionadded: 1.3
309
310If you want to loop over an array and also get the numeric index of where you are in the array as you go, you can also do that.
311It's uncommonly used::
312
313    - name: indexed loop demo
314      debug: msg="at array position {{ item.0 }} there is a value {{ item.1 }}"
315      with_indexed_items: some_list
316
317.. _flattening_a_list:
318
319Flattening A List
320`````````````````
321
322.. note:: This is an uncommon thing to want to do, but we're documenting it for completeness.  You probably won't be reaching for this one often.
323
324In rare instances you might have several lists of lists, and you just want to iterate over every item in all of those lists.  Assume
325a really crazy hypothetical datastructure::
326
327    ----
328    # file: roles/foo/vars/main.yml
329    packages_base:
330      - [ 'foo-package', 'bar-package' ]
331    packages_apps:
332      - [ ['one-package', 'two-package' ]]
333      - [ ['red-package'], ['blue-package']]
334
335As 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?::
336
337    - name: flattened loop demo
338      yum: name={{ item }} state=installed 
339      with_flattened:
340         - packages_base
341         - packages_apps
342
343That's how!
344
345.. _using_register_with_a_loop:
346
347Using register with a loop
348``````````````````````````
349
350When using ``register`` with a loop the data structure placed in the variable during a loop, will contain a ``results`` attribute, that is a list of all responses from the module.
351
352Here is an example of using ``register`` with ``with_items``::
353
354    - shell: echo "{{ item }}"
355      with_items:
356        - one
357        - two
358      register: echo
359
360This differs from the data structure returned when using ``register`` without a loop::
361
362    {
363        "changed": true,
364        "msg": "All items completed",
365        "results": [
366            {
367                "changed": true,
368                "cmd": "echo \"one\" ",
369                "delta": "0:00:00.003110",
370                "end": "2013-12-19 12:00:05.187153",
371                "invocation": {
372                    "module_args": "echo \"one\"",
373                    "module_name": "shell"
374                },
375                "item": "one",
376                "rc": 0,
377                "start": "2013-12-19 12:00:05.184043",
378                "stderr": "",
379                "stdout": "one"
380            },
381            {
382                "changed": true,
383                "cmd": "echo \"two\" ",
384                "delta": "0:00:00.002920",
385                "end": "2013-12-19 12:00:05.245502",
386                "invocation": {
387                    "module_args": "echo \"two\"",
388                    "module_name": "shell"
389                },
390                "item": "two",
391                "rc": 0,
392                "start": "2013-12-19 12:00:05.242582",
393                "stderr": "",
394                "stdout": "two"
395            }
396        ]
397    }
398
399Subsequent loops over the registered variable to inspect the results may look like::
400
401    - name: Fail if return code is not 0
402      fail:
403        msg: "The command ({{ item.cmd }}) did not have a 0 return code"
404      when: item.rc != 0
405      with_items: echo.results
406
407.. _writing_your_own_iterators:
408
409Writing Your Own Iterators
410``````````````````````````
411
412While you ordinarily shouldn't have to, should you wish to write your own ways to loop over arbitrary datastructures, you can read :doc:`developing_plugins` for some starter
413information.  Each of the above features are implemented as plugins in ansible, so there are many implementations to reference.
414
415.. seealso::
416
417   :doc:`playbooks`
418       An introduction to playbooks
419   :doc:`playbooks_roles`
420       Playbook organization by roles
421   :doc:`playbooks_best_practices`
422       Best practices in playbooks
423   :doc:`playbooks_conditionals`
424       Conditional statements in playbooks
425   :doc:`playbooks_variables`
426       All about variables
427   `User Mailing List <http://groups.google.com/group/ansible-devel>`_
428       Have a question?  Stop by the google group!
429   `irc.freenode.net <http://irc.freenode.net>`_
430       #ansible IRC chat channel
431
432