Skip to content

Instantly share code, notes, and snippets.

@d3athkai
Last active May 9, 2024 11:59
Show Gist options
  • Save d3athkai/3b1c6becc41d79f45332f238791ceb3d to your computer and use it in GitHub Desktop.
Save d3athkai/3b1c6becc41d79f45332f238791ceb3d to your computer and use it in GitHub Desktop.
Ansible Tips and Tricks

Ansible Tips and Tricks

A collection of some of my useful Ansible Tips and Tricks.


Configure default Ansible user and Python interpreter

In /etc/ansible/ansible.cfg, under [defaults], add:

remote_user=<ansible-user>
interpreter_python=/usr/bin/python3

Display command output with multiple lines using .stdout_lines instead of .stdout

This will keep the output format.
ansible.builtin.debug: msg="{{ my_var.stdout_lines }}"
Note: To use the variable for other task, only my_var.stdout can be used.


Boolean

When using when condition with true/false option, it is better to use:
when: my_var | bool == true instead of when: my_var == true
when: my_var | bool == false instead of when: my_var == false


Comparing equal string between 2 variables

Giving 2 variables, var1 and var2,

  1. to compare if their values are equal:
    when: 'var1.stdout | string in var2.stdout'
  2. to compare if their values are NOT equal:
    when: 'var1.stdout | string not in var2.stdout'

and operator for when condition

There are 2 ways to use a and operator for when condition:

  1. when: ansible_distribution=="Ubuntu" and my_var | bool, OR
  2. Listing:
when:
  - ansible_distribution=="Ubuntu"
  - my_var | bool

Convert variables to upper / lower cases

You can convert any variables to upper / lower cases respectively: {{ my_var | upper }} OR {{ my_var | lower }}


Display main IPv4 Address

{{ ansible_default_ipv4.address|default(ansible_all_ipv4_addresses[0]) }}


Use flush_handlers to ensure handlers are executed immediately

By default, handlers are only executed at the end of ALL tasks. To execute the handlers immediately, add the following task at the point you want to execute immediately:
- meta: flush_handlers


Detect the correct OS distribution with ansible_distribution

ansible_distribution detects the OS correctly as compared to ansible_os_family.
Using Ubuntu as an example, ansible_distribution returns Ubuntu while ansible_os_family returns Debian.


Execute on specific OS distribution

Often you need to execute your playbooks/tasks on specific OS distribution.
For CentOS / Red Hat, use when: ansible_distribution=="CentOS" or ansible_distribution=="RedHat".
For Debian / Ubuntu, use when: ansible_distribution=="Ubuntu" or ansible_distribution=="Debian".


Detect Raspberry Pi OS

Ansible detects Raspberry Pi OS as Debian. However, sometimes we need to perform specific Ansible tasks to Raspberry Pi OS.
To better detect Raspberry Pi OS, you can use the following:
when: ansible_distribution == "Debian" and ansible_architecture == "armv7l"


Check if a restart is needed

If a restart is required in a Linux instance, it will add a file: /var/run/reboot-required.
It contains the message *** System restart required ***.
To check using Ansible:

- name: Check if restart is required
  hosts: all
  tasks:
    - name: Check that the reboot-requied exists
      ansible.builtin.stat:
        path: /var/run/reboot-required
      register: reboot_required
    - ansible.builtin.debug:
        msg: "Reboot required"
      when: reboot_required.stat.exists

Overwrite existing playbook variable(s)

When triggering a playbook, you can overwrite any existing variable(s):
ansible-playbook playbook.yml --extra-vars "var1=abc var2=xyz"


Check if a service exists before start/stop/restart that service

With postfix service as an example, first populate all the services followed by starting/stopping/restarting postfix service:

  - name: Populate service facts
    ansible.builtin.service_facts:

  - name: Restart postfix service
    ansible.builtin.systemd:
      name: postfix
      state: restarted
    when: "'postfix' in services"

Check if a file exists before updating that file

With /etc/nginx.conf file as an example, first check /etc/nginx.conf file exists before updating that file:

  - name: Check if /etc/nginx.conf exists
    ansible.builtin.stat: 
      path: /etc/nginx.conf
    register: nginx_conf_exists

  - name: Update /etc/nginx.conf
    ansible.builtin.lineinfile:
      path: /etc/nginx.conf
      ...
    when: nginx_conf_exists.stat.exists | bool == true

Running a playbook locally

Modify the playbook to run locally on the server itself:

...
hosts: localhost
connection: local
...

Execute playbook without inventory file

Adhoc execute playbook by specifying host(s) and ssh credentials manually:
ansible-playbook -v test1.yml --inventory=<host1>,<host2>, --extra-vars "ansible_user=<user> ansible_password=<password> ansible_sudo_pass=<password>"


Getting a user's home directory

Note:

  • USER is the variable.
  • {{ getent_passwd[USER][4] }} is the variable that contains the user's home directory and can be used for other tasks.
  - name: Get user's home directory
    ansible.builtin.getent:
      database: passwd
      key: "{{ USER }}"
      split: ":"

  - name: Print user's home directory
    ansible.builtin.debug:
      msg: "{{ getent_passwd[USER][4] }}"

Getting variable from other worker nodes and run on a master node

Kubernetes will be used as an example for this case.
The Ansible inventory will be configured as follows:

[k8s-master]
192.168.1.1
[k8s-workers]
192.168.1.2
192.168.1.3
[k8s:children]
k8s-master
k8s-workers

The following playbooks will get the variable from all the worker nodes and execute them on master node:

---
- name: Getting variable from other worker nodes and run on a master node
  hosts: k8s
  become: true
  become_user: root

  tasks:
  - name: Getting variable from other worker nodes and run on a master node
    ansible.builtin.command: "kubectl get node {{ ansible_hostname }}"
    delegate_to: "{{ groups['k8s-master'][0] }}"
    when: "'k8s-workers' in group_names"

Tags for import_tasks vs include_tasks

For tags in import_tasks:

  - import_tasks: my_task.yml
    tags: "1"

For tags in include_tasks:

  - include_tasks:
      file: my_task_{{ ansible_distribution }}.yml
      apply:
        tags: "1"
    tags: "1"

Reference: ansible/ansible#30882 (comment)


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment