Introduction to Ansible

Ansible is an open-source configuration management tool, written in Python for configuration management, application deployment, task automation, and more. Michael DeHaan created Ansible in 2012, and it gained traction in 2013. Red Hat acquired Ansible in 2015. Nowadays, a large community still contributes to its development.




It’s one of the best tools to start with if you are new to network automation. Let me explain why:

  • Ansible uses an agentless architecture and primarily uses SSH to configure and manage different targets.
  • You don’t have to install agent software on the target you want to configure or manage:
    • All you need is SSH to get started.
  • Installation is simple and only takes a minute:
    • You can run Ansible from your computer.
    • You don’t need to install a separate server with many requirements to get going.
  • Ansible uses YAML, which is easy to read and write, even if you have never worked with it before.

The combination of not having to install agent software, being able to run Ansible from your computer, and using YAML make it a great tool to get started with network automation. You can run your first automation in a couple of minutes. You can use it to automate the things you currently do manually.

You can configure or manage many different targets. For example:

  • Linux and Windows servers.
  • Network devices such as routers or switches.
  • Cloud providers such as Amazon AWS.
  • Hypervisors such as VMware or Proxmox.
  • Docker containers.
  • Storage systems such as NetApp.
  • Database servers such as MySQL server.

Here are some of the things you can do with these targets:

  • Application deployment:
    • Install and update applications.
    • For example:
      • Install the Nginx web server on a Ubuntu server.
      • Install MySQL server on a Ubuntu server.
  • Configuration management:
    • Ensure applications use the latest configuration file.
    • For example:
      • Copy a Syslog configuration file to the Syslog server.
      • Configure Syslog and SNMP on all Cisco devices.
      • Configure a new VLAN on all Cisco switches.
  • Security compliance:
    • Automate the implementation of security policies and compliance requirements.
    • For example:
      • Check if all Cisco routers don’t use OSPF plain text authentication.
  • Cloud resource management:
    • Manage cloud infrastructure resources on Amazon AWS, Azure, and Google Cloud.
    • For example:
      • Create an AWS EC2 instance in the us-east-1 region.
      • Create an AWS RDS MySQL server in the us-west-1 region.
  • Patch management:
    • Apply patches and updates.
    • For example:
      • run apt update and apt upgrade on many Ubuntu servers.
  • Backups:
    • Make backups of configuration files.
    • For example:
      • Download the running configuration from all Cisco switches every day.
  • Image updates:
    • Install new software versions.
    • For example:
      • Run dir flash: on all Cisco switches and send an email when there is less than 10% free storage space.
      • Copy the latest IOS image to Cisco routers and reboot them.
  • Database administration:
    • Create users or databases.
    • For example:
      • Create a new database on a MySQL server.
      • Create a new MySQL user with a generated random password on a MySQL server.
      • Run mysqldump to create a backup of a MySQL database.

Ansible uses YAML-based playbooks with tasks you want to execute against one or more targets. Let me show you an example of a playbook real quick:

---
- name: Install Nginx
  hosts: WEB1
  become: yes

  tasks:
    - name: Install Nginx package
      package:
        name: nginx
        state: present

Even if you have never seen YAML or Ansible before, this is easy to read. This playbook targets a host named “WEB1” and installs the Nginx package.

Playbooks can be executed in parallel against multiple targets, saving time.

Also, Ansible focuses on idempotency, which means that even if you execute tasks multiple times, the target will remain in the same state.

In this lesson, I’ll give you an overview of the different Ansible components and some playbook examples. We’ll cover enough so you know what Ansible is and how to get started. When you learn Ansible, it’s best to start with some simple examples, and when you can get something to work, you can always add additional tasks and complexity later. There are no prerequisites to learning Ansible.

Components

Let’s start with the main components of Ansible. We’ll cover the most important ones that are required to get started.

Control Node

The control node is the machine where Ansible is installed and from which you run playbooks. This sounds like a fancy name, but it can be your local computer. Almost any operating that can run Python is able to run Ansible.

There are multiple options to run Ansible.

CLI

  • Ansible is CLI-based, so the only thing you need to do is type commands into the CLI to execute a playbook with tasks.
  • You can execute these from your local computer.
  • You can also use this in CI/CD. For example, in a GitHub Actions workflow:
    • When someone makes a change to a configuration file and commits it to a Git repository, it can execute Ansible and run the playbook.

GUI

  • When you use Ansible with a team or in an enterprise environment, running playbooks from the CLI might not be the best option:
    • Remembering CLI commands can be a pain.
    • You might need additional features such as role-based access control, task scheduling, etc.
    • Red Hat has two GUI options:
      • AWX: an open-source solution.
      • Ansible Automation Platform:  a paid solution that was previously known as Ansible Tower.
    • A smaller open-source project that I’ve been enjoying is Semaphore UI. It offers a GUI for Ansible with scheduling options and is nice for small environments.

