If like me you have been guilty of hard coding all of your network configuration in Ansible variables like this …

Hard Code Values
Lines: 12 Charaters: 359 Language: YAML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  vars:
    network:
      full_range: "10.0.0.0/24"
      subnets:
        - name: "subnet1"
          address_range: "10.0.0.0/27"
        - name: "subnet2"
          address_range: "10.0.0.32/27"
        - name: "subnet3"
          address_range: "10.0.0.64/27"
        - name: "subnet4"
          address_range: "10.0.0.96/27"

… then this post is for you. Not that I am saying that is wrong, but it does mean if you want to the network range you are using them you also have to update it in four other places.

Working with network ranges dynamically in Ansible

Instead of hardcoding subnet addresses, we can leverage Ansible’s powerful filters and plugins to calculate network and subnet information dynamically. This approach enhances the flexibility and maintainability of your playbooks.

A Basic Playbook

Let’s start by creating a playbook that calculates subnet addresses based on a given network range and subnet sizes.

site.yml
Lines: 78 Charaters: 2802 Language: YAML
 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
---

- name: "Network and Subnet Information Playbook"
  hosts: localhost
  gather_facts: true
  vars:
    cidr_range: "10.0.0.0/24"
    subnets:
      - name: "subnet1"
        cidr_size: 27
      - name: "subnet2"
        cidr_size: 27
      - name: "subnet3"
        cidr_size: 27
      - name: "subnet4"
        cidr_size: 27

  tasks:
    - name: "Generate network information"
      ansible.builtin.set_fact:
        network_info:
          cidr: "{{ cidr_range }}"
          available_ips: "{{ (cidr_range | ansible.utils.ipaddr('size')) - 2 }}"

    - name: "Generate subnet information"
      ansible.builtin.set_fact:
        subnet_info: "{{ subnet_info | default([]) + [{'name': item.0.name, 'cidr': cidr_range | ansible.utils.ipsubnet(item.0.cidr_size, item.1)}] }}"
      loop: "{{ subnets | zip_longest(range(subnets|length), fillvalue='')|list }}"

    - name: "Calculate detailed subnet information"
      ansible.builtin.set_fact:
        subnet_details: "{{ subnet_details | default([]) + [subnet | combine({
          'network': (subnet.cidr | ansible.utils.ipaddr('network')),
          'netmask': (subnet.cidr | ansible.utils.ipaddr('netmask')),
          'broadcast': (subnet.cidr | ansible.utils.ipaddr('broadcast')),
          'first_ip': (subnet.cidr | ansible.utils.ipaddr('1') | ansible.utils.ipaddr('address')),
          'last_ip': (subnet.cidr | ansible.utils.ipaddr('-2') | ansible.utils.ipaddr('address'))
        })] }}"
      loop: "{{ subnet_info }}"
      loop_control:
        loop_var: subnet

    - name: "Display network information"
      vars:
        network_info_output: |
          ===============================
          Network Information
          ===============================
          CIDR Range: {{ network_info.cidr }}
          Available IP Addresses: {{ network_info.available_ips }}

          Subnet Overview:
          {% for subnet in subnet_info %}
          - {{ subnet.name }}: {{ subnet.cidr }}
          {% endfor %}          
      ansible.builtin.debug:
        msg: "{{ network_info_output.split('\n') }}"

    - name: "Display detailed subnet information"
      vars:
        detailed_info: |
          ===============================
          Detailed Subnet Information
          ===============================
          {% for subnet in subnet_details %}
          {{ subnet.name }}:
            CIDR: {{ subnet.cidr }}
            Network: {{ subnet.network }}
            Netmask: {{ subnet.netmask }}
            Broadcast: {{ subnet.broadcast }}
            First Usable IP: {{ subnet.first_ip }}
            Last Usable IP: {{ subnet.last_ip }}
          {% if not loop.last %}

          {% endif %}
          {% endfor %}          
      ansible.builtin.debug:
        msg: "{{ detailed_info.split('\n') }}"

Breaking Down the Playbook

