Ansible

Overview

I use Ansible1 a lot, both at work and for the sudo.is infrastructure2, see my infra repo.

Install

Use pipx:

$ pipx install --include-deps ansible

To install additonal Python dependencies, use inject:

$ pipx inject ansible $dependency

Upgrading existing Ansible:

$ pipx upgrade --include-injected ansible

Note on using ansible.utils.ipaddr

Ansible is using the deprecated IPAddress.is_private function in netaddr to check if an IP address is private or public3.

{{ [ansible_default_ipv4.address] | ansible.utils.ipaddr("private") }}

This will now result in:

TASK [role: task] ***************************
An exception occurred during task execution.

fatal: [sudo.is]: FAILED! => {
    "changed": false
}

MSG:
AttributeError: 'IPNetwork' object has no attribute 'is_private'

Until utils.ipaddr is fixed in the base ansible_collections is fixed, older version of netaddr where IPAddress.is_private needs to be used.

$ pipx inject ansible netaddr==0.10.0

This has been fixed in ansible-collections/ansible.utils4, but has not been released yet.

Not supported URL scheme http+docker

If the target host has version 2.32.3 of python3-requests, you may get this error on community.docker tasks:

TASK [docker ] ******************************
fatal: [sudo.is]: FAILED! => {
    "changed": false
}

MSG:

Error connecting: Error while fetching server API version: Not supported URL scheme http+docker

That is because requests=2.32.0 is not compatible with the /var/run/docker.sock unix socket5. Version 3.10.2 and up of community.docker has a workaround to this issue6 (using a new and different API in reqests).

$ ansible-galaxy collection install community.docker --force

The --force flag is needed since the collection is already installed, and will install the newer version.

Configuration

Re-use SSH connections

For faster ssh connections, enable pipelining and use the ControlPersis features of ssh to re-use SSH connections instead of always reconnecting:

[defaults]
forks = 20

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o PreferredAuthentications=publickey
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
pipelining = true

This will re-use connections for 60s and then close them.

Output formats

Ansible calls output formats "callbacks"7:

  • debug: indents Ansible's output and data structures, this is a good and sane default to use.

The output format/callback can be set in the ansible.cfg config files:

[defaults]
stdout_callback = debug

The output format can also be set via an environment variable:

ANSIBLE_STDOUT_CALLBACK="debug"

Since environment variables take precedence over the config file, this will use the output format debug regardless of config file values.

ansible_managed

By default, the ansible_managed variable will render to "Ansible managed", but it can provide more useful information, such as the path of the template in a Git repo.

[defaults]
ansible_managed = ansible_managed:{{{{ lookup('pipe', 'git rev-parse --show-toplevel')|basename }}}}/roles/{{{{ role_name }}}}/{{{{ template_path }}}}{{{{ lookup('pipe', 'git log -1 ' + template_fullpath|quote) | default(false, true) | ternary("", ",UNCOMITTED") }}}}

This will include the path to the source template, as well as the short commit hash that the file is templated from.

Default to showing diff

To make Ansible show diffs by default:

[diff]
always = true

Makes --diff the default for ansible-playbook and friends.

Convergence

Prevent ansible from printing every ok task with:

ANSIBLE_DISPLAY_SKIPPED_HOSTS="false"
ANSIBLE_DISPLAY_OK_HOSTS="false"

This will only print failed or changed tasks to stdout, makes it easy to parse the output into a convergence report.

References

2

git.sudo.is/ben/infra - Ansible for sudo.is (mirrored to GitHub: benediktkr/infra)

9

Ansible Callback-Plugins, includes GIFs of the output in action.