Ansible Playbooks Beyond The Basics
Ansible Playbooks: Beyond the Basics#
Using handlers#
handlers:
- name: restart apache
service: name=apache2 state=restarted
tasks:
- name: Enable Apache rewrite module
apache2_module: name=rewrite state=present
notify: restart apache
Notify multiple handlers#
- name: Rebuild application configuration
command: /opt/app/rebuild.sh
notify:
- restart apache
- restart memcached
Have one notifier notify another#
handlers:
- name: restart apache
service: name=apache2 state=restarted
notify: restart memcached
- name: restart memcached
service: name=memcached state=restarted
Considerations#
- Skipped tasks do not run handlers
- Handlers run once, and only once, at the end of a play
- If a play fails before handlers are notified, the handlers will never run
Environment variables#
Add a line to a remote user account in .bash_profile:
- name: Add an environment variable to the remote user's shell
lineinfile: dest=~/.bash_profile regexp=^ENV_VAR= line=ENV_VAR=value
This is only available to the shell command.
Using register#
- name: Add an environment variable to the remote user's shell
lineinfile: dest=~/.bash_profile regexp=^ENV_VAR= line=ENV_VAR=value
- name: Get the value of the environment variable we just added
shell: 'source ~/.bash_profile && echo $ENV_VAR'
register: foo
- name: Print the value of the environment variable
debug: msg="The variable is {{ foo.stdout }}"
Add to Linux environment variables#
- name: Add a global environment variable
lineinfile: dest=/etc/environment regexp=^ENV_VAR= line=ENV_VAR=value
become: yes
Per-play environment variables#
Use the environment option for a file download URL, for example:
- name: Download a file using example-proxy as a proxy
get_url: url=http://www.example.com/file.tar.gz dest=~/Downloads/
environment:
http_proxy: http://example-proxy:80/
That can become cumbersome, so sometimes it is better to use a vars section.
vars section#
vars:
var_proxy:
http_proxy: http://example-proxy:80/
https_proxy: https://example-proxy:443/
tasks:
- name: Download a file using example-proxy as a proxy
get_url: url=http://www.example.com/file.tar.gz dest=~/Downloads/
environment: var_proxy
Set a system-wide proxy#
In /etc/environment:
# In the vars section of the playbook. Set to absent to disable proxy.
proxy_state: present
# In the tasks section of the playbook.
- name: Configure the proxy
lineinfile:
dest: /etc/environment
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
state: "{{ proxy_state }}"
with_items:
- { regexp: "^http_proxy=", line: "http_proxy=http://example-proxy:80/" }
- { regexp: "^https_proxy=", line: "https_proxy=https://example-proxy:443/" }
- { regexp: "^ftp_proxy=", line: "ftp_proxy=http://example-proxy:80/" }
Test a remote variable#
ansible test -m shell -a 'echo $TEST'
Variables#
Variables are similar to Python variables, but it is better to use lowercase names and avoid starting names with numbers.
Assignments#
In an inventory file:
foo=bar
In a playbook:
foo: bar
Playbook variables#
Passing in with command line:
ansible-playbook example.yml --extra-vars "foo=bar"
Pass in a JSON or YAML file:
--extra-vars "@even_more_vars.json"
In Playbook:
- hosts: example
vars:
foo: bar
tasks:
# Prints “Variable ‘foo’ is set to bar”.
- debug: msg=”Variable ‘foo’ is set to {{ foo }}”
Or with a file vars_files:
# Main playbook file. - hosts: example vars_files: - vars.yml tasks: - debug: msg=”Variable ‘foo’ is set to {{ foo }}”
Variables in vars.yml:
---
# Variables file 'vars.yml' in the same folder as the playbook.
foo: bar
Conditionally import a vars file#
Say you have centOS which uses httpd and debian that uses apache2:
apache_CentOS.yml
apache_-default.yml
---
- hosts: example
vars_files:
- [ "apache_{{ ansible_os_family }}.yml", "apache_default.yml" ]
tasks:
- service: name={{ apache }} state=running
Inventory variables#
Example of entire setting variables inline and for a group:
# Host-specific variables (defined inline). [washington] app1.example.com proxy_state=present app2.example.com proxy_state=absent # Variables defined for the entire group.
[washington:vars] cdn_host=washington.static.example.com api_version=3.0.1
Best practice#
Ansible documentation recommends not storing variables within the inventory. Instead, you can use group_vars and host_vars YAML variable files within a specific path, and Ansible will assign them to individual hosts and groups defined in your inventory
For a host app1.example.com, create:
/etc/ansible/host_vars/app1.example.com
and add variables as normal:
---
foo: bar
baz: qux
For a group of hosts, create:
/etc/ansible/group_vars/washington
Magic vars in host and group vars#
If you ever need to retrieve a specific host’s variables from another host, Ansible provides a magic hostvars variable containing all the defined host variables.
# From any host, returns "jane".
{{ hostvars['host1']['admin_user'] }}
Registered variables#
Ansible allows you to use register to store the output of a particular command in a variable at runtime
Eg.
- name: "Node: Check list of Node.js apps running."
command: forever list
register: forever_list
changed_when: false
- name: "Node: Start example Node.js app."
command: forever start {{ node_apps_location }}/app/app.js
when: "forever_list.stdout.find('{{ node_apps_location}}/app/app.js') == -1"
Accessing variables#
Simple variables (gathered by Ansible, defined in inventory files, or defined in playbook or variable
files) can be used as part of a task using syntax like {{ variable }} .
- command: /opt/my-app/rebuild {{ my_environment }}
Lists#
A list is defined:
foo_list:
- one
- two
- three
Accessing the first element:
foo[0] # Python
foo|first # Jinja
Retrieve information from a large object#
To retrieve information about the eth0 network interface:
# In your playbook.
tasks:
- debug: var=ansible_eth0
Knowing the structure of the variable you can now access elements:
{{ ansible_eth0.ipv4.address }}
{{ ansible_eth0['ipv4']['address'] }}
Facts#
Facts are variables derived from system information.
Get a list of facts:
ansible <host> -m setup
Variable preference#
- Command line always wins
- Connection variables in the inventory file
- Normal variables
- Other inventory variables
- Local facts with
gather_facts - Role default variables in
defaults/main.yml
roles should provide sane defaults playbooks should rarely define variables command line variables should be avoided where possible