Intro

Ansible is a 'radically simple', open source, IT automation platform.

Architecture

blog/ansible-overview/ansible-architecture.svg

Control Node
The Ansible control node is the central point of management for managed nodes. Configuration is pushed to the managed nodes from the control node.

Managed Node
A managed node is a device that will be configured by an ansible control node. Ansible supports many types of operating systems including network operating systems from Juniper and linux distributions such as RedHat.

Playbook
A playbook is a list of 'plays' that will be executed on the managed nodes. Plays are a list tasks that will be performed such as a yum update or copying a file.

Inventory
The inventory is a list of managed nodes that playbooks will be run against. Managed nodes in the inventory can be combined into groups, for example; Sydney and Melbourne.

Install

Requirements
Linux managed nodes require python 2.4 or later. If the python version is less than 2.5 python-simplejson is required.

Control Node
The Control node requires python 2.4 or later. Ansible is only required to be installed on the control node. Neither ansible or any agents are required on the managed nodes.

Python
Ansible can be installed via pip, python 3 support is in beta as of Ansible version 2.2. I prefer to install ansible within a virtual environment.

cmd
mkdir ~/envs
virtualenv ~/envs/ansible-env
source ~/envs/ansible-env/bin/activate
pip install ansible

Yum
RPM's are available in the EPEL release for Yum based systems such as RedHat and Centos.

Apt
Apt based systems such as Ubuntu and Debian can install via a PPA.

More details can be found in the ansible installation documentation.

Configuration

Ansible configuration can be defined in multiple locations. The following search paths will be checked for an ansible.cfg file from highest to lowest preference.

  • $ANSIBLE_CONFIG environment variable.
  • ./ansible.cfg current working directory.
  • ~/.ansible.cfg users home directory.
  • /etc/ansible/ansible.cfg default location.

When a playbook is run the first file found will be the configuration that is used, configuration files will not be merged. The ansible.cfg file uses the .ini format. Sections are defined with square brackets [defaults]. Variables are defined like so inventory=/etc/ansible/hosts.

The ansible configuration documentation has details regarding all configuration options.

Inventory

Ansible inventory files define managed nodes that playbooks can be run against. Inventories can be statically created or dynamically built from another inventory system such as CMDB.

Static Inventory
Static inventory files are similar to .ini file format. There are three types of host definitions.

  • test.lab.local singular host
  • [sydney] group of hosts
  • [australia:children] group of groups

Example static inventory

file
test.lab.local

[sydney]
host1.lab.sydney
host2.lab.sydney

[melbourne]
host1.lab.melbourne
host2.lab.melbourne

[australia:children]
sydney
melbourne

Dynamic Inventory
A dynamic inventory script can be written in any language as long as it returns valid JSON. The script must support two arguments: --list and --host and must be executable.

The --list argument must return a key/value pair of group to host mappings.

cmd
[user@control-node ~]$ ./inventoryscript --list
{
  "sydney"  : [ "host1.lab.sydney", "host2.lab.sydney" ],
  "melbourne"  : [ "host1.lab.melbourne", "host2.lab.melbourne" ]
}

The --host argument must return a key/value pair of host to variable mappings or an empty dictionary.

cmd
[user@control-node ~]$ ./inventoryscript --host host1.lab.sydney
{
    "ntp_server" : "ntp.lab.sydney",
    "syslog_server" : "syslog.lab.sydney"
}

Inventory Usage
The inventory that will be used can be defined in the following two ways from highest to lowest preference.

  • -i /path/to/inventory argument at playbook runtime
  • inventory=/path/to/inventory variable in the ansible.cfg file
The default inventory location is /etc/ansible/hosts.

Multiple inventory files can be used if the parameter used with the -i argument or the inventory variable defined in the ansible.cfg file is a directory.

If using a directory the files will be read in alphabetical order. The order of files is important if a group of groups in one file refers to a group in another file.

See the ansible inventory documentation for more details.

Playbook

Playbooks can describe a desired configuration state and/or the steps to implement change on a managed host.

A playbook defines a list of 'plays' that will be executed against managed nodes. Each play contains one or more tasks which will be executed against the defined hosts group. Task utilise modules to perform the actions on the managed nodes.

