Ansible Concepts
Tags: ansible, linux, automation, devops
Published: 2021-07-17 00:00:00

---
title: "Ansible Concepts"
date: "2021-07-17"
lastmod: "2021-10-06"
summary: "What is Ansible? Explore main concepts, identify key components, understand content structure and organization, compare to traditional shell scripting"
tags:
  - ansible
  - linux
  - automation
  - devops
---
## Ansible Overview

**Ansible** is an automation engine that executes tasks described in **YAML** + **Jinja2** to bring the target system to a known state.

At its core, **Ansible** is a python module:

```shell
(ansible) [sysadmin@cnt84 ~]$ pip list | egrep -i -- 'ansible|jinja|yaml|markup'
ansible      4.3.0
ansible-core 2.11.3
Jinja2       3.0.1
MarkupSafe   2.0.1
PyYAML       5.4.1
```

The engine along with other helpers are available as stand-alone programs that can be run directly from the command line:

- `ansible` : used to run individual tasks.
- `ansible-playbook` : used to execute a batch of tasks in multiple targets.
- `ansible-config` : used to define engine configuration settings.
- `ansible-doc` : used to show documentation of several ansible components.
- `ansible-galaxy` : used to interact with the ansible packaging system.

## Ansible Tasks

The minimum execution unit in **Ansible** is a task. Each task will focus on managing a particular component of the target host.

The main difference between an **Ansible Task** and traditional shell script commands is that the task defines the _what_ (target state) and not the _how_ (action).

For example, to set file permissions using a **Bash** script you need to define _how_ it will be done:

```bash
#!/bin/bash

echo "Example: Set file permissions"
/usr/bin/chmod '0644' '/tmp/test.file'
```

To achieve the same result using **Ansible Tasks** you define _what_ will be the end-state of the component:

```yaml
---
- name: "Example: Set file permissions"
  ansible.builtin.file:
    mode: "0644"
    path: "/tmp/test.file"
```

Here the component is the Linux file `/tmp/test.file` managed by the `ansible.builtin.file` **Ansible Module** and the end-state is defined by the `mode` attribute.

See below common shell script actions and their equivalent **Ansible Tasks**

| Bash                               | Ansible                                             |
| ---------------------------------- | --------------------------------------------------- |
| Action: Copy FileA to DestinationB | Target State: FileA must be present in DestinationB |
| Action: Set FileA Owner to UserX   | Target State: UserX must own FileA                  |
| Action: Start the ServiceX         | Target State: ServiceX must be in started state     |
| Action: Install PackageX           | Target State: PackageX must be installed            |

## Ansible Modules

**Ansible Modules** are used to represent components in **Ansible Tasks** and are intended to hide the complexity of how the component is manipulated to achieve the desired end-state.

A set of modules are already included in **Ansible** for managing common components. For example:

| Component        | Module Name     | End-State                                                   |
| ---------------- | --------------- | ----------------------------------------------------------- |
| Linux File       | copy            | Source File is present in the Target Path                   |
| Linux File       | file            | File attributes are set (ownership, permissions, etc)       |
| Linux File       | lineinfile      | The text line is included (or not) in the target file       |
| Linux OS Package | package         | The OS package is present (or not) in the target host       |
| Linux OS Service | service         | The OS service is started (or not) in the target host       |
| OS User          | user            | OS User is created (or not) in the target host              |
| OpenSSH          | authorized_keys | OpenSSH key is present (or not) in the authorized_keys file |

Additional modules developed by product owners or the OSS community are available at the _Ansible Galaxy_ site and can be installed using the `ansible-galaxy` command.

For example, to add the `openssh_keypair` **Ansible Module** included in the community provided `community.crypto.openssh_keypair` **Ansible Collection**:

```shell
(ansible) [sysadmin@cnt84 ~]$ ansible-galaxy collection install community.crypto
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/community-crypto-1.7.1.tar.gz to /home/sysadmin/.ansible/tmp/ansible-local-321219he000qkm/tmpd12y1_sb/community-crypto-1.7.1-xrp950jz
Installing 'community.crypto:1.7.1' to '/home/sysadmin/.ansible/collections/ansible_collections/community/crypto'
community.crypto:1.7.1 was installed successfully

```

## Ansible Playbooks

Similar to what scripts are for traditional shells like _bash_, **Ansible Playbooks** are _YAML_ files used to create automation jobs.

The minimum **Ansible Playbook** contains one or more **Ansible Tasks** and the explicit declaration of the target host where the end-state will be set.

The following example playbook sets `test-server21` as the target host where the end-state for the package component will be set. In this case, the desired end-state is to have the package `lsof` installed.

```yaml
- hosts: test-server21
  tasks:
    - name: "Deploy LSOF tool"
      ansible.builtin.package:
        name:
          - "lsof"
      state: "present"
```

**Ansible Playbooks** can contain additional features to facilitate the creation of complex automation jobs:

- Job management: defines how tasks are going to be executed based on the number of target hosts (sequentially, in parallel, etc.)
- Error handling: provides features for recovering from failed tasks.
- Event Handlers: defines special tasks that are only executed when certain conditions are met. For example, the application X configuration reload handler is executed when the configuration update task sets a new parameter value
- External Variable Definition: allows the inclusion of **YAML** files that contains variables definitions only.
- Roles: allows the inclusion of roles. Roles are similar to **Ansible Modules** but implemented using **Ansible Tasks**.

To run **Ansible Playbooks** use the command `ansible-playbook`:

```shell
(ansible) [sysadmin@cnt84 ~]$ ansible-playbook hello_world.yml

PLAY [localhost] *****************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************
ok: [localhost]

TASK [Hello World] ***************************************************************************************************
ok: [localhost] => {
    "msg": "Hello World"
}

PLAY RECAP ***********************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

## YAML and Jinja

**Ansible** doesn't have a language on its own for defining tasks.
Instead, it uses the data definition language **YAML** for declaring desired end-state and the template engine **Jinja2** for embedding simple programming functions into **YAML** files.

For example, let's define the `file_path` variable in a stand-alone **YAML** file:

```yaml
---
file_path: "/tmp/test.file"
```

Now we can use the `file_path` variable to set the `path` attribute.
To use the variable a **Jinja2** template must be added to the **YAML** declaration using the expression container `"{{ }}"`:

```yaml
---
- name: "Example: Include variable from file"
  include_vars:
    file: "example_var_definition.yml"
- name: "Example: Set file permissions"
  ansible.builtin.file:
    mode: "0644"
    path: "{{ file_path }}"
```

## Ansible Infrastructure

**Ansible** defines two types of infrastructure components:

- _Control Node_: central compute node where the automation engine is installed and from where **Ansible Tasks** are executed.
- _Managed Node_: target node that **Ansible Tasks** will manage to reach the defined end-state. Nodes can be regular compute nodes (RedHat Linux Enterprise, Ubuntu, etc.) or non-compute nodes such as storage devices, network devices, appliances, etc.

Communication between _Control Nodes_ and _Managed Nodes_ is implemented using **Connection Plugins**. The default plugin for Linux nodes is _ssh_.

Use the command `ansible-doc -t connection -l` to show available plugins:

```shell
(ansible) [sysadmin@cnt84 ~]$ ansible-doc -t connection -l | egrep '^ssh'
ssh                            connect via ssh client binary
```

For tasks that requires privileged access to manage the component **Ansible** provides **Become Plugins**. The default plugin for Linux based nodes is _sudo_.

Use the command `ansible-doc -t become -l` to show available plugins:

```shell
(ansible) [sysadmin@cnt84 ~]$ ansible-doc -t become -l | egrep '^sudo|^su'
su                           Substitute User
sudo                         Substitute User DO
```

## Content Organization

**Ansible** doesn't enforce a strict directory structure for content organization. Instead, it provides configuration parameters that can be used to define locations based on the resource type.

The following is a basic directory structure that can be used for simple to medium size deployments:

| Path         | Content                                                         | Ansible Parameter         |
| ------------ | --------------------------------------------------------------- | ------------------------- |
| etc/         | **Ansible** configuration files                                 | ANSIBLE_CONFIG            |
| files/       | Site wide data files                                            |                           |
| inventories/ | **Ansible Playbooks** inventory files, host_vars and group_vars | ANSIBLE_INVENTORY         |
| collections/ | Collections installed from Ansible-Galaxy                       | ANSIBLE_COLLECTIONS_PATHS |
| roles/       | **Ansible Roles**                                               | ANSIBLE_ROLES_PATH        |
| playbooks/   | **Ansible Playbooks**                                           | ANSIBLE_PLAYBOOK_DIR      |
| logs/        | Execution logs                                                  | ANSIBLE_LOG_PATH          |

Once resources are organized you should consider managing the content using a version control system like GIT. This is key to implement the _infrastructure as code_ strategy.

## References & Resources

- Ansible Community Documentation: [https://docs.ansible.com/ansible_community.html](https://docs.ansible.com/ansible_community.html)
- Ansible Galaxy: [https://galaxy.ansible.com](https://galaxy.ansible.com/)
- Jinja2: [https://jinja.palletsprojects.com/en/3.0.x](https://jinja.palletsprojects.com/en/3.0.x/)
- YAML: [https://yaml.org/spec](https://yaml.org/spec/)
- GIT: [https://git-scm.com](https://git-scm.com/)
- Infrastructure-as-code Tutorial: [https://serdigital64.github.io](https://serdigital64.github.io/post/ansible/implementing-infrastructure-as-code-with-ansible-and-git/)
- A:Platform64 Automation Platform: [https://serdigital64.github.io](https://serdigital64.github.io/post/projects/project-aplatform64/)

## Copyright information

This article is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/). For copyright information on the product or products mentioned inhere refer to their respective owner.

## Disclaimer

Opinions presented in this article are personal and belong solely to me, and do not represent people or organizations associated with me in a professional or personal way. All the information on this site is provided "as is" with no guarantee of completeness, accuracy or the results obtained from the use of this information.