Ansible

22 Nov 2022

What is Ansible

Ansible is an open sourceIT automation tool that automates provisioning, configuration management, application deployment, orchestration, and many other manual IT processes.

Ansible can be used to automate the installation of software, automate daily tasks, provision infrastructure, improve security and compliance, patch systems, and share automation across the entire organization using SSH.

Installation

MacOS

To install Ansible onto MacOS, we’ll be using Homebrew

brew install ansible

Verify the installation with the version command ansible --version.

ansible --version
ansible [core 2.13.4]

Linux

To install Ansible onto a Debian based family, you can use the built-in repository.

sudo apt install ansible

Verify the installation.

ansible --version

Inventory File

Ansible uses a file called “inventory” to store the computers (nodes) that can connect too and manage.

Inventory files can be formatted in INI or YAML.

A simple example of this shows two groups of servers called “groupname” and “group2” in an INI format.

[groupname]
192.168.1.10
192.168.1.11

[group2]
myserver.home.lan
192.168.1.12

In YAML this would look like:

groupname:
 hosts:
  192.168.1.10:
  192.168.1.11:
group2:
 hosts:
  myserver.home.lan:
  192.168.1.12:

We can list all hosts within an inventory file using issuing the command

ansible-inventory -i path\to\inventory --list

Inventory Children

Ansible has some default groups, they are all and ungrouped, in addition we can create children groups, as shown in the example below.

This creates a group called “myapp”, which contains the groups “app” and “db”.

[app]
192.168.1.10
192.168.1.11

[db]
192.168.1.12

[myapp:children]
app
db

In YAML this would look like:

myapp:
  app:
    hosts:
      192.168.1.10:
      192.168.1.11:
  db:
    hosts:
      192.168.1.12:

Inventory Variables

Specific host variables

Variables can be added to the inventory file, such as specific user per node, as shown in the example below:

[groupname]
192.168.1.10 ansible_user=admin

In YAML this would look like:

groupname:
 hosts:
  192.168.1.10:
   ansible_user: admin

Another simple, but effective use case are Alias’:

[groupname]
server1 ansible_host=192.168.1.10

In YAML this would look like:

groupname:
 server1:
  ansible_host: 192.168.1.10

Group variables

Variables can be created for the whole group including parent groups, as shown in the example below

[groupname]
192.168.1.10
192.168.1.11

[groupname:vars]
ansible_user=admin

In YAML this would look like:

groupname:
  hosts:
    192.168.1.10:
    192.168.1.11:
  vars:
    ansible_user: admin

Ranges

The last option I want to mention is the use of ranges for nodes, for example:

[groupname]
192.168.1.[10:11]

In YAML this would look like:

groupname:
  hosts:
    192.168.1.[10:11]

This can be used for FQDNs as well, including alphabetric ranges

groupname:
  hosts:
    www[01:10].example.com
groupname:
  hosts:
    db-[a:d].example.com

SSH Authorised Key

When connecting to hosts Ansible will use SSH authorised keys. To generate a SSH authorised key follow these steps:

  1. On the Ansible host generate a new key using ssh-keygen.
  2. Copy the key to the server using ssh-copy-id [email protected]
  3. Test the connection by SSHing onto the server

Passwords can still be used with either the switch -k or --ask-pass, but is not the recommended approach for servers, but can be useful for communicating with network devices such as switches.

First Ansible Command

Now we can execute our first Ansible command.

Note: Python must be installed on the node.

The example Ansible command below uses the inventory file, combined with a groupname to identify which computers to target for the module ping command.

The -u switch must be used if the username is different that the Ansible host, or this can be configured within the inventory file.

ansible -i pathto\inventory groupname -m ping -u admin

Another useful module is setup, this retrieves all the information Ansible can about the host.

ansible -i pathto\inventory groupname -m setup -u admin

To run ad-hoc commands you can remove the -m module switch, use the -a, argument switch. Ansible by default will use the -m command module if none is specified.

In the example below we are issuing the command free -h, which returns available memory of the nodes.

ansible -i pathto\inventory group -a "free -h" -u admin

This is the same as running:

ansible -i pathto\inventory group -m command -a "free -h" -u admin

Note: By default, Ansible connects to 5 systems at a time in parallel, these are called forks. Forks can be modified by changing the Strategy.

Ansible CFG file

Ansible configuration files can be used to create overrides to the default value. A simple example of this could be using the current directory inventory file, removing the requirement of including the inventory file path in our commands:

touch ansible.cfg
[defaults]
INVENTORY = inventory.yml

A full list of Ansible configurations can be found on the Ansible Configuration Settings page.

Limit the Hosts

There may be cases where you’ll need to limit the command to a host.

To limit, which nodes are affected you can use

ansible -i pathto\inventory --limit "db"

To exclude, or negate limit, this will apply the command to all except “db”.

ansible -i pathto\inventory all --limit 'all:!db'

Background Tasks

To run tasks asynchronously by keeping the connection open on the node until completion. This will run the task in the background.

The example below uses -b to “become” administrator, as this is a requirement of apt upgrade -y. Become can be turn on or off for globally or for a particular task if required.

The arguments required for asynchronous tasks are -B for Background, with a time in seconds before termination. The -P argument is for Polling.

ansible -i pathto\inventory group -b -B 3600 -P 0 -a "apt upgrade -y" 

To check the progress of a job you’ll need the “ansible_job_id” that is provided when you run the above command.

ansible -i pathto\inventory group -m async_status -a "jid=290649113753.244308"

Playbooks

YAML files start with a ---, this indicates the beginning of the YAML document called front matter, you can also end the document using ... at the end.

Example YAML Playbook

---
- name: Playbook Name
  hosts: groupname
  # become admin for all tasks
  become: true
  
  tasks:
    - name: Install apache
      # use package: for cross-platform
      apt:
        name: apache2
        state: present

    - name: Copy configuration files
      copy:
        src: "example.com.conf"
        dest: "/etc/apache2/sites-available/example.com.conf"
        owner: www-data
        group: www-data
        # mode: u=rwx,g=rx,o=r
        mode: 0754
          
    - name: Is Apache Started
      service:
        name: httpd
        state: started
        # enabled: true
        enabled: yes

It’s always worth running your YAML through a linter, such as YAML Lint.

To run the playbook, call the ansible-playbook command with your inventory file and the playbook you wish to run.

ansible-playbook -i inventory main.yml

Multiple Lines

Using multiple links within YAML can be achieved through:

  • Literal Block Scalar (Pipe) - |, which will include a newline and any trailing spaces.
  • Folded Block Scalar (Greater than) - >, which will fold a new line into a space.

Examples include

include_newline: |
    each new line
    is a new line
    
folded_newline: >
    a single line of text
    shown with multiple lines

Play Variables and Environment Variables

Variables can be contained within your main YAML, like in the example below.

---
- name: Playbook Name
  hosts: groupname
  
  vars:
   proxy_vars:
     http_proxy: http://proxy:80/
     https_proxy: https://proxy:443/

These variables can then be used per task.

---
- name: Playbook Name
  hosts: groupname
  
  vars:
   proxy_vars:
     http_proxy: http://proxy:80/
     https_proxy: https://proxy:443/
     
  tasks:
    - name: Taskname
      environment: proxy_vars

Variables can also be used with handlebars

---
- name: Playbook Name
  hosts: groupname
  
  vars:
   package: httpd
     
  tasks:
    - name: Taskname
      package:
        name: ""
        state: present

Variables can be included from another YAML file, as shown in the code below.

---
key: value
---
- name: Playbook Name
  hosts: groupname
  
  vars_files:
    - vars.yml

To environment variables for all nodes within the playbook, you can configure at the top level

---
- name: Playbook Name
  hosts: groupname
  
  environment:
    http_proxy: http://proxy:80/
   https_proxy: https://proxy:443/

Lastly we can use information gathered from the “setup” module and use them as variables such as `ansible_os_family’.

Handlers

Handlers run at the end of the playbook; a handler can call other handlers.

Handlers can be created then called only when a change is made on the node, for example restarting a service if the configuration has been changed.

The below handler is called via the notify command.

To run the handlers as soon as possible using the meta: flush_handlers task.

By default, handlers will not be actioned if the play fails, this can be override by using --force-handlers on the Ansible command.

handlers:
  - name: Restart apache
    ansible.builtin.service:
      name: apache
      state: restarted
      
tasks:
- name: Change Config
  ansible.builtin.template:
    src: mysite.conf
    dest: /etc/mysite.conf
  notify: Restart apache
  
- name: flush handlers immediately
  meta: flush_handlers


Modules

Commonly used modules to get started with:

  • service
  • apt
  • yum
  • package
  • get_url
  • unarchive
  • command
  • copy
  • debug20
Back to Top