Playbooks are writen in YAML which is an easy to read and write key/value data serialization language.

Playbooks have four sections

  • hosts host or host group in an inventory file
  • vars variables that will be used in templates
  • tasks will to be executed against hosts
  • handlers perform actions like restarting services

Example Playbook

file
---
- hosts: sydney

  vars:
    ssh_port: 22

  tasks:
  - name: ensure openssh is at the latest version
    yum: name=openssh-server state=latest

  - name: write the openssh config file
    template: src=~/playbooks/templates/sshd.j2 dest=/etc/ssh/sshd_config
    notify:
    - restart sshd

  handlers:
  - name: restart sshd
    service: name=sshd state=restarted
Note
Changes made by Ansible are idempotent so it will not make any changes to managed nodes unless a configuration diff is detected.

Find out more about ansible playbooks in the official documentation.

Modules

Modules are where the magic happens in ansible. Modules are invoked with tasks in playbooks or with the ansible ad hoc command -m argument.

There are three types of modules in ansible.

  • Core ship with anisble, maintained by core ansible team
  • Extras ship with ansible, maintained by community
  • 3rd Party anisble galaxy, maintained by community
An example is the yum module that can be used to perform yum related task such as yum install.

Example yum module usage in playbook

file
tasks:
  - name: ensure openssh is at the latest version
    yum: name=openssh-server state=latest

When utilizing the ansible ad hoc command the -m module_name [-a module_args] i used to specify a module. Note the [-a module_args] is an optional parameter.

The setup module which is used to collect facts about a device is automatically run on hosts during playbook execution.

Refer to the ansible modules documention for more details.

Variables

Variables are technique for managing dynamic values throughout your code. Ansible variable names must start with a letter and only contain letters, numbers or underscores.

Variables in ansible have three scopes

  • Global set from the command line or defined in anisble.cfg file
  • Play set in plays vars and include_vars
  • Host set via host groups or on hosts by the inventory, facts or registered tasks
Within each scope there are many places a variable can be defined.

Variable Precedence
If a variable with the same name is defined in multiple places, the variable with the higher precedence will be used.

From ansible 2.x the variable precedence is as follows from most to least preferred

  • -e extra vars argument with ansible ad-hoc command
  • task vars task scope only
  • block vars task block scope
  • role vars and include_vars
  • set_facts module
  • registered vars in a play
  • vars_files in a play
  • vars_prompt in a play
  • vars in a play
  • facts gathered about a host
  • host_vars in a playbook
  • group_vars in a playbook
  • host_vars in an inventory
  • group_vars in an inventory
  • vars in a static or dynamic inventory
  • defaults file for a role

More on variable precedence can be found in the official documentation.

Facts
When a playbook is run against a host, there is an implicit module named the setup module that gathers facts about a device. Fact gathering can be disabled by setting the gather_facts: false argument.

Example facts variables

file
"ansible_all_ipv4_addresses": [
    "REDACTED IP ADDRESS"
],
"ansible_all_ipv6_addresses": [
    "REDACTED IPV6 ADDRESS"
],
...
long list of values
...
"ansible_virtualization_role": "guest",
"ansible_virtualization_type": "VMware"

Variable Usage
Depending on where a variable is being defined determines the format to set the variable. Below are some examples of how to define a variable.

  • {{ inventory_hostname }} templates and playbooks
  • ansible_hostname=host1.lab.sydney inventory files
  • hostname: host1.lab.sydney var_files and vars argument in playbook

When a variable is defined in a playbook with the {{ ... }} syntax and it starts an argument, it must be enclosed in quotes. For example: template: src='{{ base_path }}/motd.j2'

More information regarding variables can be found in the documentation.

Templates

Templates in ansible utilize the Jinja2 template language. Templates allow for the re-use of files in a DRY (dont repeat yourself) fashion. For example a template can be written for a MOTD banner that has variables that will be substituted for the inventory hostname.

Jinja2 templates are basically text files that have control structures and variables that get manipulated by the templating engine on rendering. By convention jinja2 files should end with a .j2 extension, but this is not required.

Delimiters

