Introduction — Part 1
Ansible is a powerful open-source configuration management and provisioning tool that stands alongside industry favorites like Chef, Puppet, and Salt. Designed to streamline the process of managing and configuring nodes, Ansible enables administrators to control multiple devices from a single, centralized machine. Ansible simplifies the execution of configuration tasks, making it an essential tool for efficient and effective infrastructure management.
Why Use Ansible?
Ansible stands out as a preferred choice for many organizations due to its simplicity, power, and flexibility.
Here are some key reasons why Ansible is a valuable tool for managing your infrastructure:
- Simple: Ansible uses a straightforward syntax written in YAML format.
- Agentless: There is no need to install any agents or additional software on the client systems.
- Powerful and Flexible: Ansible powerful features allow you to manage even the most complex IT workflows with ease.
- Idempotent: Ansible ensures that changes are only made when necessary to achieve the desired state.
Ansible Basic Components
Understanding the key components of Ansible is essential for effectively utilizing its capabilities.
Here’s a breakdown of the fundamental elements that make up Ansible:
- Control Node: The central management point where Ansible and its code are executed.
- Managed Nodes: These are the network devices and systems managed by the control node.
- Tasks: The individual actions executed by Ansible.
- Playbooks: YAML files that contain a list of tasks.
- Inventory: A list of managed nodes. An inventory file is also sometimes called a “host file”.
- Modules: Units of code Ansible executes. Each module has a particular use, from administering users on a specific type of database to managing VLAN interfaces on a specific type of network device. Take a look at the list of all modules.
By understanding these components, you can effectively use Ansible to automate and streamline your IT operations.
Ansible for Network Automation
Ansible initially focused on managing servers but extended its capabilities to include network devices. The core concepts, including modules and playbooks, remain consistent between server and network management.
Ansible is known for its simplicity and accessibility, particularly for network engineers who may not have a background in programming.
The primary requirement is an understanding of YAML, the human-readable data serialization standard used to write Ansible playbooks.
Ansible allows for parallel execution of tasks across multiple nodes, significantly speeding up deployment times and ensuring consistent configurations.
Unlike most Ansible modules, network modules do not run on the managed nodes due to the inability of most network devices to run Python. Instead, these modules are executed on the Ansible control node.
This different methodology ensures that Ansible can still manage network devices effectively. Additionally, network modules use the control node as a destination for backup files, this approach allows Ansible to provide consistent network management and backup capabilities without the need for Python on the network devices themselves.
Getting Started with Ansible
Ansible modules support a wide range of vendors, device types, and actions, so you can manage your entire network with a single automation tool.
Ansible handles communication between the control node and managed nodes through multiple protocols:
- network_cli by SSH: A widely-used protocol for managing network devices, ensuring secure and reliable communication.
- netconf by SSH: A protocol designed specifically for network management, offering enhanced capabilities for configuration and monitoring.
- httpapi by HTTP/HTTPS: A flexible and efficient protocol for interacting with network devices that support RESTful APIs.
Network platforms supported by Ansible are:
- Arista: eos: Providing robust automation capabilities for Arista’s Extensible Operating System.
- Cisco: ios, iosxr, nxos: Enabling comprehensive management of Cisco’s diverse range of network operating systems, from traditional IOS to the advanced features of IOS-XR and NX-OS.
- Juniper: junos: Facilitating powerful automation for Juniper’s Junos OS, known for its flexibility and performance.
- VyOS: vyos: Supporting automation for VyOS, an open-source network operating system, ideal for various networking tasks.
With Ansible’s extensive support for different network platforms and protocols, you can streamline your network automation processes and achieve greater operational efficiency.
Installation of Ansible
Ansible can be installed on most Unix systems, with the only dependency being Python 2.7 or Python 3.5. For detailed installation instructions, you can refer to the Ansible documentation website, which provides guidance for various platforms. I install Ansible on an Ubuntu machine using the following commands:
sudo apt update
sudo apt install software-properties-common
sudo apt-add-repository ppa:ansible/ansible
sudo apt install ansible
These steps ensure that your Ubuntu system is updated, necessary software properties are installed, the Ansible repository is added, and Ansible itself is installed.
To ensure that your Ansible installation is successful and operational, you can run the following commands in your terminal:
zolo@u22s:~$ ansible --version
ansible [core 2.16.7]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/zolo/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
ansible collection location = /home/zolo/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (/usr/bin/python3)
jinja version = 3.0.3
libyaml = True
Executing this command will display information about the installed version of Ansible, confirming that it is correctly installed and ready for use.
Additionally, you can test Ansible’s connectivity and functionality by running a simple ping command against the localhost:
zolo@u22s:~$ ansible localhost -m ping
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}
Running this command should result in a successful ping response from the ‘localhost’, indicating that Ansible can communicate with the target host and execute commands successfully.
Establish a Manual Connection to Managed Node
To use Ansible, you need to confirm your credentials, connect to a network device manually and retrieve its configuration. For example, for my router:
zolo@u22s:~$ ssh admin@172.16.10.11
(admin@172.16.10.11) Password:
r1#sh ip int bri
Interface IP-Address OK? Method Status Protocol
FastEthernet0/0 172.16.10.11 YES NVRAM up up
FastEthernet0/1 unassigned YES NVRAM administratively down down
FastEthernet1/0 unassigned YES unset administratively down down
r1#exit
Connection to 172.16.10.11 closed.
Run Your First Command
Instead of manually connecting and running a command on the network device, you can retrieve its configuration with a single, stripped-down Ansible command:
zolo@u22s:~$ ansible all -i 172.16.10.12, -c network_cli -u admin -k -m ios_facts -e ansible_network_os=ios
SSH password:
172.16.10.12 | SUCCESS => {
"ansible_facts": {
"ansible_net_api": "cliconf",
"ansible_net_gather_network_resources": [],
"ansible_net_gather_subset": [
"default"
],
"ansible_net_hostname": "core-sw",
"ansible_net_image": "tftp://255.255.255.255/unknown",
"ansible_net_iostype": "IOS",
"ansible_net_model": "3725",
"ansible_net_operatingmode": "autonomous",
"ansible_net_python_version": "3.10.12",
"ansible_net_serialnum": "FTX0945W0MY",
"ansible_net_system": "ios",
"ansible_net_version": "12.4(15)T14",
"ansible_network_resources": {}
},
"changed": false
}
In this example, Ansible targets all hosts defined in the inventory, specifying a custom inventory file with -i
, connecting using network_cli
as the connection type with -c
, authenticating with the username admin
using -u
, prompting for a password with -k
, executing the ios_facts
module to gather facts about IOS devices, and specifying the network platform as ios
using -e
.
Key Parameters
-i
: Specifies the list of managed nodes, separated by commas.-c
: Defines the connection type.-u
: Specifies the username for authentication.-k
: Prompts for a password.-m
: Specifies the module name.-e
: Defines additional variables, such as the network platform.
These ad-hoc commands provide quick and efficient ways to perform specific tasks across your infrastructure using Ansible’s versatile command-line interface.
Ansible Configuration and Setting
Ansible supports several sources for configuring its behavior, including a configuration file named ansible.cfg
, environment variables, command-line options, playbook keywords, and variables.
This flexibility allows users to customize and optimize Ansible for their specific environments and needs.
The Configuration File
Changes to Ansible’s behavior can be made in a configuration file. Ansible will search for this configuration file in the following order:
ANSIBLE_CONFIG
(environment variable if set)ansible.cfg
(in the current directory)~/.ansible.cfg
(in the home directory)/etc/ansible/ansible.cfg
Ansible will process the list and use the first configuration file it finds, ignoring all others. This hierarchical search allows for different configurations based on the context in which Ansible is being used.
The ansible-config Utility
The ansible-config
utility is a powerful tool for viewing, listing, or dumping the various settings available for Ansible. This utility helps users manage and understand their Ansible configurations effectively.
You can use the ansible-config view
command to print the content of your current ansible.cfg
file. For example:
zolo@u22s:~/ansible-networking-lab$ ansible-config view
[defaults]
inventory=./inventory.cfg
gathering=explicit
host_key_checking=false
deprecation_warnings=false
interpreter_python=auto
retry_files_enabled=false
This output reflects the exact settings in the ansible.cfg
file for the current directory.
To dump all of Ansible’s current configuration settings, use the following command:
ansible-config dump
This command provides a comprehensive view of all configuration settings, making it easier to troubleshoot and understand Ansible’s behavior in your environment.
To see a list of all different parameters, their default values, and the possible values you can set, use the ansible-config list
command:
ansible-config list
This command is particularly useful for discovering configurable options and understanding the defaults that Ansible uses.
How Ansible Works — Precedence Rules
To give you maximum flexibility in managing your environments, Ansible offers many ways to control how Ansible behaves: how it connects to managed nodes and how it works once it has connected.
Ansible offers four sources for controlling its behavior. In order of precedence from lowest (most easily overridden) to highest (overrides all others), the categories are:
- Configuration settings: The settings in your
ansible.cfg
file. - Command-line options: Flags and parameters passed directly in the command line when running Ansible commands.
- Playbook keywords: Directives specified within your playbooks that control how tasks are executed.
- Variables: Values defined in your inventory files, playbooks, or directly in tasks, often providing the most granular level of control.
Each category overrides any information from all lower-precedence categories. For example, a playbook keyword will override any configuration setting.
Within each precedence category, specific rules apply. However, generally speaking, ‘last defined’ wins and overrides any previous definitions.
Example Configuration
Below is an example configuration file (ansible.cfg
) that might be used in a typical lab setup:
[defaults]
inventory=./inventory.cfg
gathering=explicit
host_key_checking=false
deprecation_warnings=false
interpreter_python=auto
retry_files_enabled=false
- inventory: Specifies the path to the inventory file.
- gathering: Controls the gathering of facts;
explicit
means facts are only gathered when explicitly requested. - host_key_checking: Disables SSH key checking to avoid issues with new hosts.
- deprecation_warnings: Disables deprecation warnings to reduce clutter in output.
- interpreter_python: Automatically determines the Python interpreter to use.
- retry_files_enabled: Disables the creation of retry files for failed playbook runs.
This configuration helps streamline Ansible’s operation in a controlled lab environment, ensuring that tasks run smoothly without unnecessary interruptions or checks.
Ansible Inventory Setting
Inventory serves as the foundation for managing hosts within our infrastructure using Ansible. It consolidates information about target hosts, including their IP addresses or Fully Qualified Domain Names (FQDNs). The simplest inventory is a single file with a list of hosts and groups. The default location for this file is /etc/ansible/hosts
. You can specify a different inventory file at the command line using the -i <path>
option or in configuration using inventory
.
Formats for Inventory Files
You can create your inventory file in various formats, the most common being INI
and YAML
. For this tutorial, see my GNS3 Lab Setup.
A basic INI format might look like this as per my lab setup:
[router]
172.16.10.11
[core]
172.16.10.12
[switch]
172.16.10.13
172.16.10.14
The headings in brackets are group names. These group names are used to classify hosts, allowing you to decide which hosts to control at specific times and for particular purposes.
To build an inventory file using DNS names instead of IP addresses, you would replace the IP with the DNS names of your devices. Here’s how you can modify the example:
[router]
r1
[core]
core-sw
[switch]
access1
access2
Ensure that the DNS names you use are resolvable from the system where you are running Ansible. This means that your DNS settings should be correctly configured to resolve these names to the appropriate IP addresses.
Here’s that same basic inventory file in YAML format:
all:
children:
router:
hosts:
172.16.10.11:
core:
hosts:
172.16.10.12:
switch:
hosts:
172.16.10.13:
172.16.10.14:
In this YAML inventory, all
is the top-level group that contains all hosts and groups. The hosts
key under all
lists each host along with its properties, such as ansible_host
. The children
key under all
contains subgroups like router
, core
, and switch
, each with their respective hosts. This structure ensures the inventory is organized and easily manageable.
By default, we can also reference two groups without defining them. The
all
group targets all our hosts in the inventory, andungrouped
contains any host that isn’t part of any user-defined group.
Defining Host Aliases
Another useful functionality is the option to define aliases for hosts in the inventory. For example, we can run Ansible against the host alias core-sw
if we define it in the inventory as:
core-sw ansible_host=172.16.10.12
Inventory and Variables
An important aspect of Ansible’s setup is variable assignment and management. Ansible offers many different ways of setting variables, and defining them in the inventory is one of them.
Host Variables
For example, let’s define variables for each host in our inventory:
[router]
r1 ansible_host=172.16.10.11 ansible_user=bob
[core]
core-sw ansible_host=172.16.10.12 ansible_user=joe
Ansible-specific connection variables such as ansible_user
or ansible_host
are examples of host variables defined in the inventory.
Group Variables
Similarly, variables can also be set at the group level in the inventory, offering a convenient way to apply variables to hosts with common characteristics:
[router]
r1 ansible_host=172.16.10.11 ansible_user=bob ansible_password=bob123
[core]
core-sw ansible_host=172.16.10.12 ansible_user=joe ansible_password=joe123
[campus:children]
router
core
[campus:vars]
ansible_network_os=ios
ansible_connection=network_cli
Although setting variables in the inventory is possible, it’s usually not the preferred way. Instead, store separate host and group variable files to enable better organization and clarity for your Ansible projects. Note that host and group variable files must use YAML syntax.
In the same directory where we keep our inventory file, we can create two folders named group_vars
and host_vars
containing our variable files. For example, we could have a file group_vars/campus.yaml
that contains all the variables for the campus network:
---
# group_vars/campus.yaml
ansible_user: admin
ansible_password: cisco
ansible_network_os: ios
ansible_connection: network_cli
Let’s view the generated inventory with the command:
zolo@u22s:~/ansible-networking-lab$ ansible-inventory -i inventory.yaml --graph
@all:
|--@ungrouped:
|--@campus:
| |--@router:
| | |--r1
| |--@core:
| | |--core-sw
| |--@switch:
| | |--access1
| | |--access2
##################################################################################
zolo@u22s:~/ansible-networking-lab$ ansible-inventory -i inventory.yaml --host r1
{
"ansible_connection": "network_cli",
"ansible_host": "172.16.10.11",
"ansible_network_os": "ios",
"ansible_password": "cisco",
"ansible_user": "admin",
"hostname": "r1"
}
The command fetches information from our hosts/inventory file and creates several groups according to our configuration/setup.
Key Points About Inventory
- Host Information: Inventory files contain crucial information about target hosts, such as their IP addresses and connection variables.
- Default Location: The default location for the inventory file is
/etc/ansible/hosts
, though you can specify a different location as needed. - File Format: Inventory can be written in either
INI
orYAML
format, providing flexibility in structuring and organizing host information. - Grouping Hosts: Group names, enclosed in brackets, serve to classify hosts based on common attributes or roles they fulfill within the infrastructure.
- Special Groups: The
campus
group is an example of a special group used for categorizing network devices, enabling targeted management of specific device types. - Default Groups: Ansible includes two default groups, namely
all
andungrouped
. Theall
group encompasses every host defined in the inventory, while theungrouped
group comprises hosts that do not belong to any other group aside fromall
.
By using the inventory, Ansible users can effectively organize, manage, and automate tasks across their infrastructure, streamlining operations and enhancing efficiency.
Test Ansible — Playbook
The playbook is Ansible’s configuration, deployment, and orchestration language. Each playbook is composed of one or more plays in a list. One play is a collection of one or more tasks, and a task is a single action that you want to execute through Ansible. In this section, we’ll create a basic playbook to gather facts from an IOS device and display them using Ansible’s debug module.
Here’s an example playbook:
---
- name: "Playbook-1: Get ios facts"
hosts: all
gather_facts: false
connection: network_cli
tasks:
- name: "P1T1: Get config from ios_facts"
ios_facts:
gather_subset: all
- name: "P1T2: Display debug message"
debug:
msg: "The {{ ansible_net_hostname }} has {{ ansible_net_iostype }} platform"
Let’s break down the playbook:
- Playbook Definition: The playbook starts with
---
to denote the beginning of a YAML document. Each playbook consists of one or more plays. - Play Name: The play is named “Playbook-1: Get ios facts”. This is for your reference and helps identify the purpose of the play.
- Hosts: The
hosts
directive specifies the target devices for this play. In this case,all
means it will run on all hosts defined in your inventory. - Gather Facts:
gather_facts: false
means that Ansible's default fact-gathering mechanism is disabled. Instead, we'll use theios_facts
module to gather facts. - Connection Type:
connection: network_cli
indicates that the play will use CLI to connect to network devices. - Tasks: Each play consists of a list of tasks. In this play, we have two tasks:
- P1T1: Get config from ios_facts: This task uses the
ios_facts
module to gather all possible facts from the IOS device.gather_subset: all
means we are gathering all available facts. - P1T2: Display debug message: This task uses the
debug
module to display a custom message that includes the gathered facts. The message is dynamically generated using Jinja2 templating, inserting the device's hostname (ansible_net_hostname
) and platform type (ansible_net_iostype
).
Running the Playbook
To run this playbook, ensure you have your inventory file set up correctly. Execute the playbook with the following command:
zolo@u22s:~/ansible-networking-lab$ ansible-playbook 01_play.yaml
PLAY [Playbook-1: Get ios facts] ********************************************************************************************************************
TASK [P1T1: Get config from ios_facts] **************************************************************************************************************
ok: [access1]
ok: [access2]
ok: [r1]
ok: [core-sw]
TASK [P1T2: Display debug message] ******************************************************************************************************************
ok: [r1] => {
"msg": "The r1 has IOS platform"
}
ok: [access1] => {
"msg": "The access1 has IOS platform"
}
ok: [core-sw] => {
"msg": "The core-sw has IOS platform"
}
ok: [access2] => {
"msg": "The access2 has IOS platform"
}
PLAY RECAP ******************************************************************************************************************************************
access1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
access2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
core-sw : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
r1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
This command tells Ansible to run the playbook (playbook.yml
) using the specified inventory file.
Creating and running a simple Ansible playbook is a powerful way to automate tasks on network devices.