Remote State
Terraform documentation on Terraform Backend
Microsoft documentation on Remote State Storage for terraform
Here's another good article by Matthew Davis which contains the info to use an SAS token for your backend
Create Azure Storage
Storing Terraform state remotely allows multiple developers to work on the same environment from different machines. Terraform state can be stored in Azure and then referenced in the backend configuration of your Terraform scripts. Public access is allowed to Azure storage to store Terraform state. First create the remote state storage account in your subscription using the Azure CLI:
az login
az account set --subscription <subscription_id>
export RESOURCE_GROUP_NAME=<resource-group-name>
export STORAGE_ACCOUNT_NAME=<storage-account-name>
export CONTAINER_NAME=<container-name>
az group create --name $RESOURCE_GROUP_NAME --location uksouth
az storage account create --resource-group $RESOURCE_GROUP_NAME --name $STORAGE_ACCOUNT_NAME --sku Standard_LRS --encryption-services blob
az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCOUNT_NAME
Configure Backend
To use remote state in Azure Storage, configure the backend block of your terraform block:
terraform {
required_version = "~>v1.1.5"
required_providers {
azurerm = {
version = "~>2.36"
source = "hashicorp/azurerm"
}
}
backend "azurerm" {
resource_group_name = "<resource-group-name>"
storage_account_name = "<storage-account-name>"
container_name = "<container-name>"
key = "project1.terraform.tfstate"
}
}
When you run terraform init
, terraform will need to authenticate to Azure to create
the backend. If you have already logged-in with Azure CLI, terraform will use this authenticated
connection to create the backend for you. If you're deploying your infrastructure using Terraform
Cloud or Azure DevOps you can create an SAS token to authenticate to the backend Storage Account.
To obtain SAS certificate for a Storage Account, you can use the following Azure CLI commands:
ACCOUNT_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query '[0].value' -o tsv)
END_DATE=`date -u -d "1 year" '+%Y-%m-%dT%H:%MZ'`
SAS_KEY=$(az storage container generate-sas -n $CONTAINER_NAME --account-key $ACCOUNT_KEY --account-name $STORAGE_ACCOUNT_NAME --https-only --permissions dlrw --expiry $END_DATE -o tsv)
ARM_SAS_TOKEN=$SAS_KEY
Terraform will automatically use the SAS key if you assign this to the environment variable $ARM_SAS_TOKEN, or you can add the value to your backend config using 'sas_token':
backend "azurerm" {
resource_group_name = "<resource-group-name>"
storage_account_name = "<storage-account-name>"
container_name = "<container-name>"
key = "project1.terraform.tfstate"
sas_token = var.sas_key_value
}
Alternatively you could use an access key for the Storage Account to authenticate the backend. This can also be done using an environment variable:
ACCOUNT_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query '[0].value' -o tsv)
export ARM_ACCESS_KEY=$ACCOUNT_KEY
The SAS token is the better option since this gives access to the Storage Blob for the backend which is what is needed in this case: the Storage Account Key gives access to other resources in the storage account. Either way, the access key should be stored and retrieved from a key vault for added security:
export ARM_SAS_TOKEN=$(az keyvault secret show --name terraform-backend-key --vault-name myKeyVault --query value -o tsv)
Using Backend Config
Rather than placing the values for the backend-config keys in your Terraform scripts you can store these values in a config file and use the '-backend-config' option in during terraform initialisation to use those values:
#### BEGIN /app/backend_config.conf #######
resource_group_name = ""
storage_account_name = ""
container_name = ""
key = ""
sas_token = ""
#### END /app/backend_config.conf #######
terraform init --backend-config=/app/backend_config.conf
The value for sas_token can be omitted from the backend-config file, and instead supplied using the ARM_SAS_TOKEN environment variable.
Inspecting State
Whether you use a remote or local state storage, you can inspect the configuration of
individual items using terraform state show
. For instance if you have defined
a resource group in your config files as azurerm_resource_group.rg
, you can
query the configuration using:
terraform state show azurerm_resource_group.rg
Importing Resources
You can import resources into your state file using terraform import. However you will also need to create the configuration file for the imported resource. You can automate the generation of the configuration file using configuration-driven import. Create an import.tf, e.g.:
import {
id = "resource_id"
to = "azurerm_linux_virtual_machine.my_vm"
}
You can then run terraform plan
to generate a corresponding configuration file:
terraform plan -generate-config-out=generated.tf
Renaming Resources
Sometimes you might want to rename a resource in your state file without
causing the resource to be re-deployed. For instance if you originally
gave your azurerm_resource_group a specific name, e.g. 'project_x', but now
just want to call it 'rg'. You can do this by first creating a copy of the
resource block and then using the terraform state mv
command. Once
removed, you can then delete the original block and update any references
to point to the new resource name:
resource "azurerm_resource_group" "project_x" {
name = var.rg_name
location = var.location
tags = var.default_tags
}
resource "azurerm_resource_group" "rg" {
name = var.rg_name
location = var.location
tags = var.default_tags
}
Use terraform state mv azurerm_resource_group.project_x azurerm_resource_group.rg
to update the state without changing the deployed resources. Before running
terraform plan
, remove the azurerm_resource_group.project_x
block and update
any references to it from other resources to point to azurerm_resource_group.rg
.