The playbook is designed to generate and display network and subnet information based on a given CIDR range. Let’s break it down:

  1. Playbook Header:

    • Name: “Network and Subnet Information Playbook”
    • Hosts: localhost (runs on the local machine)
    • gather_facts: true (collects system information)
  2. Variables:

    • cidr_range: Defines the main network CIDR (10.0.0.0/24)
    • subnets: A list of 4 subnets, each with a name and CIDR size (27 bits)
  3. Tasks:
    The playbook contains several tasks that generate and display network information.

Tasks

  1. Generate network information:

    • Creates a ’network_info’ fact containing the CIDR range and available IP addresses
    • Uses the ‘ansible.utils.ipaddr’ filter to calculate the number of available IPs
  2. Generate subnet information:

    • Creates a ‘subnet_info’ list containing each subnet’s name and CIDR
    • Uses ‘ansible.utils.ipsubnet’ to calculate subnet CIDRs
    • Utilizes a loop with zip_longest to iterate over subnets
  3. Calculate detailed subnet information:

    • Expands on ‘subnet_info’ to create ‘subnet_details’
    • Adds network, netmask, broadcast, first IP, and last IP for each subnet
    • Uses various ‘ansible.utils.ipaddr’ filters to calculate these values
  4. Display network information:

    • Creates a formatted output string with network overview
    • Uses ansible.builtin.debug to display the information
  5. Display detailed subnet information:

    • Creates a formatted output string with detailed subnet information
    • Uses ansible.builtin.debug to display the information

Understanding the ansible.utils.ipsubnet function

The ‘ansible.utils.ipsubnet’ function is used to calculate subnet information based on a given network and subnet size. In this playbook, it’s used as follows:

Dynamic Network Configuration
Lines: 1 Charaters: 90 Language: YAML
1
cidr_range | ansible.utils.ipsubnet(item.0.cidr_size, item.1)

  • cidr_range: The base network (10.0.0.0/24)
  • item.0.cidr_size: The size of the subnet (27 in this case)
  • item.1: The index of the current subnet (0, 1, 2, 3)

This function calculates the CIDR for each subnet within the main network. For example:

  • Subnet 1: 10.0.0.0/27
  • Subnet 2: 10.0.0.32/27
  • Subnet 3: 10.0.0.64/27
  • Subnet 4: 10.0.0.96/27

Understanding the ansible.utils.ipaddr function

The ‘ansible.utils.ipaddr’ function is a versatile filter for IP address manipulation. In this playbook, it’s used in several ways, as you can see from the examples below the ‘ansible.utils.ipaddr’ function accepts various options to perform different operations on IP addresses and networks, making it a powerful tool for network-related tasks in Ansible playbooks.

Calculating available IPs

Dynamic Network Configuration
Lines: 1 Charaters: 71 Language: YAML
1
 cidr_range | ansible.utils.ipaddr('size')

This calculates the total number of IP addresses in the CIDR range.

Getting network address

Dynamic Network Configuration
Lines: 1 Charaters: 77 Language: YAML
1
 subnet.cidr | ansible.utils.ipaddr('network')

This extracts the network address from a CIDR.

Getting netmask

Dynamic Network Configuration
Lines: 1 Charaters: 74 Language: YAML
1
subnet.cidr | ansible.utils.ipaddr('netmask')

This calculates the subnet mask for a given CIDR.

Getting broadcast address

Dynamic Network Configuration
Lines: 1 Charaters: 76 Language: YAML
1
subnet.cidr | ansible.utils.ipaddr('broadcast')

This calculates the broadcast address for a subnet.

Calculating first and last usable IPs

Dynamic Network Configuration
Lines: 2 Charaters: 177 Language: YAML
1
2
subnet.cidr | ansible.utils.ipaddr('1') | ansible.utils.ipaddr('address')
subnet.cidr | ansible.utils.ipaddr('-2') | ansible.utils.ipaddr('address')

These calculate the first and last usable IP addresses in a subnet.

Running the Playbook

Running the site.yml playbook using the following:

Running the playbook
ansible-playbook site.yml

Gives the following output:

The Full Output
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit
localhost does not match 'all'

PLAY [Network and Subnet Information Playbook] *************************************

