Cheat Sheet - Ansible

Iteration

Loops

- name: add several users
  user:
    name: "{{ item }}"
    state: present
    groups: "wheel"
  loop:
     - testuser1
     - testuser2

If you have defined a YAML list in a variables file, or the ‘vars’ section, you can also do:

loop: "{{ somelist }}"

Packages

Some plugins like, the yum and apt modules can take lists directly to their options, this is more optimal than looping over the task. See each action’s documentation for details, for now here is an example:

- name: optimal yum
  yum:
    name: "{{ list_of_packages }}"
    state: present

- name: non optimal yum, not only slower but might cause issues with interdependencies
  yum:
    name: "{{item}}"
    state: present
  loop: "{{ list_of_packages }}"

Note that the types of items you iterate over do not have to be simple lists of strings. If you have a list of hashes, you can reference subkeys using things like:

- name: add several users
  user:
    name: "{{ item.name }}"
    state: present
    groups: "{{ item.groups }}"
  loop:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }

Also be aware that when combining Conditionals with a loop, the when: statement is processed separately for each item. See The When Statement for an example.

To loop over a dict, use the dict2items Dict Filter:

- name: create a tag dictionary of non-empty tags
  set_fact:
    tags_dict: "{{ (tags_dict | default({})) | combine({item.key: item.value}) }}"
  loop: "{{ tags | dict2items }}"
  vars:
    tags:
      Environment: dev
      Application: payment
      Another: "{{ doesnotexist | default() }}"
  when: item.value != ""

Pick host from group:

docker_host: "{{ groups['dockerhosts'][0] }}"

Pass variables to playbook

# short
ansible-playbook release.yml -e "version=1.23.45 other_variable=foo"

# long
ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"

# as json
ansible-playbook release.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'

# from file and from cli
ansible-playbook release.yml --extra-vars "@some_file.json" --extra-vars "other_variable=foo"

Join a list

- name: Concatenate the public keys
  set_fact:
    my_joined_list: "{{ my_list | join('\n') }}"

List ansible facts

ansible -m setup -i srv-dev-app1b, all

Only gather subset of facts

ansible all -m setup -a 'gather_subset=network,virtual' -i environments/aws-eu-dev/hosts --limit=srv-dev-app1b

List hosts in inventory

ansible-inventory --list --yaml --playbook-dir=environments/development

Reliably get remote user name

ansible_user is used when we want to specify default SSH user in ansible hosts file whereas remote_user is used in playbook context: https://github.com/ansible/ansible/blob/c600ab81ee/lib/ansible/playbook/play_context.py#L46-L55

# the magic variable mapping dictionary below is used to translate
# host/inventory variables to fields in the PlayContext
# object. The dictionary values are tuples, to account for aliases
# in variable names.

MAGIC_VARIABLE_MAPPING = dict(
    connection       = ('ansible_connection',),
    remote_addr      = ('ansible_ssh_host', 'ansible_host'),
    remote_user      = ('ansible_ssh_user', 'ansible_user'),
    port             = ('ansible_ssh_port', 'ansible_port'),

Here is an example of using ansible_user in ansible hosts file:

[targets]

localhost              ansible_connection=local
other1.example.com     ansible_connection=ssh        ansible_user=mpdehaan
other2.example.com     ansible_connection=ssh        ansible_user=mdehaan

Use {{ ansible_user_id }} to detect the user name on the remote machine that Ansible operates under even if you set become: true. For the become user, use {{ ansible_become_user }} instead (default=root)

Become when default sudo is unavailable

# DC privilege workaround:
# executing commands as admin via sudo is not possible, the only way to
# use admin privileges is to become the root user with "sudo su"
ansible_become_method: "su"
ansible_become_exe: "sudo su -"

List all Ansible hostvars

Playbook: hostvars.yml

---

- hosts: all
  tasks:
    - name: Print all variables for each remote device
      debug:
        var: hostvars[inventory_hostname]

Run it with:

# Run against all hosts in the inventory
ansible-playbook -i "environments/aws-eu-dev/hosts" hostvars.yml

# Limit to specific hosts (requires "hosts: all" in the play)
ansible-playbook -i "environments/aws-eu-dev/hosts" hostvars.yml --limit=host1,host2

# Specify one or more hosts without an inventory
# Note the comma (,) at the end; this signals that it's a list, not a file.
ansible-playbook -i "host1,host2," hostvars.yml

Helper scripts

Multi vault edit

#/usr/bin/env bash

# https://docs.ansible.com/ansible/latest/user_guide/vault.html#editing-encrypted-files

SCRIPT_DIR=$(dirname "$0")
cd "${SCRIPT_DIR}/../"

vault_files=$(find ./environments -name 'vault.yml' | sort)

# handle "Input is not from a terminal" error that can break the terminal
# Vim expects its stdin to be the same as its controlling terminal but xargs
# sets it to /dev/null
# see: https://superuser.com/questions/336016/invoking-vi-through-find-xargs-breaks-my-terminal-why

case "$(uname -s)" in
   Darwin)
     echo "$vault_files" | fzf -e -m | xargs -o ansible-vault edit
     ;;

   Linux)
     echo "$vault_files" | fzf -e -m | xargs bash -c '</dev/tty ansible-vault edit "$@"' ignoreme
     ;;
esac

Bad character finder

#!/bin/bash

TARGET_PATH=${1:-$PWD}

# U+00A0  non-breaking space (NBSP)
# U+02002 en space
# U+02003 em space
# U+02004 three-per-em space
# U+02005 four-per-em space
# U+02006 six-per-em space
# U+02007 figure space
# U+02008 punctuation space
# U+02009 thin space
# U+0200A hair space
# U+0202F narrow no-break space
# U+0205F medium mathematical space
# U+03000 ideographic space

case "$(uname -s)" in

  Darwin*)
    command -v ggrep >/dev/null 2>/dev/null || { echo 'Please run: brew install gnu-grep'; exit 1; }
    command -v gsed >/dev/null 2>/dev/null || { echo 'Please run: brew install gnu-sed'; exit 1; }
    ggrep --recursive --files-with-matches --binary-files=without-match --perl-regexp "\xa0" "${TARGET_PATH}" | xargs gsed -i 's/\xC2\xA0/ /g'
  ;;

  Linux*)
    grep --recursive --files-with-matches --binary-files=without-match --perl-regexp "\xa0" "${TARGET_PATH}" | xargs sed -i 's/\xC2\xA0/ /g'
  ;;

  *)
    echo 'unsupported OS'; exit 1;
  ;;
esac

Last updated