1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
|
#!/usr/bin/env ansible-playbook
# ^ Trick: the above line can be used to make your play an executable
# you also must add 'x' permissions to the file
#
# this file is based on phred's 'pedantically commented playbook'
# https://github.com/phred/ansible-examples/blob/master/pedantically_commented_playbook.yml
#
---
# ^^^ YAML documents can begin with the document separator "---"
# and end with the "...", neither is needed for Ansible # as it does not
# support multiple YAML documents per file, but some linters incorrectly insist
# you must have it ....
#
# The '#' is a comment character, so any line starting with it will be ignored by Ansible.
# Blank lines are ignored, so can be used # to create spacing to your taste.
# Note about YAML: like Python, cares about whitespace, it requires actual spaces, tabs won't work.
# Indent consistently throughout.
# Two-space or four space indents is what most users prefer, but do whatever you like.
#
# If you're new to YAML, keep in mind that YAML documents, like XML
# documents, represent a tree-like structure of nodes and text. More
# familiar with JSON? Think of YAML as a strict (with spaces) but also more flexible
# JSON (fewer significant characters, e.g., :, "", {}, [] and liberal quoting).
# Also, JSON is a subset of YAML, so most YAML parser can read JSON just the same.
#
# The curious may read more about YAML at:
# http://www.yaml.org/spec/1.1/current.html
# there is a 1.2, but Ansible uses pyyaml which is mostly 1.1
# the following line configures 'vim' to handle 2 space indents.
# vim:ff=unix ts=2 sw=2 ai expandtab
###
# Notice the minus (-) on the line below, this is the start of a 'list' in YAML
# In Ansible this is the 'list of plays' which starts this playbook.
# Plays map the inventory hosts to the tasks, the most minimal play you can have
# just requires 'hosts'
- hosts: all
###########
# Play keyword: hosts
# Required: yes
# Description:
# The selection of hosts (or host) that the tasks in this play play should apply to.
#
## Example values:
# hosts: all -- applies to all hosts
# hosts: host1 -- apply ONLY to the host that inventory defines as 'host1'
# hosts: group1 -- apply to all hosts in group1
# hosts: group1,group2 -- apply to hosts in group1 & group2
# hosts: group1,host1 -- hosts in group1 AND host
#
## now using host patterns (TODO: url)
# hosts: group1,!group3 -- hosts in group1 that are not in group3
# hosts: group1,&group3 -- hosts in group1 that are also in group3
# hosts: group1:&group3 -- same as above, but using : instead of , as separator
# hosts: group1:!group2:&group3 -- hosts in group1 what are not in group2 but are also in group3
#
## Using a variable value for 'hosts'
#
# You can, in fact, set hosts to a variable, for example:
#
# hosts: '{{mygroups}}' -- apply to all hosts specified in the variable 'mygroups'
#
# This is handy for testing playbooks, running the same playbook against a
# staging environment before running it against production, occasional
# maintenance tasks, and other cases where you want to run the playbook
# against just a few systems rather than a whole group.
# Note that the variable cannot be set in inventory, since we need to know the hosts
# before we can use inventory variables. So normally 'extra vars' are used, as you can
# see below.
#
# If you set hosts as shown above, then you can specify which hosts to
# apply the playbook to on each run as so:
#
# ansible-playbook playbook.yml --extra-vars="mygroups=staging"
#
# Use --extra-vars to set the variable to any combination of groups, hostnames,
# or host patterns just like the examples in the previous section.
#
name: my heavily commented play
###########
# Play keyword: name
# Default: play###
# Required: no
# Description: Just a description to document the play
gather_facts: yes
###########
# Play keyword: gather_facts
# Default: None
# Required: no
# Description:
# This controls if the play will trigger a 'fact gathering task' (aka 'gather_facts' or 'setup' action) to get information about the remote target.
# These facts normally provide useful variables on which to base decisions and task inputs. For example `ansible_os_distribution` can tell us if
# the target is a RHEL, Ubuntu or FreeBSD machine (among others), number of CPUs, RAM, etc.
# TODO: url to fact gathering
remote_user: login_user
###########
# Play keyword: user
# Default: depends on connection plugin, for ssh it is 'current user executing Ansible'
# Required: no
# Description:
# Remote user to login on remote targets and 'normally' execute the tasks as
become: True
###########
# Play keyword: become
# Default: False
# Required: no
# Description:
# If True, always use privilege escalation to run tasks from this play, just like passing the
# --become flag to ansible or ansible-playbook.
become_user: root
###########
# Play keyword: become_user
# Default: None
# Required: no
# Description:
# When using privilege escalation this is the user you 'become' after login with the remote_user
# for example you login to the remote as 'login_user' then you 'become' root to execute the tasks
become_method: sudo
###########
# Play keyword: become_method
# Default: sudo
# Required: no
# Description:
# When using privilege escalation this chooses the become plugin to use for privilege escalation.
# use `ansible-doc -t become -l` to list all the options.
connection: ssh
###########
# Play keyword: connection
# Default: ssh
# Required: no
# Description:
# This sets which connection plugin Ansible will use to try to communicate with the target host.
# notable options are paramiko (python implementation of ssh, mostly useful in corner cases in which the ssh cli
# does not work well with the target. Also 'local' which forces a 'local fork' to execute the task, but normally
# what you really want is `delegate_to: localhost` see examples below in 'Run things locally!' entry.
# use `ansible-doc -t connection -l` to list all the options.
vars:
###########
# Play keyword: vars
# Default: none
# Required: no
# Description:
# Mapping of variables defined for this play, normally for use in templates or as variables for tasks.
# to get the value just use {{color}} to reference that value
color: brown
# Mapping structures allow complex variables structures, to use you can reference
# the variable name with {{web['memcache']}} when using nested key value or {{web}}
# when using the whole structure..
web:
memcache: 192.168.1.2
httpd: apache
# lists use a slightly different notation {{ mylist[1] }} to get 'b', they are 0 indexed.
mylist:
- a
- b
- c
# Variables can be dynamically set via Jinja templates, to be filled when consumed.
#
# In this playbook, this will always evaluate to False, because 'color'
# is set to 'brown' above.
#
# When ansible interprets the following, it will first expand 'color' to
# 'brown' and then evaluate 'brown' == 'blue' as a Jinja expression.
is_color_blue: "{{ color == 'blue' }}"
# TODO: (url variables)
vars_files:
##########
# Play keyword: vars_files
# Required: no
# Description:
# Specifies a list of YAML files to load variables from.
#
# Always evaluated after the 'vars' section, no matter which section
# occurs first in the playbook. Examples are below.
#
# Example YAML for a file to be included by vars_files:
# ---
# monitored_by: phobos.mars.nasa.gov
# fish_sticks: "good with custard"
# ... # (END OF DOCUMENT)
#
# Remove the indentation & comments of course, the '---' should be at
# the left margin in the variables file.
#
# Include a file from this absolute path
- /srv/ansible/vars/vars_file.yml
# Include a file from a path relative to this playbook
- vars/vars_file.yml
# By the way, variables set in 'vars' or as extra vars are available here.
- vars/{{something}}.yml
# It's also possible to pass an array of files, in which case
# Ansible will loop over the array and include the first file that
# exists. If none exist, ansible-playbook will halt with an error.
#
# An excellent way to handle platform-specific differences.
- [ 'vars/{{platform}}.yml', vars/default.yml ]
# Files in vars_files process in order, so later files can
# provide more specific configuration:
- [ 'vars/{{host}}.yml' ]
# Hey, but if you're doing host-specific variable files, you might
# consider setting the variable for a group in your inventory and
# adding your host to that group. Just a thought.
vars_prompt:
##########
# Play keyword: vars_prompt
# Required: no
# Description:
# A list of variables that Ansible will prompt for manual input each time this playbook
# runs. Used for sensitive data and also things like release numbers that
# vary on each deployment.
#
# Ansible won't prompts for this value if already provided, like when
# passed through --extra-vars, but not from inventory.
#
# Also it won't prompt if it detects that it is a non interactive session.
# For example, when called from cron.
#
- name: passphrase
prompt: "Please enter the passphrase for the SSL certificate"
private: yes
# The input won't be echoed back to the terminal when private (default yes)
# Not sensitive, but something that should vary on each playbook run.
- name: release_version
prompt: "Please enter a release tag"
private: no
# you can even have a default
- name: package_version
prompt: "Please enter a package version"
default: '1.0'
# You can find more advanced features in https://docs.ansible.com/ansible/latest/user_guide/playbooks_prompts.html
roles:
##########
# Play keyword: roles
# Required: no
# Description: A list of roles to import and execute in this play. Executes AFTER pre_tasks and play fact gathering, but before 'tasks'.
# TODO url roles + url to 'play stages'
tasks:
##########
# Play keyword: tasks
# Required: no
# Description: A list of tasks to perform in this play. Executes AFTER roles and before post_tasks
# A simple task
# Each task must have an action. 'name' is optional but very useful to document what the task does
- name: Check that the target can execute Ansible tasks
action: ping
##########
# Ansible modules do the work!, 'action' is not needed, you can use the 'action itself' as part of the task
- file: path=/tmp/secret mode=0600 owner=root group=root
#
# Format 'action' like above:
# <modulename>: <module parameters>
#
# Test your parameters using:
# ansible -m <module> -a "<module parameters>"
#
# Documentation for the stock modules:
# http://ansible.github.com/modules.html
# normally most will want to use 'k: v' notation instead of 'k=v' used above (but useful for adhoc execution).
# while both formats are mostly interchangable, `k: v` is more explicit, 'type friendly' and simpler to escape.
- name: Ensure secret is locked down
file:
path: /tmp/secret
mode: '0600'
owner: root
group: root
# note that 'action options' are indented inside the option, while 'task keywords' stay on the top level
##########
# Use variables in the task! It expands on use.
- name: Paint the server
command: echo {{color}}
# you can also define variables at the task level
- name: Ensure secret is locked down
file:
path: '{{secret_file}}'
mode: '0600'
owner: root
group: root
vars:
secret_file: /tmp/secret
##########
# Trigger handlers when things change!
#
# Most Ansible actions can detect and report when something changed.
# Like if file permissions were not the same as requested,
# a file's content is different or a package was installed (or removed)
# When a change is reported, the task assumes the 'changed' status.
# Ansible can optionally notify one or more Handlers.
# Handlers are like normal tasks, the main difference is that they only
# run when notified.
# A common use is to restart a service after updating it's configuration file.
# https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#handlers-running-operations-on-change
# TODO: explain handler per stage execution
# This will call the "Restart Apache" handler whenever 'copy' alters
# the remote httpd.conf.
- name: Update the Apache config
copy:
src: httpd.conf
dest: /etc/httpd/httpd.conf
notify: Restart Apache
# Here's how to specify more than one handler
- name: Update our app's configuration
copy:
src: myapp.conf
dest: /etc/myapp/production.conf
notify:
- Restart Apache
- Restart Redis
##########
# Include tasks from another file!
#
# Ansible can insert a list of tasks from another file. The file
# must represent a list of tasks, which is different than a play.
#
# Task list format:
# ---
# - name: create user
# user: name={{myuser}} color={{color}}
#
# - name: add user to group
# user: name={{myuser}} groups={{hisgroup}} append=true
# ... # (END OF DOCUMENT)
#
# A 'tasks' YAML file represents a list of tasks. Don't use playbook
# YAML for a 'tasks' file.
#
# Remove the indentation & comments of course, the '---' should be at
# the left margin in the variables file.
# TODO: point at import_playbook, includes and roles
# In this example the user will be 'sklar'
# and 'color' will be 'red' inside new_user.yml
- import_tasks: tasks/new_user.yml
vars:
myuser: sklar
color: red
# In this example the user will be 'mosh'
# and $color will be 'mauve' inside new_user.yml
- import_tasks: tasks/new_user.yml
vars:
myuser: mosh
color: mauve
##########
# Run a task on each thing in a list!
#
# Ansible provides a simple loop facility. If 'loop' is provided for
# a task, then the task will be run once for each item in the provided
# list. Each iteration will create the 'item' variable with a different value.
- name: Create a file named via variable in /tmp
file: path=/tmp/{{item}} state=touched
loop:
- tangerine
- lemon
- name: Loop using a variable
file: path=/tmp/{{item}} state=touched
loop: '{{mylist}}'
vars:
# defined here, but could be anywhere before the task runs
# also note that YAML lists can be flush with their key,
# we normally indent for clarity, but this form is also correct.
mylist:
- tangerine
- lemon
##########
# Conditionally execute tasks!
#
# Sometimes you only want to run an action when a under certain conditions.
# Ansible supports using conditional Jinja expression, executing the task only when 'True'.
#
# If you're trying to run an task only when a value changes,
# consider rewriting the task as a handler and using 'notify' (see below).
#
- name: "shutdown all ubuntu"
command: /sbin/shutdown -t now
when: '{{is_ubuntu|bool}}'
- name: "shutdown the if host is in the government"
command: /sbin/shutdown -t now
when: "{{inventory_hostname in groups['government']}}"
# another way to write the same.
- name: "shutdown the if host is in the government"
command: /sbin/shutdown -t now
when: "{{'government' in group_names}}"
# Ansible has some built in variables, you can check them here (TODO url)
# inventory_hostname is the name of the current host the task is executing for (derived from the hosts: keyword)
# group_names has the list of groups the current host (inventory_hostname) is part of
# groups is a mapping of the inventory groups with the list of hosts that belong to them
##########
# Run things as other users!
#
# Each task has optional keywords that control which
# user a task should run as and whether or not to use privilege escalation
# (like sudo or su) to switch to that user.
- name: login in as postgres and dump all postgres databases
shell: pg_dumpall -w -f /tmp/backup.psql
remote_user: postgres
become: False
- name: login normally, but sudo to postgres to dump all postgres databases
shell: pg_dumpall -w -f /tmp/backup.psql
become: true
become_user: postgres
become_method: sudo
##########
# Run things locally!
#
# Each task can also be delegated to the control host
- name: create tempfile
local_action: shell dd if=/dev/urandom of=/tmp/random.txt count=100
# which is equivalent to the following
- name: create tempfile
shell: dd if=/dev/urandom of=/tmp/random.txt count=100
delegate_to: localhost
# delegate_to can use any target, but for the case above, it is the same as using local_action
# TODO url to delegation and implicit localhost
handlers:
##########
# Play keyword: handlers
# Required: no
# Description:
# Handlers are tasks that run when another task has changed something.
# See above for examples on how to trigger them.
# The format to define a handler is exactly the same as for tasks.
# Note that if multiple tasks notify the same handler in a playbook run
# that handler will only run once for that host.
#
# Handlers are referred to by name or using the listen keyword.
# They will be run in the order declared in the playbook.
# For example: if a task were to notify the handlers in reverse order like so:
#
# - task: ensure file does not exist
# file:
# name: /tmp/lock.txt
# state: absent
# notify:
# - Restart application
# - Restart nginx
#
# The "Restart nginx" handler will still run before the "Restart application"
# handler because it is declared first in this playbook.
# this one can only be called by name
- name: Restart nginx
service:
name: nginx
state: restarted
# this one can be called by name or via any entry in the listen keyword
- name: redis restarter
service:
name: redis
state: restarted
listen:
- Restart redis
# Any module can be used for the handler action
# even though this can be triggered multiple ways and times
# it will only execute once per host
- name: restart application that should really be a service
command: /srv/myapp/restart.sh
listen:
- Restart application
- restart myapp
# It's also possible to include handlers from another file. Structure is
# the same as a tasks file, see the tasks section above for an example.
- import_tasks: handlers/site.yml
# NOTE: this is not a complete list of all possible keywords in a play or task (TODO: url playbook object and keywords), just an example of very common options.
# below is the "totally optional" YAML "End of document" marker.
...
|