PAUL'S BLOG

Learn. Build. Share. Repeat.

Getting Started With Terraform on Azure

2021-04-24 7 min read Tutorial

Check out the official Get Started - Azure tutorial here

Terraform basics

Download and Install

  • Download terraform from https://www.terraform.io/downloads.html

  • Extract the executable and add the folder to your PATH variable

  • Open a shell and type terraform version

  • The basic commands we’ll use are:

    • terraform init
    • terraform plan
    • terraform apply
    • terraform destroy
  • I advise you to use the terraform fmt command often to ensure your scripts well formatted according to HashiCorp’s style conventions

Folder structure

Terrafrom executes on the files within a folder that are named with the .tf extension. Here is what a typical folder structure looks like:

.
├── main.tf
├── outputs.tf
└── variables.tf
  • main.tf is where the deployment scripts will be written to
  • outputs.tf is where the output variables will be defined
  • variables.tf is where the variables will be defined

Optionally, you can add a main.auto.tfvars files to set values for your variables. Defining them here will allow you to avoid passing in variable values on terraform apply. More on that here.

HashiCorp Configuration Language (HCL)

HashiCorp domain specific language for defining infrastructure deployment details. This is especially beneficial for organizations with multi-cloud strategies.

Authoring HCL is pretty straight-forward and there are tons of example code found on Terraform docs website.

Basic structure of an Azure deployment requires the following provider and resources:

  1. The azurerm provider
  2. An azurerm_* resource
  3. Optionally, azurerm_* data can be used to import pre-existing resources into your Terraform state file

Here is a sample where we use the azurerm provider to deploy a resource group

main.tf

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "rg" {
  name     = var.rg_name
  location = var.rg_location
  tags     = var.rg_tags
}

In the example above, you can see that the syntax looks a bit like JSON but IMO a little easier to read.

Providers are required so that Terraform will know which modules it needs to download to execute the deployment.

Keep a close eye on provider versions as they are updated constantly to keep up to date with cloud providers. Not all resources are available in azurerm providers. Some deployments may require the use of azurerm_template_deployment whereby you can have Terraform execute ARM templates

As you define resources, you’ll notice they all begin with the resource keyword followed by the resource type defined in the azurerm provider. In the example above, we are referencing a azurerm_resource_group for our Azure resource group deployment and defining it as rg. In object-oriented programming concepts, think of rg as an instance of azurerm_resource_group. These instances are used as pointers to specific deployments. Makes sense to keep the instance names unique as you can have multiple resources in a single deployment and Terraform will need a way to identify each unique instance.

For example, we can add the next bit of HCL code to deploy an Azure Container Registry in our resource group.

resource "azurerm_container_registry" "acr" {
  name                = var.acr_name
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  sku                 = var.acr_sku
  admin_enabled       = var.acr_admin_enabled
}

As you can see, our acr deployment requires a resource_group_name and location so we can simply reference it from the rg instance.

This is pretty much all you need to know to be dangerous with Terraform.

Additional details can be found at the links below:

Last but not least, I’d recommend you use VSCode as your editor with the hashicorp.terraform extension installed to assist with HCL editing using the Terraform language server.

Let’s go deploy to Azure using Terraform

Take a look at the following article to understand how Terraform can authenticate to Azure

We will simply use Azure CLI locally or in Azure Cloud Shell

Azure Cloud Shell can also be accessed using Windows Terminal

Open your favorite shell and login to azure-cli

az login
az account show

# if the incorrect subscription is selected, set it using this command
az account set -s <your_subscription_id>

Create a new directory

mkdir terraform-sample
cd terraform-sample

Finding syntax to deploy a resource is as simple as browsing to the docs for azurerm, filtering for a resource, copying the example code, and modify as needed. Will deploy a storage account

When searching for resources to deploy, make sure you are looking at resources and not data. More on that below when we get into state.

Create your variables.tf file and add in the following code:

variable "resourcegroupname" {
  type    = string
  default = "myresourcegroup11242020x"
}

