An introduction to Ansible: Install & Deployment.

Amit Gujar
7 min readSep 12, 2023

It has been a while since I wrote about the IaaC tool; previously, I focused on the provisioning tool. Today, I’m going to show you how to effortlessly install a cutting-edge configuration management tool that will empower you to easily set up and manage remote servers like a pro! Yes, This tutorial is all about Ansible. Before we dive into the details let’s take a quick look at some of the prerequisites for the tutorial.

Ansible Illustration

Requirements:

  1. Linux machines with root access.
  2. Azure subscription.

Note: You can use docker for Linux machines as well.

What you will learn 🤩:

  1. How to install and configure Ansible on the controller node.
  2. Writing inventory and playbooks.
  3. Installing packages on managed nodes.
  4. Deploying virtual machine on Azure using playbooks.
  5. Bonus commands.

Ansible needs 1 controller node and other managed nodes. I will be using WSL as a controller node & VMS in Azure as a managed node.

I have written a shell script to create virtual machines on Azure. This script will also automatically configure the public key of your local machine to the cloud instances. (Script execution may take time, Ignore all the warnings)

Note: If you are creating the machines on the portal via GUI then you need to add the public key of a local machine.

Let’s Start 😎:

Note: Perform all the steps on the local machine/controller node only.

Step 1: Run the following script to create multiple machines on the cloud.

(2 is recommended, 1 is enough as well 😅)

The script will show you the public addresses of the virtual machines on completion. We will be using these to test connections and include them in the inventory file later on.

group=ansibleNodesRG
size=$1

read -p "How many machines you want to create? = " instance

if [ -z $instance ] || [ $instance -eq 0 ]; then
echo "\nNo size is provided, Please specify the size and Try Again."
exit 1
fi

generate_keys() {
if [ -f ~/.ssh/id_rsa ]; then
echo "SSH keys already exist, Deleting...."
rm -rf ~/.ssh
fi

echo "\n Generating new ssh keys.\n"
yes "" | ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/id_rsa
}
generate_keys

echo "\nInitializing VM Creation Process.\n"

az group create -g $group -l centralindia 2> /dev/null
if [ $? -ne 0 ]; then
az group create -g group -l centralindia
fi

az network vnet create \
-n vm-net \
-g $group \
-l centralindia \
--address-prefixes '192.168.0.0/16' \
--subnet-name subnet \
--subnet-prefixes '192.168.0.0/24'

if [ -z $size ]; then
echo "\nNo size is provided, Using default size Standard_B1s for machine"
size=B1s
fi

vm_create() {
for i in $(seq 1 $instance);
do
az vm create \
-n Machine$i \
-g $group \
-l centralindia \
--size Standard_$size \
--image Ubuntu2204 \
--admin-username amitgujar \
--vnet-name vm-net \
--subnet subnet \
--public-ip-sku Standard \
--generate-ssh-keys \
--ssh-key-values ~/.ssh/id_rsa.pub

az vm open-port -g $group -n Machine$i --port 80
done
}
vm_create

update_vm() {
for i in $(seq 1 $instance);
do
az vm run-command invoke \
-g $group \
-n Machine$i \
--command-id RunShellScript \
--script "sudo apt update -y"

done
}

update_vm

vm_listIpAddress() {
for i in $(seq 1 $instance);
do
az vm list-ip-addresses \
-n Machine$i \
-g $group | grep ipAddress | cut -d':' -f2
done
}

vm_listIpAddress

Step 2: Install Ansible and Python3 on the local machine.

sudo apt install python3.10 -y
sudo apt-add-repository ppa:ansible/ansible
sudo apt update -y
sudo apt install ansible -y

Step 3: Verify the ansible installation.

Step 4: Verify the ssh connection of the cloud machines. Use the public IPs from Step 1.

Machine 1
Machine 2

We need to write our first inventory file. We can directly change the main inventory file located in /etc/ansible/hosts. However, it is a good practice to create a per-project inventory.

Inventory is a file that contains information about the management nodes that Ansible controls.

Step 5: Create the inventory file and add the following lines.

[azure]
ip address of the machine 1
ip address of the machine 2

[azure:vars]
ansible_user=amitgujar
ansible_python_interpreter=/usr/bin/python3
inventory file

It’s time to test the connection of managed nodes and master node.

Step 6: Enter the following command.

ansible all -i inventory -m ping

(If you get the success message then a connection is successful)

Connection status

After writing the inventory file, the next step is to create a playbook.

A playbook is a file containing a series of tasks to be executed on a remote server.

Step 7: Create the playbook.yaml file and add the following lines.

- name: Installing packages
hosts: azure
become: true
tasks:
- name: Installing nginx
ansible.builtin.package:
name: nginx
state: present

Step 8: Enter the following command to execute the playbook against the managed nodes.

ansible-playbook -i inventory playbook.yaml
Playbook execution

To check if nginx is installed, use the curl command.

nginx status

We have successfully installed the nginx package on the managed nodes. By using the same steps you can install different packages as well.

Deployment on Azure:

Step 1: Install Azure CLI

curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

