(2017, 2019)

Ansible is an IT automation package. Its big competitor is SaltStack.


Installation & Getting Started

Ansible works over ssh, so we don’t need to install anything on clients. The control machine must have Python (2.6+ or 3.5+).

# apt-get install ansible

Even on the control machine, there’s no database, no running daemon, just commands to run as needed.

Ansible uses any settings it finds in ~/.ssh/config (e.g., jump host). By default, it uses SFTP but can be configured to use SCP. Ansible assumes the use of SSH keys, but can be given the --ask-pass or --ask-become-pass option.

Ansible doesn’t just have to connect remotely over SSH. The transports are pluggable, and there are options for managing things locally, as well as managing chroot, lxc, and jail containers. A mode called ‘ansible-pull’ can also invert the system and have systems ‘phone home’ via scheduled git checkouts to pull configuration directives from a central repository.

Basic definitions:

Ansible looks for its configuration in this order:

  1. ANSIBLE_CONFIG environment variable
  2. $PWD/ansible.cfg
  3. $HOME/ansible.cfg
  4. /etc/ansible/ansible.cfg

The config file defines the location of the hosts inventory:

inventory      = /etc/ansible/hosts


Ansible acts on multiple machines from the inventory. A dynamic inventory is possible (LDAP, Cobbler, etc.), but the file /etc/ansible/hosts is the default inventory (or specify with the -i flag):

# Ex 1: Ungrouped hosts, specify before any group headers.

For hosts with non-22 SSH ports, specify them like The inventory can contain variable definitions, per-host or per-group, for later use in playbooks: http_port=8080

Make groups-of-groups using the :children suffix:


Two implicit groups exist: all and ungrouped.

Have Ansible scan the hosts file to list all hosts it recognizes or all hosts from a particular group:

$ ansible all --list-hosts
$ ansible webservers --list-hosts

Check which of the hosts in the inventory respond to pings:

