Post

Ansible Part 0.5 - Create multiple target hosts

Preparing your environment to start using Ansible

Ansible Part 0.5 - Create multiple target hosts

Return to index

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

Tutorial - Using the Azure Portal to create the VMs

1. :rocket: 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. :rocket: 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. :rocket: 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 and id_rsa.pub

2. :rocket: 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. :rocket: 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.

  1. 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. :rocket: 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. :rocket: 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.

REMEMBER: update any details like the name of the RG as needed in the Ansible Playbook

This post is licensed under CC BY 4.0 by the author.

Trending Tags