Provision Azure Infrastructure using Terraform and GitHub Actions
Tags: azure, terraform, github, devops
Published: 2022-09-17 20:48:20

In this article, I will explain how to provision Azure infrastructure using Terraform and GitHub Actions. Before we go further let’s discuss what’s Terraform and GitHub Actions are.

## What’s Terraform?
Terraform is an infrastructure as a code (IaC) tool created by HashiCorp. We can write the whole cloud infrastructure as a code and we don’t need to interact with the cloud service provider’s console. Everything can be automated.


![Terraform](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/da11fqgncw87l8h9uugb.png)

## Why GitHub Actions?
We know we define the infrastructure as a code. So the DevOps and Operations team members should have access to that code and they need a way to update the infrastructure automatically when they update the code. That’s where GitHub Actions comes in.

GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform of GitHub that allows you to automate your build, test, and deployment pipeline.


![GitHub Actions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/no4tnotqq1xn5i443bbs.png)

So, our plan is to put the Azure infrastructure code in a GitHub repository and use GitHub Actions to trigger the Terraform and update the infrastructure when someone changes the code.

## Prerequisites:

- You should configure Azure CLI in your local machine.
- You should have a valid Azure subscription.

## Follow me 😉

1.Create a service principal using the below command and save the output for later use. (Replace the ServicePrincipalName with a meaningful name and SubscriptionID with your Azure subscription ID)

```
az ad sp create-for-rbac --name ServicePrincipalName --role Contributor --scopes /subscriptions/SubscriptionID
```

---

2.Create an Azure storage account container to store the Terraform state.

```
# Create Resource Group
az group create -n tfstates -l eastus2

# Create Storage Account
az storage account create -n kstfstateaccount -g tfstates -l eastus2 --sku Standard_LRS

# Create Storage Account Container
az storage container create -n tfstate --account-name kstfstateaccount
```

---

3.Create a GitHub repository and commit your Terraform config files. For example, I use the following Terraform configs to provision a resource group and a container registry.

Here’s the code for “main.tf” 👇

```
provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "default" {
  name     = "container-registry-rg"
  location = "East US 2"

  tags = {
    environment = "Production"
  }
}

resource "azurerm_container_registry" "acr" {
  name                = "kspcontainerregistry"
  resource_group_name = azurerm_resource_group.default.name
  location            = "East US 2"
  sku                 = "Standard"
  admin_enabled       = true
}

output "acr_login_server" {
  value = azurerm_container_registry.acr.login_server
}

output "acr_admin_username" {
  value     = azurerm_container_registry.acr.admin_username
  sensitive = true
}

output "acr_admin_password" {
  value     = azurerm_container_registry.acr.admin_password
  sensitive = true
}
```

When GitHub Actions and team members interact with the same infrastructure, everybody needs to access the same terraform state file. So we are going to initialize the Terraform backend and store the state file in the Azure storage account container we previously created.

Here’s the code for “backend.tf” 👇

```
terraform {
  backend "azurerm" {
    resource_group_name  = "tfstates"
    storage_account_name = "kstfstateaccount"
    container_name       = "tfstate"
    key                  = "terraform.tfstate"
  }
}
```

Now we can push the Terraform config files to our GitHub repository.

---

4.Add the service principal credentials as GitHub Repository Secrets.

Go to your GitHub repository settings and then the Secrets section. Now add the following repository secrets (Use the values you got from the first step).

- AZURE_AD_CLIENT_ID — Service Principal App/ Client ID
- AZURE_AD_CLIENT_SECRET — Service Principal Password/ Client Secret
- AZURE_AD_TENANT_ID — Service Principal Tenant ID
- AZURE_SUBSCRIPTION_ID — Subscription ID


![GitHub Repository Secrets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hb59w55bm7m86rnlj85f.png)

---

5.Finally, add the GitHub Actions configuration file to your GitHub repository.

Here’s the GitHub Actions configuration file “actions.yml” 👇

```
name: 'Terraform'
 
on:
  push:
    branches:
    - main
  pull_request:
 
jobs:
  terraform:
    name: 'Terraform'
    env:
      ARM_CLIENT_ID: ${{ secrets.AZURE_AD_CLIENT_ID }}
      ARM_CLIENT_SECRET: ${{ secrets.AZURE_AD_CLIENT_SECRET }}
      ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      ARM_TENANT_ID: ${{ secrets.AZURE_AD_TENANT_ID }}

    runs-on: ubuntu-latest
    environment: production
 
    # Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
    defaults:
      run:
        shell: bash
 
    steps:
    # Checkout the repository to the GitHub Actions runner
    - name: Checkout
      uses: actions/checkout@v2
 
    - name: 'Terraform Format'
      uses: hashicorp/terraform-github-actions@master
      with:
        tf_actions_version: 0.14.8
        tf_actions_subcommand: 'fmt'
        tf_actions_working_dir: "./terraform"
         
    - name: 'Terraform Init'
      uses: hashicorp/terraform-github-actions@master
      with:
        tf_actions_version: 0.14.8
        tf_actions_subcommand: 'init'
        tf_actions_working_dir: "./terraform"
 
    - name: 'Terraform Validate'
      uses: hashicorp/terraform-github-actions@master
      with:
        tf_actions_version: 0.14.8
        tf_actions_subcommand: 'validate'
        tf_actions_working_dir: "./terraform"
         
    - name: 'Terraform Plan'
      uses: hashicorp/terraform-github-actions@master
      with:
        tf_actions_version: 0.14.8
        tf_actions_subcommand: 'plan'
        tf_actions_working_dir: "./terraform"
 
    - name: Terraform Apply
      if: github.ref == 'refs/heads/main'
      uses: hashicorp/terraform-github-actions@master
      with:
        tf_actions_version: 0.14.8
        tf_actions_subcommand: 'apply'
        tf_actions_working_dir: "./terraform"
```

You need to add this config file to the “.github/workflows” folder. So your folder structure should look like this.


![Folder Structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3mdoklkv1kxsc89wmsg3.png)

Now everything is configured perfectly and you can push all commits to the repository. To see the pipeline status, you can visit the repository Actions section.


![GitHub Actions Workflow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vzvw2u8ka4tut7qp5isd.png)

Thank you for reading and please let me know if there are any mistakes. 🫡