Automating your Azure virtual network gateway with infrastructure-as-code

In this article, we will demonstrate how to take advantage of infrastructure-as-code (IaC) to deploy and maintain your virtual network gateway in Azure. Even if your network team is not acquainted with IaC, we will provide a simple process for them to create a VPN connection in just a few steps that do not require networking expertise.

In the proposed scenario, the customer does not want his precious network engineer spending time creating a VPN connection, troubleshoot, and other time-consuming tasks. The current process is about creating one file containing the documentation for that partner and updating a file. That’s it! The same concept can be used for any other applications/services you have deployed or planned to provision resources in Microsoft Azure.

Overview

When configuring a virtual network gateway in Microsoft Azure, we represent the customer/partner with a local network gateway and a connection object.

The local network gateway will have the public IP address (Item 1) and the internal IP range of that given customer/partner (Item 2), as depicted in the image below.

Azure virtual network gateway

The connection is the glue between the virtual network gateway and the local network gateway. If we look at any given connection, we will see the Shared Key (Item 1), which is the key agreed between both partners and is required to establish the connection.

We also see all pieces of the puzzle together on the right side (Item 2). The virtual network gateway, virtual network, and local network gateway are tied together to establish the connection.

As long as we have the partner on the other side pointing his VPN canon to the same address, and they are sharing the same Shared Key, we are game, and we are ready to roll!

Azure virtual network gateway

The proposed scenario for our article is depicted in the diagram below. We will have a consistent process to create new customers that will connect to our virtual network gateway.

On the left side (repository), we will have an arm template for each piece of infrastructure (virtual network, VPN, and VPN client). The VPN client will be responsible for providing the local network gateway and the connection.

The parameter files will address the specifics to create the resources. The most important one for our thought process is having an individual parameter file for each customer/partner that will connect. That will be used to keep the configuration modular and at the same time keep documentation of our VPNs neat for reviews and auditing purposes.

The other feature that we will use is the Azure DevOps using the pipeline as a code. Any new customer/partner will be a new task in the pipeline, allowing the cloud administrator to perform a few changes in a repo and then go back to watch the waves on the beach while the process takes care of itself.

Azure virtual network gateway

Creating an Azure virtual network gateway

The following code can be used to provision a virtual network gateway. The idea is that the cloud administrator provides a few parameters: VPN gateway name, virtual network (which was created in the previous step), the number of public IP addresses resources, and the SKU.

We are using the defaultValue option to fill out automatically if the cloud administrator misses the parameter files.

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "vpnGateway":{
            "type": "string",
            "defaultValue": "VPN-CaC-Corp"
        },
        "virtualNetwork":{
            "type": "string",
            "defaultValue": "VNET-CaC-Corp"
        },
        "PIP":{
            "type": "array",
            "defaultValue": ["VPN-CaC-Corp-PIP-Primary","VPN-CaC-Corp-PIP-Secondary"]
        },
        "sku":{
            "type": "string",
            "defaultValue": "VpnGw3"
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Network/publicIPAddresses",
            "apiVersion": "2020-05-01",
            "name": "[parameters('pip')[copyIndex()]]",
            "location": "[resourcegroup().location]",
            "sku": {
                "name": "Basic"
            },
            "properties": {
                "publicIPAllocationMethod": "Dynamic"
            },
            "copy": {
                "name": "Copy-PublicIP",
                "count":"[length(parameters('pip'))]"
            }
        },
        {
            "type": "Microsoft.Network/virtualNetworkGateways",
            "apiVersion": "2020-05-01",
            "name": "[parameters('vpnGateway')]",
            "location": "[resourcegroup().location]",
            "dependsOn": [
                "[resourceId('Microsoft.Network/publicIPAddresses', parameters('PIP')[0])]",
                "[resourceId('Microsoft.Network/publicIPAddresses', parameters('PIP')[1])]"
            ],
            "properties": {
                "enablePrivateIpAddress": false,
                "ipConfigurations": [
                    {
                        "name": "default",
                        "properties": {
                            "privateIPAllocationMethod": "Dynamic",
                            "publicIPAddress": {
                                "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('PIP')[0])]"
                            },
                            "subnet": {
                                "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetwork'), 'GatewaySubnet')]"
                            }
                        }
                    },
                    {
                        "name": "activeActive",
                        "properties": {
                            "privateIPAllocationMethod": "Dynamic",
                            "publicIPAddress": {
                                "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('PIP')[1])]"
                            },
                            "subnet": {
                                "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetwork'), 'GatewaySubnet')]"
                            }
                        }
                    }
                ],
                "sku": {
                    "name": "[parameters('sku')]",
                    "tier": "[parameters('sku')]"
                },
                "gatewayType": "Vpn",
                "vpnType": "RouteBased",
                "enableBgp": false,
                "activeActive": true,
                "vpnGatewayGeneration": "Generation2"
            }
        }
    ]
}

Creating the local network gateway and connection through code

Here is the interesting part (one of them, anyway!), The template file requires a few parameters, and we are using defaultValue for troubleshooting. If the cloud administrator/network administrators miss the settings in the parameter file, those default values will be entered. If we see those weird values, we know at a glance that we need to update the parameters file.

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "partner_name":{
            "type": "string",
            "defaultValue": "PartnerName"
        },
        "partner_destination":{
            "type": "string",
            "defaultValue": "1.1.1.1"
        },
        "partner_network":{
            "type": "array",
            "defaultValue": "['2.2.2.2']"
        },
        "partner_shared":{
            "type": "string",
            "defaultValue": "PartnerSharedSecret"
        },
        "vpnGateway":{
            "type": "string"
        }               
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Network/localNetworkGateways",
            "apiVersion": "2020-05-01",
            "name": "[parameters('partner_name')]",
            "location": "[resourcegroup().location]",
            "properties": {
                "localNetworkAddressSpace": {
                    "addressPrefixes": "[parameters('partner_network')]"
                },
                "gatewayIpAddress": "[parameters('partner_destination')]"
            }
        },
        {
            "type": "Microsoft.Network/connections",
            "apiVersion": "2020-05-01",
            "name": "[parameters('partner_name')]",
            "location": "[resourcegroup().location]",
            "dependsOn": [
                "[resourceid('Microsoft.Network/localNetworkGateways',parameters('partner_name'))]"
            ],
            "properties": {
                "virtualNetworkGateway1": {
                    "id": "[resourceId('Microsoft.Network/virtualNetworkGateways',parameters('vpnGateway'))]"
                },
                "localNetworkGateway2": {
                    "id": "[resourceId('Microsoft.Network/localNetworkGateways',parameters('partner_name'))]"
                },
                "connectionType": "IPsec",
                "connectionProtocol": "IKEv2",
                "routingWeight": 0,
                "sharedKey": "[parameters('partner_shared')]",
                "enableBgp": false,
                "useLocalAzureIpAddress": false,
                "usePolicyBasedTrafficSelectors": false,
                "expressRouteGatewayBypass": false,
                "dpdTimeoutSeconds": 0
            }
        }
    ]
}

Here is the parameter file for a specific customer/partner. We need to create copies of this file, label the file as vpnclient.customer.json and place it in the repo.

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "partner_name":{
            "value": "ConnectionName"
        },
        "partner_destination":{
            "value": "y.y.y.y"
        },        
        "partner_network":{
            "value": ["x.x.x.x/24"]
        },        
        "partner_shared":{
            "value": "Add"
        },
        "vpnGateway":{
            "value": "VPN-CaC-Corp"
        }
    }
}

Configuring the Azure Pipeline

The final portion is to instruct the network team/cloud administrator to add a new task in the Azure Pipeline. Basically, the only two areas are listed in bold in the code below. As soon as we commit to the master branch, it will trigger the Azure Pipeline, which will process the new file that we have just created in the previous step.

    - task: AzureResourceManagerTemplateDeployment@3
      displayName: VPNClient_customerName
      inputs:
        deploymentScope: 'Resource Group'
        azureResourceManagerConnection: 'AzureDevOps.SvcConnection'
        subscriptionId: ${{ parameters.p_subscriptionId }}
        action: 'Create Or Update Resource Group'
        resourceGroupName: ${{ parameters.p_resourcegroupName }}
        location: ${{ parameters.p_location }}
        templateLocation: 'Linked artifact'
        csmFile: '$(System.DefaultWorkingDirectory)/vpnclient.json'
        csmParametersFile: '$(System.DefaultWorkingDirectory)/vpnclient.customerName.json'
        deploymentMode: 'Incremental'

Infrastructure-as-code and Azure resources: The big picture!

Before going to check the waves on the beach (after all, we don’t have to spend time creating VPN connections), let’s do a recap of the files we touched to make this article a reality.

The JSON in orange are the template files, and the ones in sort of red are the parameter files. We can expand our structure to support several environments by creating new parameter files and creating customers/partners connections by creating new files (VPNClient.Name.json).

Much easier than it used to be!

In this article, we went over the process to automate a simple task to create VPN connections that in the past required expertise in network gear and special permissions to log on to the devices. We leveraged a software-defined network from Azure to accomplish the task with a simple change in the repo where the service/workload is being stored.

Besides that, we have historical information about changes, documentation of our partners, and backup, which is not that simple to achieve using just networking gear and expensive hours of a network SME.

Featured image: Shutterstock

About The Author

Leave a Comment

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Scroll to Top