Jinja2 templates have the following delimiters which define functionality to be executed by the templating engine.
  • {% verbatim %}{% ... %}{% endverbatim %} statements eg: for, if and include
  • {% verbatim %}{{ ... }}{% endverbatim %} variables and expressions
  • {% verbatim %}{# ... #}{% endverbatim %} comments
  • {% verbatim %}{# ... #}{% endverbatim %} line statements (not used in ansible afaik)
Templates are used in ansible playbooks via the template module

Example template

file
This is a MOTD file for {{ inventory_hostname }}
{% if ansible_all_ipv4_addresses %}
  {% for ip in ansible_all_ipv4_addresses %}
    {{ ip }}
  {% endfor %}
{% endif %}

Example template usage in playbook

file
tasks:
  - template: src=templates/motd.j2
              dest=/etc/motd
              owner=root
              group=root
              mode="0600"

See the official Jinja2 docs for the full gamut of options available. More about the template module can be found in the official ansible docs.

Roles

Ansible roles allow for the abstraction of tasks, making them more portable and re-usable. Rather than having a playbook that builds out a web app, you could seperate the tasks into roles, for example, a role to install and configure the database, another role to install and configure the web server. You could then potentially use these roles to build out any kind of application.

Roles can be uploaded to ansible galaxy and shared with the community. To create a role there is a nice helper command anisble-galaxy init role_name which will build out the directory structure required for a role.

Example Role Directory Structure

output
roles/example_role/
|-- README.md
|-- defaults
|   `-- main.yml
|-- files
|-- handlers
|   `-- main.yml
|-- meta
|   `-- main.yml
|-- tasks
|   `-- main.yml
|-- templates
|-- tests
|   |-- inventory
|   `-- test.yml
`-- vars
    `-- main.yml

Ansible Galaxy

3rd party modules can be uploaded to ansible galaxy for re-use by others. Ansible galaxy roles are tied to a github account. Starting at ansible version 1.8 it is possible to use git sources other than ansible galaxy.

ansible-galaxy command line tool

The ansible-galaxy args cli command can be used to search, list, initialize, install and remove ansbile roles.

cmd
ansible-galaxy --help
Usage: ansible-galaxy [delete|import|info|init|install|list|login|remove|search|setup] [--help] [options] ...

ansible-galaxy search junos

Found 2 roles matching your search:

 Name                     Description
 ----                     -----------
 Juniper.junos            Network build automation of Junos devices.
 reloadzhang.junos-stdlib Network build automation of Junos devices.

A comprehensive guide to getting started with ansible galay can be found at the ansible galaxy about page.

Ansible Vault

Ansible provides a 'vault' to securely stores secrets. The ansible-vault args command is used to create, edit, encrypt and decrypt vault files. Ansible uses a python library to symetrically encrypt a file with AES 256 bit encryption using a password as the key. The default editor for ansible vault is vi, this can be changed by exporting the $EDITOR environment variable. Ansible vault files use YAML syntax for key:value pairs.

ansible-vault command line tool
cmd
ansible-vault --help
Usage: ansible-vault [create|decrypt|edit|encrypt|rekey|view] [--help] [options] vaultfile.yml

It is recommended to name the ansible vault file vault and store it under the group_vars/ directory. Vault variable names should be prefixed withvault_.

Access ansible-vault

There are three methods to tell ansible to access the vault during playbook execution:

  • --ask-vault-pass provide key at runtime.
  • --vault-password-file /path/to/file path to a file containing the key.
  • ANSIBLE_VAULT_PASSWORD_FILE via an environment variable.

More on ansible vault can be found in the official docs.

Ansible Tower

Ansible tower provides a nice web interface for controlling ansible managed nodes with enterprise features.

  • Role base access control
  • LDAP integration
  • Centralized logging
  • Playbook scheduler

Anisble tower is build on a Django web stack with a postgres backend. An ansible playbook is used to install ansible tower after an initial wizard is used to define the configuration variables.

Installation

There are three installation methods for ansible tower

  • Single machine, internal database
  • Single machine, external database
  • Active/Passive, multiple machines, external database. API only available on primary node

More on ansible tower can be found in the official documentation.