Skip to content

Ansible: Basic Usage

Note

This page contains {{ jinja2_style_variable_syntax }} in example commands or configs. Replace them with the appropriate values when using the code or values in question.

Basic Usage

Set up an inventory file

At its most basic, an inventory file contains a list of hostnames, with one per line. If no inventory file is specified, Ansible defaults to /etc/ansible/hosts

You can also create host groups by listing them under a group name specified within square brackets.

You can also set per-host or per-group variables that can be referenced in Jinja2 templates (see below). Some variables, like ansible_user, are used to change ansible's default behavior. In the following example, Ansible will attempt to connect to servers listed under [groupa] as ansible_setup_user, while it will attempt to connect to servers listed under [groupb] as administator

[groupa]
host1.a.example.com color=red location=philadelphia
host2.a.example.com color=green location=burlington
[groupb]
host1.b.example.com color=blue location=toronto

[groupa:vars]
ansible_user=ansible_setup_user

[groupb:vars]
ansible_user=administrator

Run a command on all hosts in inventory

ansible all -a {{ COMMAND }} -i {{ INVENTORY_FILE }}

Run a module on all hosts in inventory

ansible all -m {{ MODULE }} -i {{ INVENTORY_FILE }}

Group Headers

You can group hosts with Group Headers, and specifically target them by Header:

In inventory.txt:

[grouped-hosts]
grouped-host-0
grouped-host-1
Ping tagged machines:

ansible grouped-hosts -m {{ MODULE }} -i {{ INVENTORY_FILE }}

Playbooks

Playbooks are YAML files that define a more complex processes than running individual commands or modules.Since this is just a syntax and filetype-detection plugin there is nothing to configure, once a file has been identified as a Jinja file it will be highlighted appropriately. Any file with the extension .jinja will be recognised as a Jinja file.

Here is an example playbook, which ensures that Apache is up-to-date and running on a RHEL/CentOS system (copied from middlewareinventory.com)

---
  - name: Playbook
    hosts: webservers
    become: yes
    become_user: root
    tasks:
      - name: ensure apache is at the latest version
        yum:
          name: httpd
          state: latest
      - name: ensure apache is running
        service:
          name: httpd
          state: started

Running an Ansible Playbook

ansible-playbook -i {{ INVENTORY_FILE }} {{ PLAYBOOK_FILE }}

Templating

Ansible playbooks can use Jinja2 templates to dynamically generate configuration files they reference, or even parts of themselves.

For instance, I have a simple playbook I use to determine how a given Linux host's distro is identified by Ansible, helping me figure out how to modify behavior for different distros. It has the following contents

---
- name: Get Server Distribution
  hosts: all
  gather_facts: true
  tasks:
    - name: Ansible fact OS
      debug:
        msg: "Distro: {{ ansible_distribution }} (like {{ ansible_distribution_file_variety }}); Distro Family: {{ ansible_os_family }}"

See the official docs for details.

Conditionals

Conditional execution of a task can help create a more portable playbook. For example, I have a playbook called guest-init.playbook.yml that I use when setting up new Linux VMs. The following is an excerpt of that file that illustrates the usage of conditionals

    - name: Update apt cache on Debian-like systems
      ansible.builtin.apt:
        update_cache: true
      when: ansible_facts['os_family'] == "Debian"

    - name: Enable the Extra Packages for Enterprise Linux repository on RHEL-like systems
      ansible.builtin.dnf:
        name: epel-release
        state: latest
      when: ansible_facts['os_family'] == "RedHat" and ansible_facts['distribution'] != "Fedora"

    - name: Upgrade installed software (sensible distros)
      ansible.builtin.package:
        name: '*'
        state: latest
      when: ansible_facts['os_family'] != "Archlinux"

    - name: Upgrade installed software btw
      community.general.pacman:
        update_cache: yes
        upgrade: yes
      when: ansible_facts['os_family'] == 'Archlinux'

With the conditionals, I have managed to do the following: * On Debian, Ubuntu, and related systems, update the apt cache. * On RedHat-like systems, other than Fedora, enable EPEL * On non-Arch systems, update installed packages * On Arch systems, update installed packages. This is done separately because ansible.builtin.package does not work with Arch's pacman package manager.

Galaxy

Quoting the Galaxy Documentation homepage (and actually using block quotes as intended for once):

About Galaxy

Galaxy is a hub for finding and sharing Ansible content.

Use Galaxy to jump-start your automation project with great content from the Ansible community. Galaxy provides pre-packaged units of work known to Ansible as Roles, and new in Galaxy 3.2, Collections.

Installing Roles

Default path (requires root)

ansible-galaxy install {{ role_id }}

Alternate path (does not require root)

ansible-galaxy install --roles-path {{ install_path }} {{ role_id }}