When you are just getting started, remember that there are different options, but for now, stick to running Ansible from your local computer.

Managed Nodes

A managed node is the target machine you want to configure or manage with Ansible.  It primarily uses SSH, but there are other options, such as:

  • WinRM: Used to manage Windows machines.
  • NETCONF: A protocol to manage network devices.
  • APIs: Allows Ansible to communicate with services or network devices through APIs.
  • VMWare: Interact with VMWare vSphere.
  • PSRP: PowerShell Remoting Protocol.
  • Docker: Interact with Docker containers directly.
  • Kubernetes: Interact with Kubernetes clusters directly.

There are many other connection types that interact with managed nodes.

Inventory

The inventory is a list of managed nodes, and it can be static or dynamic:

  • Static: a static file where you list the managed node IP addresses or hostnames.
  • Dynamic: a generated inventory file from a script or external source.

You can also organize hosts into groups. For example, you could define the hostnames for all your network switches and group them under a group named “switches”. You can then execute playbooks against a group instead of individual hosts. Inventory files can be in INI format or YAML.

Playbooks

In the playbook, you define in YAML the tasks you want to run and against what managed nodes. Ansible will run each task after another. It’s also possible to define a handler, a special type of task that only runs when notified. This is useful for tasks you only want to run in specific scenarios. For example, let’s say you want to update a software package that requires a restart after updating. You could use a task for the update, and a handler to restart the service. The handler will only be notified after a successful outcome from the task. Otherwise, you would restart the service for no reason.

To make playbooks dynamic, you can use variables and templates. Variables can be used for dynamic values, and templates generate files from variables. Ansible uses Jinja templates. For example, you could use a variable that sets the hostname of a Cisco switch. Let’s say the hostname is SW1. With a template, you could create a configuration file for the switch that uses that variable to create the hostname SW1 command.

Besides playbooks, you can also run ad-hoc commands with Ansible.

Roles

Roles help to organize playbooks. For example, let’s say we have a playbook to configure a router. The playbook has two tasks:

  • Configure the hostname.
  • Configure a loopback interface.

Now, we want to configure a playbook to configure a switch. This playbook also has two tasks:

  • Configure the hostname.
  • Configure a VLAN.

We can copy the code for the task to create the hostname from our router playbook, but that’s inefficient. Also, it’s a pain to maintain because if you want to update the task, you’ll have to do it in two playbooks.

Instead, we can move the task configuring the hostname to a role. In the playbook, we only have to refer to the role instead of the task. A role can even contain more tasks. For example, you could create a “Cisco IOS device” role which has tasks you want to apply to all your Cisco devices. Such as configuring SSH, a Syslog server, SNMP server, and more. Roles are great to avoid code redundancy and keep your playbooks short and to the point.

Also, you don’t have to configure these roles from scratch. You can find a huge collection of roles at Ansible Galaxy. These are roles that are created by the community. You can download them and use them in your playbooks.

Modules

Modules are units of code that Ansible executes on managed nodes. They perform tasks such as managing files, users, packages, etc.

For example, here is a playbook:

---
- name: Install and start Apache on Ubuntu
  hosts: webservers
  become: yes
  tasks:
    - name: Ensure Apache is installed
      apt:
        name: apache2
        state: present
        update_cache: yes

    - name: Ensure Apache is started
      service:
        name: apache2
        state: started

In this playbook, apt is the module for the APT (Advanced Packaging Tool). This is the package management system for Debian-based Linux distros such as Ubuntu. Here is another example:

- name: Copy configuration file
  copy:
    src: /path/to/local/file.conf
    dest: /path/on/remote/server/file.conf
    owner: root
    group: root
    mode: '0644'

This is the copy module that lets you copy files from a source to a destination.  And the last one:

---
- name: Configure interface on Cisco IOS device
  hosts: cisco_devices
  gather_facts: no
  connection: network_cli
  vars:
    interface_name: GigabitEthernet1
    ip_address: 192.168.1.1
    subnet_mask: 255.255.255.0
  tasks:
    - name: Configure interface IP address
      ios_config:
        lines:
          - interface {{ interface_name }}
          - ip address {{ ip_address }} {{ subnet_mask }}
          - no shutdown
        save_when: changed

This uses the ios_config module you can use for Cisco IOS configurations.

Plugins

Plugins are pieces of code that modify Ansible’s core functionality. They allow you to customize how Ansible executes tasks and interacts with managed nodes. There are different plugin types:

  • Action Plugins: Extend or override the behavior of modules.
  • Callback Plugins: Alter output that appears when running Ansible or take actions on task completion.
  • Connection Plugins: Define how Ansible communicates with remote systems.
  • Filter Plugins: Transform data for templates and other Ansible operations.
  • Lookup Plugins: Retrieve data from external sources.
  • Vars Plugins: Load additional variables into a playbook.
  • Inventory Plugins: Provide dynamic sources of inventory data beyond static files.

