Getting started with GitHub Actions and Bicep - Part 3

I want to create my resource groups and my virtual network in this part of the blog series. To create these resources will use a combination of Bicep files, where I split my code into an execution file with parameters and a generic template file. I am doing this to not limit my options for deploying environments. In fact, the last post in this series will be to deploy the same environment but switching the execution code with a PowerShell script.

The first thing I want to do is creating a new branch in my GitHub repository. I will do this from VS Code by clicking the branch icon in the bottom left corner and then selecting “Create new branch” in the top midsection of the screen.

I will name my branch “RG_and_vNet”

I always verify that the branch I just created is also the branch I am working in. I do this by checking the branch name in the lower-left corner.

I can now update my resource group file to contain the resource groups I want to create. I have done this in the code below. Finally, I will save the updated ResourceGroup.bicep file and commit it to GitHub.

targetScope = 'subscription'
param Location string = 'WestEurope'
param companyPrefix string = 'bicep'

var ResourceGroups = [  
  'rg-${companyPrefix}-sharedservices-network-001'
  'rg-${companyPrefix}-sharedservices-vm-001'
  'rg-${companyPrefix}-citrix-network-001'
  'rg-${companyPrefix}-citrix-vm-001'
  'rg-${companyPrefix}-citrix-workers-001'  
]

resource resourcegroups 'Microsoft.Resources/resourceGroups@2021-01-01' = [for ResourceGroup in ResourceGroups: {
  location: Location
  name: ResourceGroup
}]

Next, I want to create my Bicep file for the virtual network in Azure. I found the documentation for the template here, but it contains more options that I want to use, so after I removed all the items I don’t want to use, it looks like this.

param Name string 
param Prefix string 
param Subnets array
param Location string
param dnsServers array

resource vnet 'Microsoft.Network/virtualNetworks@2021-02-01' = {
  name: Name
  location: Location
  properties: {
    addressSpace: {
      addressPrefixes: [
        Prefix
      ]
    }
    dhcpOptions: {
      dnsServers: dnsServers
    }
    subnets: [for Subnet in Subnets: {
      name: Subnet.name
      properties: {
        addressPrefix: Subnet.Prefix
      }
    }]
  }
}
output name string = vnet.name

The code above will create a virtual network with custom DNS servers and an array of subnets. I will save this file in a new folder called “Templates” placed in a new folder called “Network”. I like to organize my code this way to easier find the files I want to edit.

I now have the template for my virtual network, and I am now ready to create the execution code for the virtual networks I want to create. First, I will create a new Bicep file called “DeployNetwork.bicep” and place it in the “Network” folder. I find it easiest to write the modules first and then my parameters and variables afterward. I will create two modules, one for each vNet I want to create. In the code below, you can see that I have an entry in each module for each parameter I created in my template file. I will normally use variables or parameters for my execution code, but this is not strictly needed. I have two modules, one for a Citrix vNet and one for Shared Services vNet. Each of the modules has a scope set for a resource group. The bicep deployment can be scoped to either management group, subscription, or resource group. Since I want to deploy into two resource groups, I need to target my execution file at the subscription level.

module CitrixvNet './Templates/vNet.bicep' = {
  name: 'Citrix-vNet-Deployment'
  params: {
    dnsServers: Citrix_vNet_dnsServers
     Location: Location
     Subnets: Citrix_vNet_Subnets
     Name: Citrix_vNetName
     Prefix: Citrix_vNet_Prefix
  }
  dependsOn: [
    SharedservicevNet
  ]
  scope: resourceGroup(Citrix_ResourceGroup)
}

module SharedservicevNet './Templates/vNet.bicep' = {
  name: 'Sharedservice-vNet-Deployment'
  params: {
    dnsServers: Sharedservice_vNet_dnsServers
     Location: Location
     Subnets: Sharedservice_vNet_Subnets
     Name: Sharedservice_vNet_Name
     Prefix: Sharedservice_vNet_Prefix
  }
  scope: resourceGroup(Sharedservice_ResourceGroup)
}

Now that I have the modules in place, I can see which parameters and variables I need to create, so I will go ahead and do that. Below, I put the targetScope as the first line to indicate that this deployment is targeted at the subscription level. Next, I have a parameter called “companyPrefix”, which has a default value of “bicep”. I created this parameter to allow the execution of different environments based on either the value in the file or a value coming from the Github Actions execution. This could be from a RestAPI call, for instance.

