Azure Infrastructure Automation with Gitlab CI

Introduction

Creating new resources in Azure is very easy thanks to Azure Portal, but what if you want to create an infrastructure with code. One of the options is to use ARM templates for infrastructure deployment. If you want to implement a proper CI/CD against your ARM templates, Gitlab CI can be a very strong candidate. Let's assume if you have a pipeline to create the infrastructure and delete it upon a successful deployment in order to save cost. Wouldn't it be perfect? If you say "yes", let's go deeper and see how I implemented the solution.

Setting up Gitlab Project

I am not going into the details of how you can create a Gitlab project as it is done by simply clicking a few buttons, but the most important part of the automation is to create variables which we will use in Gitlab shared runners to authenticate to Azure Cloud.

You can add variables by clicking "Settings" and then "CI/CD" in your project. The "Variables" blade should be visible and you can introduce such variables:

Do not forget to mark your variables as "Masked". Otherwise they will be visible in the Gitlab CI pipeline and they contain sensitive information about your Azure subscription

If you wish to mark your variables protected, please remember that they can be only accessed from protected branches

Setting up .gitlab-ci.yaml

Now it is time to set up your pipeline to login to the Azure service principal account and deploy resources defined in your subscription. I am using Gitlab shared runners and "mcr.microsoft.com/azure-cli" container image to run the pipeline.

One of the important things is to use a dedicated resource group for the deployment tests so we can destroy whatever is inside that resource group to save costs.

variables:
  DEFAULT_RG:
    description: "Default resource group to deploy the resources for testing"
    value: "test-rg"
  DEFAULT_LOCATION:
    description: "Default location of the testing resource group"
    value: "northeurope"
  
default:
  image: mcr.microsoft.com/azure-cli
  before_script:
    - az login --service-principal --username $SP_ID --password $SP_SECRET --tenant $TENANT_ID
    - az account set --subscription $SUBSCRIPTION_ID
    - set -euo pipefail

set -euo pipefail will prevent pipeline to run further steps if one of the variables is empty

The remaining of the pipeline can change based on what you want to test but let me paste you the full content here so you can see what I am testing under that small scope. The respective Gitlab project can also be found here.

variables:
  DEFAULT_RG:
    description: "Default resource group to deploy the resources for testing"
    value: "test-rg"
  DEFAULT_LOCATION:
    description: "Default location of the testing resource group"
    value: "northeurope"
  
default:
  image: mcr.microsoft.com/azure-cli
  before_script:
    - az login --service-principal --username $SP_ID --password $SP_SECRET --tenant $TENANT_ID
    - az account set --subscription $SUBSCRIPTION_ID
    - set -euo pipefail

stages:
  - validate
  - deploy
  - destroy

validate virtual network:
  stage: validate
  script:
    - az deployment group validate -g ${DEFAULT_RG} -f virtual-network/virtual-network.json -p virtual-network/virtual-network.parameters.json
  
validate virtual machine:
  stage: validate
  script:
    - az deployment group validate -g ${DEFAULT_RG} -f virtual-machine/linux-vm.json -p virtual-machine/linux-vm.parameters.json -p adminPassword=$VM_INITIAL_PASSWORD

deploy virtual network:
  stage: deploy
  script:
    - az deployment group create -g ${DEFAULT_RG} -f virtual-network/virtual-network.json -p virtual-network/virtual-network.parameters.json

deploy virtual machine:
  stage: deploy
  script:
    - az deployment group create -g ${DEFAULT_RG} -f virtual-machine/linux-vm.json -p virtual-machine/linux-vm.parameters.json -p adminPassword=$VM_INITIAL_PASSWORD
  needs:
    - job: "deploy virtual network"

destroy test environment:
  stage: destroy
  script:
    - az deployment group create -g ${DEFAULT_RG} -f resource-group/empty-resource-group.json --mode "Complete"

This pipeline will run validation of the resource to be created under the test resource group and will deploy them. After the successful deployment, it will use a very good trick to delete all resources in the resource group by doing a complete deployment with an empty Azure resource.

Conclusion

The above approach can let you test your ARM templates with Gitlab CI pipeline at a minimal cost. There are various ways of testing Azure deployment including Azure DevOps pipeline but I wanted to test it under Gitlab CI.

Last updated