Don’t worry about these when you are just getting started. I’ll give you an example, though, so you know what they are about. Here is a list of connection plugins:

$ ansible-doc -t connection -l
[DEPRECATION WARNING]: ansible.netcommon.napalm has been deprecated. See the plugin documentation for more details.
This feature will be removed from ansible.netcommon in a release after 2022-06-01. Deprecation warnings can be disabled
 by setting deprecation_warnings=False in ansible.cfg.
ansible.builtin.local          execute on controller
ansible.builtin.paramiko_ssh   Run tasks via Python SSH (paramiko)
ansible.builtin.psrp           Run tasks over Microsoft PowerShell Remoting Protocol
ansible.builtin.ssh            connect via SSH client binary
ansible.builtin.winrm          Run tasks over Microsoft's WinRM
ansible.netcommon.httpapi      Use httpapi to run command on network appliances
ansible.netcommon.libssh       (Tech preview) Run tasks using libssh for ssh connection
ansible.netcommon.napalm       Provides persistent connection using NAPALM
ansible.netcommon.netconf      Provides a persistent connection using the netconf protocol
ansible.netcommon.network_cli  Use network_cli to run command on network appliances
ansible.netcommon.persistent   Use a persistent unix socket for connection
community.aws.aws_ssm          connect to EC2 instances via AWS Systems Manager
community.docker.docker        Run tasks in docker containers
community.docker.docker_api    Run tasks in docker containers
community.docker.nsenter       execute on host running controller container
community.general.chroot       Interact with local chroot
community.general.funcd        Use funcd to connect to target
community.general.iocage       Run tasks in iocage jails
community.general.jail         Run tasks in jails
community.general.lxc          Run tasks in lxc containers via lxc python library
community.general.lxd          Run tasks in lxc containers via lxc CLI
community.general.qubes        Interact with an existing QubesOS AppVM
community.general.saltstack    Allow ansible to piggyback on salt minions
community.general.zone         Run tasks in a zone instance
community.libvirt.libvirt_lxc  Run tasks in lxc containers via libvirt
community.libvirt.libvirt_qemu Run tasks on libvirt/qemu virtual machines
community.okd.oc               Execute tasks in pods running on OpenShift
community.vmware.vmware_tools  Execute tasks inside a VM via VMware Tools
containers.podman.buildah      Interact with an existing buildah container
containers.podman.podman       Interact with an existing podman container
kubernetes.core.kubectl        Execute tasks in pods running on Kubernetes

This shows you all the different connection plugins Ansible can use to connect with managed nodes.

Secrets Management

Secrets are sensitive information such as API keys, tokens, usernames, passwords, etc. When using Ansible, you’ll have to work with secrets sooner or later.

There are different ways to deal with this:

  • Hardcode them in your playbooks.
  • Prompts to manually enter secrets.
  • Use a secrets manager.

Hardcoding secrets in playbooks is a bad idea. Never do this because YAML files are saved in clear text. When a playbook never leaves your computer, you might get away with it, but when you want to put your code under version control, you don’t want secrets to appear in your Git repository.

Another option is to use prompts. You could configure your playbook so that you have to supply a secret when you run it. That’s fine for something as simple as a username, but it’s annoying to do this repeatedly for passwords or API keys. Also, when you are the only one who can supply secrets, you can’t use Ansible in automation, such as with a CI/CD pipeline.

The third option is a secrets manager, a tool or service for securely storing and managing sensitive information. There are many external solutions, such as:

  • Hashicorp Vault
  • AWS Secrets Manager
  • Azure Key Vault

Ansible also has a secrets manager named Ansible Vault. This is an AES 256-bit encrypted YAML file where you can store all your secrets. It is an easy option to get started with. You define your secrets in a YAML file, and they are encrypted when you save the file. You can commit this encrypted YAML file to a Git repository and nobody will be able to read it.

Configuration

Let’s look at some examples.

Installation

Let’s start with the installation. You can use Ansible on most operating systems:

  • Linux:
    • Debian-based: sudo apt install ansible
    • CentOS-based: yum install ansible
  • Windows:
    • Use Windows Subsystem for Linux (WSL) and run sudo apt install ansible
  • MacOS:
    • brew install ansible.

That’s all it takes.

SSH

We need to use the SSH clients on our control node. If you don’t have an SSH keypair, create one:

$ ssh-keygen
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/ubuntu/.ssh/id_ed25519): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/ubuntu/.ssh/id_ed25519
Your public key has been saved in /home/ubuntu/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:e8zFT0jhPZmylkUe7XpmkG2v7PD2X9SgX2hqLwr7XwA ubuntu@ANSIBLE
The key's randomart image is:
+--[ED25519 256]--+
|            . o. |
|           . = +.|
|          E + O+ |
|           + Bo+=|
|        S   X ++=|
|         + o B.o=|
|        o + +.+=.|
|         + ..=+ .|
|        ..oo.++.+|
+----[SHA256]-----+

We now have a public and private key for SSH.

File and Folder Structure

We’ll start with some files and folders. I created an Ansible folder which has the following files and folder:

/ansible
│
├── inventory.ini                     # Inventory file containing host information.
│
├── playbooks                         # Directory containing playbook files.
│   ├── install-web-server.yml        # Playbook for web server installation.
│   ├── ospf-configure-network.yml    # Playbook to configure OSPF.

We’ll modify these files as we go along.

Ubuntu Server

We’ll start with two Ubuntu servers. I created two VMs with these IP addresses:

  • 10.65.90.1
  • 10.65.90.2





We’ll see if we can turn these into web servers.

There are two ways to authenticate:

  • Password authentication: type in the password each time you want to connect.
  • Public key authentication: use the SSH key pair.

Typing in the SSH password every time is a pain, so it’s best to use public key authentication. This is easy thanks to the ssh-copy-id command. We’ll copy our public key from the control node to the Ubuntu servers. Here’s the first one:

$ ssh-copy-id ubuntu@10.65.90.1
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ubuntu/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
(ubuntu@10.65.90.1) Password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'ubuntu@10.65.90.1'"
and check to make sure that only the key(s) you wanted were added.

And the second one:

$ ssh-copy-id ubuntu@10.65.90.2
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ubuntu/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
(ubuntu@10.65.90.2) Password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'ubuntu@10.65.90.2'"
and check to make sure that only the key(s) you wanted were added.

Now, when I try to log into these Ubuntu servers from my control node, it won’t ask me for the password anymore and uses public key authentication:

$ ssh ubuntu@10.65.90.1
Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-45-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Thu Oct 10 13:55:50 UTC 2024

  System load:  0.0               Processes:             133
  Usage of /:   28.4% of 7.19GB   Users logged in:       0
  Memory usage: 6%                IPv4 address for eth0: 10.65.90.1
  Swap usage:   0%


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


Last login: Thu Oct 10 13:47:03 2024 from 10.65.20.1
ubuntu@WEB1:~$

That takes care of the SSH part. Let’s modify our inventory.ini file:

[web]
10.65.90.1 ansible_user=ubuntu
10.65.90.2 ansible_user=ubuntu

I created a group named “web” with two entries. I also specified the username of these machines.

Let’s create a install-web-server.yml playbook:

---
- name: Install Nginx on web servers
  hosts: web
  become: yes

  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes

    - name: Install Nginx
      apt:
        name: nginx
        state: present

    - name: Ensure Nginx is started and enabled
      service:
        name: nginx
        state: started
        enabled: yes

This playbook updates the APT cache and uses apt on a Ubuntu machine to install the Nginx package and then make sure the Nginx service is running. Let’s execute it:

$ ansible-playbook -i inventory.ini ./playbooks/install-web-server.yml

PLAY [Install Nginx on web servers] *********************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************
ok: [10.65.90.1]
ok: [10.65.90.2]

TASK [Update apt cache] *********************************************************************************************************************************************
changed: [10.65.90.2]
changed: [10.65.90.1]

TASK [Install Nginx] ************************************************************************************************************************************************
changed: [10.65.90.2]
changed: [10.65.90.1]

TASK [Ensure Nginx is started and enabled] **************************************************************************************************************************
ok: [10.65.90.2]
ok: [10.65.90.1]

PLAY RECAP **********************************************************************************************************************************************************
10.65.90.1                 : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
10.65.90.2                 : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

And that’s it. We just installed Nginx on two web servers. If you open http://10.65.90.1 in a web browser, you should see a Nginx welcome page.

We created an inventory file and a playbook, executed it, and it worked.

Cisco IOS XE

Let’s try if we can manage two Cisco IOS XE routers.

We're Sorry, Full Content Access is for Members Only...

If you like to keep on reading, Become a Member Now!

  • Learn any CCNA, CCNP and CCIE R&S Topic. Explained As Simple As Possible.
  • Try for Just $1. The Best Dollar You’ve Ever Spent on Your Cisco Career!
  • Full Access to our 801 Lessons. More Lessons Added Every Week!
  • Content created by Rene Molenaar (CCIE #41726)
512 Sign Ups in the last 30 days
satisfaction-guaranteed

  • 100% Satisfaction Guaranteed!
  • You may cancel your monthly membership at any time.
  • No Questions Asked!

Tags:


Ask a question or start a discussion by visiting our Community Forum