Terraform

Terraform azurerm Provider in Practice: Common Azure Configurations and Associate Exam Tips

2026-04-19
NicheeLab Editorial Team

The azurerm provider is the Terraform plugin that calls the Azure Resource Manager (ARM) API. This guide walks through the spots people stumble on in practice — authentication methods, switching subscriptions, defaulting tags and regions — together with what the Associate exam expects.

This article assumes stable, documented behavior. Version-dependent features are called out where relevant, and we recommend verifying them in a sandbox environment.

Overview of the azurerm Provider and Version Management

The azurerm provider is the component Terraform uses to create, update, and delete resources via Azure Resource Manager (ARM). Terraform core handles declaration, dependency resolution, and diff computation, while the provider performs the actual API calls.

In production, always pin the version with required_providers and explicitly include features {} in the provider block. features is a required empty block for azurerm — remove it and init fails. By default the provider also auto-registers ARM Resource Providers (Microsoft.Compute, etc.). When your org's guardrails forbid auto-registration, use skip_provider_registration.

  • On the Associate exam, version pinning via required_providers and the mandatory nature of features {} come up often.
  • Only use skip_provider_registration when corporate policy or pre-registration is already in place.
  • Behavior can differ across proxies, Cloud Shell, and other environments, so verify init/plan on both developer machines and CI.

Relationship between Terraform and the azurerm provider, including the auth flow (conceptual)

terraform applyazurerm ProviderAzure CLI / Managed Identity / Service PrincipalAzure Resource Manager (ARM) API

Minimal configuration (version pinning, features, and controlling registration skip)

terraform {
  required_version = ">= 1.5.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.100"
    }
  }
}

provider "azurerm" {
  features {}
  # 事前にRP登録済みなら true を検討
  # skip_provider_registration = true
}

Comparing Authentication Methods and How to Choose (CLI / Managed Identity / Service Principal / OIDC)

azurerm supports several authentication methods. Choose by execution environment: Azure CLI for local development, Managed Identity for runners hosted on Azure, and secretless federation (OIDC) or Service Principals for CI/CD.

Using environment variables (ARM_CLIENT_ID, etc.) keeps secrets out of the provider block. OIDC federation includes version-dependent settings, so check the option names against the official docs for the version you're using.

  • The minimum local setup is to run az login and set use_azure_cli = true.
  • For Managed Identity, don't forget to grant RBAC at the subscription or resource scope.
  • CI is trending toward secretless operation (OIDC). When that isn't available, pass an SP client secret or certificate via environment variables.
Auth MethodTypical Use CaseStrengthsCaveats
Azure CLIDeveloper machines and Cloud ShellMinimal setup, instant to tryGenerally not recommended for CI; depends on the executing user's context
Managed IdentityAzure VM/VMSS, ACI, Functions, Pipeline Agents, etc.No secrets, no rotation neededRequires proper RBAC on the executing resource; not usable off-Azure
Service Principal (secret / certificate)Widely used across CI/CD systemsStable behavior, easy to least-privilegeRequires secret management and rotation; watch for expiration
OIDC FederationModern CI like GitHub Actions or Azure DevOpsSecretless, secure, and low operational overheadProvider-version-dependent options; create the Federated Credential on the SP beforehand

Representative authentication examples

# 1) Azure CLI を利用
provider "azurerm" {
  features {}
  use_azure_cli = true
}

# 2) Managed Identity(システム割り当て)
provider "azurerm" {
  features {}
  use_msi         = true
  subscription_id = var.subscription_id
  tenant_id       = var.tenant_id
}

# 3) Service Principal(環境変数で供給)
# export ARM_CLIENT_ID=... ARM_CLIENT_SECRET=... ARM_TENANT_ID=... ARM_SUBSCRIPTION_ID=...
provider "azurerm" {
  features {}
}

# 4) OIDC(フェデレーション)
# バージョンによりオプション名が異なるため公式を要確認
# 例: use_oidc = true, oidc_token_file_path = var.oidc_token_file_path など

Switching Subscriptions and Tenants with Provider Aliases

For deployments that span multiple subscriptions, use provider aliases. The safest pattern is to isolate subscription_id, tenant_id, and the auth method per alias, then explicitly declare the provider on each resource.

Frequently swapping the Azure CLI account context within a single run is a recipe for flakiness. In CI, define aliases up front and pick between them explicitly during plan/apply.

  • Pin the target with provider = azurerm.alias on both resource and data blocks.
  • When the same resource name appears in multiple subscriptions, avoid collisions via keys or naming conventions.
  • Separating environments with workspaces and switching subscriptions with aliases is easier to manage.

Multi-subscription deployment using aliases

provider "azurerm" {
  alias           = "hub"
  features        {}
  subscription_id = var.hub_subscription_id
  tenant_id       = var.tenant_id
  use_azure_cli   = true
}

provider "azurerm" {
  alias           = "spoke"
  features        {}
  subscription_id = var.spoke_subscription_id
  tenant_id       = var.tenant_id
  use_azure_cli   = true
}

resource "azurerm_resource_group" "rg_hub" {
  name     = "rg-hub-shared"
  location = var.location
  provider = azurerm.hub
}

resource "azurerm_resource_group" "rg_spoke" {
  name     = "rg-spoke-app"
  location = var.location
  provider = azurerm.spoke
}

Defaulting Region, Naming Conventions, and Tags

The standard pattern is to centralize the region in variables and codify naming in locals. This makes reviews easier, and the exam rewards the same reproducibility and readability.

azurerm supports default_tags at the provider level. default_tags and per-resource tags are merged, and when keys collide the resource value wins. This is great for cross-cutting tag propagation and governance.

