One of the main features of using Ansible for Network Automation is the use of roles, and there are many advantages of using Ansible roles which I will describe below.
When you start to build up a collection of playbooks you will soon discover that there will be duplication and you find yourself writing or using the same blocks of code over. With the use of roles you can define a specific function that relates to a common use case and then reference that role in other playbooks.
Lets give you an example
I am a Cisco Network Engineer and use Ansible to create configuration templates, I have created specific roles for groups of devices i.e switches or routers and then more specific roles for configuration tasks i.e config_vlan or config_localpw. This post will focus specifically on network / Cisco related examples but the concepts of Ansible roles remain the same.
Each role has it’s own set of templates which can reference specific data within that role or more global data from group_vars or host_vars
Ansible Roles Example
When using Ansible roles you need to create a specific folder structure, with a minimum of three folders, tasks, templates and vars. Within those folders I have a single file in each folder.
This Anisble role is called config_localpw it is used to create a local username and password on a Cisco device and can easily be referenced from any playbook I have. It also allows me to have global standard local user account defined in group_vars and also if I want to be more specific I can define local project variables within the role.
Ansible is a great way to get started with network automation.
tasks\main.yml
Within the tasks folder I have the playbook task that I want to run this specifies the source of the template I want to use and the destination of where the generated configuration is put. It then defines which items I want to run against, in this case routers.
name: Generate Configuration Files
template: src=local_pw.j2 dest="~/network-programmability/SCRIPTS/Ansible/configs/{{item.hostname}}.txt"
with_items: "{{ routers }}"
templates\local_pw.j2
Within the templates folder I have a Jinja2 template called local_pw.j2
username {{ user_type[item.user_type].admin_un }} password {{ user_type[item.user_type].admin_pw }} privilege {{ user_type[item.user_type].admin_priv }}
This is a very simple template that basically is creating two lines of configuration which will look like this
username roger password Cisco 12
username roger privilege 15
The elements in italics are being defined in vars\main.yml and also if required in group_vars/all.yml (this will be explained later)
vars\main.yml
Within the vars file I have a yml file called main.yml which defines the information, or where to find the information we want to put into our config
--- routers: - hostname: R1 user_type: admin - hostname: R2 user_type: admin
This defines that within routers I have two devices called R1 and R2 and I want to create an account of user_type admin – these can also be tied to real devices you have in your Ansible hosts file and then when the files are created can be pushed to the live devices.
If you now reference the code in the local_pw.j2 file above you can see that to generate the code I want it uses username and then pulls the data from item.user_type which says I want an admin user. In this example I am using group_vars/all.yml to hold this information. By default Anisble will look in the vars\main.yml file to see if it’s defined there, then it will look within the role in defaults\main.yml (we are not using this) and then it will look in group_vars\all.yml. Any information listed here can be referenced by any role or playbook.
This is referred to as Ansible Variables Precendence
group_vars\all.yml
Within this file I have created a section titled user_type:
user_type: admin: admin_un: roger admin_pw: Cisco12 admin_priv: 15
Here you can see I have defined some very basic values.
So the theory is we run the playbook which references my config_localpw role and pulls the data from group_vars\all.yml and creates me 2 configuration files R1.txt and R2.txt which I could then push to those devices to configure a local username and password.
What ties everything together is my templates.yml file which just references the role
--- - name: Generate Configuration Files from Roles hosts: localhost roles: - config_localpw
In future examples I will show you you can reference this role from within a larger playbook.
We run the playbook – ansible-playbook templates.yml
I now have two files in my Configs directory called R1.txt and R2.txt and if we cat one of these files you can see the configuration I wanted.
# Local PW Template for IOS username roger password Cisco12 username roger privilege 15
I can now push these files directly to these devices or use this config as part of a bigger configuration template creation.
Using Ansible Galaxy to create roles
In this example I created the folder structure manually, but there is an easier way by using a feature of Ansible Galaxy
Within your terminal simply enter the command
ansible-galaxy init test-role
Running this command will configure the entire folder structure for the test-role
Conclusion
Whilst this might seem like a lot of work to create 2 lines of code, you might be thinking why not just copy and paste that code onto the two devices, that is true in the case of two devices, but what if it was 1000 devices.
You can run this playbook and reference the local_pw role and be 100$ sure you have 1000 configuration templates that will all be the same.
Check out this post on the Ansible Inventory File for details on how to add hosts
At any time during a larger playbook creation where I might want to drop in a local pw I can simply reference this role. And the main advantage of using Ansible roles is you can then utilise other roles, and build on your templates. You configure the template once, you configure the role once and then just adjust the data you want to input.
I hope this Ansible Tutorial has been educational to you and I will be publishing more very soon.
Other Ansible Pages: