In this part of the blog series, I want to create the VPN connection from Azure to my on-premises lab environment.

As I did in the last part, I will create a new branch in Github for my work. I am doing this to ensure that my main branch is always running without errors. I will call this new branch for “VPN”.

The first step is to create the template file for the Virtual Network Gateway in Azure. I found the documentation for this item here . As with some of the other resources, there are a lot of settings that I can use, but not all of them are needed. I adapted the template for my needs, and it now looks like the code below. I have saved this file as “VirtualNetworkGateway.bicep” and placed it under the same template folder as the virtual network.

param virtualNetworkGatewayName string 
param rgName string 
param location string = resourceGroup().location
param sku string 

@allowed([
  'Vpn'
  'ExpressRoute'
])
param gatewayType string = 'Vpn'

@allowed([
  'RouteBased'
  'PolicyBased'
])
param vpnType string = 'RouteBased'
param VirtualNetworkName string 
param SubnetName string = 'GatewaySubnet'
param PublicIpAddressName string 
param enableBGP bool = false

resource PublicIpAddressName_resource 'Microsoft.Network/publicIPAddresses@2021-02-01' = {
  name: PublicIpAddressName
  location: location
  properties: {
    publicIPAllocationMethod: 'Dynamic'
  }
}

resource virtualNetworkGateway 'Microsoft.Network/virtualNetworkGateways@2021-02-01' = {
  name: virtualNetworkGatewayName
  location: location
  properties: {
    gatewayType: gatewayType
    ipConfigurations: [
      {
        name: 'default'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          subnet: {
            id: resourceId(rgName, 'Microsoft.Network/virtualNetworks/subnets', VirtualNetworkName, SubnetName)
          }
          publicIPAddress: {
            id: resourceId(rgName, 'Microsoft.Network/publicIPAddresses', PublicIpAddressName)
          }
        }
      }
    ]
    vpnType: vpnType
    enableBgp: enableBGP
    sku: {
      name: sku
      tier: sku
    }
  }
  dependsOn: [
    PublicIpAddressName_resource
  ]
}
output vngid string = virtualNetworkGateway.id

The code above contains a new expression called “@Allowed”, this expression allows me to control which values are approved for the parameter listed below this expression. This is a great way to ensure that the values passed on are correct when you have a set of approved values. To create a Virtual Network Gateway, I need to create a public IP address, so I have a resource for that, and I have made the resource for the gateway dependent on this public IP address. I now need to create the execution file that will use this template. I will call this file “DeployVPN.bicep”. Unlike the virtual network deployment file, I have chosen to add all the values into the module section instead of creating parameters and variables for them. I have pasted in the code for the deployment file below.

module virtualNetworkGateway './Templates/VirtualNetworkGateway.bicep' = {
  name: 'VirtualNetworkGateway'
  params: {
    enableBGP: false
    gatewayType: 'Vpn'
    location: resourceGroup().location
    PublicIpAddressName: 'pip-vng-sharedservices-001'
    rgName: resourceGroup().name 
    sku: 'Basic'
    SubnetName: 'GatewaySubnet'
    virtualNetworkGatewayName: 'vng-sharedservices-001'
    VirtualNetworkName: 'vnet-sharedservices-001'
    vpnType: 'RouteBased'
  }
}

Next, I will create the template for the Local Network Gateway and place it in the same folder as the others. The documentation on Local Network Gateways can be found here . I have pasted my template code below.

param localNetworkGatewayName string
param location string = resourceGroup().location
param gatewayIpAddress string
param addressPrefixes array = []

resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2021-02-01' = {
  name: localNetworkGatewayName
  location: location
  properties: {
    localNetworkAddressSpace: {
      addressPrefixes: addressPrefixes
    }
    gatewayIpAddress: gatewayIpAddress
  }
}
output lngid string = localNetworkGateway.id

The last bit of the VPN connection is what Microsoft calls Connection. This is what ties the Virtual Network Gateway together with the Local Network Gateway. The documentation for the connection can be found here . I have pasted in my code below. '

