Terraform: Storing Terraform State in Azure Storage

Terraform is arguably one of the most popular tools these days. It seems that everyone is using it! It’s a very powerful tool, and definitely a game changer. Let’s talk about it a little bit, and get started with a simple project.

Infrastructure as Code

Terraform is Hashicorp’s infrastructure as Code tool. It’s used for building, changing, and managing infrastructure in a repeatable and safe way.

Infrastructure as code is the process of managing infrastructure through files rather than manually configuring resources in a user interface. Terraform uses its own configuration language, called the Hashicorp Configuration Language (HCL).

With Terraform, your code can be used time and time again, with the same outcomes every time. It’s faster and it’s easier to control and document. Another great benefit of Terraform is that it’s platform agnostic.

State Management

When a project is initialized, Terraform creates a state file. This file will contain all the configurations and deployment plans, and it will be used to make changes to the infrastructure. By default, this file is stored locally when you run terraform apply. This isn’t ideal if you’re working with a team and it’s not safe, since this file may contain sensitive information.

A solution would be to store this file in an Azure Storage account. To be even safer, we can store the access key to the storage account in a Key Vault, the place in Azure where you keep your secrets. We’ll do that in this project. Just follow along!

creating a Service Principal

A Service Principal is an application within Azure Active Directory used to secure access to Azure resources. To be allowed access, an application must be represented by a Service Principal. The Service Principal defines what the application can actually do in the specific Azure AD tenant.

Configuring a Service Principal Using the Azure CLI

First, we’ll need to login to Azure CLI and then retrieve the subscription ID. We’ll need this data for the next command.

$ az login
$ az account list

We then run the following command, replacing <subscription id> with your own.

$ az ad sp create-for-rbac --role="Contributor" --scopes="subscriptions/<subscription id>"

The contributor role will grant full access to manage all resources.

This is the output you should see, for both commands:

The values generated from the az ad sp create-for-rbac will be used by Terraform to access the resources.

Configuring the Storage Account

Now, to configure the Storage account, we’ll create a resource group, the Storage account itself, a blob container inside it, where the Terraform state files will reside, and retrieve the access key. We’ll begin by creating variables, that will be used along the way.

$ RESOURCE_GROUP_NAME=tfstate
$ STORAGE_ACCOUNT_NAME=tfstate$RANDOM
$ CONTAINER_NAME=tfstate

We’ll then run the commands to generate the resources. The first will create a resource group. The second will create the storage account. The sku parameter standard_LRS means Standard Locally Redundant Storage. The third command will store the storage account’s access key in the variable named ACCOUNT_KEY. Here, tsv means tab-separated values. The fourth command will create the blob container inside the Storage account.

$ az group create --name $RESOURCE_GROUP_NAME --location westeurope

$ az storage account create --resource-group $RESOURCE_GROUP_NAME --name $STORAGE_ACCOUNT_NAME --sku Standard_LRS --encryption-services blob

$ ACCOUNT_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --acount-name $STORAGE_ACCOUNT_NAME --query [0].value -o tsv)

$ az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCOUNT_NAME --account-key $ACCOUNT_KEY

We can run an echo command to output all the values we will be needing.

$ echo "storage_account_name: $STORAGE_ACCOUNT_NAME"

$ echo "container_name: $CONTAINER_NAME"

$ echo "access_key: $ACCOUNT_KEY"

Now we can store the access key in a Key Vault, where it will be safe and protected. First we’ll create the Vault. The name has to be unique. Then we’ll send the key to the vault. Lastly, we’ll export this value as an environment variable named ARM_ACCESS_KEY.

$ az keyvault create --name "tfsecretvault" --resource-group $RESOURCE_GROUP_NAME --location westeurope

$ az keyvault secret set --vault-name"tfsecretvault" --name "accesskey" --value $ACCOUNT_KEY

$ export ARM_ACCESS_KEY=$(az keyvault secret show --name accesskey --vault-name tfsecretvault --query value -o tsv)

Now we’re set to run the Terraform commands.

Configuring the State Back End

The Terraform state back end will be configured when you first run terraform init. The command should look like this. In this Terraform configuration file, a resource group will also be created.

We then run terraform init. Terraform will fetch the necessary plugin to communicate with Azure. When it-s done, we run terraform plan. As you can see from the output, it says acquiring state lock. Lastly, we run terraform apply. This will actually apply the desired state, and deploy the resources to Azure.

Because we stored the access key in the Key Vault and in an environment variable named ARM_ACCESS_KEY, the key won’t be written to the Terraform state file.

Here’s the file, sitting quietly in the container.

When you run terraform plan again, Terraform will consult this file, and will compare to the actual state of the resources. Terraform is super powerful!

In the next article, we’ll explore more of Terraform.

Please let me know how you like this article. Is there a mistake? Could I do something different? Is everything clear?

Thanks for reading! See you in the next deployment!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s