Tips & Tricks
Not your usual usage
Ansible Fest NYC 2015
#>whoami
● currently: ansible core team member (bcoca)
● helpdesk/application support
● programmer/analyst/software engineer
● QA, systems & network administrator
● release manager, DBA, information security,
● “Tech Janitor”
#>apropos ansible
● Configuration management
● Release management
● Automation framework
● Orchestration system
● Distributed batch executor
____________________
/ It runs a TASK * x 
 on a HOST * x /
--------------------
 ^__^
 (oo)_______
(__) )/
||----w |
|| ||
#LIVE>multiply_shell
● allows you to reuse your shell magic
● must be non interactive
● plays well with traditional unix tools
● just multiply by ### hosts
● requires some work for nicer outputs
● -t == json file database per host
#LIVE>ansible_shell
#>ansible webs -m shell -a "awk '{print $9}'
/var/log/nginx/access.log|sort |uniq -c |sort -k1,1nr
2>/dev/null|column -t"
web1 | success | rc=0 >>
204417 200
48108 304
8550 302
6541 301
1696 404
269 206
web2 | success | rc=0 >>
205807 200
43762 304
...
#LIVE>procmail
-- procmail
:0 #send back http code live report
* ^From.*@example.com
* ^Subject:.*500 report
|ansible-playbook ~/plays/report.yml
--- #report.yml
…
- shell: "awk '{print $9}' …
register: report
- mail: to={{lookup(‘env’,’FROM’)}} body={{report}} …
______________
< you got mail >
--------------
 , ,
 /( )`
  ___ / |
/- _ `-/ '
(//   /
/ / | ` 
O O ) / |
`-^--'`< '
(_.) _ ) /
`.___/` /
`-----' /
<----. __ / __ 
<----|====O)))==) ) /====
<----' `--' `.__,' 
| |
 /
