Ansible Part 0.5 - Create multiple target hosts
Preparing your environment to start using Ansible
Create multiple Target Hosts with Azure VMs
Your target host is the server that you want to configure using Ansible. This tutorial creates tow or more Azure VMs as a target machine.
This can be done in a variety of ways and I have included guidance on how to do this both manually in the Azure Portal and as a bonus via using Ansible.
The Ansible Code will be run on your managed node and you will need a personal Azure Subscription with some credit in it.
SAVE CREDITS: Destroy the VM using lesson 0.4 as soon as possible after completing the tutorial to avoid incurring additional cost.
This tutorial is based on this Microsoft Guide
Dependencies
- If you are setting the VMs up via Ansible you have configured your control host appropriately as per this guide
- You will need a personal Azure Subscription Microsoft Guide
- You will need a service principle for your Azure Subscription. See the tutorials here:
-
Microsoft: Create a Service Principle
- Follow the full tutorial
-
Microsoft: Store secrets for consumption by Ansible
- I recommend following option 1 and storing your credentials on the control host
-
Microsoft: Create a Service Principle
- You will need the Azure Command Line installed as part of step 4. Microsoft Guide
Tutorial - Using the Azure Portal to create the VMs
1.
Create the first VM
Follow the below guide to create a VM in the portal.
Please use the following configuration when creating your VM:
1
2
3
4
5
6
7
8
9
10
11
Resource Group Name: learnAnsibleRG
Virtual Machine Name : myVM-1
Region: uksouth
Image: Ubuntu Server 24.04
Size: Standard_DS1_v2
Username: adminuser
SSH Key: SSH Public Key
Source: Generate New Key Pair
Key Name: myKey
PUBLIC IP: Make sure to have a public IP address otherwise you will not be able to connect unless your control host is in the same private network.
DOWNLOAD SSH KEY: Ensure you download the private key! Move the
.pem
file to your~/.ssh
directory
Follow this guide: Microsoft Guide to Create A VM
2.
Create the second VM
Follow the guide in Step 1 but with the following modifications:
- VM Name:
myvm-2
- Key: use the same key which you downloaded when you created
myVM-1
- You will need a different NIC and Public IP Address but you can re-use all the same resources (e.g. NSG, RG, etc…)
Make sure to record the Public IP address.
Tutorial - Using Ansible to Create the VM
All below commands are executed from a bash terminal.
1.
Create an SSH Key Pair on your control node
- This will be used as the SSH key to access both VMs
1
2
3
4
ssh-keygen -m PEM -t rsa -b 4096
# Enter the file name - default id_rsa
# Enter with NO passphrase
EXPECTED OUTPUT: you should see two files
id_rsa
andid_rsa.pub
2.
Create and update the playbook with your desired configuration
- create the playbook
1 2 3 4 5 6 7 8
mkdir ~/create_vms_ansible nano ~/create_vms_ansible/build_multiple_azure_vms.yml # Copy and Paste the below YAML in # Replace the names with your choices # copy in the public key created in step 1 - should be saved in id_rsa.pub file in .ssh # you can also use a SED command PUBLIC_KEY=$(cat ~/.ssh/id_rsa.pub) sed -i "s|PUBLIC_KEY_DATA|$PUBLIC_KEY|g" ~/create_vms_ansible/build_azure_vms.yml
Copy in the below playbook
- Git Repo Code Snippet
- Update the details in the
vars
section as necessary - Copy the public key value into the
PUBLIC_KEY_DATA
placeholder. I recommend using the sed command detailed above to help.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
---
# This playbook uses the Azure ansible modules to create a VM in Azure.
# You need to have the service principle details as per the dependencies in the tutorial
- name: Create Multiple Azure VMs
hosts: localhost
connection: local
vars:
resource_group_name: learnAnsibleRG
location: uksouth
vnet_name: myVnet
subnet_name: mySubnet
nsg_name: myNetworkSecurityGroup
vm_username: adminuser
key_data: "PUBLIC_KEY_DATA"
vm_one:
public_ip_name: myPublicIP-1
nic_name: myNIC-1
vm_name: myVM-1
vm_size: Standard_DS1_v2
vm_two:
public_ip_name: myPublicIP-2
nic_name: myNIC-2
vm_name: myVM-2
vm_size: Standard_DS1_v2
tasks:
- name: Create resource group
azure.azcollection.azure_rm_resourcegroup:
name: "{{ resource_group_name }}"
location: "{{ location }}"
- name: Create virtual network
azure.azcollection.azure_rm_virtualnetwork:
resource_group: "{{ resource_group_name }}"
name: "{{ vnet_name }}"
address_prefixes: "10.0.0.0/16"
- name: Add subnet
azure.azcollection.azure_rm_subnet:
resource_group: "{{ resource_group_name }}"
name: "{{ subnet_name }}"
address_prefix: "10.0.1.0/24"
virtual_network: "{{ vnet_name }}"
- name: Create Network Security Group that allows SSH and HTTP
azure.azcollection.azure_rm_securitygroup:
resource_group: "{{ resource_group_name }}"
name: "{{ nsg_name }}"
rules:
- name: SSH
protocol: Tcp
destination_port_range: 22
access: Allow
priority: 1001
direction: Inbound
- name: HTTP
protocol: Tcp
destination_port_range: 80
access: Allow
priority: 1002
direction: Inbound
# This is created using blocks for ease of copying in a tutorial,
# it would be preferable to use a loop and include_task feature of ansible to avoid duplicating the information.
- name: Create VM Block
block:
- name: Create public IP address
azure.azcollection.azure_rm_publicipaddress:
resource_group: "{{ resource_group_name }}"
allocation_method: Static
name: "{{ vm_one.public_ip_name }}"
register: output_ip_address_vm_one
- name: Public IP of VM
ansible.builtin.debug:
msg: "The public IP is {{ output_ip_address_vm_one.state.ip_address }}."
- name: Create virtual network interface card
azure.azcollection.azure_rm_networkinterface:
resource_group: "{{ resource_group_name }}"
name: "{{ vm_one.nic_name }}"
virtual_network: "{{ vnet_name }}"
subnet: "{{ subnet_name }}"
security_group: "{{ nsg_name }}"
ip_configurations:
- name: ipconfig1
public_ip_address_name: "{{ vm_one.public_ip_name }}"
primary: true
- name: Create VM
azure.azcollection.azure_rm_virtualmachine:
resource_group: "{{ resource_group_name }}"
name: "{{ vm_one.vm_name }}"
vm_size: "{{ vm_one.vm_size }}"
admin_username: "{{ vm_username }}"
ssh_password_enabled: false
ssh_public_keys:
- path: "/home/{{ vm_username }}/.ssh/authorized_keys"
key_data: "{{ key_data }}"
network_interfaces: "{{ vm_one.nic_name }}"
image:
offer: 0001-com-ubuntu-server-jammy
publisher: Canonical
sku: 22_04-lts
version: latest
- name: Public IP of VM
ansible.builtin.debug:
msg: "The public IP is {{ output_ip_address_vm_one.state.ip_address }}."
- name: Set IP of VM
ansible.builtin.set_fact:
vm_one_public_ip: "{{ output_ip_address_vm_one.state.ip_address }}"
- name: Create VM Block
block:
- name: Create public IP address
azure.azcollection.azure_rm_publicipaddress:
resource_group: "{{ resource_group_name }}"
allocation_method: Static
name: "{{ vm_two.public_ip_name }}"
register: output_ip_address_vm_two
- name: Public IP of VM
ansible.builtin.debug:
msg: "The public IP is {{ output_ip_address_vm_two.state.ip_address }}."
- name: Create virtual network interface card
azure.azcollection.azure_rm_networkinterface:
resource_group: "{{ resource_group_name }}"
name: "{{ vm_two.nic_name }}"
virtual_network: "{{ vnet_name }}"
subnet: "{{ subnet_name }}"
security_group: "{{ nsg_name }}"
ip_configurations:
- name: ipconfig1
public_ip_address_name: "{{ vm_two.public_ip_name }}"
primary: true
- name: Create VM
azure.azcollection.azure_rm_virtualmachine:
resource_group: "{{ resource_group_name }}"
name: "{{ vm_two.vm_name }}"
vm_size: "{{ vm_two.vm_size }}"
admin_username: "{{ vm_username }}"
ssh_password_enabled: false
ssh_public_keys:
- path: "/home/{{ vm_username }}/.ssh/authorized_keys"
key_data: "{{ key_data }}"
network_interfaces: "{{ vm_two.nic_name }}"
image:
offer: 0001-com-ubuntu-server-jammy
publisher: Canonical
sku: 22_04-lts
version: latest
- name: Public IP of VM
ansible.builtin.debug:
msg: "The public IP is {{ output_ip_address_vm_two.state.ip_address }}."
- name: Set IP of VM
ansible.builtin.set_fact:
vm_two_public_ip: "{{ output_ip_address_vm_two.state.ip_address }}"
- name: Print IPs of VMs
ansible.builtin.debug:
msg:
- "The public IP of VM One is {{ vm_one_public_ip }}."
- "The public IP of VM Two is {{ vm_two_public_ip }}."
The key data is sensitive and should not be committed into a repo!
3.
Run the playbook
LEARN: there is no inventory file because the commands all run on the localhost (the control node).
PERMISSIONS: your service principle will need to be assigned Contributor role on the subscription you are creating the VMs in.
- Export your Azure Service Principle Credentials as Variables if you have not created a configuration file as outlined in the Dependencies.
1
2
3
4
export AZURE_SUBSCRIPTION_ID=<subscription_id>
export AZURE_CLIENT_ID=<service_principal_app_id>
export AZURE_SECRET=<service_principal_password>
export AZURE_TENANT=<service_principal_tenant_id>
2. Run the below commands on your control host
1
2
3
4
5
6
7
8
9
# Install the dependencies
ansible-galaxy collection install azure.azcollection --force
sudo pip3 install -r ~/.ansible/collections/ansible_collections/azure/azcollection/requirements.txt
# IMPORTANT: remember to have stored or exported your Azure Service Principle Credentials
# Run the playbook
ansible-playbook ~/create_vms_ansible/build_azure_vms.yml
3. Capture the output of the playbook to find the public ip address.
4.
Verify the VM exists
Execute the below commands, ensuring you update the VM name if you changed it.
1
2
3
4
5
6
7
az login
# This should open up an interactive prompt
VM_ONE_NAME=myVM-1
VM_TWO_NAME=myVM-2
az vm list -d -o table --query "[?name=='$VM_ONE_NAME']"
az vm list -d -o table --query "[?name=='$VM_TWO_NAME']"
5.
Test connection
From your control host attempt to SSH into each VM.
1
2
IP=<insert from output of Ansible>
ssh adminuser@$IP -i ~/.ssh/id_rsa
Tutorial - Destroy your VMs with Ansible or Azure
When you have finished your tutorial remember to destroy the VMs to reduce costs.
- Follow the instructions in this post Destroy target host - Azure VM
REMEMBER: update any details like the name of the RG as needed in the Ansible Playbook