variable "location" {
  type    = string
  default = "westus2"
}

variable "storageaccountname" {
  type    = string
  default = "mystorageaccount11242020"
}

I have default values here so that we don’t have to pass in any values on terraform apply or use tfvars file (for now)

Create your main.tf file and add in the following code:

provider "azurerm" {
  version = "=2.37.0"
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = var.resourcegroupname
  location = var.location
}

resource "azurerm_storage_account" "example" {
  name                     = var.storageaccountname
  resource_group_name      = azurerm_resource_group.example.name
  location                 = azurerm_resource_group.example.location
  account_tier             = "Standard"
  account_replication_type = "GRS"

  tags = {
    environment = "dev"
  }
}

Since this is the first time we are running the Terraform commands in this directory, we need to issue the terraform init command to get the azurerm plugins

Plan your deployment using terraform plan command. Take a look at the output and confirm the resources you intend to deploy

Apply changes to your Azure subscription by using the terraform apply command. You’ll need to type in the word yes to confirm the deployment. Optionally, you can use the terraform --auto-approve if you do not want to be bothered by the confirmation request

Log into the Azure Portal and confirm your resources are deployed then use the terraform destroy command to delete.

State?!?

You’ll notice in your working directory that as you deployed your resources, there are new files with tfstate named something like this:

  • terraform.tfstate
  • terraform.tfstate.backup

These files are here because Terraform manages state of your deployment. It will understand what is and what isn’t deployed based on your script and will keep that info in these files.

If you need to reference resources that were deployed in your Azure subscription outside of Terraform, you can import the details of the resource using data modules in the azurerm provider.

This is okay if you are working on demos and keeping the state files on your machine. If you need to work on production deployments, you’ll need a remote state management solution.

You have a few options here:

  1. Use Azure Storage to keep track of state files
  2. Use Terraform Cloud - we’ll use this in the next exercise

Terraform cloud

Check out the official Get Started - Terraform Cloud tutorial here

If you don’t already have an account on Terraform Cloud, create one

Login to Terraform Cloud

Create an organization

Create a workspace and choose a workflow - let’s click “CLI driven workflow”

Create a workspace

In order for Terraform Cloud to connect to your Azure subscription, you’ll need to create a new service principal and add the object’s values as variables in your workspace

Create a service principal using Azure CLI

az ad sp create-for-rbac -n tf-operator

The output will contain all that you need to configure your workspace variables. Map the values to these specific environment variables in your workspace

  • ARM_CLIENT_ID
  • ARM_CLIENT_SECRET
  • ARM_TENANT_ID
  • ARM_SUBSCRIPTION_ID

Workspace variables

Create a new file in your working directory called backend.tf and add in the following code

terraform {
  backend "remote" {
  organization = "<your_organization_name>"

  workspaces {
    name = "<your_workspace_name>"
  }
}

If you still have tfstate files in your working directory, delete them

Now that we added a new backend, we’ll need to run terraform init

Run terraform plan and notice it is being run in Terraform Cloud

Run terraform apply and notice it is being run in Terraform Cloud

In Terraform Cloud, navigate to your workspace, and click on the Runs tab to view results

Test run

GitOps

Check out the official Automate Terraform with GitHub Actions tutorial here

In GitHub, create a new repository, add your .tf files, commit, and push

Browse to Terraform Cloud settings and create a new API token for github-actions.

Browse to your GitHub and navigate to Settings.

In Settings, click on Secret and add a new Repository secret named TF_API_TOKEN

Paste in the token you generated in Terraform Cloud

GitHub secrets

Navigate to Actions and add in the setup-terraform action

Make a change to your terraform source code, do a push, and you’ll notice the GitHub Action queueing a new plan and apply in Terraform Cloud

Some Tips

If using Bash, set an alias for the terraform command to something short like tf so you don’t have to spell out terraform each time

alias tf=terraform

Use Hashicorp’s azurerm provider docs as your guide!

Always plan to build and use modules as maintaining monolithic deployments in a single main.tf file can get overwhelming after a while