______( (_ / ______
,' ,-----' | 
`--{__________) /
#UTIL>small_scripts
● tries not to be a programming language
● but … sometimes its very useful as such
● plays can wrap existing roles/task lists
● vars_prompt/pause allow for interactivity
● -e “var=val” for completely batch
● -e @file.json: you can use json data files
#UTIL>/sbin/departed
#!/usr/bin/ansible-playbook
---
- name: Ensure only valid users
hosts: all
gather_facts: False
sudo: True
vars_files:
#departed: [ alan, bcoca, isaac, mathew, willy ]
- /etc/departed_users.yml
tasks:
- name: Delete departed user and all it’s files
user: name={{item}} state=absent remove=yes
with_items: “{{departed}}”
#UTIL>/bin/release_apps
#!/usr/bin/ansible-playbook
- hosts: localhost
vars_prompt:
- name: app_name
prompt: “Which app do you want to deploy?”
- name: app_version
prompt: “Choose version/tag (default HEAD)”
default: ‘HEAD’
tasks:
- git: repo=git@myreposerver/{{app_version}} version={{app_version}} ...
...
- hosts: app_servers
serial: 1
tasks:
- pause: "are you sure you want to stop all services?"
- name: shush nagios
nagios: action=silence host={{inventory_hostname}}
delegate_to: {{monitor}}
- name: nginx graceful stop
service: name=nginx state=stopped
- name: stop uwsgi
service name=uwsgi state=stopped …
...
______________________
/ for reusability, use 
 includes and roles /
----------------------

 __
UooU.'@@@@@@`.
__/(@@@@@@@@@@)
(@@@@@@@@)
`YY~~~~YY'
|| ||
#QA>verify
● The same way I do things , I can check them
● Gentle learning curve for your test creator
● Checks don’t normally need root
● check_mode and diff_mode
● assert/fail, no need to read the output!
#QA>check_server
- hosts: app_server
tasks:
- users: name=appuser state=present
name: verify that app user is present
- file: path=/to/app/dir owner=appuser mode=0700
name: check that app dir has proper permissions
- service: name={{item}} state=started
name: check that services are running
with_items: [‘nginx’, ‘uwsgi’]
- postgres_user: name=dbapp1 password=secretrole_attr_flags=NOSUPERUSER
name: check app user is accessible via app server
____________________________________
/ or if your playbook is idempotent, 
 just run it again Sam! /
------------------------------------

 
 /
( )
.( o ).
#QA>check_app
- hosts: app_servers
tasks:
- stat: path=/var/run/tomcat/webapps/myapp.jar
register: jar
- assert:
that:
- jar.checksum == lookup(‘consul_kv’,‘myapp_csum’)
- stat: path=/var/run/app2.pid
- wait_for: port=8080
- uri: return_content=’app1 OK’
#AUDIT>verify --- qa?
● The same way I do, I can check
● Gentle learning curve for your auditor
● Checks don’t normally need root
● check_mode and diff_mode
#AUDIT>check_firewall
# verify firewall after manual config
- wait_for:
port: “{{item}}”
host: prod.example.com
delegate_to: outside.host.com
with_items: [‘80, ‘443’]
- wait_for: port={{item}} host=prod.example.com
delegate_to: outside.host.com
failed_when: not left_door_open|failed
register: left_door_open
when: item not in [‘80’, ‘443’]
with_sequence: start=1 end=1024
______________
< or call nmap >
--------------


___
{~._.~}
( Y )
()~*~()
(_)-(_)
#AUDIT>check_file_changes
# from vars today_file: checks/{{inventory_hostname}}/{{today}}.txt
- find: paths=/etc recurse=Y size=1 age=1d
registered: fchanged
- assert:
that:
- fchanged|length == 0
- assert:
that:
- item.checksum == lookup(‘pipe’, ‘grep ‘ + item + ‘ /checks/latest| cut -f2’)
with_items: “{{fchanged}}”
- local_action: template src=checksums.j2 dest={{today_file}}
- local_action: file src={{today_file}} path=/checks/latest state=link
______________________
/ Just until you setup 
 aide|osiris|tripwire /
----------------------


/_)o<
| 
| O . O|
_____/
#AUDIT>facts_drift
● set fact caching to use jsonfile
● make git repo or checkout in cache dir
● set incron to commit when file changes
● now git log shows facts change over time
● filter out time facts (or not)
● … so ... tower will do this for me?
#AUDIT>file_changes_xattr
● {{ansible_managed}} (changed or lack info)
● use xattr to keep metadata with the file
● requires user_xattr on mount
● great ETL, can keep correct file transforms
● does not affect copy/template ‘changed’
#HACK>expand_ansible
● roles: as shared libraries
● plugins: there are more than modules
● callbacks: send events
● notification modules: specific events
● dynamic modules: if you crave abstraction
#HACK>tidy
# tidy_expected: [‘conf1.cfg’, conf2.cfg’]
- find: paths={{tidy_path}} #/etc/myapp
register: existing
- file: path={{item.path}} state=absent
when: item.path|basename not in tidy_expected
with_items: “{{existing.files|default([ ])}}”
register: removed
- mail: body=“{{removed}}”
#HACK>ansible_events
syslog_json callback plugin
def __init__(self):
self.logger = logging.getLogger('ansible logger')
self.logger.setLevel(logging.DEBUG)
self.handler = logging.handlers.SysLogHandler(
address = (os.getenv('SYSLOG_SERVER','locahost'),
os.getenv('SYSLOG_PORT',514)),
facility=logging.handlers.SysLogHandler.LOG_USER
)
self.logger.addHandler(handler)
....
def runner_on_ok(self, host, res):
self.logger.info('RUNNER_ON_OK ' + host + ' ' 
+ json.dumps(res, sort_keys=True))
def runner_on_skipped(self, host, item=None):
self.logger.info('RUNNER_ON_SKIPPED ' + host)
#HACK>ansible_events
osx_say callback plugin
def say(msg, voice):
subprocess.call([SAY_CMD, msg, "--voice=%s" % (voice)])
def __init__(self):
# plugin disable itself if say is not present
if not os.path.exists(SAY_CMD):
self.disabled = True
print "%s does not exist, plugin %s disabled" % 
(SAY_CMD, os.path.basename(__file__))
…
def runner_on_failed(self, host, res, ignore_errors=False):
say("Failure on host %s" % host, FAILED_VOICE)
def runner_on_ok(self, host, res):
say("pew", LASER_VOICE)
________________________________________________
/ https://github.com/mpdehaan/ansible-and-juliet 
 /
------------------------------------------------


,;;;;;;;,
;;;;;;;;;;;,
;;;;;'_____;'
;;;(/))))|((
_;;((((((|))))
/ |_
.--~(  ~))))))))))))
/  `-(((((((((((
| | ` ) | /|)
| | `. _/ _____/ |
| , `~ /
|   /
| `. `| /
| ~- ` /
____~._/~ -_, (
|-----|  ';;
| | :;;;' 
| / | |
| | |
#HACK>executing tasks
- action:
module: <module name>
- action: <module name>
- <module name>:
● optionally ‘local_action’ instead of ‘action’
● module name as a variable {{mymodule}}
#HACK>abstract package
- include_vars: “{{ansible_os_distribution|default
(‘default’)}}.yml”
- name: install apache
action: “{{ansible_pkg_mgr}} name={{item}}
state=present”
with_items: “{{apache_pkgs}}”
- template:
src: “{{apache_config}}.j2”
dest: /etc/{{apache_config}}
owner: “{{apache_user}}”
group: “{{apache_group}}”
notify: “apache_restart”
#HACK>abstract package
Redhat.yml
---
apache_user: httpd
apache_group: httpd
apache_config: /etc/httpd/conf/httpd.conf
apache_pkgs:
- httpd
- mod_ssl
- php-fpm
apache_service: httpd
#HACK>abstract package
Debian.yml
---
apache_user: www-data
apache_group: www-data
apache_config: /etc/apache2/httpd.conf
apache_pkgs:
- apache2-mpm
- libapache2-mod-ssl
- php5-fpm
apache_service: apache2
__________________
/ can break 
 apachectl utils /
------------------


.--.
|o_o |
|:_/ |
//  
(| | )
/'_ _/`
___)=(___/
#THE END>wait 6 && exit
● Ansible was born to play well with Unix
● Roles allow for reuse and sharing
● Plugins are where you code
● Plugins are useful to non programmers.
● callbacks, lookups, filters, etc are also plugins
● Many ways to make Ansible work for you
__________
< goodbye! >
----------
 ^__^
 (oo)_______
(__) )/
||----w |
|| ||

Ansible tips & tricks

  • 1.
    Tips & Tricks Notyour usual usage Ansible Fest NYC 2015
  • 2.
    #>whoami ● currently: ansiblecore team member (bcoca) ● helpdesk/application support ● programmer/analyst/software engineer ● QA, systems & network administrator ● release manager, DBA, information security, ● “Tech Janitor”
  • 3.
    #>apropos ansible ● Configurationmanagement ● Release management ● Automation framework ● Orchestration system ● Distributed batch executor ____________________ / It runs a TASK * x on a HOST * x / -------------------- ^__^ (oo)_______ (__) )/ ||----w | || ||
  • 4.
    #LIVE>multiply_shell ● allows youto reuse your shell magic ● must be non interactive ● plays well with traditional unix tools ● just multiply by ### hosts ● requires some work for nicer outputs ● -t == json file database per host
  • 5.
    #LIVE>ansible_shell #>ansible webs -mshell -a "awk '{print $9}' /var/log/nginx/access.log|sort |uniq -c |sort -k1,1nr 2>/dev/null|column -t" web1 | success | rc=0 >> 204417 200 48108 304 8550 302 6541 301 1696 404 269 206 web2 | success | rc=0 >> 205807 200 43762 304 ...
  • 6.
    #LIVE>procmail -- procmail :0 #sendback http code live report * ^From.*@example.com * ^Subject:.*500 report |ansible-playbook ~/plays/report.yml --- #report.yml … - shell: "awk '{print $9}' … register: report - mail: to={{lookup(‘env’,’FROM’)}} body={{report}} … ______________ < you got mail > -------------- , , /( )` ___ / | /- _ `-/ ' (// / / / | ` O O ) / | `-^--'`< ' (_.) _ ) / `.___/` / `-----' / <----. __ / __ <----|====O)))==) ) /==== <----' `--' `.__,' | | / ______( (_ / ______ ,' ,-----' | `--{__________) /
  • 7.
    #UTIL>small_scripts ● tries notto be a programming language ● but … sometimes its very useful as such ● plays can wrap existing roles/task lists ● vars_prompt/pause allow for interactivity ● -e “var=val” for completely batch ● -e @file.json: you can use json data files
  • 8.
    #UTIL>/sbin/departed #!/usr/bin/ansible-playbook --- - name: Ensureonly valid users hosts: all gather_facts: False sudo: True vars_files: #departed: [ alan, bcoca, isaac, mathew, willy ] - /etc/departed_users.yml tasks: - name: Delete departed user and all it’s files user: name={{item}} state=absent remove=yes with_items: “{{departed}}”
  • 9.
    #UTIL>/bin/release_apps #!/usr/bin/ansible-playbook - hosts: localhost vars_prompt: -name: app_name prompt: “Which app do you want to deploy?” - name: app_version prompt: “Choose version/tag (default HEAD)” default: ‘HEAD’ tasks: - git: repo=git@myreposerver/{{app_version}} version={{app_version}} ... ... - hosts: app_servers serial: 1 tasks: - pause: "are you sure you want to stop all services?" - name: shush nagios nagios: action=silence host={{inventory_hostname}} delegate_to: {{monitor}} - name: nginx graceful stop service: name=nginx state=stopped - name: stop uwsgi service name=uwsgi state=stopped … ... ______________________ / for reusability, use includes and roles / ---------------------- __ UooU.'@@@@@@`. __/(@@@@@@@@@@) (@@@@@@@@) `YY~~~~YY' || ||
  • 10.
    #QA>verify ● The sameway I do things , I can check them ● Gentle learning curve for your test creator ● Checks don’t normally need root ● check_mode and diff_mode ● assert/fail, no need to read the output!
  • 11.
    #QA>check_server - hosts: app_server tasks: -users: name=appuser state=present name: verify that app user is present - file: path=/to/app/dir owner=appuser mode=0700 name: check that app dir has proper permissions - service: name={{item}} state=started name: check that services are running with_items: [‘nginx’, ‘uwsgi’] - postgres_user: name=dbapp1 password=secretrole_attr_flags=NOSUPERUSER name: check app user is accessible via app server ____________________________________ / or if your playbook is idempotent, just run it again Sam! / ------------------------------------ / ( ) .( o ).
  • 12.
    #QA>check_app - hosts: app_servers tasks: -stat: path=/var/run/tomcat/webapps/myapp.jar register: jar - assert: that: - jar.checksum == lookup(‘consul_kv’,‘myapp_csum’) - stat: path=/var/run/app2.pid - wait_for: port=8080 - uri: return_content=’app1 OK’
  • 13.
    #AUDIT>verify --- qa? ●The same way I do, I can check ● Gentle learning curve for your auditor ● Checks don’t normally need root ● check_mode and diff_mode
  • 14.
    #AUDIT>check_firewall # verify firewallafter manual config - wait_for: port: “{{item}}” host: prod.example.com delegate_to: outside.host.com with_items: [‘80, ‘443’] - wait_for: port={{item}} host=prod.example.com delegate_to: outside.host.com failed_when: not left_door_open|failed register: left_door_open when: item not in [‘80’, ‘443’] with_sequence: start=1 end=1024 ______________ < or call nmap > -------------- ___ {~._.~} ( Y ) ()~*~() (_)-(_)
  • 15.
    #AUDIT>check_file_changes # from varstoday_file: checks/{{inventory_hostname}}/{{today}}.txt - find: paths=/etc recurse=Y size=1 age=1d registered: fchanged - assert: that: - fchanged|length == 0 - assert: that: - item.checksum == lookup(‘pipe’, ‘grep ‘ + item + ‘ /checks/latest| cut -f2’) with_items: “{{fchanged}}” - local_action: template src=checksums.j2 dest={{today_file}} - local_action: file src={{today_file}} path=/checks/latest state=link ______________________ / Just until you setup aide|osiris|tripwire / ---------------------- /_)o< | | O . O| _____/
  • 16.
    #AUDIT>facts_drift ● set factcaching to use jsonfile ● make git repo or checkout in cache dir ● set incron to commit when file changes ● now git log shows facts change over time ● filter out time facts (or not) ● … so ... tower will do this for me?
  • 17.
    #AUDIT>file_changes_xattr ● {{ansible_managed}} (changedor lack info) ● use xattr to keep metadata with the file ● requires user_xattr on mount ● great ETL, can keep correct file transforms ● does not affect copy/template ‘changed’
  • 18.
    #HACK>expand_ansible ● roles: asshared libraries ● plugins: there are more than modules ● callbacks: send events ● notification modules: specific events ● dynamic modules: if you crave abstraction
  • 19.
    #HACK>tidy # tidy_expected: [‘conf1.cfg’,conf2.cfg’] - find: paths={{tidy_path}} #/etc/myapp register: existing - file: path={{item.path}} state=absent when: item.path|basename not in tidy_expected with_items: “{{existing.files|default([ ])}}” register: removed - mail: body=“{{removed}}”
  • 20.
    #HACK>ansible_events syslog_json callback plugin def__init__(self): self.logger = logging.getLogger('ansible logger') self.logger.setLevel(logging.DEBUG) self.handler = logging.handlers.SysLogHandler( address = (os.getenv('SYSLOG_SERVER','locahost'), os.getenv('SYSLOG_PORT',514)), facility=logging.handlers.SysLogHandler.LOG_USER ) self.logger.addHandler(handler) .... def runner_on_ok(self, host, res): self.logger.info('RUNNER_ON_OK ' + host + ' ' + json.dumps(res, sort_keys=True)) def runner_on_skipped(self, host, item=None): self.logger.info('RUNNER_ON_SKIPPED ' + host)
  • 21.
    #HACK>ansible_events osx_say callback plugin defsay(msg, voice): subprocess.call([SAY_CMD, msg, "--voice=%s" % (voice)]) def __init__(self): # plugin disable itself if say is not present if not os.path.exists(SAY_CMD): self.disabled = True print "%s does not exist, plugin %s disabled" % (SAY_CMD, os.path.basename(__file__)) … def runner_on_failed(self, host, res, ignore_errors=False): say("Failure on host %s" % host, FAILED_VOICE) def runner_on_ok(self, host, res): say("pew", LASER_VOICE) ________________________________________________ / https://github.com/mpdehaan/ansible-and-juliet / ------------------------------------------------ ,;;;;;;;, ;;;;;;;;;;;, ;;;;;'_____;' ;;;(/))))|(( _;;((((((|)))) / |_ .--~( ~)))))))))))) / `-((((((((((( | | ` ) | /|) | | `. _/ _____/ | | , `~ / | / | `. `| / | ~- ` / ____~._/~ -_, ( |-----| ';; | | :;;;' | / | | | | |
  • 22.
    #HACK>executing tasks - action: module:<module name> - action: <module name> - <module name>: ● optionally ‘local_action’ instead of ‘action’ ● module name as a variable {{mymodule}}
  • 23.
    #HACK>abstract package - include_vars:“{{ansible_os_distribution|default (‘default’)}}.yml” - name: install apache action: “{{ansible_pkg_mgr}} name={{item}} state=present” with_items: “{{apache_pkgs}}” - template: src: “{{apache_config}}.j2” dest: /etc/{{apache_config}} owner: “{{apache_user}}” group: “{{apache_group}}” notify: “apache_restart”
  • 24.
    #HACK>abstract package Redhat.yml --- apache_user: httpd apache_group:httpd apache_config: /etc/httpd/conf/httpd.conf apache_pkgs: - httpd - mod_ssl - php-fpm apache_service: httpd
  • 25.
    #HACK>abstract package Debian.yml --- apache_user: www-data apache_group:www-data apache_config: /etc/apache2/httpd.conf apache_pkgs: - apache2-mpm - libapache2-mod-ssl - php5-fpm apache_service: apache2 __________________ / can break apachectl utils / ------------------ .--. |o_o | |:_/ | // (| | ) /'_ _/` ___)=(___/
  • 26.
    #THE END>wait 6&& exit ● Ansible was born to play well with Unix ● Roles allow for reuse and sharing ● Plugins are where you code ● Plugins are useful to non programmers. ● callbacks, lookups, filters, etc are also plugins ● Many ways to make Ansible work for you __________ < goodbye! > ---------- ^__^ (oo)_______ (__) )/ ||----w | || ||