Azure Deployment using Terraform Cloud – Overview, Prerequisites, Sample Code, and Resources

I’ve been using Terraform Cloud to create Azure Environments for some time, and in this post, I wanted to share an overview of Terraform Cloud, the prerequisites for deploying Azure environments using it, and provide additional resources to help get started. Note – this article is not exhaustive, and there are other ways of getting started and different potential setups – this is just based on my approach and experience. I’ve also written this article to bring together lots of bits of information that are available in other locations, guides, and sites – to create a reference point.

✅ If you are just getting started with Terraform for Azure – why not check out my Azure Terraform Walkthrough Series here?


What is Terraform Cloud?

Terraform Cloud is a hosted version of Terraform, by HashiCorp, that provides the following key features (and more – this is just a few that are key for me!):


The benefits of Remote State Management

One of the huge benefits of using Terraform Cloud is Remote State Management, which takes away the challenge of managing Storage, Backup, and configuring manual security for the State File – within Terraform Cloud, Remote State Management takes care of these tasks, reducing the overhead that State Management brings using other tooling that requires setup and configuration of remote State Storage.

You can read more about Remote State Management and State in Terraform Cloud here:


How I work with Terraform Cloud – deploying Azure Lab and Demo Environments

For my lab and demo environments, the diagram below shows how I am using a combination of Visual Studio Code, GitHub, Terraform Cloud, and Microsoft Azure for deployment.

Diagram showing Visual Studio code saving into a GitHub Repository. This Repository is then used by Terraform Cloud, which deploys infrastructure to Azure.
A simple overview of my Terraform Cloud Azure Lab Process

I have Terraform Cloud configured to run a plan whenever a commit is made on the GitHub repo – so I can fix any errors I’ve made in the code! However, all deployment to my Azure Subscription requires a manual confirmation to start the apply phase.

In production environments this process is likely to be expanded across Development/Test/UAT environments and with more automated approvals, but for my lab setup, this setup is simple and serves my needs well.


Pricing

For a full overview of pricing and plans – see here: https://developer.hashicorp.com/terraform/cloud-docs/overview


Getting Started – The Prerequisites

Getting started with Terraform Cloud for Azure is really easy, and there are numerous resources, blogs, and guides to help. At a basic level, to start you will need the following in place:

  • An Azure Subscription
  • A Terraform Cloud Account – sign in or sign up here.
  • A Repository to store code – I am using GitHub. For a guide on creating a repo, see here.
  • Tooling to edit code – I am using Visual Studio Code
  • Code to deploy – if you need sample environment, you can download my Sample Lab here.
  • Service Principal Details – so that Terraform Cloud can interact with Azure.

Creating a Service Principal

A Service Principal in Azure provides an identity that we can use to allow services to interact with our Azure environment. The Service Principal is a set of credentials (which should be treated as sensitive), that are usually added to another system – in our case, Terraform Cloud. You can read more about and how to create a Service Principal here.

Creating a Service Principal is straightforward, and I’m using an Azure CLI command to do this:

az ad sp create-for-rbac -n tfc-demo-lab-env --role Contributor --scopes /subscriptions/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Note: replace your Subscription ID in the above – you may also wish to deploy to multiple Subscriptions or to a Resource Group, based on your own environment.

This will provide an output that includes 4 important details – save these (and treat these as sensitive!) as you will need these later on to setup Terraform Cloud:

  • ARM_CLIENT_ID
  • ARM_CLIENT_SECRET
  • ARM_TENANT_ID
  • ARM_SUBSCRIPTION_ID

These details can now be added to Terraform Cloud – ensure that you mark these as Sensitive. You may wish to use these credentials across multiple Workspaces, and this guide explains how to configure this: Create a credentials variable set | Terraform | HashiCorp Developer.


Sample Code Repository – A simple lab you can deploy to get started!

I have created a sample repository that provides a starting point, and sets up the required elements that are required as variables within Terraform Cloud. Just to note – this is the way I am deploying, but there are other environments you could use, and different ways you can deploy. The lab I’ve shared is just a starting point that builds the following out:

  • Resource Groups
  • Virtual WAN
  • Virtual Networks and Subnets
  • Key Vault
  • Bastion

Note: this lab has since been updated and now includes more features – A blog post will be released on this soon!

All Resources are deployed within a single Subscription that is defined using the “subscription_id” variable. You will also note that a Map Variable is used to define the Azure Regions – if you require more regions, just add another variable to the Map.

Two Regions:

regions = {
  region1 = {
    region = "uksouth"
    code   = "uks"
    cidr   = "10.10.0.0/19"
  }
  region2 = {
    region = "eastus"
    code   = "eus"
    cidr   = "10.11.0.0/19"
  }
}

Example – Adding another Region:

regions = {
  region1 = {
    region = "uksouth"
    code   = "uks"
    cidr   = "10.10.0.0/19"
  }
  region2 = {
    region = "eastus"
    code   = "eus"
    cidr   = "10.11.0.0/19"
  }
  region3 = {
    region = "ukwest"
    code   = "ukw"
    cidr   = "10.12.0.0/19"
  }
}

A couple of Sample Lab considerations…

To get started using this environment you can connect Terraform Cloud to GitHub, without modifying the files – just clone the sample repo I have shared. However, it’s worth noting that there are two files that have additional configuration, that you may not have seen if you’ve been working locally with Terraform previously:

Provider.tf

Within the provider.tf we need to define our Subscription, Client ID and Secret, and Tenant ID – the same outputs that our Service Principal code generated above.

# Provider
provider "azurerm" { 
   subscription_id = var.subscription_id 
   tenant_id       = var.tenant_id 
   client_id       = var.client_id 
   client_secret   = var.client_secret 
   # Provider Configuration options 
   features { 
     key_vault { 
       purge_soft_delete_on_destroy = true 
     } 
   } 
 }

Variables.tf

We also need to define these within the variables.tf – I choose to do this as per the below and comment out. It keeps the code neat and also means that if I need to work locally, I can do so without having to make many changes to my code:

# Service Principal 
 variable "client_id" {} 
 variable "client_secret" {} 
 variable "tenant_id" {} 
 variable "subscription_id" {} 
 # Admin Account (For Key Vault Access Policy) 
 variable "admin_id" {} 
 # Naming 
 variable "lab_name" {} 
 # Regional  
 variable "regions" { 
   type = map(any) 
   default = { 
     region1 = { 
       region = "uksouth" 
       code   = "uks" 
       cidr   = ["10.10.0.0/16"] 
     } 
   } 
 }

You may also notice above, I have  a variable defined as “admin_id” and “lab_name” – I’ve added these to cover off the below requirements:

  • admin_id – This is the GUID of the account that is used to administrate the lab – so in my case this is my personal account. This is used within the code to set an access policy on the Key Vault (to ensure that the admin has access). See Line 30 below:
# Key Vault
resource "random_id" "kvname" {
  byte_length = 5
  prefix      = "kv-"
}
data "azurerm_client_config" "current" {}
resource "azurerm_key_vault" "keyvault" {
  name                        = random_id.kvname.hex
  resource_group_name         = azurerm_resource_group.rg-sec["region1"].name
  location                    = var.regions.region1.region
  enabled_for_disk_encryption = true
  tenant_id                   = var.tenant_id
  soft_delete_retention_days  = 7
  purge_protection_enabled    = false

  sku_name = "standard"

  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.object_id

    key_permissions = ["Get", ]

    secret_permissions = ["Get", "Backup", "Delete", "List", "Purge", "Recover", "Restore", "Set", ]

    storage_permissions = ["Get", ]
  }
  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = var.admin_id

    key_permissions = ["Get", ]

    secret_permissions = ["Get", "Backup", "Delete", "List", "Purge", "Recover", "Restore", "Set", ]

    storage_permissions = ["Get", ]
  }
  tags = { environment = "security", region = var.regions.region1.code, tfcreated = "true" }
}
  • lab_name – This one is more simple, and I’m just using this for naming and tagging. For example, see the Virtual WAN section below:
# Virtual WAN
resource "azurerm_virtual_wan" "virtual-wan1" {
  name                = "virtualwan-${var.lab_name}-${var.regions.region1.code}-01"
  resource_group_name = azurerm_resource_group.rg-con["region1"].name
  location            = var.regions.region1.region

  # Configuration 
  office365_local_breakout_category = "OptimizeAndAllow"

  tags = { environment = "connectivity", region = var.regions.region1.code, tfcreated = "true", lab = var.lab_name }
}

✅ Once you have an Azure Subscription, Code Editor, GitHub Repository, and Service Principal, you are ready to start deploying to Azure using Terraform Cloud!


Next Steps

Now that we have all of the required elements in place – you are ready to start using Terraform Cloud. To help start, I’d recommend following the Get Started Guide from HashiCorp, and use this to deploy the sample environment that I have shared. To help guide this, for Azure deployment specifically, I would recommend working through the following articles in particular:

  1. Create a credentials variable set | Terraform | HashiCorp Developer – This will enable us to add our Service Principal Details to Terraform Cloud.
  2. Create a workspace | Terraform | HashiCorp Developer – This will enable us to create a Workspace to hold our deployment Project.
  3. Create infrastructure | Terraform | HashiCorp Developer – Finally, we can deploy our infrastructure using Terraform Cloud.

✅ I hope this article has been useful in providing an overview, prerequisites, Sample Lab, and Resources for Azure deployment using Terraform Cloud – until next time!


Further Reading and Additional Resources
Skip to content