$ansible all -m ping | SUCCESS => {
	"changed": false,
	"ping": "pong"
} | SUCCESS => {
	"changed": false,
	"ping": "pong"
} | SUCCESS => {
	"changed": false,
	"ping": "pong"
} | SUCCESS => {
	"changed": false,
	"ping": "pong"
} | SUCCESS => {
	"changed": false,
	"ping": "pong"

Ad-Hoc Commands

Ad-hoc commands are things too quick, simple, or single-use to write as a playbook.

Ping all the hosts:

$ ansible all -m ping

We have a [kvm] group of hypervisors specified in /etc/ansible/hosts. What guests are on each?

$ ansible kvm --ask-become-pass --become -a "virsh list --all"

For commands that require shell features (even ones as basic as pipes) use the “shell” module:

$ ansible detroit -m shell -a 'echo $TERM'

(The default module is command.)

The -f flag sets the number of parallel processes (default 5).

$ ansible kvm -f 10 -m shell -a 'cat /etc/group | tac'

So far we’ve been demoing simple command execution, but most Ansible modules are not simple imperative scripts. Instead, they use a declarative model, calculating and executing the actions required to reach a specified final state. Furthermore, they achieve a form of idempotence by checking the current state before they begin, and if the current state matches the specified final state, doing nothing. However, we also recognize that running arbitrary commands can be valuable, so Ansible easily supports both.

Transfer a file directly to many servers:

$ ansible atlanta -m copy -a "src=/etc/hosts dest=/tmp/hosts"

Change file ownership or permissions with the file module:

$ ansible webservers -m file -a "dest=/srv/foo/b.txt mode=600 owner=mdehaan group=mdehaan"

Delete directories (recursively) and delete files:

$ ansible webservers -m file -a "dest=/path/to/c state=absent"

Add or remove users:

$ ansible all -m user -a "name=foo password=<crypted password here>"
$ ansible all -m user -a "name=foo state=absent"

Trigger a Git pull:

$ ansible webservers -m git -a "repo= dest=/srv/myapp version=HEAD"

Make sure a service is started, restarted, or stopped:

$ ansible webservers -m service -a "name=httpd state=started"
$ ansible webservers -m service -a "name=httpd state=restarted"
$ ansible webservers -m service -a "name=httpd state=stopped

Long-running operations can run in the background, with optional timeout and status polling:

$ ansible all -B 3600 -P 0 -a "/usr/bin/long_running_operation --do-stuff"
$ ansible -m async_status -a "jid=488359678239.2844"
$ ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"

Report “facts” — discovered variables about the client(s):

$ ansible all -m setup


Playbooks are Ansible’s configuration, deployment, and orchestration language.

Write playbooks in YAML.

Before trying to edit YAML with Vim, add this to your .vimrc:

au! BufNewFile,BufReadPost *.{yaml,yml} set filetype=yaml foldmethod=indent
autocmd FileType yaml setl ts=2 sw=2 sts=2 expandtab

Each playbook lists one or more “plays”. A “play” links hosts to tasks. Tasks are calls to Ansible modules.

- hosts: webservers
	http_port: 80
	max_clients: 200
  remote_user: root
  - name: ensure apache is at the latest version
	yum: name=httpd state=latest
  - name: write the apache config file
	template: src=/srv/httpd.j2 dest=/etc/httpd.conf
	- restart apache
  - name: ensure apache is running (and enable it at boot)
	service: name=httpd state=started enabled=yes
	- name: restart apache
	  service: name=httpd state=restarted

Run a playbook:

$ ansible-playbook myplaybook.yml

Check the playbook syntax:

$ ansible-playbook --check myplaybook.yml

Lint a playbook (Debian has an ansible-lint package):

$  ansible-lint myplaybook.yml

Walk through a playbook step-by-step:

$ ansible-playbook --step myplaybook.yml

Make a playbook executable and head it with this shebang like to run it like a shell script:


A playbooks can contain {{ variables }}. Register the results of shell commands as variables like:

  - shell: /usr/bin/foo
    register: foo_result
    ignore_errors: True


Show available modules:

$ ansible-doc -l

Ansible offers many modules from the very basic and universal to the esoteric.

Interesting modules include: apt, at, copy, cron, dnf, docker*, git, group, hostname, htpasswd, include, ipnetns (v2.5+), iptables, letsencrypt, lvol (lvm), mail, mount, mysql, nmcli, package, sysctl, systemd, user, virt (libvirt), vyos_, win_*, yum.

Ansible Vault

Ansible Vault stores secrets. Vault does file-level symmetric encryption. One of the main problems it solves is preserving secrets as part of a versioned repository without exposing them.

$  ansible-vault create secrets.yml
New Vault password:
Confirm New Vault password:
$  cat secrets.yml 
$  ansible-vault edit secrets.yml
Vault password:
$  ansible-vault view secrets.yml
Vault password:
A very secret thing.
$  ansible-vault rekey secrets.yml
Vault password:
New Vault password:
Confirm New Vault password:
Rekey successful

How is this more useful that any generic symmetric encryption utility? We can name the secret values, and use them as variables in an Ansible playbook.

$  ansible-vault encrypt_string --stdin-name 'key-for-secret'
New Vault password: 
Confirm New Vault password: 
Reading plaintext input from stdin. (ctrl-d to end input)
My secret value.key-for-secret: !vault |
Encryption successful

(Terminate value entry with CTRL-d, not ENTER!)

Paste the resulting encrypted variable definition into the Ansible playbooks along with any other variables. Use the variable later in the playbook by enclosing it in (quoted for YAML safety) Jinja-style double-curly-brackets.

- hosts: webservers
    http_port: 8080
    key-for-secret: !vault |
- name: Add web config
    set_passwd: "{{ key-for-secret }}"

When running a playbook with encrypted variables, use --ask-vault-pass:

ansible-playbook --ask-vault-pass -i inventory_file my_playabook.yml