TASK [Gathering Facts] *************************************************************
ok: [localhost]

TASK [Generate network information] ************************************************
ok: [localhost]

TASK [Generate subnet information] *************************************************
ok: [localhost] => (item=[{'name': 'subnet1', 'cidr_size': 27}, 0])
ok: [localhost] => (item=[{'name': 'subnet2', 'cidr_size': 27}, 1])
ok: [localhost] => (item=[{'name': 'subnet3', 'cidr_size': 27}, 2])
ok: [localhost] => (item=[{'name': 'subnet4', 'cidr_size': 27}, 3])

TASK [Calculate detailed subnet information] ***************************************
ok: [localhost] => (item={'name': 'subnet1', 'cidr': '10.0.0.0/27'})
ok: [localhost] => (item={'name': 'subnet2', 'cidr': '10.0.0.32/27'})
ok: [localhost] => (item={'name': 'subnet3', 'cidr': '10.0.0.64/27'})
ok: [localhost] => (item={'name': 'subnet4', 'cidr': '10.0.0.96/27'})

TASK [Display network information] *************************************************
ok: [localhost] => {
    "msg": [
        "===============================",
        "Network Information",
        "===============================",
        "CIDR Range: 10.0.0.0/24",
        "Available IP Addresses: 254",
        "",
        "Subnet Overview:",
        "- subnet1: 10.0.0.0/27",
        "- subnet2: 10.0.0.32/27",
        "- subnet3: 10.0.0.64/27",
        "- subnet4: 10.0.0.96/27",
        ""
    ]
}

TASK [Display detailed subnet information] ****************************************
ok: [localhost] => {
    "msg": [
        "===============================",
        "Detailed Subnet Information",
        "===============================",
        "subnet1:",
        "  CIDR: 10.0.0.0/27",
        "  Network: 10.0.0.0",
        "  Netmask: 255.255.255.224",
        "  Broadcast: 10.0.0.31",
        "  First Usable IP: 10.0.0.1",
        "  Last Usable IP: 10.0.0.30",
        "",
        "subnet2:",
        "  CIDR: 10.0.0.32/27",
        "  Network: 10.0.0.32",
        "  Netmask: 255.255.255.224",
        "  Broadcast: 10.0.0.63",
        "  First Usable IP: 10.0.0.33",
        "  Last Usable IP: 10.0.0.62",
        "",
        "subnet3:",
        "  CIDR: 10.0.0.64/27",
        "  Network: 10.0.0.64",
        "  Netmask: 255.255.255.224",
        "  Broadcast: 10.0.0.95",
        "  First Usable IP: 10.0.0.65",
        "  Last Usable IP: 10.0.0.94",
        "",
        "subnet4:",
        "  CIDR: 10.0.0.96/27",
        "  Network: 10.0.0.96",
        "  Netmask: 255.255.255.224",
        "  Broadcast: 10.0.0.127",
        "  First Usable IP: 10.0.0.97",
        "  Last Usable IP: 10.0.0.126",
        ""
    ]
}

PLAY RECAP ************************************************************************
localhost: ok=6  changed=0 unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

As you can see, we are getting quite alot of information about the network with only telling Ansible the CIDR range to use and the number of subnets and their size.

Deploying an Azure Virtual Network

This Ansible playbook automates the creation of an Azure Virtual Network (VNet) and multiple subnets. It leverages the same network calculation concepts as our previous example, applying them to real Azure resources. The playbook creates a VNet with a specified CIDR range and dynamically generates subnets based on given parameters. It uses Azure Resource Manager modules to interact with Azure, and concludes by displaying detailed information about the created network infrastructure - which you probably would never include in your own Playbooks - I have included it here so we can see the output.

This automation streamlines the process of setting up complex network configurations in Azure, ensuring consistency and reducing manual errors.

The Playbook itself looks like the following:

The Azure Vnet Playbook

azure.yml
Lines: 101 Charaters: 3647 Language: YAML
  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
---

