For a few months now I’ve been dabbling into automation – more specifically automation of the Infrastructure Fabric [remember I want to dissuade the usage of ‘Networking’]. I began this journey for several reasons:
- I want to make Fabric tasks simpler
- I want to expand my skillset
- I want to understand the DevOps way of doing things
- There is a paradigm shift in IT
- To maintain a level of relevancy
So I looked at a few programming languages [Python, PowerShell, Ruby…] and tools [Puppet, Chef, Ansible, SALT…] and found a preference for [now] Ansible & Python.
I choose Ansible for a few reasons:
- It’s written in Python, hence preference of Python
- It’s agentless – No need to have an agent (daemon) running in the background on the end device to consume resources and increase overhead
- It’s a configuration management tool
- Library of modules are extensive
- Probably a lot easier to learn than the others
- Has many integrations points
As I browsed through Ansible’s website – more specifically their integration page, https://www.ansible.com/integrations, I decided to draw a diagram of some of those points.
Below is a diagram I put together of the integration points Ansible has with many vendors and/or technologies. I’ve also added interoperability across solution verticals. This diagram doesn’t represent every integration point with Ansible, just at the time of my research.
NOTE: The diagram is based on the vendors/solutions I interact with… Yes, Cisco does integrate with Ansible if you were going to ask 🙂
Nonetheless – the idea, for me, is to apply what I know from my vast years in IT – to a DevOps model. In other words evolve into – FabricOps? NetOps? InfraOps? [You get the idea, I’m still working on the nomenclature.]
The point is… it’s the application that runs business outcomes and survivability – it makes sense to adapt a DevOps’ish model on how we build the Fabric and apply CI/CD principles.
For practical purposes and not diving too deep into the Ansible: Ansible run Playbooks. In the Playbooks, are plays and each play has a list of tasks assigned to be executed against a set of hosts, which is located in an inventory file. The task(s) are executed sequentially and typically calls a module.
If you want to look at it from a football analogy, this is how I interpret Ansible Playbooks, Plays, and Tasks
In this blog – I’ll build an Ansible controller [where the Playbooks & tasks are ran], build two Ansible managed nodes, show a few adhoc commands, and run some Playbooks.
DISCLAIMER: The material presented in this blog is for educational and training purposes only. Neither the author(s) nor Ahaliblogger© assume any liability or responsibility to any person or entity with respect to loss or damages incurred from the information contained in this blog.
The diagram below is showing simply how the Ansible controller and the managed nodes are connected. Since Ansible is a configuration management tool – it makes sense for these devices to communicate via the MGMT/OOBM network.
Ok let’s go ahead and build out an Ansible Server for our network. We’ll be using a CentOS VM. If you’ve read any of my blogs – you’ll see that this is my preferred Linux distribution.
Task 1: Download and install CentOS 7
You can find the CentOS image here: https://www.centos.org/download/. Minimal version is probably recommend. There is really no need for the ‘extra’ stuff.
Add a user, if you like as per the following:
useradd ansible; passwd ansible; su - ansible
NOTE: I’ll be using user root for all configurations
Once CentOS is installed. It’s best to perform an update and install net-tools
[root@localhost ~]# yum -y update [root@localhost ~]# yum –y install net-tools
Task 2: Configure miscellaneous items for Ansible Controller
After the updates, you’ll configure the Controller’s:
- IP (if you want a static one)
- DNS (if you like)
- NTP (if desired)
- Hostname (you should)
- Host files (if you don’t feel like configure a DNS server… but you should have one if you read my blog on how to configure a DNS server)
- Disable firewall and netmanager services
Task 3: Install Ansible prerequisites
The easiest way to install Ansible on CentOS is by using standard package repository and EPEL repository. This will install python on your controller
[root@ansible-ctlr ~]# yum -y install epel-release
NOTE: The epel-release is specific to CentOS. In case, you were wondering.
NOTE: You may also run ‘yum install -y python2 epel-release’ instead of the above syntax, if you’d like.
Next, check the python version, mine is 2.7.5
[root@ansible-ctlr ~]# python --version Python 2.7.5 [root@ansible-ctlr ~]#
Install python pip
yum -y install python-pip
Task 4: Install Ansible
Now we can install ansible
[root@ansible-ctlr ~]# yum -y install ansible
After installation is completed – check the Ansible version. Your output show be similar
[root@ansible-ctlr ~]# ansible --version ansible 2.7.2 config file = /etc/ansible/ansible.cfg configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python2.7/site-packages/ansible executable location = /usr/bin/ansible python version = 2.7.5 (default, Jul 13 2018, 13:06:57) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] [root@ansible-ctlr ~]#
NOTE: If you want to install the latest version of Ansible, you pip: pip install ansible
From your output, ansible is located in /etc/ansible/
[root@ansible-ctlr ~]# cd /etc/ansible/ [root@ansible-ctlr ansible]# ls ansible.cfg hosts roles [root@ansible-ctlr ansible]#
It is in this location where you’d typically need to be in order to run your Ansible Playbooks… we’ll leave this alone for now and work on the managed nodes to which we’ll run Ansible Playbooks against.
Task 5: Install managed nodes to run Ansible playbooks against
NOTE: DO NOT update the nodes. Playbooks will be used to install those and other stuff.
Just like in Tasks 1 & 2 – Install CentOS on two managed nodes. Make sure the IPs are on the same subnet as the controller and update the /etc/hosts file on your Ansible controller, too.
After the nodes are set-up, we can leave them for now.
Task 6: SSH to Managed Nodes to store Key fingerprint
On the Ansible controller, SSH into the nodes and connect to store the ECDSA key fingerprint to the list of known hosts
[root@ansible-ctlr ~]# ssh ansible-host1.ahaliblogger.local The authenticiyty of host 'ansible-host1.ahaliblogger.local (192.168.64.119)' can't be established. ECDSA key fingerprint is SHA256:Lh3LfingerprintMJs6CpqKtgWJWi8UPy6U. ECDSA key fingerprint is MD5:b3:c3:e3:f2:f1:29:1b:00:00:00:36:36:e9:5d:ef:eb. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'ansible-host1.ahaliblogger.local,192.168.64.119' (ECDSA) to the list of known hosts. email@example.com's password: Last login: Tue Nov 27 14:47:13 2018 [root@ansible-host1 ~]# [root@ansible-host1 ~]# exit logout Connection to ansible-host1.ahaliblogger.local closed. [root@ansible-ctlr ~]# [root@ansible-ctlr ~]# ssh ansible-host2.ahaliblogger.local The authenticity of host 'ansible-host2.ahaliblogger.local (192.168.64.120)' can't be established. ECDSA key fingerprint is SHA256:M+vX9IHEPfingerprintPwuDZgeEOsd3DB9kagDr2A. ECDSA key fingerprint is MD5:a8:d6:68:6a:00:00:00:53:c6:36:96:21:39:0d:bd:02. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'ansible-host2.ahaliblogger.local,192.168.64.120' (ECDSA) to the list of known hosts. firstname.lastname@example.org's password: Last login: Tue Nov 27 15:08:20 2018 [root@ansible-host2 ~]# exit logout Connection to ansible-host2.ahaliblogger.local closed. [root@ansible-ctlr ~]# [root@ansible-ctlr ~]#
NOTE: The view the file where your ‘known’ hosts are in – run the following:
[root@ansible-ctlr etc]# cd ~/.ssh/ [root@ansible-ctlr .ssh]# ls known_hosts [root@ansible-ctlr .ssh]# vi known_hosts
Task 7: Run SSH key-gen on Ansible controller and copy the public key to Managed Nodes
This task is based on my previous blog posting: No Password SSH… No Problem!
If completed correctly, you should have three files on your Ansible controller
[root@ansible-ctlr ~]# cd ~/.ssh/ [root@ansible-ctlr .ssh]# ls id_rsa id_rsa.pub known_hosts [root@ansible-ctlr .ssh]#
The Managed Nodes will now have an authorized key file in their ~/.ssh/ directory
[root@ansible-host1 ~]# cd ~/.ssh [root@ansible-host1 .ssh]# ls authorized_keys [root@ansible-host1 .ssh]#
Now your Ansible controller can run task against your managed nodes without being prompted for a password.
Task 8: Create ansible.cfg file, project directory, and inventory file
The ansible.cfg file is used to specify generic settings such as escalate permissions, where to find the ‘inventory’ file, and other items. It’s best to create this file relative to your project directory. To know more about the ansible.cfg – navigate to the one provided when Ansible is first installed: vi /etc/ansible/ansible.cfg
My ansible.cfg looks like this:
[root@ansible-ctlr install]# vi ansible.cfg [defaults] inventory = inventory remote_user = root host_key_checking = false [privilege_escalation] become = True become_method = sudo become_user = root become_ask_pass = False ~ ~ ~ ~ :wq! [root@ansible-ctlr install]# [root@ansible-ctlr install]#
Since I defined an inventory file named ‘inventory’ [no too creative, right]. I’ll have to define that file and add my nodes to it.
From a best practice perspective, it’s best to organize Ansible into projects. So on the Ansible controller, make a directory for your project and create a file called inventory
[root@ansible-ctlr ~]# [root@ansible-ctlr ~]# mkdir install [root@ansible-ctlr ~]cd install [root@ansible-ctlr install~]# touch inventory
This file will be used by Ansible to determine what nodes to run tasks on. My inventory file looks like this:
[root@ansible-ctlr install]# vi inventory [all] ansible-host1.ahaliblogger.local ansible-host2.ahaliblogger.local [web] ansible-host2.ahaliblogger.local ~ ~ wq:! [root@ansible-ctlr install]#
I’ve listed both nodes under the host group ‘[all]’ and only ansible-host2 under the host group ‘[web]’. We’ll see shortly how these are used…
With all that done, let’s run a simple Ansible command to test if we done this correctly. Run the following on the controller:
NOTE: The host group ‘[all]’ is always present and doesn’t have to be defined
With all that done, let’s run a simple Ansible command to test if we done this correctly. Run the following on the controller:
[root@ansible-ctlr install]# ansible all -i inventory --list-hosts
Your output should look similar
If it does – then you created the inventory file correctly and successfully ran your first Ansible command!!!
Task 9: Running adhoc commands
Now that where all set-up, let’s run a few adhoc commands. We’ll run the following adhoc command
[root@ansible-ctlr install]# ansible ansible-host1.ahaliblogger.local -m command -a who
This command list who is connected to the host specified [ansible-host1.ahaliblogger.local]
The output looks like this:
[root@ansible-ctlr install]# ansible ansible-host1.ahaliblogger.local -m command -a who ansible-host1.ahaliblogger.local | CHANGED | rc=0 >> root tty1 2018-12-12 19:23 root pts/0 2018-12-12 19:24 (192.168.64.97) root pts/1 2018-12-12 20:41 (192.168.64.118) [root@ansible-ctlr install]#
So it looks like I have two user connected to this host [192.168.64.97 & .118] .118 is the Ansible Controller and .97 is my local machine.
Now I’ll run the same command on all my hosts:
[root@ansible-ctlr install]# ansible all -i inventory -m command -a who ansible-host1.ahaliblogger.local | CHANGED | rc=0 >> root tty1 2018-12-12 19:23 root pts/0 2018-12-12 19:24 (192.168.64.97) root pts/1 2018-12-12 20:45 (192.168.64.118) ansible-host2.ahaliblogger.local | CHANGED | rc=0 >> root tty1 2018-12-12 19:24 root pts/0 2018-12-12 19:24 (192.168.64.97) root pts/1 2018-12-12 20:45 (192.168.64.118) [root@ansible-ctlr install]#
As you can see it lists the host and who’s connected to them 🙂 !
Task 10: Create Ansible Playbooks
Ok, so let’s delve into Playbooks that would:
- Update our hosts and install net-tools to run ifconfig
- Disable NetworkManager and Firewall Services
- Update the host’s hosts file to include the Ansible Controller’s FQDN
- Install NTP service
- Install NGINX on ansible-host2 and move a file to the html directory
On my Ansible Controller, and in the project directory of ‘install’, I’ll create a YAML [to be] file called basic-update.yml
[root@ansible-ctlr install]# ls ansible.cfg inventory [root@ansible-ctlr install]# touch basic-update.yml [root@ansible-ctlr install]# ls ansible.cfg basic-update.yml inventory [root@ansible-ctlr install]#
Every Ansible Playbook should content the following header:
--- - name: This will perform a basic yum -y update on target host machines hosts: all remote_user: root become: yes … … …
This breaks down into:
To continue, my Playbook for updating my hosts and installing net-tools is as follows:
NOTE: You must pay careful attention to your syntax and its structure in the file! Very important
[root@ansible-ctlr install]# vi basic-update.yml --- - name: This will perform a basic yum -y update on target host machines and then install net-tools hosts: all remote_user: root become: yes tasks: - name: Run yum update yum: name=* state=latest - name: run yum install net-tools yum: name=net-tools state=latest ~ ~ ~ ~ :wq! [root@ansible-ctlr install]#
Very simple, huh? Yep. This playbook can now be ran on my two hosts, or four, or four thousand hosts. Just imagine having to manually cut-n-paste all your commands on all your hosts. How long will that take???
The next Playbook for turning off and disabling Firewalld & NetworkManager services, as well as adding the Ansible controller to the nodes /etc/hosts file is as follows:
--- - name: This will turning off and disable Firewalld & NetworkManager services, and aslo add ansible-ctlr to the node’s /etc/hosts file hosts: all remote_user: root become: yes tasks: - name: Stop NetworkManager and disable service: name=NetworkManager state=stopped enabled=no - name: Stop firewalld and disable service: name=firewalld state=stopped enabled=no - name: Update /etc/hosts file to include Ansible Controller lineinfile: path: /etc/hosts regexp: '^192.\.168\.64\.118' line: '192.168.64.118 ansible-ctlr.ahaliblogger.local ' owner: root group: root mode: 0644 ~ ~ ~ ~
The Playbook for installing NTP
--- - name: This will install and start NTP services hosts: all gather_facts: true remote_user: root become: yes tasks: - name: Install NTP yum: name=ntp state=latest - name: Start DHCP service and start on boot systemd: name: ntpd state: started enabled: yes ~ ~ ~ ~
And the Playbook for installing NGINX on ansible-host2 and passing a file to its /usr/share/html directory
--- - name: This will install NGINX on ansible-host2 and copy a file to its html directory hosts: web remote_user: root become: yes tasks: - name: run yum install epel-release yum: name=epel-release state=latest - name: install the latest version of nginx yum: name=nginx state=latest - name: Start nginx service and start on boot systemd: name=nginx state=started enabled=yes - name: copy: src: /root/install/ansible-file-to-nginx.txt dest: /usr/share/nginx/html owner: root group: root mode: 0644 ~ ~ ~ ~
NOTE: Notice that ‘hosts’ is now pointing to ‘[web]’ host group in the inventory file.
So now we have 4 Ansible Playbooks to carry out our list of objectives.
Task 11: Running the Playbooks
Ok – The playbooks are complete, Let’s run these books!
First is the Playbook to update our target hosts. The Ansible syntax, specific for updating our target hosts is:
ansible-playbook -i inventory basic-update.yml
This break down into:
When we run this Playbook we’ll see the following:
From the output on the Ansible Server we can ascertain that:
- We’re able to reach the nodes [ ansible-host1 &2] successfully [shown in green]
- Changes were performed successfully [shown in yellow] on the nodes per the ansible playbook’s Tasks [ yum update and yum install net—tools]
- No failures…if there were – it would have shown in red.
Now my nodes have been updated and I’m able to use ‘ifconfig’ to see my interfaces and ip addresses.
Playbook for Disabling NetworkManager and Firewall Services:
[root@ansible-ctlr install]# ansible-playbook -i inventory stop-services.yml
Running the playbook would yield a similar result:
Playbook for updating the host’s hosts file to include the Ansible Controller’s FQDN:
[root@ansible-ctlr install]# ansible-playbook -i inventory update-hostsfile.yml
Playbook to install NTP services
[root@ansible-ctlr install]# ansible-playbook -i inventory install-ntp.yml
And the Playbook to install NGINX on Ansible-host2 and move a file to the html directory:
[root@ansible-ctlr install]# ansible-playbook -i inventory install-nginx.yml
All of the playbooks were ran successfully with zero failures! Just imagine using these same playbooks, tweaking the content a bit to match your desired results you seeking on your networking. Whether it be infrastructure changes, bring nodes online, or parsing nodes for information…
Also imagine running a single Ansible playbook across a thousand  nodes and having all those changes happen simultaneous without ever logging into every – single – one – of – them! A task that would take days to complete can happen in minutes or a few hours!
Check out the video below to see these playbooks in action.
Hope you enjoyed this blog! Time to Automate!