Terraform on Azure: Provider Setup and First Resource
Configure the AzureRM provider, authenticate safely, and apply your first Terraform-managed resource.
The provider block
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}Authenticating to Azure
The simplest path for local development: install the Azure CLI, run az login once to authenticate interactively in a browser, and the AzureRM provider automatically picks up that session — no keys to manage by hand.
For automated pipelines (CI/CD), use a service principal instead: az ad sp create-for-rbac generates credentials that get passed via environment variables (ARM_CLIENT_ID, ARM_CLIENT_SECRET, ARM_SUBSCRIPTION_ID, ARM_TENANT_ID) rather than typed into any .tf file.
A first real resource
resource "azurerm_resource_group" "example" {
name = "itcertfoundry-example-rg"
location = "East US"
}The actual workflow
terraform init terraform plan terraform apply terraform destroy
Azure-specific things that trip people up
Almost everything in Azure has to live inside a resource group first — plan to create that resource before anything that depends on it, and reference it by name rather than hardcoding the group twice.
Azure region names are inconsistent about spacing and casing across the portal, CLI, and Terraform docs ("East US" versus "eastus") — always copy the exact string Terraform's provider docs expect rather than guessing from the portal.
Guardrails worth having from day one
Run terraform plan and read it before every apply, same discipline as any other cloud provider — Azure billing surprises are just as real as AWS ones.
Move state to a remote backend (azurerm storage account backend is the native pattern) as soon as more than one person or one pipeline touches the same infrastructure.
Real scenarios you'll actually face
"terraform apply fails immediately with a resource-group-not-found error, even though I defined the resource group right there in the same file." Terraform builds its dependency graph from resource references, not from the order things are written in the file — if a resource references the group by a hardcoded name string instead of `azurerm_resource_group.example.name`, Terraform has no way to know it needs to create the group FIRST. Reference the actual resource attribute, not a copy-pasted string of its name, and the ordering resolves itself automatically.
"The pipeline's Terraform run fails with an authentication error, but it works fine from my own laptop." Your laptop is using your interactive `az login` session; the pipeline has no browser to log in with and needs its own identity — a service principal with its own client ID/secret passed via environment variables. A missing or expired `ARM_CLIENT_SECRET` in the pipeline's environment is the most common cause, and it's easy to miss specifically because local testing never exercises that code path at all.
"An apply partially completed, then failed, and now I'm not sure what state everything is actually in." Terraform applies resources according to its dependency graph, and a failure partway through leaves everything created up to that point correctly recorded in state, and everything after it not yet created — it's not silently broken, it's honestly incomplete. `terraform plan` immediately after a partial failure shows you exactly what's left to create; running `terraform apply` again is usually the correct next step, not a state teardown, since Terraform will simply pick up where it actually stopped.
Want a printable copy?
This complete guide is also available as a professionally formatted PDF.
Download PDF ↓