Skip to main content
Version: v1.10 (stable)

Local Terraform development with Topaz

In this tutorial, we will create a Terraform project that provisions Azure resources against a local Topaz emulator. We will run the full init → plan → apply → destroy workflow, configure the azurerm provider for Topaz, and verify that resources are created and cleaned up correctly.

What you will build​

In this tutorial, Terraform will create:

  • A resource group
  • A storage account

Everything runs locally against Topaz. No real Azure resources are created.

Prerequisites​

Before you start:

  • Topaz installed
  • DNS setup completed (see Getting started)
  • Topaz certificate trusted by your OS and tooling (see Getting started)
  • Terraform installed (terraform --version)
  • Azure CLI installed (az --version)
Before you start

Topaz must be running and the Azure CLI pointed at it. See Getting started and Azure CLI integration if you have not done this yet.

Step 1: Create a Terraform project​

Create a clean working directory:

mkdir -p topaz-terraform-tutorial
cd topaz-terraform-tutorial

Create providers.tf for the azurerm provider:

terraform {
required_version = ">= 1.6.0"

required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "= 4.67.0"
}
}
}

provider "azurerm" {
features {}

# Important: host:port only. Do not prefix with https://
metadata_host = "topaz.local.dev:8899"

# Topaz does not emulate full RP registration flow.
resource_provider_registrations = "none"
}

If you want to use the azapi provider instead (or alongside azurerm), the configuration is different — azapi does not use metadata_host. Instead, use the endpoint list attribute and set disable_instance_discovery = true:

terraform {
required_version = ">= 1.6.0"

required_providers {
azapi = {
source = "azure/azapi"
version = "~> 2.0"
}
}
}

variable "subscription_id" {
type = string
}

provider "azapi" {
subscription_id = var.subscription_id
use_msi = false
use_oidc = false
use_cli = true
disable_instance_discovery = true

endpoint = [{
resource_manager_endpoint = "https://topaz.local.dev:8899/"
active_directory_authority_host = "https://topaz.local.dev:8899/"
resource_manager_audience = "https://topaz.local.dev:8899/"
}]
}

Pass the subscription ID via an environment variable:

export TF_VAR_subscription_id=00000000-0000-0000-0000-000000000001

Create main.tf:

resource "azurerm_resource_group" "rg" {
name = "rg-topaz-tutorial"
location = "westeurope"
}

resource "azurerm_storage_account" "sa" {
name = "topaztutorialsa001"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}

Set a deterministic subscription in your shell:

export ARM_SUBSCRIPTION_ID=00000000-0000-0000-0000-000000000001

Step 2: Run Terraform workflow​

Initialize:

terraform init

Create a plan:

terraform plan -out tfplan

Apply:

terraform apply -auto-approve tfplan

You should see resources created successfully.

Step 3: Verify with Azure CLI​

Confirm resources exist in Topaz:

az group show --name rg-topaz-tutorial --output table
az storage account show --name topaztutorialsa001 --resource-group rg-topaz-tutorial --output table

Step 4: Clean up​

Destroy resources:

terraform destroy -auto-approve

Common gotchas​

1) metadata_host includes scheme​

Symptom:

  • Terraform errors with malformed endpoint such as https://https://...

Fix:

  • Use metadata_host = "topaz.local.dev:8899"
  • Do not include https://

2) SubscriptionNotFound​

Symptom:

  • terraform apply fails because the subscription does not exist

Fix:

  • Start Topaz with --default-subscription 00000000-0000-0000-0000-000000000001
  • Export the same ID in ARM_SUBSCRIPTION_ID

3) TLS certificate errors​

Symptom:

  • CERTIFICATE_VERIFY_FAILED from az or provider auth flow

Fix:

  • Ensure topaz.crt is trusted in your OS trust store
  • Ensure Azure CLI certificate bundle is updated (see Azure CLI integration)

4) Wrong active cloud in Azure CLI​

Symptom:

  • az commands hit real Azure instead of Topaz

Fix:

  • Check cloud: az cloud show --output table
  • Switch if needed: az cloud set -n Topaz

5) Provider registration errors​

Symptom:

  • AzureRM tries RP registration calls unsupported by local emulator behavior

Fix:

  • Keep resource_provider_registrations = "none"

6) azapi endpoint block syntax error​

Symptom:

  • Terraform errors with Blocks of type "endpoint" are not expected here

Fix:

  • endpoint in the azapi provider is an attribute (list), not a block — use endpoint = [{ ... }] syntax, not endpoint { ... }

7) azapi TLS or instance discovery failure​

Symptom:

  • azapi provider fails during init or apply trying to reach login.microsoftonline.com

Fix:

  • Add disable_instance_discovery = true to your azapi provider block
  • Ensure SSL_CERT_FILE (or your OS trust store) includes the Topaz certificate so the Go-based provider trusts TLS connections to topaz.local.dev

Next steps​

  • Extend main.tf with Key Vault or Service Bus resources
  • Add this workflow to CI using a local Topaz container
  • See Terraform integration for reference configuration details
Star on GitHub