param connectionName string
param location string = resourceGroup().location
param connectionType string
param virtualNetworkGatewayId string
param enableBgp bool
param sharedKey string
param localNetworkGatewayId string

resource connectionName_resource 'Microsoft.Network/connections@2021-02-01' = {
  name: connectionName
  location: location
  properties: {
    connectionType:  connectionType    
    virtualNetworkGateway1: {
      id: virtualNetworkGatewayId
      properties: {
        
      }
    }
    enableBgp: enableBgp
    sharedKey: sharedKey
    localNetworkGateway2: {
      id: localNetworkGatewayId
      properties: {
        
      }
    }
  }
  dependsOn: []
}

With all the templates in place, I can update the execution code to include the Local Network Gateway and the Connection. Below is my updated execution code. I did obscure my public IP address and preshared key for security’s sake.

module virtualNetworkGateway './Templates/VirtualNetworkGateway.bicep' = {
  name: 'VirtualNetworkGateway'
  params: {
    enableBGP: false
    gatewayType: 'Vpn'
    location: resourceGroup().location
    PublicIpAddressName: 'pip-vng-sharedservices-001'
    rgName: resourceGroup().name 
    sku: 'Basic'
    SubnetName: 'GatewaySubnet'
    virtualNetworkGatewayName: 'vng-sharedservices-001'
    VirtualNetworkName: 'vnet-bicep-sharedservices-001'
    vpnType: 'RouteBased'
  }
}
module localNetworkGateway './Templates/LocalNetworkGateway.bicep' = {
  name: 'LocalNetworkGateway'
  params: {
    addressPrefixes: [
      '192.168.1.0/24'
      '192.168.10.0/24'
    ]
    gatewayIpAddress: '80.80.80.80'
    localNetworkGatewayName: 'lng-sharedservices-001'
    location: resourceGroup().location
  }
}

module connection './Templates/Connection.bicep' = {
  name: 'connection'
  params: {
    connectionName: 'cnt-sharedservices-001'
    connectionType: 'IPSec'
    enableBgp: false
    localNetworkGatewayId: localNetworkGateway.outputs.lngid
    location: resourceGroup().location
    sharedKey: 'dhsjkdlahldk23e2mda'
    virtualNetworkGatewayId: virtualNetworkGateway.outputs.vngid
  }
}

At this point, I have all my coding done for the VPN connection, so I can now update my Github actions YAML file to include VPN in my deployment. Notice below that I have updated the branch to be the new working branch.

name: CI

# Controls when the action will run. 
on:
  # Triggers the workflow on push or pull request events but only for the main branch
  push:
    branches: [ VPN ]
  pull_request:
    branches: [ main ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master

    - name: Login to Azure
      uses: azure/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS_BLOGSERIES }}

    - name: Deploy resource group
      uses: azure/arm-deploy@main
      with:
        scope: subscription
        subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }}
        region: westeurope
        template: ./ResourceGroup/ResourceGroup.bicep

    - name: Deploy virtual network
      uses: azure/arm-deploy@main
      with:
        scope: subscription
        subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }}
        region: westeurope
        template: ./Network/DeployNetwork.bicep

    - name: Deploy VPN connection
      uses: azure/arm-deploy@main
      with:
        scope: resourcegroup
        resourceGroupName: rg-bicep-sharedservices-network-001
        subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }}
        region: westeurope
        template: ./Network/DeployVPN.bicep
        deploymentMode: incremental

I will now save all my files and commit them to my Github repository under the new branch. This will also kick off the Github action, and my VPN connection will then be deployed. Deploying the Virtual Network Gateway is a lengthy process that normally takes 30-45 min based on my experience. Below you can see the connection has been deployed and the status is now “Connected”.

When the deployment is done, I now have the VPN part of my code completed, and the Github action can deploy all the items I currently have in my code. I will again merge my code into the main branch and delete the VPN working branch.

For the next part of this blog series I will deploy two virtual machines and join them to my on-premises domain.

Stay tuned.