features.resource_group.prevent_deletion_if_contains_resources blocks accidental deletion of resource groups that still hold resources (the default can vary by version, so it's best to set it explicitly with clear intent).

  • Storage account names must be lowercase alphanumeric and have length limits. Branch naming per resource type inside locals.
  • Standardize a minimum tag set across the project — environment, owner, cost-center, and so on.
  • Reference location from a single variable whenever possible. For multi-region, use a readable map.

Patterns for location, naming, and default tags

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

variable "env" { type = string }
variable "owner" { type = string }

locals {
  name_prefix = lower(join("-", [var.env, "app01"]))
  rg_name     = "rg-${local.name_prefix}"
}

provider "azurerm" {
  features {
    resource_group {
      prevent_deletion_if_contains_resources = true
    }
  }
  default_tags {
    tags = {
      environment = var.env
      owner       = var.owner
    }
  }
}

resource "azurerm_resource_group" "main" {
  name     = local.rg_name
  location = var.location
  tags = {
    # 同じキーがある場合はこの値が優先される
    owner = var.owner
  }
}

Common Pitfalls and Debugging Tips

When Resource Provider auto-registration is blocked, certain resource creations fail with 404 or AuthorizationFailed. Setting skip_provider_registration = true doesn't always solve the root cause, so your operational design should pick a clear lane: pre-register, or allow auto-registration.

With Service Principals, the classic failures are expiration and insufficient permissions. Watch for cases where Contributor isn't enough (Key Vault data plane, for example). Azure CLI auth depends on the current az account set subscription, so it's not recommended for CI.

Isolate failures by cross-checking Terraform debug logs against ARM error messages. Reproduce with plan first; on retries, tuning -parallelism can sidestep transient throttling.

  • Use az account show / az account list --refresh to confirm the CLI's current context.
  • Set ARM_CLIENT_ID/SECRET/TENANT_ID/SUBSCRIPTION_ID explicitly to remove the CLI dependency.
  • Long-running CI jobs can fail when tokens expire mid-run, so break them into smaller steps.

Environment variables and logs for debugging

# Service Principal(シークレット)の例
export ARM_CLIENT_ID=00000000-0000-0000-0000-000000000000
export ARM_CLIENT_SECRET=********
export ARM_TENANT_ID=11111111-1111-1111-1111-111111111111
export ARM_SUBSCRIPTION_ID=22222222-2222-2222-2222-222222222222

# 詳細ログ(必要な時だけ)
export TF_LOG=TRACE
export TF_LOG_PATH=./terraform.trace.log

# Azure CLI の現在サブスクリプションを明示
az account set --subscription $ARM_SUBSCRIPTION_ID

Remote State (Azure Storage Backend) and Locking

The azurerm backend is separate from the provider — it's declared in the terraform block's backend section. State is stored in an Azure Storage blob, and locking is implemented via blob leases.

For CI, pre-provision the backend resource group, storage account, and container with IaC, and grant access via Azure AD (preferred) or SAS. Enabling versioning and soft delete on the container (the bucket-equivalent) makes recovery easy.

  • Isolate the state storage's operations and permissions, and standardize naming like prod/tfstate.
  • Use a per-workspace key to prevent collisions.
  • Authentication works with Azure CLI, Managed Identity, or Service Principal.

azurerm backend example (declared in the terraform block, not in provider)

terraform {
  backend "azurerm" {
    resource_group_name  = "rg-tfstate"
    storage_account_name = "sttfstate1234"
    container_name       = "tfstate"
    key                  = "${terraform.workspace}.tfstate"
  }
}

# 認証は実行環境に依存(例:az login、Managed Identity、ARM_* 環境変数)

Check Your Understanding

Associate

問題 1

Your organization deploys to two Azure subscriptions (hub and spoke) simultaneously with different RBAC. Which is the recommended approach to switch the target per resource with minimum risk?

  1. Define provider aliases and explicitly set provider = azurerm.<alias> on each resource
  2. Set a different subscription_id directly on each resource per terraform.workspace
  3. Switch subscriptions via az login each time and reuse a single provider
  4. Vary only tenant_id by variable while keeping subscription_id shared

正解: A

Splitting concurrent multi-subscription deployments via provider aliases is the safest and most reproducible approach. Declaring the provider on each resource locks in the target regardless of runtime context or CLI state. Swapping subscription_id directly on resources isn't a common pattern, and manual CLI switching introduces instability. Varying only tenant doesn't satisfy the requirement either.

Frequently Asked Questions

Why is the features block required? Do I still need it if it is empty?

The azurerm provider expresses internal feature toggles through the features block during initialization. Even when empty, the block is required by spec, and omitting it causes an error at init time.

When default_tags and per-resource tags conflict, which wins?

When the same key exists in both, the resource-level tags win. The practical pattern is to put org-wide defaults in default_tags and override exceptions on the resource itself.

Should the azurerm backend be configured inside the provider block?

No. The backend is declared in the terraform block's backend section, independent of the provider. It specifies state storage and locking, which is a different role from the provider that drives API calls.

Check what you learned with practice questions

Practice with certification-focused question sets

無料で問題を解いてみる
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
Terraform

HCL Syntax: Terraform's Configuration Language (2026)

HCL2 fundamentals for Terraform — blocks, attributes, expres...

Terraform

Terraform Authoring & Operations Pro: Complete Guide (2026)

Tactics for the Terraform Pro exam — module authoring, works...

Terraform

Terraform Providers: Plugin Management Fundamentals (2026)

Provider mechanics — required_providers, versions, mirrors, ...

Terraform

Terraform Resource Blocks: Declarative Infra Units (2026)

Resource block fundamentals — addresses, references, common ...

Terraform

Terraform Data Sources: Read-Only External Data (2026)

Data source basics — declaration, refresh behavior, dependen...

Browse all Terraform articles (102)
© 2026 NicheeLab All rights reserved.