- name: "Create Azure Virtual Network and Subnets"
  hosts: localhost
  connection: local
  gather_facts: false

  vars:
    location: "uksouth"
    resource_group: "rg-ansible-demo-uks"
    vnet_name: "vnet-ansible-demo-uks"
    cidr_range: "10.0.0.0/24"
    subnets:
      - name: "subnet1"
        cidr_size: 27
      - name: "subnet2"
        cidr_size: 27
      - name: "subnet3"
        cidr_size: 27
      - name: "subnet4"
        cidr_size: 27

  tasks:
    - name: "Ensure resource group exists"
      azure.azcollection.azure_rm_resourcegroup:
        name: "{{ resource_group }}"
        location: "{{ location }}"

    - name: "Calculate subnet CIDRs"
      ansible.builtin.set_fact:
        subnet_info: "{{ subnet_info | default([]) + [{'name': item.0.name, 'cidr': cidr_range | ansible.utils.ipsubnet(item.0.cidr_size, item.1)}] }}"
      loop: "{{ subnets | zip_longest(range(subnets|length), fillvalue='')|list }}"

    - name: "Create virtual network"
      azure.azcollection.azure_rm_virtualnetwork:
        resource_group: "{{ resource_group }}"
        name: "{{ vnet_name }}"
        address_prefixes_cidr:
          - "{{ cidr_range }}"

    - name: "Create subnets"
      azure.azcollection.azure_rm_subnet:
        resource_group: "{{ resource_group }}"
        name: "{{ item.name }}"
        address_prefix_cidr: "{{ item.cidr }}"
        virtual_network_name: "{{ vnet_name }}"
      loop: "{{ subnet_info }}"

    - name: "Gather VNet facts"
      azure.azcollection.azure_rm_virtualnetwork_info:
        resource_group: "{{ resource_group }}"
        name: "{{ vnet_name }}"
      register: vnet_facts

    - name: "Gather VNet facts"
      azure.azcollection.azure_rm_virtualnetwork_info:
        resource_group: "{{ resource_group }}"
        name: "{{ vnet_name }}"
      register: vnet_facts

    - name: "Calculate detailed subnet information"
      ansible.builtin.set_fact:
        subnet_details: "{{ subnet_details | default([]) + [subnet | combine({
          'network': (subnet.address_prefix | ansible.utils.ipaddr('network')),
          'netmask': (subnet.address_prefix | ansible.utils.ipaddr('netmask')),
          'broadcast': (subnet.address_prefix | ansible.utils.ipaddr('broadcast')),
          'first_ip': (subnet.address_prefix | ansible.utils.ipaddr('1') | ansible.utils.ipaddr('address')),
          'last_ip': (subnet.address_prefix | ansible.utils.ipaddr('-2') | ansible.utils.ipaddr('address'))
        })] }}"
      loop: "{{ vnet_facts.virtualnetworks[0].subnets }}"
      loop_control:
        loop_var: subnet

    - name: "Display detailed VNet and Subnet information"
      vars:
        detailed_info: |
          ===============================
          Virtual Network Information
          ===============================
          Name: {{ vnet_name }}
          Resource Group: {{ resource_group }}
          Location: {{ location }}
          Address Space: {{ vnet_facts.virtualnetworks[0].address_prefixes[0] }}

          ===============================
          Detailed Subnet Information
          ===============================
          {% for subnet in subnet_details %}
          {{ subnet.name }}:
            CIDR: {{ subnet.address_prefix }}
            Network: {{ subnet.network }}
            Netmask: {{ subnet.netmask }}
            Broadcast: {{ subnet.broadcast }}
            First Usable IP: {{ subnet.first_ip }}
            Last Usable IP: {{ subnet.last_ip }}
          {% if not loop.last %}

          {% endif %}
          {% endfor %}          
      ansible.builtin.debug:
        msg: "{{ detailed_info.split('\n') }}"

Running the Playbook

Running the azure.yml playbook using the following:

Running the playbook
ansible-playbook site.yml

Gives the following output:

The Full Output
PLAY [Create Azure Virtual Network and Subnets] ************************************

TASK [Ensure resource group exists] ************************************************
changed: [localhost]

