Deploy Azure Kubernetes Service using Terraform with Azure DevOps pipeline and deploying a sample application

In my previous blog post; you may have saw how you can deploy Azure Kubernetes Service (AKS) with Application Gateway Ingress using Terraform. In this blog post I am going to show how you can deploy the same Terraform code in Azure DevOps with an Azure DevOps pipeline and deploy sample application

I have modified the Terraform code into Terraform modules, a blog post I created a while back “Creating reusable Terraform with Terraform modules” details why you should create Terraform modules. Also with this; another I recommend you read, Creating templates in Azure DevOps Pipelines as the Azure DevOps Pipeline I use will be templated

Deploy using Azure DevOps

The Azure DevOps pipeline as mentioned above, will reference various templates. it is split into 5 stages with 3 potential actions

Actions

The actions are configured as to how you would deploy terraform with plan, apply & destroy

Stages

  • terraform validate:- confirms the terraform that will be plan/applied/destroyed is valid format
  • terraform plan:- Returns a plan of the terraform
  • terraform apply:- Applies the terraform configuration
  • bootstrap:- Once the AKS cluster is built, deploy sample application
  • terraform destroy:- Destroys terraform which has been referenced in the .tfstate stored in the Azure Storage Account

Azure DevOps Pipeline Breakdown

Currently no triggers are set or any stages to run during a PR

Parameters setup are the Terraform commands: Plan, Apply or Destroy which show as below within the Azure DevOps Pipeline UI

Highlighted is the variables that can be changed to match additional Terraform setups

name: $(BuildDefinitionName)_$(date:yyyyMMdd)$(rev:.r)

trigger: none

pr: none

parameters:

  - name: Action
    displayName: Action
    type: string
    default: 'Plan'
    values:
    - Plan
    - Apply
    - Destroy

variables:
  - name: backendServiceArm
    value: 'thomasthorntoncloud'
  - name: backendAzureRmResourceGroupName
    value: 'thomasthorntoncloud'
  - name: backendAzureRmStorageAccountName
    value: 'thomasthorntontfstate'
  - name: backendAzureRmContainerName
    value: 'aksdeployazuredevops'
  - name: backendAzureRmKey
    value: 'terraform.tfstate'
  - name: environment
    value: 'production'
  - name: terraform_version
    value: '0.13.4'
  - name: action
    value: ${{ parameters.Action }}

Next is the 5 stages mentioned above

stages :   
  - stage: terraform_validate
    condition: ne('${{ parameters.Action }}', 'Destroy')
    jobs:
      - template: templates/terraform-validate.yaml
        parameters:
          backendServiceArm: ${{ variables.backendServiceArm }}
          backendAzureRmResourceGroupName: ${{ variables.backendAzureRmResourceGroupName }}
          backendAzureRmStorageAccountName: ${{ variables.backendAzureRmStorageAccountName }}
          backendAzureRmContainerName: ${{ variables.backendAzureRmContainerName }}
          backendAzureRmKey: ${{ variables.backendAzureRmKey }}
          environment: ${{ variables.environment }}
          terraform_version: ${{ variables.terraform_version }}

  - stage: terraform_plan
    dependsOn: [terraform_validate]
    condition: ne('${{ parameters.Action }}', 'Destroy')
    jobs:
      - template: templates/terraform-plan.yaml
        parameters:
          backendServiceArm: ${{ variables.backendServiceArm }}
          backendAzureRmResourceGroupName: ${{ variables.backendAzureRmResourceGroupName }}
          backendAzureRmStorageAccountName: ${{ variables.backendAzureRmStorageAccountName }}
          backendAzureRmContainerName: ${{ variables.backendAzureRmContainerName }}
          backendAzureRmKey: ${{ variables.backendAzureRmKey }}
          environment: ${{ variables.environment }}
          terraform_version: ${{ variables.terraform_version }}

  - stage: terraform_apply
    dependsOn: [terraform_plan]
    condition: ne('${{ parameters.Action }}', 'Destroy')
    jobs:
      - template: templates/terraform-apply.yaml
        parameters:
          backendServiceArm: ${{ variables.backendServiceArm }}
          backendAzureRmResourceGroupName: ${{ variables.backendAzureRmResourceGroupName }}
          backendAzureRmStorageAccountName: ${{ variables.backendAzureRmStorageAccountName }}
          backendAzureRmContainerName: ${{ variables.backendAzureRmContainerName }}
          backendAzureRmKey: ${{ variables.backendAzureRmKey }}
          environment: ${{ variables.environment }}
          terraform_version: ${{ variables.terraform_version }}

  - stage: bootstrap
    dependsOn: [terraform_apply]
    condition: ne('${{ parameters.Action }}', 'Destroy')
    jobs:
      - template: templates/az-cli.yaml

  - stage: terraform_destroy
    condition: contains('${{ parameters.Action }}', 'Destroy')
    jobs:
      - template: templates/terraform-destroy.yaml
        parameters:
          backendServiceArm: ${{ variables.backendServiceArm }}
          backendAzureRmResourceGroupName: ${{ variables.backendAzureRmResourceGroupName }}
          backendAzureRmStorageAccountName: ${{ variables.backendAzureRmStorageAccountName }}
          backendAzureRmContainerName: ${{ variables.backendAzureRmContainerName }}
          backendAzureRmKey: ${{ variables.backendAzureRmKey }}
          environment: ${{ variables.environment }}
          terraform_version: ${{ variables.terraform_version }}

As mentioned, the pipeline references templates which are found in this location

The bootstrap stage deploys a test application into AKS with Application Gateway ingress; configured within a job/AzureCLI@2 task as below:

  jobs:
    - job: azcli_resourcegroup_create
      steps:
            - task: AzureCLI@2
              displayName: 'BootStrap AKS Cluster'
              inputs:
                azureSubscription: 'thomasthorntoncloud'
                scriptType: bash
                scriptLocation: inlineScript
                addSpnToEnvironment: true
                inlineScript: |
                  #!/usr/bin/env bash
                  set -x

                  AKS_RG="azuredevopsaksdeployaks-rg"
                  AKS_NAME="azuredevopsaksdeployaks"

                  # Get AKS Credentials
                  az aks get-credentials -g $AKS_RG -n $AKS_NAME --admin

                  # For AAD Pod Identity
                  kubectl create -f https://raw.githubusercontent.com/thomast1906/thomasthorntoncloud-examples/master/Azure-AKS-Deploy-Azure-DevOps/scripts/deployment.yaml

Once the bootstrap stage has been completed, the test application will be deployed. Viewing the services and ingresses in Azure, you will notice an ingress azure-vote-front with external IP

Accessing the External IP , will load the test voting application

Awesome! Successfully deployed Azure AKS using Azure DevOps and deploying sample application!

GitHub repository containing all the configuration – Check it out & thank you for viewing!