Under the majority of the role directories you'll notice a ".yml" file. Playbooks are written in a very simple markup language called YAML, which stands for Yet Another Markup Language. YAML is used as it's even easier to read than data structure formats such as XML or JSON.
YAML files optionally begin with --- at the top of the file to signify the start of the file. Following the file start, comes the Ansible modules written in YAML syntax. YAML syntax for Ansible modules are expressed as a list of key/value pairs. Below you can see an example of YAML syntax that contains a list of dictionaries and where the dictionaries contain lists:
- name: CREATE ACI TENANT
cisco.aci.aci_tenant:
tenant: "My_Tenant"
descr: "This Tenant is for testing only"
host: "x.x.x.x"
username: "admin"
password: "superpass"
validate_certs: false
As you can see the first thing, we are doing is to introduce you is to an specific ansible module. In this case the
aci_tenant
is an ansible module. There are many ansible modules available and more added with each
release.
Keep the reference to the
ACI documentation for Ansible saved.
Also the link for
modules
is available. Just click on the link and then click on content button.
An important key to any programing language are variables and Ansible is no different. If you would have had to create 100 tenants in an ACI fabric, you dont want to have to create 100 different YAML files with the configuration that we showed above. That makes no sense.
Ansible allows the placement of variables in the YAML file via what we know as templates. This template system is called Jinja Templates and is used in many aspects of Python, the core base of Ansible. This is how the syntax would be like for the example above:
- name: CREATE ACI TENANT
cisco.aci.aci_tenant:
tenant: "{{ tenant_name }}"
descr: "{{ descr }}"
host: "{{ inventory_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: false
Variables can be defined in various locations and Ansible has a precedence system for understanding what and when a variable would be overridden if used in more than one location. The precedence for a variable can be seen below. As an example, a variable in the role defaults directory would be overridden by a variable in the playbook global_vars/all directory and file. Further, both of these are overridden by the local vars directory within the specific role, role vars.
We will be showing you a capability called module_defaults that will allow us to configure the authentication and connection elements needed for each module in a central location to simplify the structure of the tasks.
You are now going to create a new file in the group_vars directory under the
playbook/aci
directory. You will be doing this in the Visual Studio Code (VSC) editor. Right click on the
editor left pane where the group_vars
directory is and then click on New File
to create the all.yml
file in group_vars
and populate the contents of the file
for the ansible_connection
, username
, password
, and
protocol
to be
used in the ACI tasks.
group_vars/all is where you place universal variables that apply for all devices.
This new file will be called all.yml
The ansible_connection
variable is used to define the connection type to the device. For
this lab, you will be using the local
connection type, which means that Ansible will be
executing the tasks locally on the device where Ansible is installed.
---
ansible_connection: local
username: aciproglab04
password: cisco.123
The device specific variables can be defined under the host_vars directory for each device.
For this lab you are going to be leveraging a specific user that has write permissions for a specific tenant. For this reason we have created the Tenant for you. The ACI Tenant provides a user space domain for creating network elements and is controlled via RBAC.
You are now going to create a new file in the host_vars directory under the
playbook/aci
directory. Like before, you will do this in the Visual Studio Code (VSC) editor. Right click on the
editor left pane where the host_vars
directory is and then click on New File
to create the apic.yml
file in host_vars
and populate the contents of the file
with the data that will be used in your Ansible tasks that will be defined next.
host_vars/apic.yml is where you place variables that apply to your intended configuration state.
If you had variable data that is the for your tasks no matter what, then you could define that directly in
your roles/vars/main.yml
.
Again, this new file will be called apic.yml
Inset the variables for your tenant, VRFs, and application profile after the
# vars file for apic
comment:
---
tenant: aciproglab04
vrf1: POD04_vrf_1
vrf2: POD04_vrf_2
app_profile_name: POD04_APP
The Ansible documentation for each network module provides a synopsis of what function the module performs and a table of parameters or keys. The tabulated parameters inform the user which parameters are required for the module to function as a task, which parameters are optional, and what the defaults are for parameters. There are many network modules that exist to configure Tenants, VRFs, BDs, APs, EPGs, Contracts, etc. Some of the modules that are available and the ones you will use today are the following:
aci_vrf
aci_bd
aci_ap
aci_epg
Cisco is commited to Ansible and continues to expand the library with new modules in the pursuit
of having 100% correlation between the module and ACI capabilities. In a place where an Ansible module is
not available, Cisco has developed a specific module that allows you to send specific ACI configuration objects into
the fabric
via the REST interface called aci_rest
.
In the editor find the file main.yml
under the roles/apic/tasks
directory and open it by
double-clicking on it.
You will be adding the following Ansible tasks using the beforementioned modules in YAML format after the
# tasks file for apic
comment.
Pay attention to the line numbers as they should match with your editor.
- name: CREATE ACI TENANT VRF1
cisco.aci.aci_vrf:
vrf: "{{ vrf1 }}"
state: present
tenant: "{{ tenant }}"
- name: CREATE ACI TENANT VRF2
cisco.aci.aci_vrf:
vrf: "{{ vrf2 }}"
state: present
tenant: "{{ tenant }}"
- name: CREATE BRIDGE DOMAIN FOR WEB
cisco.aci.aci_bd:
bd: "web"
description: 'POD04 Web front end Bridge Domain'
state: present
vrf: "{{ vrf1 }}"
tenant: "{{ tenant }}"
- name: CREATE SUBNET FOR DB WEB
cisco.aci.aci_bd_subnet:
bd: "web"
subnet_name: "POD04_web_subnet"
description: 'POD04 Subnet for Web front end'
gateway: "10.100.4.1"
mask: 24
scope: private
state: present
tenant: "{{ tenant }}"
- name: CREATE BRIDGE DOMAIN FOR DB
cisco.aci.aci_bd:
bd: "database"
description: 'POD04 Database Bridge Domain'
state: present
vrf: "{{ vrf1 }}"
tenant: "{{ tenant }}"
- name: CREATE SUBNET FOR DB BRIDGE DOMAIN
cisco.aci.aci_bd_subnet:
bd: "database"
subnet_name: "POD04_db_subnet"
description: 'POD04 Subnet for Database Bridge Domain'
gateway: "10.100.104.1"
mask: 24
scope: private
state: present
tenant: "{{ tenant }}"
In the editor, under the same file you will be adding the following modules.
- name: CREATE ACI TENANT APPLICATION PROFILE
cisco.aci.aci_ap:
ap: "POD04_APP"
description: 'POD04 Application Profile'
state: present
tenant: "{{ tenant }}"
- name: CREATE DATABASE EPG
cisco.aci.aci_epg:
ap: "POD04_APP"
bd: "web"
epg: "web"
description: 'POD04 WEB EPG'
state: present
tenant: "{{ tenant }}"
- name: CREATE DATABASE EPG
cisco.aci.aci_epg:
ap: "POD04_APP"
bd: "database"
epg: "database"
description: 'POD04 Database EPG'
state: present
tenant: "{{ tenant }}"
Ansible refers to its host file as an inventory file. The inventory file has a default location in
/etc/ansible/hosts, but can also be specified directly within a playbook locally and used with the
-i hosts.yml
option, where hosts happens to be in the inventory file name. Within the inventory file,
you can simply list all the devices/hosts or make use of group names using brackets which classifies
devices you are controlling at what times and for what purpose.
The host file will be created in the location ~/ltrdcn-3225/ansible/playbooks/aci
. Please
goto this location and create the file hosts.yml
.
# hosts file for Ansible playbook
---
all:
children:
aci:
hosts:
apic:
ansible_host: 10.0.226.41
As previously mentioned, Ansible calls its configuration and orchestration framework "playbooks" and is a collections of "play(s)" or tasks for configuration management and deployment to a device or multiple devices. While playbooks can be represented as a single file, Ansible best practices recommend a particular directory structure for playbooks that you built using roles for better organization and reuse. You built all of this over the last three sections. You now need to build the main playbook file, which would look like the below:
The below playbook file you will use for this lab designates the following behaviors, for each role ‘x’:
Ansible allows you to define default values for modules in the playbooks. This is done by defining a
module_defaults
section in the playbook. The module_defaults
section is a dictionary
that contains key/value pairs for the default values of the modules. Cisco has introduced in latest versions
of Ansible the capability to define the default values for the ACI modules.
This capability simplifies the playbook structure and makes it more readable since it avoids the repetition in the definition of the variables for each module. Here we will define the default values needed to authenticate to the APIC, for which Ansible will pass these defaults to each task that under the roles.
The host file will be created in the location ~/ltrdcn-3225/ansible/playbooks/aci
. Please
goto this location and create the file site.yml
and add the following content.
---
# main playbook
- name: APIC Tenant Configuration playbook
hosts: apic
gather_facts: false
module_defaults:
group/cisco.aci.all:
hostname: "{{ansible_host}}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: false
use_ssl: true
roles:
- role: apic
Now we can proceed to execute the ansible playbooks.