Skip to main content
Version: v1.7 (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 initplanapplydestroy 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)

Step 1: Start Topaz with deterministic IDs

Start Topaz with a stable subscription ID and your Entra tenant ID:

topaz-host \
--default-subscription 00000000-0000-0000-0000-000000000001 \
--log-level Information

You will see the Topaz ASCII art banner, a table listing every running service with its port, and a "Default subscription created" confirmation.

Keep Topaz running for the rest of this tutorial.

Step 2: Configure Azure CLI for Topaz cloud

If you have not done this before, follow Azure CLI integration.

Minimal flow:

# One-time registration
curl -fsSL https://raw.githubusercontent.com/TheCloudTheory/Topaz/refs/heads/main/cloud.json -o cloud.json
az cloud register -n Topaz --cloud-config @"cloud.json"

# Use Topaz cloud
az cloud set -n Topaz

# Required for Topaz Entra endpoint
export AZURE_CORE_INSTANCE_DISCOVERY=false

# Login
az login

Verify:

az account show --output table

You should see the Topaz subscription listed:

Name CloudName SubscriptionId TenantId State IsDefault
---------- ----------- ------------------------------------ ----------- ------- -----------
dev-local Topaz 00000000-0000-0000-0000-000000000001 <tenant-id> Enabled True

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 4: 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 5: 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 6: 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