Skip to content

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#

  1. Command line always wins
  2. Connection variables in the inventory file
  3. Normal variables
  4. Other inventory variables
  5. Local facts with gather_facts
  6. 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