Step 2: Install pip3 & azure packages.

sudo apt install python3-pip
pip3 install -r /usr/lib/python3/dist-packages/ansible_collections/azure/azcollection/requirements-azure.txt

Step 3: Create the service principle for the subscription. Enter the following command.

az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/<SUBSCRIPTION_ID>"

Note down the following details.

{
"appId": "xxxxxx-xxx-xxxx-xxxx-xxxxxxxxxx",
"displayName": "azure-cli-2023-xxxx",
"password": "xxxxxx~xxxxxx~xxxxx",
"tenant": "xxxxx-xxxx-xxxxx-xxxx-xxxxx"
}

Step 4: Add these details into the ~/.bashrc for the authentication.

export AZURE_SUBSCRIPTION_ID=<SUBSCRIPTION_ID>
export AZURE_CLIENT_ID=<CLIENT_ID>
export AZURE_SECRET=<CLIENT_SECRET>
export AZURE_TENANT=<TENANT_ID>

Restart the terminal instance.

Step 5: Create the playbook to deploy the resource group & virtual network on the Azure portal.

- name: Creation of virtual machine
hosts: localhost
vars:
resource_name: rg-ansiblemanaged
location: centralindia
virtual_network_name: vnet-ansiblemanaged
public_ip_name: pip_ansiblemanaged
nsg_name: nsg-ansiblemanaged
subnet_name: subnet-ansiblemanaged
nic_name: nic-ansiblemanaged

tasks:
# creating a resource group
- name: Creating a resource group
azure.azcollection.azure_rm_resourcegroup:
name: "{{ resource_name }}"
location: "{{ location }}"

# creating a virtual network
- name: Creating virtual network
azure.azcollection.azure_rm_virtualnetwork:
name: "{{ virtual_network_name }}"
resource_group: "{{ resource_name }}"
location: "{{ location }}"
address_prefixes_cidr:
- "10.0.0.0/16"
# creating a subnet
- name: Adding subnet
azure.azcollection.azure_rm_subnet:
name: "{{ subnet_name }}"
resource_group: "{{ resource_name }}"
virtual_network: "{{ virtual_network_name }}"
address_prefix_cidr: "10.0.1.0/24"
# creating a public ip
- name: Creating public ip
azure.azcollection.azure_rm_publicipaddress:
name: "{{ public_ip_name }}"
resource_group: "{{ resource_name }}"
allocation_method: Static
register: output_public_ip

# creating a network security group
- name: Creating NSG
azure.azcollection.azure_rm_securitygroup:
name: "{{ nsg_name }}"
resource_group: "{{ resource_name }}"
rules:
- name: 'Allow_SSH'
protocol: Tcp
destination_port_range: 22
access: Allow
priority: 100
direction: Inbound
- name: 'Allow_web_traffic'
protocol: Tcp
destination_port_range:
- 80
- 443
access: Allow
priority: 101
direction: Inbound

# creating a network interface
- name: Creating NIC with public ip
azure.azcollection.azure_rm_networkinterface:
name: "{{ nic_name }}"
resource_group: "{{ resource_name }}"
virtual_network: "{{ virtual_network_name }}"
subnet_name: "{{ subnet_name }}"
security_group: "{{ nsg_name }}"
ip_configurations:
- name: deafult
public_ip_address_name: "{{ public_ip_name }}"
primary: true

# creating virtual machines
- name: Finally creating a VM
azure.azcollection.azure_rm_virtualmachine:
name: vm-ansiblemanaged
resource_group: "{{ resource_name }}"
vm_size: Standard_B1s
admin_username: amit
ssh_password_enabled: false
ssh_public_keys:
- path: /home/amit/.ssh/authorized_keys
key_data: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
network_interfaces:
- name: "{{ nic_name }}"
image:
offer: 0001-com-ubuntu-server-jammy
publisher: Canonical
sku: '22_04-lts'
version: latest

# connection string
- name: Connect to machine using this string
ansible.builtin.debug:
msg: "ssh amit@{{ output_public_ip.state.ip_address }}"

Step 6: Execute the playbook on localhost

ansible-playbook vm_linux.yaml

Step 7: Check if the deployment is successful.

ssh amit@ip_address_displayed_in_terminal

Note: As you can notice we don’t need managed nodes for the resource group deployment, localhost is enough.

Bonus 😉:

Ad hoc commands :

Ad hoc commands are powerful single-line commands in Ansible. They are very useful to execute singular tasks on managed nodes.

Command 1: Harness the power of SCP to transfer files.

ansible azure -i inventory -m ansible.builtin.copy -a "src=/path of controller dest=/path to target machines"
Working of ansible. builtin.copy

Command 2: Changing file permissions on remote machines.

ansible azure -i inventory -m ansible.builtin.file -a "dest=/home/amitgujar/playbook.yaml mode=777"

Command 3: Checking os-release

ansible all -i inventory -a "cat /etc/os-release"

Wrapping up:

Today we have covered several concepts of Ansible including installation, configuration, playbooks, and inventory files. Working with deployment on Azure & some ad hoc commands.

--

--