TASK [Calculate subnet CIDRs] ******************************************************
ok: [localhost] => (item=[{'name': 'subnet1', 'cidr_size': 27}, 0])
ok: [localhost] => (item=[{'name': 'subnet2', 'cidr_size': 27}, 1])
ok: [localhost] => (item=[{'name': 'subnet3', 'cidr_size': 27}, 2])
ok: [localhost] => (item=[{'name': 'subnet4', 'cidr_size': 27}, 3])

TASK [Create virtual network] ******************************************************
changed: [localhost]

TASK [Create subnets] ***************************************************************
changed: [localhost] => (item={'name': 'subnet1', 'cidr': '10.0.0.0/27'})
changed: [localhost] => (item={'name': 'subnet2', 'cidr': '10.0.0.32/27'})
changed: [localhost] => (item={'name': 'subnet3', 'cidr': '10.0.0.64/27'})
changed: [localhost] => (item={'name': 'subnet4', 'cidr': '10.0.0.96/27'})

TASK [Gather VNet facts] ************************************************************
ok: [localhost]

TASK [Gather VNet facts] *************************************************************
ok: [localhost]

TASK [Calculate detailed subnet information] *****************************************
ok: [localhost] => (item={'id': '/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-ansible-demo-uks/providers/Microsoft.Network/virtualNetworks/vnet-ansible-demo-uks/subnets/subnet1', 'name': 'subnet1', 'provisioning_state': 'Succeeded', 'address_prefix': '10.0.0.0/27', 'address_prefixes': None, 'network_security_group': None, 'route_table': None})
ok: [localhost] => (item={'id': '/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-ansible-demo-uks/providers/Microsoft.Network/virtualNetworks/vnet-ansible-demo-uks/subnets/subnet2', 'name': 'subnet2', 'provisioning_state': 'Succeeded', 'address_prefix': '10.0.0.32/27', 'address_prefixes': None, 'network_security_group': None, 'route_table': None})
ok: [localhost] => (item={'id': '/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-ansible-demo-uks/providers/Microsoft.Network/virtualNetworks/vnet-ansible-demo-uks/subnets/subnet3', 'name': 'subnet3', 'provisioning_state': 'Succeeded', 'address_prefix': '10.0.0.64/27', 'address_prefixes': None, 'network_security_group': None, 'route_table': None})
ok: [localhost] => (item={'id': '/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-ansible-demo-uks/providers/Microsoft.Network/virtualNetworks/vnet-ansible-demo-uks/subnets/subnet4', 'name': 'subnet4', 'provisioning_state': 'Succeeded', 'address_prefix': '10.0.0.96/27', 'address_prefixes': None, 'network_security_group': None, 'route_table': None})

TASK [Display detailed VNet and Subnet information] **********************************
ok: [localhost] => {
    "msg": [
        "===============================",
        "Virtual Network Information",
        "===============================",
        "Name: vnet-ansible-demo-uks",
        "Resource Group: rg-ansible-demo-uks",
        "Location: uksouth",
        "Address Space: 10.0.0.0/24",
        "",
        "===============================",
        "Detailed Subnet Information",
        "===============================",
        "subnet1:",
        "  CIDR: 10.0.0.0/27",
        "  Network: 10.0.0.0",
        "  Netmask: 255.255.255.224",
        "  Broadcast: 10.0.0.31",
        "  First Usable IP: 10.0.0.1",
        "  Last Usable IP: 10.0.0.30",
        "",
        "subnet2:",
        "  CIDR: 10.0.0.32/27",
        "  Network: 10.0.0.32",
        "  Netmask: 255.255.255.224",
        "  Broadcast: 10.0.0.63",
        "  First Usable IP: 10.0.0.33",
        "  Last Usable IP: 10.0.0.62",
        "",
        "subnet3:",
        "  CIDR: 10.0.0.64/27",
        "  Network: 10.0.0.64",
        "  Netmask: 255.255.255.224",
        "  Broadcast: 10.0.0.95",
        "  First Usable IP: 10.0.0.65",
        "  Last Usable IP: 10.0.0.94",
        "",
        "subnet4:",
        "  CIDR: 10.0.0.96/27",
        "  Network: 10.0.0.96",
        "  Netmask: 255.255.255.224",
        "  Broadcast: 10.0.0.127",
        "  First Usable IP: 10.0.0.97",
        "  Last Usable IP: 10.0.0.126",
        ""
    ]
}