targetScope = 'subscription'
param companyPrefix string = 'bicep'
var Location = 'WestEurope'
var Sharedservice_ResourceGroup = 'rg-${companyPrefix}-sharedservices-network-001'
var Sharedservice_vNet_Name = 'vnet-${companyPrefix}-sharedservices-001'
var Sharedservice_vNet_Prefix = '172.16.0.0/16'
var Sharedservice_vNet_Subnets = [
  {
    name: 'GatewaySubnet'
    prefix: '172.16.0.0/26'
  }
  {
    name: 'snet-sharedservices-adds-001'
    prefix: '172.16.0.64/26'
  }      
]
var Sharedservice_vNet_dnsServers = [
  '192.168.10.10'
]
var Citrix_ResourceGroup = 'rg-${companyPrefix}-citrix-network-001'
var Citrix_vNetName = 'vnet-${companyPrefix}-citrix-001'
var Citrix_vNet_Prefix = '172.17.0.0/16'
var Citrix_vNet_Subnets = [
  {
    name: 'snet-citrix-vm-001'
    prefix: '172.17.0.0/26'
  }
  {
    name: 'snet-citrix-workers-001'
    prefix: '172.17.1.0/24'
  }
  {
    name: 'snet-citrix-workers-002'
    prefix: '172.17.2.0/24'
  }
]
var Citrix_vNet_dnsServers = [
  '192.168.10.10'
]

I now have the code for my virtual networks ready, and before I move on, I want to ensure that it is running as expected using Github actions. First, I will update my deploy.yml file to include the virtual network deployment, so the file now looks like the code below. Notice that I have changed the branch for push triggers to run for my working branch and not the main branch. Normally, I have actions run on either a development branch or the main branch, but this is easier for the current scenario since we are testing this 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: [ RG_and_vNet ]
  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

I now have the following resource groups in Azure and I also have the virtual networks as shown below.

I now know that my code can deploy my resource groups and virtual networks, so the last thing I want to cover in this part of the series is peering the two virtual networks together. So, I will create a new template under my network folder and call it “Peering.bicep”. I found the documentation on network peering here , and I adjusted that to fit my needs, and that created the code below. Notice that I have a lookup for my remote virtual network to make it easier to read and deploy the code.

param virtualNetworkName string
param allowForwardedTraffic bool = true
param allowGatewayTransit bool = false
param allowVirtualNetworkAccess bool = true
param useRemoteGateways bool = true
param remoteResourceGroup string
param remoteVirtualNetworkName string

resource remotevnet 'Microsoft.Network/virtualNetworks@2021-02-01' existing = {
  name: remoteVirtualNetworkName
  scope: resourceGroup(remoteResourceGroup)  
}

resource peering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2021-02-01' = {
  name: '${virtualNetworkName}/Peering-To-${remoteVirtualNetworkName}'
  properties: {
    allowForwardedTraffic: allowForwardedTraffic
    allowGatewayTransit: allowGatewayTransit
    allowVirtualNetworkAccess: allowVirtualNetworkAccess
    useRemoteGateways: useRemoteGateways
    remoteVirtualNetwork: {
      id: remotevnet.id
    }
  }
}

Next, I will add the peering modules to the “DeployNetwork.bicep” file to have all my virtual network configurations in one deployment file. I have added the lines below into the deployment file and notice that I use the output from the virtual network deployment module as input to my peering module.

module CitrixPeering './Templates/Peering.bicep' = {
  name: 'CitrixvNetPeering'
  params: {
    allowForwardedTraffic: true
    allowGatewayTransit: false
    allowVirtualNetworkAccess: true
    remoteResourceGroup: 'rg-${companyPrefix}-sharedservices-network-001'
    remoteVirtualNetworkName: 'vnet-${companyPrefix}-sharedservices-001'
    useRemoteGateways: false
    virtualNetworkName: CitrixvNet.outputs.name
  }
  dependsOn: [
    CitrixvNet
  ]
  scope: resourceGroup(Citrix_ResourceGroup)
}
module SharedservicePeering './Templates/Peering.bicep' = {
  name: 'SharedServicevNetPeering'
  params: {
    allowForwardedTraffic: true
    allowGatewayTransit: true
    allowVirtualNetworkAccess: true
    remoteResourceGroup: 'rg-${companyPrefix}-citrix-network-001'
    remoteVirtualNetworkName: 'vnet-${companyPrefix}-citrix-001'
    useRemoteGateways: false
    virtualNetworkName: SharedservicevNet.outputs.name
  }
  dependsOn: [
    CitrixPeering
  ]
  scope: resourceGroup(Sharedservice_ResourceGroup)
}

I can now save the two new files and commit them to my repository. I don’t need to change anything for the Github action since I updated the deployment file I already configured. After the deployment, I can see that my peering is working.

Before I close this part of the blog series, I will merge my changes into the main branch and delete the working branch for the network. This is done in Github by going to “Pull requests” and then click on “Compare & pull request”.

I fill out the fields as shown below with new title and description.

Github will list my commits and run tests to ensure that my code won’t conflict with the existing code. Finally, when all the lights are green, I will click on “Merge pull request”.

When the pull request has been merged, I will delete the branch I merge code from.

This will end this part of the blog series. Next, I will create the VPN connection from Azure to my on-premises lab environment.

Stay tuned.

Comments