Terraform and OpenTofu on Azure: Getting Started with the AzureRM Provider

· 1 min read · Terraform & OpenTofu Tutorials

Azure with Terraform in four steps #

The same workflow that you use for AWS works for Microsoft Azure: only the provider and the resource names change. This tutorial creates a resource group, a virtual network with a subnet and a storage account, and stores the state in Azure.

1. Authentication #

Install the Azure CLI and sign in:

$ az login
$ az account list --output table
$ az account set --subscription "<subscription-id>"

The azurerm provider uses that session for local development. In CI/CD use a service principal or, better, workload identity federation with OIDC, which is the equivalent of GitHub Actions with AWS OIDC, so there are no stored secrets.

2. Provider configuration #

providers.tf
terraform {
  required_version = ">= 1.6"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
  }
}

provider "azurerm" {
  features {}

  subscription_id = var.subscription_id
}

variable "subscription_id" {
  type = string
}

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

Since version 4 of the provider the subscription_id is required. The empty features {} block is mandatory.

3. Resources #

Everything in Azure lives inside a resource group:

main.tf
resource "azurerm_resource_group" "main" {
  name     = "rg-ditwl-demo"
  location = var.location

  tags = {
    environment = "demo"
    managed_by  = "opentofu"
  }
}

resource "azurerm_virtual_network" "main" {
  name                = "vnet-ditwl-demo"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  address_space       = ["10.0.0.0/16"]
}

resource "azurerm_subnet" "private" {
  name                 = "snet-private"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.1.0/24"]
}

resource "random_string" "suffix" {
  length  = 6
  upper   = false
  special = false
}

resource "azurerm_storage_account" "main" {
  name                            = "stditwl${random_string.suffix.result}"
  resource_group_name             = azurerm_resource_group.main.name
  location                        = azurerm_resource_group.main.location
  account_tier                    = "Standard"
  account_replication_type        = "LRS"
  min_tls_version                 = "TLS1_2"
  allow_nested_items_to_be_public = false
}

Storage account names must be globally unique, lowercase, letters and numbers only, up to 24 characters, hence the random suffix.

4. Run it #

$ tofu init
$ tofu plan -var subscription_id=<subscription-id>
$ tofu apply -var subscription_id=<subscription-id>

Delete everything with tofu destroy, or by deleting the resource group.

Compare with AWS #

AWS Azure
Account / Organizations Subscription / Management groups
VPC Virtual network (VNet)
Security group Network security group (NSG)
EC2 Virtual machine
S3 Storage account (Blob)
IAM role Managed identity and RBAC role assignment
KMS Key Vault
EKS AKS

Remote state in Azure Storage #

Create a storage account and a container for the state, then configure the backend:

backend.tf
terraform {
  backend "azurerm" {
    resource_group_name  = "rg-tfstate"
    storage_account_name = "sttfstateditwl"
    container_name       = "tfstate"
    key                  = "demo.terraform.tfstate"
    use_azuread_auth     = true
  }
}

Blob leases provide locking. See backends.

Next steps #

Variables and outputs, modules, and for Kubernetes see Terraform and EKS (the concepts are the same for AKS). Check the Azure Verified Modules for production-ready modules. Compare with Google Cloud.

#Terraform #OpenTofu #Azure