PLAY RECAP ***************************************************************************
localhost:   ok=8  changed=3  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

As you can see, we have the VNet and subnets all created as well.

Extending the Playbook further

We could extend the playbook further, for example we could deploy an Azure Application Gateway into one of the subnets and get its static internal IP address by figuring out the forth IP address in subnet1 by using:

Adding an Azure Application Gateway
Lines: 50 Charaters: 2021 Language: YAML
 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
- name: "Calculate 4th IP address of subnet1"
  ansible.builtin.set_fact:
    appgw_ip: "{{ subnet_details | selectattr('name', 'equalto', 'subnet1') | map(attribute='cidr') | first | ansible.utils.ipaddr('4') | ansible.utils.ipaddr('address') }}"

- name: "Create public IP address for Application Gateway"
  azure.azcollection.azure_rm_publicipaddress:
    resource_group: "{{ resource_group }}"
    name: "pip-agw-ansible-demo-uks"
    allocation_method: Static
    sku: Standard

- name: "Create Application Gateway"
  azure.azcollection.azure_rm_applicationgateway:
    resource_group: "{{ resource_group }}"
    name: "pip-agw-ansible-demo-uks"
    sku:
      name: Standard_v2
      tier: Standard_v2
    gateway_ip_configurations:
      - subnet:
          id: "{{ vnet_facts.virtualnetworks[0].subnets | selectattr('name', 'equalto', 'subnet1') | map(attribute='id') | first }}"
        name: appGatewayIpConfig
    frontend_ip_configurations:
      - public_ip_address: "pip-agw-ansible-demo-uks"
        name: appGatewayFrontendIP
    frontend_ports:
      - port: 80
        name: appGatewayFrontendPort
    backend_address_pools:
      - name: appGatewayBackendPool
    backend_http_settings_collection:
      - port: 80
        protocol: Http
        cookie_based_affinity: Disabled
        name: appGatewayBackendHttpSettings
    http_listeners:
      - frontend_ip_configuration: appGatewayFrontendIP
        frontend_port: appGatewayFrontendPort
        name: appGatewayHttpListener
    request_routing_rules:
      - rule_type: Basic
        http_listener: appGatewayHttpListener
        backend_address_pool: appGatewayBackendPool
        backend_http_settings: appGatewayBackendHttpSettings
        name: rule1
    ip_configurations:
      - subnet:
          id: "{{ vnet_facts.virtualnetworks[0].subnets | selectattr('name', 'equalto', 'subnet1') | map(attribute='id') | first }}"
        private_ip_address: "{{ appgw_ip }}"
        private_ip_allocation_method: Static

Summary

In this post, we’ve explored the power of dynamic network configuration in Ansible, moving beyond hardcoded values to create more flexible and maintainable playbooks. We started with a local playbook that calculates subnet information based on a given CIDR range, utilizing Ansible’s powerful ansible.utils.ipsubnet and ansible.utils.ipaddr functions.

We then applied these concepts to a real-world scenario, creating an Azure Virtual Network with multiple subnets. This approach not only simplifies our code but also makes it more adaptable to changes in network requirements.

Key takeaways include:

  1. Using ansible.utils.ipsubnet to dynamically calculate subnet CIDRs
  2. Leveraging ansible.utils.ipaddr for detailed IP address manipulation
  3. Applying these techniques to Azure VNet and subnet creation
  4. Extending the playbook to include advanced resources like Azure Application Gateway

By adopting these practices, you can create more robust, flexible, and error-resistant network configurations in your Ansible playbooks. Whether you’re working with local environments or cloud providers like Azure, these techniques will help you manage network infrastructure more effectively. I would encourage you to experiment with these methods in your own projects. As you become more comfortable with dynamic network calculations, you’ll find that your infrastructure-as-code becomes more powerful and adaptable to changing requirements.