Ansible tricks

ansible Jul 31, 2021

I want to show you some tricks and solutions that I designed along my journey automating stuff with Ansible. To use these tricks doesn't matter if you are new in the Ansible world or already are a power user.

How to learn Ansible?

If you want to learn a lot of stuff from a really competent person, Jeff Geerling has an awesome playlist on his YouTube channel. The geerlingguy (his GitHub/Twitter name account) was part of the Ansible team and still maintaining many Ansible roles and collections. This is the best source that I can recommend for any beginner and even more advanced users today (2021).

Ansible 101
Ansible 101 introduces Ansible for Linux server administration, based on the best-selling book, Ansible for DevOps.

Free Ansible Workshops available on GitHub:

GitHub - ansible/workshops: Training Course for Ansible Automation Platform
Training Course for Ansible Automation Platform. Contribute to ansible/workshops development by creating an account on GitHub.

Instruqt - Self-paced and interactive lab training:

Experience Ansible Automation Platform with Self-Paced Labs
Experience Ansible in your web browser! These labs will provide prescriptive self-paced labs to for a variety of different use-cases.

Free Red Hat introduction to Ansible and Automation Controller (AWX):

Ansible Basics: Automation Technical Overview | DO007
Learn the basics of Ansible automation and configuration management with Ansible Basics: Automation Technical Overview (DO007).

A good reference to an article from my work college John Westcott:

Tolerable Ansible
It was challenging to make Ansible correctly initiate and monitor the process, as you will see in this blog post there are features within Ansible which make it possible to handle these error conditions to achieve the desired effects for cases like this.

Basics

# Ansible requires python3 on the machine in order to work,
# but does exist one trick to install python3 with ansible:
- name: Bootstrap a host without python3 installed
  raw: dnf install -y python3
ansible: install python3
# Assuming a inventory file at ./inventory
ansible-inventory -i ./inventory --list
Convert an inventory file to a JSON format

Common structures

# Simple loops
- name: Ensure some OS packages are installed.
  apt: name="{{ item }}" state=latest
  loop:
    - git
    - golang
    - python3-pip
    - jq
  become: true
ansible: install multiple packages using apt

Install binary from remote

A common problem that I face is installing the latest version of an application from a GitHub release, so here are two examples of how to do that.

Install binary from GitHub release:

# sops
- name: Get latest 'sops' release metadata
  uri:
    url: https://api.github.com/repos/mozilla/sops/releases
    return_content: yes
  register: _sops_github_releases
  failed_when: "'.linux' not in _sops_github_releases.content"

- name: Ensure latest 'sops' release is installed
  get_url:
    url: "{{ item.browser_download_url }}"
    dest: /usr/local/bin/sops
    mode: 0755
  loop: "{{ _sops_github_releases.json[0].assets}}"
  when: "'.linux' in item.name"
  become: true
ansible: install from GitHub release and extract all files

Extract specific files from remote:

# age
- name: Get latest 'age' release metadata
  uri:
    url: https://api.github.com/repos/FiloSottile/age/releases
    return_content: yes
  register: _age_github_releases
  failed_when: "'linux-amd64.tar.gz' not in _age_github_releases.content"

- name: Ensure latest 'age' release is installed
  ansible.builtin.unarchive:
    src: "{{ item.browser_download_url }}"
    dest: /usr/local/bin
    extra_opts:
      - --strip=1
      - --wildcards
      - '*/age*'
    mode: 0755
    remote_src: yes
  loop: "{{ _age_github_releases.json[0].assets }}"
  when: "'linux-amd64' in item.name"
  become: true
ansible: install from GitHub release and extract only specific files

Conditionals

How to create more complex and host specific checks.

# requires "ansible_facts" enabled

# Conditional for "Ubuntu" only
#
#  ansible_facts['distribution'] = [Amazon, CentOS, Fedora, Ubuntu, ...]
- name: Install using apt
  apt:
    name: htop
    state: latest
  when: ansible_facts['distribution'] == "Ubuntu"

# You can also defined for OS family
#
#  ansible_facts['os_family'] = [RedHat, Debian]
- name: Install using apt
  dnf:
    name: htop
    state: latest
  when: ansible_facts['distribution'] == "RedHat"
ansible: how to check the distro family?

Content in file

Ansible offers two major ways to ensure that a text content is present in a file.

# https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html
- name: Ensure bash_autocomplete is enabled
  ansible.builtin.lineinfile:
    path: ~/.bashrc
    regexp: '^source /usr/share/bash-completion/bash_completion'
    line: source /usr/share/bash-completion/bash_completion
ansible: how to ensure a single text line in present in a file?
# https://docs.ansible.com/ansible/latest/collections/ansible/builtin/blockinfile_module.html
- name: Add multiple lines to "~/.bashrc"
  ansible.builtin.blockinfile:
    path: ~/.bashrc
    marker: "<!-- {mark} ANSIBLE MANAGED BLOCK -->"
    block: |
      source <(kubectl completion bash)
      alias k="kubectl"
ansible: how to ensure a single text line in present in a file?

Error handling

- name: Check for compatible operating system
  fail:
    msg: >-
      You are using an incompatible operating system.
      Supported operating systems are: [Fedora, Debian, Ubuntu]
  when: not ansible_facts['distribution'] in ["Fedora", "Debian", "Ubuntu"]
ansible: how to raise a helpful error message and then fail
- name: Assert "gather_facts" was run
  ansible.builtin.assert:
    that:
      - ansible_facts is defined
ansible: assert conditional, in a test language style

Check for installed package

Sometimes you may want to check if a package is installed and raise a proper error, without actually installing something or needing root access.

- name: Define required package name
  set_fact:
    package_name_check: "htop"

- name: Check "package_name_check" var exist
  fail:
    msg: "{{ package_name_check }} is not defined"
  when: package_name_check is not defined

- name: Search for the package in the DNF repos
  dnf:
    list: "{{ package_name_check }}"
  register: result

- name: Assert that the package is actually installed
  vars:
    is_package_installed: "{{ 'installed' in (result.results | map(attribute='yumstate') | list) }}"
  fail:
    msg: "The package '{{ package_name_check }}' is not installed!"
  when: is_package_installed is not true
ansible: check for installed package and raise an error

Tags

Luiz Costa

I am a senior software engineer at Red Hat / Ansible. I love automation tools, games, and coffee. I am also an active contributor to open-source projects on GitHub.