├── Chapter01 └── script.txt ├── Chapter02 └── script.txt ├── Chapter03 └── script.txt ├── Chapter04 └── script.txt ├── Chapter05 └── script.txt ├── Chapter06 └── script.txt ├── Chapter07 └── script.txt ├── Chapter08 └── script.txt ├── Chapter09 └── script.txt ├── LICENSE └── README.md /Chapter01/script.txt: -------------------------------------------------------------------------------- 1 | mastery.example.name 2 | 3 | 4 | 5 | [web] 6 | mastery.example.name 7 | 8 | [dns] 9 | backend.example.name 10 | 11 | [database] 12 | backend.example.name 13 | 14 | [frontend:children] 15 | web 16 | 17 | [backend:children] 18 | dns 19 | database 20 | 21 | 22 | 23 | 24 | [web] 25 | mastery.example.name ansible_host=192.168.10.25 26 | 27 | [dns] 28 | backend.example.name 29 | 30 | [database] 31 | backend.example.name 32 | 33 | [frontend:children] 34 | web 35 | 36 | [backend:children] 37 | dns 38 | database 39 | 40 | [web:vars] 41 | http_port=88 42 | proxy_timeout=5 43 | 44 | [backend:vars] 45 | ansible_port=314 46 | 47 | [all:vars] 48 | ansible_ssh_user=otto 49 | 50 | 51 | 52 | - name: add new node into runtime inventory 53 | add_host: 54 | name: newmastery.example.name 55 | groups: web 56 | ansible_host: 192.168.10.30 57 | 58 | 59 | --- 60 | - name: limit example play 61 | hosts: all 62 | gather_facts: false 63 | 64 | tasks: 65 | - name: tell us which host we are on 66 | debug: 67 | var: inventory_hostname 68 | 69 | 70 | 71 | --- 72 | - name: limit example play 73 | hosts: all 74 | gather_facts: false 75 | 76 | tasks: 77 | - name: tell us which host we are on 78 | debug: 79 | var: inventory_hostname 80 | 81 | - name: grab variable data from backend 82 | debug: 83 | var: hostvars['backend.example.name']['ansible_port'] 84 | 85 | 86 | 87 | 88 | --- 89 | - hosts: localhost 90 | gather_facts: false 91 | 92 | vars: 93 | - a_var: derp 94 | 95 | pre_tasks: 96 | - name: pretask 97 | debug: 98 | msg: "a pre task" 99 | changed_when: true 100 | notify: say hi 101 | 102 | roles: 103 | - role: simple 104 | derp: newval 105 | 106 | tasks: 107 | - name: task 108 | debug: 109 | msg: "a task" 110 | changed_when: true 111 | notify: say hi 112 | 113 | post_tasks: 114 | - name: posttask 115 | debug: 116 | msg: "a post task" 117 | changed_when: true 118 | notify: say hi 119 | 120 | 121 | 122 | 123 | - meta: flush_handlers 124 | 125 | 126 | 127 | . 128 | +-- a_vars_file.yaml 129 | +-- mastery-hosts 130 | +-- relative.yaml 131 | +-- tasks 132 | +-- a.yaml 133 | +-- b.yaml 134 | 135 | 136 | 137 | 138 | --- 139 | something: "better than nothing" 140 | 141 | 142 | 143 | 144 | --- 145 | - name: relative path play 146 | hosts: localhost 147 | gather_facts: false 148 | 149 | vars_files: 150 | - a_vars_file.yaml 151 | 152 | tasks: 153 | - name: who am I 154 | debug: 155 | msg: "I am mastery task" 156 | 157 | - name: var from file 158 | debug: var: something 159 | 160 | - include: tasks/a.yaml 161 | 162 | 163 | 164 | --- 165 | - name: where am I 166 | debug: 167 | msg: "I am task a" 168 | 169 | - include: b.yaml 170 | 171 | 172 | 173 | --- 174 | - name: who am I 175 | debug: 176 | msg: "I am task b" 177 | 178 | 179 | 180 | hostname:groupname:*.example:~(web|db)\.example\.com 181 | 182 | 183 | 184 | Webservers[0]:webservers[2:4] 185 | 186 | 187 | 188 | Webservers:&dbservers 189 | Webservers:!dbservers 190 | 191 | 192 | 193 | --- 194 | - name: play with a {{ var_name }} 195 | hosts: localhost 196 | gather_facts: false 197 | 198 | vars: 199 | - var_name: not-mastery 200 | 201 | tasks: 202 | - name: set a variable 203 | set_fact: 204 | task_var_name: "defined variable" 205 | 206 | - name: task with a {{ task_var_name }} 207 | debug: 208 | msg: "I am mastery task" 209 | 210 | - name: second play with a {{ task_var_name }} 211 | hosts: localhost 212 | gather_facts: false 213 | 214 | tasks: 215 | - name: task with a {{ runtime_var_name }} 216 | debug: 217 | msg: "I am another mastery task" 218 | 219 | 220 | 221 | 222 | - name: add a keypair to nova 223 | os_keypair: 224 | cloud={{ cloud_name }} 225 | name=admin-key 226 | wait=yes 227 | 228 | - name: add a keypair to nova 229 | os_keypair: 230 | cloud: "{{ cloud_name }}" 231 | name: admin-key 232 | wait: yes 233 | 234 | 235 | 236 | 237 | [ssh_connection] 238 | pipelining=true 239 | 240 | 241 | 242 | - name: get the operators name 243 | pause: 244 | prompt: "Please enter your name" 245 | register: opname 246 | 247 | 248 | --extra-vars "foo=bar owner=fred" 249 | --extra-vars '{"services":["nova-api","nova-conductor"]}' 250 | --extra-vars @/path/to/data.yaml 251 | 252 | lookup('', 'plugin_argument') 253 | 254 | 255 | 256 | - name: show data from etcd 257 | debug: 258 | msg: "{{ lookup('etcd', 'mastery') }}" 259 | 260 | 261 | 262 | hash_var: 263 | fred: 264 | home: Seattle 265 | transport: Bicycle 266 | 267 | 268 | 269 | hash_var: 270 | fred: 271 | transport: Bus 272 | 273 | 274 | 275 | 276 | hash_var: 277 | fred: 278 | transport: Bus 279 | 280 | 281 | 282 | 283 | hash_var: 284 | fred: 285 | home: Seattle 286 | transport: Bus 287 | 288 | 289 | 290 | 291 | -------------------------------------------------------------------------------- /Chapter02/script.txt: -------------------------------------------------------------------------------- 1 | --- 2 | - name: show me an encrypted var 3 | hosts: localhost 4 | gather_facts: false 5 | 6 | vars_files: 7 | - a_vars_file.yaml 8 | 9 | tasks: 10 | - name: print the variable 11 | debug: 12 | var: something 13 | 14 | 15 | 16 | 17 | --- 18 | - name: show me an encrypted var 19 | hosts: localhost 20 | gather_facts: false 21 | 22 | vars_files: 23 | - a_vars_file.yaml 24 | 25 | tasks: 26 | - name: print the variable 27 | debug: 28 | var: something 29 | no_log: true 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Chapter03/script.txt: -------------------------------------------------------------------------------- 1 | setting = {{ setting }} 2 | {% if feature.enabled %} 3 | feature = True 4 | {% else %} 5 | feature = False 6 | {% endif %} 7 | another_setting = {{ another_setting }} 8 | 9 | 10 | 11 | 12 | --- 13 | - name: demo the template 14 | hosts: localhost 15 | gather_facts: false 16 | vars: 17 | setting: a_val 18 | feature: 19 | enabled: true 20 | another_setting: b_val 21 | tasks: 22 | - name: pause with render 23 | pause: 24 | prompt: "{{ lookup('template', 'demo.j2') }}" 25 | 26 | 27 | 28 | API = cinder{{ 'v2' if api.v2 else '' }} 29 | 30 | 31 | 32 | --- 33 | - name: demo the template 34 | hosts: localhost 35 | gather_facts: false 36 | vars: 37 | api: 38 | v2: true 39 | tasks: 40 | - name: pause with render 41 | debug: 42 | msg: "API = cinder{{ 'v2' if api.v2 else '' }}" 43 | 44 | 45 | 46 | 47 | # data dirs 48 | {% for dir in data_dirs %} 49 | data_dir = {{ dir }} 50 | {% endfor %} 51 | 52 | 53 | 54 | # data dirs 55 | {% for dir in data_dirs %} 56 | data_dir = {{ dir }} 57 | {% else %} 58 | # no data dirs found 59 | {% endfor %} 60 | 61 | 62 | 63 | --- 64 | - name: demo the template 65 | hosts: localhost 66 | gather_facts: false 67 | vars: 68 | data_dirs: [] 69 | tasks: 70 | - name: pause with render 71 | pause: 72 | prompt: "{{ lookup('template', 'demo.j2') }}" 73 | 74 | 75 | 76 | 77 | # data dirs 78 | {% for dir in data_dirs %} 79 | {% if dir != "/" %} 80 | data_dir = {{ dir }} 81 | {% endif %} 82 | {% else %} 83 | # no data dirs found 84 | {% endfor %} 85 | 86 | 87 | 88 | # data dirs 89 | {% for dir in data_dirs if dir != "/" %} 90 | data_dir = {{ dir }} 91 | {% else %} 92 | # no data dirs found 93 | {% endfor %} 94 | 95 | 96 | 97 | # data dirs 98 | {% for dir in data_dirs if dir != "/" %} 99 | {% if loop.first %} 100 | data_dir = {{ dir }}, 101 | {% else %} 102 | {{ dir }}, 103 | {% endif %} 104 | {% else %} 105 | # no data dirs found 106 | {% endfor %} 107 | 108 | 109 | 110 | --- 111 | - name: demo the template 112 | hosts: localhost 113 | gather_facts: false 114 | vars: 115 | data_dirs: ['/', '/foo', '/bar'] 116 | tasks: 117 | - name: pause with render 118 | pause: 119 | prompt: "{{ lookup('template', 'demo.j2') }}" 120 | 121 | 122 | 123 | 124 | # data dirs. 125 | {% for dir in data_dirs if dir != "/" %} 126 | {% if loop.first %} 127 | data_dir = {{ dir }}{{ ',' if not loop.last else '' }} 128 | {% else %} 129 | {{ dir }}{{ ',' if not loop.last else '' }} 130 | {% endif %} 131 | {% else %} 132 | # no data dirs found 133 | {% endfor %} 134 | 135 | 136 | 137 | {% macro comma(loop) %} 138 | {{ ',' if not loop.last else '' }} 139 | {%- endmacro -%} 140 | # data dirs. 141 | {% for dir in data_dirs if dir != "/" %} 142 | {% if loop.first %} 143 | data_dir = {{ dir }}{{ comma(loop) }} 144 | {% else %} 145 | {{ dir }}{{ comma(loop) }} 146 | {% endif %} 147 | {% else %} 148 | # no data dirs found 149 | {% endfor %} 150 | 151 | 152 | {% macro test() %} 153 | {{ test.name }} 154 | {%- endmacro -%} 155 | {{ test() }} 156 | 157 | 158 | 159 | {% macro test(var_a='a string') %} 160 | {{ test.arguments }} 161 | {%- endmacro -%} 162 | {{ test() }} 163 | 164 | 165 | 166 | {% macro test(var_a='a string') %} 167 | {{ test.arguments }} 168 | {{ test.defaults }} 169 | {%- endmacro -%} 170 | {{ test() }} 171 | 172 | 173 | 174 | 175 | {% macro test() %} 176 | {{ kwargs }} 177 | {{ test.catch_kwargs }} 178 | {%- endmacro -%} 179 | {{ test(unexpected='surprise') }} 180 | 181 | 182 | 183 | 184 | {% macro test() %} 185 | {{ varargs }} 186 | {{ test.catch_varargs }} 187 | {%- endmacro -%} 188 | {{ test('surprise') }} 189 | 190 | 191 | 192 | {% macro test() %} 193 | The text from the caller follows: 194 | {{ caller() }} 195 | {%- endmacro -%} 196 | {% call test() %} 197 | This is text inside the call 198 | {% endcall %} 199 | 200 | 201 | 202 | {% macro test(group, hosts) %} 203 | [{{ group }}] 204 | {% for host in hosts %} 205 | {{ host }} {{ caller(host) }} 206 | {%- endfor %} 207 | {%- endmacro -%} 208 | {% call(host) test('web', ['host1', 'host2', 'host3']) %} 209 | ssh_host_name={{ host }}.example.name ansible_sudo=true 210 | {% endcall %} 211 | {% call(host) test('db', ['db1', 'db2']) %} 212 | ssh_host_name={{ host }}.example.name 213 | {% endcall %} 214 | 215 | 216 | 217 | {{ my_word | lower }} 218 | 219 | 220 | 221 | {{ answers | replace('no', 'yes') }} 222 | 223 | 224 | 225 | {{ answers | replace('no', 'yes') | lower }} 226 | 227 | 228 | 229 | --- 230 | - name: demo the template 231 | hosts: localhost 232 | gather_facts: false 233 | tasks: 234 | - name: debug the template 235 | debug: 236 | msg: "{{ answers | replace('no', 'yes') | lower }}" 237 | 238 | 239 | 240 | 241 | {% if some_variable is defined %} 242 | {{ some_variable }} 243 | {% else %} 244 | default_value 245 | {% endif %} 246 | {{ some_variable | default('default_value') }} 247 | 248 | 249 | 250 | max_threads: {{ play_hosts | count }} 251 | 252 | 253 | 254 | - name: backup the database 255 | shell: mysqldump -u root nova > /data/nova.backup.sql 256 | delegate_to: "{{ groups['db_servers'] | random }}" 257 | run_once: true 258 | 259 | 260 | 261 | {{ math_result | round | int }} 262 | 263 | 264 | 265 | --- 266 | - name: demo the filters 267 | hosts: localhost 268 | gather_facts: false 269 | tasks: 270 | - name: fail a task 271 | debug: 272 | msg: "I am not a change" 273 | register: derp 274 | - name: only do this on change 275 | debug: 276 | msg: "You had a change" 277 | when: derp | changed 278 | - name: only do this on success 279 | debug: 280 | msg: "You had a success" 281 | when: derp | success 282 | 283 | 284 | 285 | --- 286 | - name: demo the filters 287 | hosts: localhost 288 | gather_facts: false 289 | tasks: 290 | - name: shuffle the cards 291 | debug: 292 | msg: "{{ ['Ace', 'Queen', 'King', 'Deuce'] | shuffle }}" 293 | 294 | 295 | 296 | --- 297 | - name: demo the filters 298 | hosts: localhost 299 | gather_facts: false 300 | tasks: 301 | - name: demo basename 302 | debug: 303 | msg: "{{ '/var/log/nova/nova-api.log' | basename }}" 304 | 305 | 306 | 307 | --- 308 | - name: demo the filters 309 | hosts: localhost 310 | gather_facts: false 311 | tasks: 312 | - name: demo filter 313 | debug: 314 | msg: "{{ '~/.stackrc' | expanduser }}" 315 | 316 | 317 | 318 | 319 | --- 320 | - name: demo the filters 321 | hosts: localhost 322 | gather_facts: false 323 | tasks: 324 | - name: read file 325 | slurp: 326 | src: derp 327 | register: derp 328 | - name: display file content (undecoded) 329 | debug: 330 | var: derp.content 331 | - name: display file content (decoded) 332 | debug: 333 | var: derp.content | b64decode 334 | 335 | 336 | 337 | - name: check database version 338 | shell: neutron-manage current |grep juno 339 | register: neutron_db_ver 340 | failed_when: false 341 | - name: upgrade db 342 | command: neutron-manage db_sync 343 | when: neutron_db_ver|failed 344 | 345 | 346 | 347 | - name: check database version 348 | command: neutron-manage current 349 | register: neutron_db_ver 350 | - name: upgrade db 351 | command: neutron-manage db_sync 352 | when: not neutron_db_ver.stdout | search('juno') 353 | 354 | 355 | 356 | - name: install pips with versions 357 | pip: name={{ item.name }} version={{ item.ver }} 358 | with_items: pips 359 | when: item.ver is defined 360 | - name: install pips without versions 361 | pip: name={{ item.name }} 362 | with_items: pips 363 | when: item.ver is undefined 364 | 365 | 366 | 367 | 368 | - name: install pips 369 | pip: name={{ item.name }} version={{ item.ver | default(omit) }} 370 | with_items: pips 371 | 372 | 373 | --- 374 | - name: demo the filters 375 | hosts: localhost 376 | gather_facts: false 377 | 378 | tasks: 379 | - name: string methods 380 | debug: 381 | msg: "{{ 'foo bar baz'.upper().split() }}" 382 | 383 | 384 | --- 385 | - name: demo the logic 386 | hosts: localhost 387 | gather_facts: false 388 | vars: 389 | num1: 10 390 | num3: 10 391 | tasks: 392 | - name: logic and comparison 393 | debug: 394 | msg: "Can you read me?" 395 | when: num1 >= num3 and num1 is even and num2 is not defined 396 | 397 | 398 | 399 | 400 | 401 | 402 | -------------------------------------------------------------------------------- /Chapter04/script.txt: -------------------------------------------------------------------------------- 1 | -name: broken website 2 | uri: 3 | url: http://notahost.nodomain 4 | 5 | 6 | - name: broken website 7 | uri: 8 | url: http://notahost.nodomain 9 | ignore_errors: true 10 | 11 | 12 | 13 | - name: query sessions 14 | command: /sbin/iscsiadm -m session 15 | register: sessions 16 | 17 | 18 | - name: query sessions 19 | command: /sbin/iscsiadm -m session 20 | register: sessions 21 | failed_when: sessions.rc not in (0, 21) 22 | 23 | 24 | - name: delete branch bad 25 | command: git branch -D badfeature 26 | args: 27 | chdir: /srv/app 28 | 29 | 30 | - name: check if branch badfeature exists 31 | command: git branch 32 | args: 33 | chdir: /srv/app 34 | register: branches 35 | - name: delete branch bad 36 | command: git branch -D badfeature 37 | args: 38 | chdir: /srv/app 39 | when: branches.stdout | search('badfeature') 40 | 41 | 42 | 43 | 44 | - name: delete branch bad 45 | command: git branch -D badfeature 46 | args: 47 | chdir: /srv/app 48 | register: gitout 49 | failed_when: 50 | - gitout.rc != 0 51 | - not gitout.stderr | search('branch.*not found') 52 | 53 | 54 | - name: delete branch bad 55 | command: git branch -D badfeature 56 | args: 57 | chdir: /srv/app 58 | register: gitout 59 | failed_when: 60 | - gitout.rc != 0 61 | - not gitout.stderr | search('branch.*not found') 62 | changed_when: gitout.rc == 0 63 | 64 | 65 | #!/bin/bash 66 | rm -rf /srv/whiskey/tango 67 | mkdir /srv/whiskey/tango 68 | 69 | 70 | - name: discover tango directory 71 | stat: path=/srv/whiskey/tango 72 | register: tango 73 | - name: run frobitz 74 | script: files/frobitz --initialize /srv/whiskey/tango 75 | when: not tango.stat.exists 76 | 77 | 78 | 79 | - name: run frobitz 80 | script: files/frobitz creates=/srv/whiskey/tango 81 | 82 | 83 | - name: discover iscsi sessions 84 | command: /sbin/iscsiadm -m session 85 | register: sessions 86 | failed_when: 87 | - sessions.rc != 0 88 | - not sessions.stderr | 89 | search('No active sessions') 90 | changed_when: false 91 | 92 | 93 | --- 94 | - name: error handling 95 | hosts: localhost 96 | gather_facts: false 97 | 98 | tasks: 99 | - block: 100 | - name: delete branch bad 101 | command: git branch -D badfeature 102 | args: 103 | chdir: /srv/app 104 | 105 | - name: this task is lost 106 | debug: 107 | msg: "I do not get seen" 108 | 109 | rescue: 110 | - name: cleanup task 111 | debug: msg: "I am cleaning up" 112 | 113 | - name: cleanup task 2 114 | debug: 115 | msg: "I am also cleaning up" 116 | - name: task after block 117 | debug: 118 | msg: "Execution goes on" 119 | 120 | 121 | 122 | 123 | always: 124 | - name: most important task 125 | debug: 126 | msg: "Never going to let you down" 127 | 128 | 129 | 130 | --- 131 | - name: error handling 132 | hosts: localhost 133 | gather_facts: false 134 | 135 | tasks: 136 | - block: 137 | - name: delete branch bad 138 | command: git branch -D badfeature 139 | args: 140 | chdir: /srv/app 141 | register: gitout 142 | failed_when: 143 | - gitout.rc != 0 144 | - not gitout.stderr | search('branch.*not found') 145 | 146 | - name: this task is lost 147 | debug: 148 | msg: "I do not get seen" 149 | 150 | rescue: 151 | - name: cleanup task 152 | debug: 153 | msg: "I am cleaning up" 154 | 155 | - name: cleanup task 2 156 | debug: 157 | msg: "I am also cleaning up" 158 | 159 | always: 160 | - name: most important task 161 | debug: 162 | msg: "Never going to let you down" 163 | 164 | - name: task after block 165 | debug: 166 | msg: "Execution goes on" 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /Chapter05/script.txt: -------------------------------------------------------------------------------- 1 | -name: broken website 2 | uri: 3 | url: http://notahost.nodomain 4 | 5 | 6 | - name: broken website 7 | uri: 8 | url: http://notahost.nodomain 9 | ignore_errors: true 10 | 11 | 12 | - name: query sessions 13 | command: /sbin/iscsiadm -m session 14 | register: sessions 15 | 16 | 17 | - name: query sessions 18 | command: /sbin/iscsiadm -m session 19 | register: sessions 20 | failed_when: sessions.rc not in (0, 21) 21 | 22 | 23 | 24 | - name: delete branch bad 25 | command: git branch -D badfeature 26 | args: 27 | chdir: /srv/app 28 | 29 | 30 | - name: check if branch badfeature exists 31 | command: git branch 32 | args: 33 | chdir: /srv/app 34 | register: branches 35 | - name: delete branch bad 36 | command: git branch -D badfeature 37 | args: 38 | chdir: /srv/app 39 | when: branches.stdout | search('badfeature') 40 | 41 | 42 | 43 | - name: delete branch bad 44 | command: git branch -D badfeature 45 | args: 46 | chdir: /srv/app 47 | register: gitout 48 | failed_when: 49 | - gitout.rc != 0 50 | - not gitout.stderr | search('branch.*not found') 51 | 52 | 53 | 54 | - name: delete branch bad 55 | command: git branch -D badfeature 56 | args: 57 | chdir: /srv/app 58 | register: gitout 59 | failed_when: 60 | - gitout.rc != 0 61 | - not gitout.stderr | search('branch.*not found') 62 | changed_when: gitout.rc == 0 63 | 64 | 65 | 66 | #!/bin/bash 67 | rm -rf /srv/whiskey/tango 68 | mkdir /srv/whiskey/tango 69 | 70 | 71 | 72 | - name: discover tango directory 73 | stat: path=/srv/whiskey/tango 74 | register: tango 75 | - name: run frobitz 76 | script: files/frobitz --initialize /srv/whiskey/tango 77 | when: not tango.stat.exists 78 | 79 | 80 | - name: run frobitz 81 | script: files/frobitz creates=/srv/whiskey/tango 82 | 83 | - name: discover iscsi sessions 84 | command: /sbin/iscsiadm -m session 85 | register: sessions 86 | failed_when: 87 | - sessions.rc != 0 88 | - not sessions.stderr | 89 | search('No active sessions') 90 | changed_when: false 91 | 92 | 93 | --- 94 | - name: error handling 95 | hosts: localhost 96 | gather_facts: false 97 | 98 | tasks: 99 | - block: 100 | - name: delete branch bad 101 | command: git branch -D badfeature 102 | args: 103 | chdir: /srv/app 104 | 105 | - name: this task is lost 106 | debug: 107 | msg: "I do not get seen" 108 | 109 | rescue: 110 | - name: cleanup task 111 | debug: 112 | msg: "I am cleaning up" 113 | 114 | - name: cleanup task 2 115 | debug: 116 | msg: "I am also cleaning up" 117 | - name: task after block 118 | debug: 119 | msg: "Execution goes on" 120 | 121 | 122 | 123 | 124 | always: 125 | - name: most important task 126 | debug: 127 | msg: "Never going to let you down" 128 | 129 | 130 | --- 131 | - name: error handling 132 | hosts: localhost 133 | gather_facts: false 134 | 135 | tasks: 136 | - block: 137 | - name: delete branch bad 138 | command: git branch -D badfeature 139 | args: 140 | chdir: /srv/app 141 | register: gitout 142 | failed_when: 143 | - gitout.rc != 0 144 | - not gitout.stderr | search('branch.*not found') 145 | 146 | - name: this task is lost 147 | debug: 148 | msg: "I do not get seen" 149 | 150 | rescue: 151 | - name: cleanup task 152 | debug: 153 | msg: "I am cleaning up" 154 | 155 | - name: cleanup task 2 156 | debug: 157 | msg: "I am also cleaning up" 158 | 159 | always: 160 | - name: most important task 161 | debug: 162 | msg: "Never going to let you down" 163 | 164 | - name: task after block 165 | debug: 166 | msg: "Execution goes on" 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /Chapter06/script.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Ansible-Second-Edition/fbf9856610ba0560360b442ffffdb09875ab3b1e/Chapter06/script.txt -------------------------------------------------------------------------------- /Chapter07/script.txt: -------------------------------------------------------------------------------- 1 | --- 2 | - name: variable introspection demo 3 | hosts: localhost 4 | gather_facts: false 5 | 6 | tasks: 7 | - name: do a thing 8 | uri: 9 | url: https://derpops.bike 10 | register: derpops 11 | 12 | - name: show derpops 13 | debug: 14 | msg: "derpops value is {{ derpops }}" 15 | 16 | 17 | 18 | 19 | --- 20 | - name: variable introspection demo 21 | hosts: localhost 22 | gather_facts: false 23 | 24 | tasks: 25 | - name: do a thing 26 | uri: 27 | url: https://derpops.bike 28 | register: derpops 29 | 30 | - name: show derpops 31 | debug: 32 | var: derpops.server 33 | 34 | 35 | 36 | --- 37 | - name: variable introspection demo 38 | hosts: localhost 39 | 40 | tasks: 41 | - name: show a complex hash 42 | debug: 43 | var: ansible_default_ipv4 44 | 45 | 46 | 47 | --- 48 | - name: variable introspection demo 49 | hosts: localhost 50 | 51 | tasks: 52 | - name: show a complex hash 53 | debug: 54 | var: ansible_default_ipv4.flags[-1] 55 | 56 | 57 | 58 | {{ derp['herp'] }} 59 | 60 | 61 | 62 | {{ derp.herp }} 63 | 64 | 65 | 66 | 67 | --- 68 | - name: sub-element access styles 69 | hosts: localhost 70 | gather_facts: false 71 | vars: 72 | - derp: 73 | keys: 74 | - c 75 | - d 76 | tasks: 77 | - name: subscript style 78 | debug: 79 | var: derp['keys'] 80 | - name: dot notation style 81 | debug: 82 | var: derp.keys 83 | 84 | 85 | 86 | --- 87 | - name: sub-element access styles 88 | hosts: localhost 89 | gather_facts: false 90 | strategy: debug 91 | 92 | vars: 93 | - derp: 94 | keys: 95 | - c 96 | - d 97 | 98 | tasks: 99 | - name: subscript style 100 | debug: 101 | var: derp['keys'] 102 | 103 | - name: failing task 104 | debug: 105 | msg: "this is {{ derp['missing'] }}" 106 | 107 | - name: final task 108 | debug: 109 | msg: "my only friend the end" 110 | 111 | 112 | 113 | 114 | --- 115 | - name: remote code debug 116 | hosts: debug.example.com 117 | gather_facts: false 118 | 119 | tasks: 120 | - name: a remote module execution 121 | systemd: 122 | name: dnsmasq 123 | state: stopped 124 | enabled: no 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /Chapter08/script.txt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | 4 | 5 | import shutil 6 | 7 | def main(): 8 | module = AnsibleModule( 9 | argument_spec = dict( 10 | source=dict(required=True, type='str'), 11 | dest=dict(required=True, type='str') 12 | ) 13 | ) 14 | 15 | shutil.copy(module.params['source'], 16 | module.params['dest']) 17 | 18 | 19 | module.exit_json(changed=True) 20 | 21 | 22 | from ansible.module_utils.basic import * 23 | 24 | 25 | if __name__ == '__main__': 26 | main() 27 | 28 | 29 | --- 30 | - name: test remote_copy module 31 | hosts: localhost 32 | gather_facts: false 33 | 34 | tasks: 35 | - name: ensure foo 36 | file: 37 | path: /tmp/foo 38 | state: touch 39 | 40 | - name: do a remote copy 41 | remote_copy: 42 | source: /tmp/foo 43 | dest: /tmp/bar 44 | 45 | 46 | 47 | 48 | import shutil 49 | 50 | DOCUMENTATION = ''' 51 | --- 52 | module: remote_copy 53 | version_added: future 54 | short_description: Copy a file on the remote host 55 | description: 56 | - The remote_copy module copies a file on the remote host from a given source to a provided destination. 57 | options: 58 | source: 59 | description: 60 | - Path to a file on the source file on the remote host 61 | required: True 62 | dest: 63 | description: 64 | - Path to the destination on the remote host for the copy 65 | required: True 66 | author: 67 | - Jesse Keating 68 | ''' 69 | 70 | 71 | 72 | EXAMPLES = ''' 73 | # Example from Ansible Playbooks 74 | - name: backup a config file 75 | remote_copy: 76 | source: /etc/herp/derp.conf 77 | dest: /root/herp-derp.conf.bak 78 | ''' 79 | 80 | 81 | module.exit_json(changed=True, source=module.params['source'], 82 | dest=module.params['dest']) 83 | 84 | 85 | RETURN = ''' 86 | source: 87 | description: source file used for the copy 88 | returned: success 89 | type: string 90 | sample: "/path/to/file.name" 91 | dest: 92 | description: destination of the copy 93 | returned: success 94 | type: string 95 | sample: "/path/to/destination.file" 96 | gid: 97 | description: group ID of destination target 98 | returned: success 99 | type: int 100 | sample: 502 101 | group: 102 | description: group name of destination target 103 | returned: success 104 | type: string 105 | sample: "users" 106 | uid: 107 | description: owner ID of destination target 108 | returned: success 109 | type: int 110 | sample: 502 111 | owner: 112 | description: owner name of destination target 113 | returned: success 114 | type: string 115 | sample: "fred" 116 | mode: 117 | description: permissions of the destination target 118 | returned: success 119 | type: int 120 | sample: 0644 121 | size: 122 | description: size of destination target 123 | returned: success 124 | type: int 125 | sample: 20 126 | state: 127 | description: state of destination target 128 | returned: success 129 | type: string 130 | sample: "file" 131 | ''' 132 | 133 | 134 | 135 | 136 | facts = {'rc_source': module.params['source'], 137 | 'rc_dest': module.params['dest']} 138 | 139 | module.exit_json(changed=True, ansible_facts=facts) 140 | 141 | 142 | 143 | - name: show a fact 144 | debug: 145 | var: rc_dest 146 | 147 | 148 | 149 | - name: do a remote copy 150 | remote_copy: 151 | source: /tmp/foo 152 | dest: /tmp/bar 153 | register: mycopy 154 | 155 | - name: set facts from mycopy 156 | set_fact: 157 | rc_dest: "{{ mycopy.dest }}" 158 | 159 | 160 | module = AnsibleModule( 161 | argument_spec = dict( 162 | source=dict(required=True, type='str'), 163 | dest=dict(required=True, type='str') 164 | ), 165 | supports_check_mode=True 166 | ) 167 | 168 | 169 | 170 | if not module.check_mode: 171 | shutil.copy(module.params['source'], 172 | module.params['dest']) 173 | 174 | 175 | 176 | def cloud_truth(a): 177 | return a.replace("the cloud", "somebody else's computer") 178 | 179 | 180 | class FilterModule(object): 181 | '''Cloud truth filters''' 182 | def filters(self): 183 | return {'cloud_truth': cloud_truth} 184 | 185 | 186 | 187 | --- 188 | - name: test cloud_truth filter 189 | hosts: localhost 190 | gather_facts: false 191 | vars: 192 | statement: "I store my files in the cloud" 193 | tasks: 194 | - name: make a statement 195 | debug: 196 | msg: "{{ statement | cloud_truth }}" 197 | 198 | 199 | 200 | from ansible.plugins.callback import default 201 | class CallbackModule(default.CallbackModule): 202 | CALLBACK_VERSION = 2.0 203 | CALLBACK_TYPE = 'stdout' 204 | CALLBACK_NAME = 'shrug' 205 | def v2_on_any(self, *args, **kwargs): 206 | msg = '\xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf' 207 | self._display.display(msg.decode('utf-8') * 8) 208 | 209 | 210 | 211 | [defaults] 212 | stdout_callback = shrug 213 | 214 | 215 | 216 | #!/usr/bin/env python 217 | # 218 | 219 | 220 | import json 221 | import argparse 222 | 223 | 224 | inventory = {} 225 | inventory['web'] = {'hosts': ['mastery.example.name'], 226 | 'vars': {'http_port': 80, 227 | 'proxy_timeout': 5}} 228 | inventory['dns'] = {'hosts': ['backend.example.name']} 229 | inventory['database'] = {'hosts': ['backend.example.name'], 230 | 'vars': {'ansible_ssh_user': 'database'}} 231 | inventory['frontend'] = {'children': ['web']} 232 | inventory['backend'] = {'children': ['dns', 'database'], 233 | 'vars': {'ansible_ssh_user': 'blotto'}} 234 | inventory['errors'] = {'hosts': ['scsihost']} 235 | inventory['failtest'] = {'hosts': ["failer%02d" % n for n in 236 | range(1,11)]} 237 | 238 | 239 | 240 | allgroupvars = {'ansible_ssh_user': 'otto'} 241 | 242 | 243 | 244 | hostvars = {} 245 | hostvars['web'] = {'ansible_ssh_host': '192.168.10.25'} 246 | hostvars['scsihost'] = {'ansible_ssh_user': 'jkeating'} 247 | 248 | 249 | parser = argparse.ArgumentParser(description='Simple Inventory') 250 | parser.add_argument('--list', action='store_true', 251 | help='List all hosts') 252 | parser.add_argument('--host', help='List details of a host') 253 | args = parser.parse_args() 254 | 255 | 256 | 257 | if args.list: 258 | for group in inventory: 259 | ag = allgroupvars.copy() 260 | ag.update(inventory[group].get('vars', {})) 261 | inventory[group]['vars'] = ag 262 | print(json.dumps(inventory)) 263 | 264 | 265 | elif args.host: 266 | print(json.dumps(hostvars.get(args.host, {}))) 267 | 268 | 269 | --- 270 | - name: test the inventory 271 | hosts: all 272 | gather_facts: false 273 | 274 | tasks: 275 | - name: hello world 276 | debug: 277 | msg: "Hello world, I am {{ inventory_hostname }}. 278 | My username is {{ ansible_ssh_user }}" 279 | 280 | 281 | 282 | 283 | hostvars['scsihost'] = {'ansible_ssh_user': 'jkeating'} 284 | 285 | inventory['_meta'] = {'hostvars': hostvars} 286 | 287 | parser = argparse.ArgumentParser(description='Simple Inventory') 288 | Next we'll change the --host handling to raise an exception: 289 | elif args.host: 290 | raise StandardError("You've been a bad boy") 291 | 292 | 293 | 294 | 295 | $ source ./hacking/env-setup 296 | 297 | 298 | $ test/runner/ansible-test integration -v posix/ci/ 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | -------------------------------------------------------------------------------- /Chapter09/script.txt: -------------------------------------------------------------------------------- 1 | --- 2 | - name: boot server 3 | hosts: localhost 4 | gather_facts: false 5 | 6 | 7 | tasks: 8 | - name: boot the server 9 | os_server: 10 | auth: 11 | auth_url: "CLOUDURL" 12 | username: "jlk" 13 | password: "PASSWORD" 14 | project_name: "jlk" 15 | flavor: "1" 16 | image: "Fedora 25" 17 | key_name: "jlk" 18 | network: "internal" 19 | name: "mastery1" 20 | 21 | 22 | tasks: 23 | - name: boot the server 24 | os_server: 25 | auth: 26 | auth_url: "CLOUDURL" 27 | username: "jlk" 28 | password: "PASSWORD" 29 | project_name: "jlk" 30 | flavor: "1" 31 | image: "Fedora 25" 32 | key_name: "jlk" 33 | network: "internal" 34 | name: "mastery1" 35 | register: newserver 36 | 37 | - name: show floating ip 38 | debug: 39 | var: newserver.openstack.accessIPv4 40 | 41 | 42 | 43 | 44 | - name: add new server 45 | add_host: 46 | name: "mastery1" 47 | ansible_ssh_host: "{{ newserver.openstack.accessIPv4 }}" 48 | ansible_ssh_user: "fedora" 49 | 50 | 51 | 52 | - name: configure server 53 | hosts: mastery1 54 | gather_facts: false 55 | 56 | tasks: 57 | - name: install python 58 | raw: "sudo dnf install -y python python2-dnf" 59 | 60 | 61 | 62 | 63 | - name: install imagemagick 64 | dnf: 65 | name: "ImageMagick" 66 | become: "yes" 67 | 68 | 69 | 70 | 71 | --- 72 | - name: configure server 73 | hosts: mastery1 74 | gather_facts: false 75 | remote_user: fedora 76 | 77 | tasks: 78 | - name: install python 79 | raw: "sudo dnf install -y python python2-dnf" 80 | 81 | - name: install imagemagick 82 | dnf: 83 | name: "ImageMagick" 84 | become: "yes" 85 | 86 | 87 | 88 | 89 | FROM docker.io/fedora:25 90 | 91 | RUN dnf install -y cowsay nginx 92 | RUN "daemon off;" >> /etc/nginx/nginx.conf 93 | RUN cowsay boop > /usr/share/nginx/html/index.html 94 | 95 | EXPOSE 80 96 | 97 | CMD /usr/sbin/nginx 98 | 99 | 100 | 101 | --- 102 | - name: build an image 103 | hosts: localhost 104 | gather_facts: false 105 | 106 | tasks: 107 | - name: build that image 108 | docker_image: 109 | path: . 110 | state: present 111 | name: fedora-moo 112 | 113 | - name: start the container 114 | docker_container: 115 | name: playbook-container 116 | image: fedora-moo 117 | ports: 8080:80 118 | state: started 119 | 120 | 121 | 122 | 123 | --- 124 | - name: build an image 125 | hosts: localhost 126 | gather_facts: false 127 | 128 | tasks: 129 | - name: start the container 130 | docker_container: 131 | name: playbook-container 132 | image: docker.io/fedora:25 133 | ports: 8080:80 134 | state: started 135 | command: sleep 500 136 | 137 | - name: make a host 138 | add_host: 139 | name: playbook-container 140 | ansible_connection: docker 141 | 142 | - name: do things 143 | hosts: playbook-container 144 | gather_facts: false 145 | 146 | tasks: 147 | - name: install things 148 | raw: dnf install -y python-dnf 149 | 150 | - name: install things 151 | dnf: 152 | name: "{{ item }}" 153 | with_items: 154 | - nginx 155 | - cowsay 156 | 157 | - name: configure nginx 158 | lineinfile: 159 | line: "daemon off;" 160 | dest: /etc/nginx/nginx.conf 161 | 162 | - name: boop 163 | shell: cowsay boop > /usr/share/nginx/html/index.html 164 | 165 | - name: run nginx 166 | shell: nginx & 167 | 168 | 169 | version: "1" 170 | services: 171 | cowsay: 172 | image: docker.io/fedora:25 173 | ports: 174 | - "8081:80" 175 | command: ['nginx'] 176 | 177 | 178 | 179 | 180 | --- 181 | - hosts: cowsay 182 | gather_facts: false 183 | 184 | tasks: 185 | - name: install things 186 | raw: dnf install -y python-dnf 187 | 188 | - name: install things 189 | dnf: 190 | name: "{{ item }}" 191 | with_items: 192 | - nginx 193 | - cowsay 194 | 195 | - name: configure nginx 196 | lineinfile: 197 | line: "daemon off;" 198 | dest: /etc/nginx/nginx.conf 199 | 200 | - name: boop 201 | shell: cowsay boop > /usr/share/nginx/html/index.html 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Mastering Ansible Second Edition 5 | This is the code repository for [Mastering Ansible Second Edition]( https://www.packtpub.com/networking-and-servers/mastering-ansible-second-edition?utm_source=github&utm_medium=repository&utm_content=9781787125681), published by Packt. It contains all the supporting 6 | project files necessary to work through the book from start to finish. 7 | 8 | ## About the Book 9 | This book provides you with the knowledge you need to understand how Ansible 2.1 works at a fundamental level and leverage its advanced capabilities. You'll learn how to encrypt Ansible content at rest and decrypt data at runtime. You will master the advanced features and capabilities required to tackle the complex automation challenges of today and beyond. 10 | 11 | ## Instructions and Navigation 12 | All of the code is organized into folders. The commands and instructions will look like the following: 13 | ``` 14 | --- 15 | - name: play with a {{ var_name }} 16 | hosts: localhost 17 | gather_facts: false 18 | 19 | vars: 20 | - var_name: not-mastery 21 | 22 | tasks: 23 | - name: set a variable 24 | set_fact: 25 | task_var_name: "defined variable" 26 | 27 | - name: task with a {{ task_var_name }} 28 | debug: 29 | msg: "I am mastery task" 30 | 31 | - name: second play with a {{ task_var_name }} 32 | hosts: localhost 33 | gather_facts: false 34 | 35 | tasks: 36 | - name: task with a {{ runtime_var_name }} 37 | debug: 38 | msg: "I am another mastery task" 39 | ``` 40 | 41 | ## Related products: 42 | * [Learning Ansible 2 - Second Edition](https://www.packtpub.com/networking-and-servers/learning-ansible-2-second-edition?utm_source=github&utm_medium=repository&utm_content=9781786464231) 43 | 44 | * [Ansible 2 for Beginners [Video]](https://www.packtpub.com/networking-and-servers/ansible-2-beginners-video?utm_source=github&utm_medium=repository&utm_content=9781786465719) 45 | 46 | * [Implementing DevOps with Ansible 2](https://www.packtpub.com/networking-and-servers/implementing-devops-ansible-2?utm_source=github&utm_medium=repository&utm_content=9781787120532) 47 | 48 | ### Suggestions and Feedback 49 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSe5qwunkGf6PUvzPirPDtuy1Du5Rlzew23UBp2S-P3wB-GcwQ/viewform) if you have any feedback or suggestions. 50 | ### Download a free PDF 51 | 52 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
53 |

https://packt.link/free-ebook/9781787125681

--------------------------------------------------------------------------------