Terraform integration
Topaz can be used as a local Azure target for Terraform, enabling full azurerm local testing without a real Azure subscription. Configure Terraform providers to discover endpoints from Topaz metadata instead of Azure public cloud, and run your complete init → plan → apply → destroy workflow locally.
This page explains how Terraform integration works, how to configure it, and which settings are required.
How it works
Terraform still uses the standard Azure providers, but endpoint discovery is redirected to Topaz:
- Terraform providers read cloud metadata from Topaz ARM metadata endpoint.
- Providers authenticate against Topaz Entra endpoints.
- Resource management operations are sent to Topaz ARM/resource endpoints.
- Resources are stored in Topaz local persistence.
In practice, this means your Terraform workflow (init, plan, apply, destroy) stays the same, but runs locally.
Prerequisites
- Topaz installed and running
- DNS setup completed (see Getting started)
- Certificate trusted by your runtime/tooling (or run in a containerized setup that already handles this)
- Terraform installed
Start Topaz for Terraform
Use a deterministic subscription ID so your Terraform runs are repeatable:
topaz start \
--default-subscription 00000000-0000-0000-0000-000000000001 \
--log-level Information
Provider configuration
Topaz supports Terraform with both azurerm and azapi providers.
AzureRM provider
Use this minimal provider configuration:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "= 4.67.0"
}
}
}
provider "azurerm" {
features {}
# Important: host:port only (no scheme)
metadata_host = "topaz.local.dev:8899"
# Topaz does not emulate full RP registration flow.
resource_provider_registrations = "none"
}
AzAPI provider
The azapi provider requires its own endpoint configuration to reach Topaz instead of management.azure.com. Use the endpoint attribute (an HCL list) and set disable_instance_discovery = true so the Go provider does not try to contact Microsoft's login discovery service:
terraform {
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/"
}]
}
If you use both providers together, combine them in one terraform {} block and configure each provider as shown above.
AzureAD provider
The azuread provider manages Entra (Azure AD) resources such as applications, service principals, users, and groups. Like azurerm, it only needs metadata_host to redirect endpoint discovery to Topaz:
terraform {
required_providers {
azuread = {
source = "hashicorp/azuread"
version = "~> 3.0"
}
}
}
provider "azuread" {
# Host and port only — no scheme
metadata_host = "topaz.local.dev:8899"
}
The provider sends Microsoft Graph API calls to Topaz automatically once metadata_host is set. No additional endpoint overrides are required.
Example resources:
resource "azuread_application" "example" {
display_name = "my-app"
}
resource "azuread_service_principal" "example" {
client_id = azuread_application.example.client_id
}
resource "azuread_group" "example" {
display_name = "my-group"
security_enabled = true
}
resource "azuread_user" "example" {
user_principal_name = "user@mytenant.onmicrosoft.com"
display_name = "Example User"
password = "P@ssw0rd!"
force_password_change = false
}
Pass the subscription ID via an environment variable so it matches whatever Topaz was started with:
export TF_VAR_subscription_id=00000000-0000-0000-0000-000000000001
Why these fields matter
metadata_host: tells AzureRM and AzureAD where to fetch cloud metadata.resource_provider_registrations = "none": avoids AzureRM trying provider registration APIs that are not fully emulated.endpoint(azapi): overrides all three ARM endpoints (resource manager, authority host, and audience) so the provider never contacts Azure public cloud.disable_instance_discovery(azapi): prevents the Go-based provider from validating the authority URL against Microsoft's login discovery service, which is unreachable when pointing at a local emulator.
Authentication configuration
For local development with Topaz, AzureRM authentication typically comes from the Azure CLI session.
Recommended flow:
- Configure Azure CLI for Topaz cloud (see Azure CLI integration).
- Login with Azure CLI.
- Run Terraform in the same environment.
If needed, set explicit env vars so provider and tooling are deterministic:
export ARM_SUBSCRIPTION_ID=00000000-0000-0000-0000-000000000001
Example resource
resource "azurerm_resource_group" "example" {
name = "rg-local"
location = "westeurope"
}
Then run:
terraform init
terraform apply -auto-approve
terraform destroy -auto-approve
Configuration options and behavior
| Option | Provider | Required | Notes |
|---|---|---|---|
metadata_host | azurerm, azuread | Yes | Host and port only — no scheme (e.g. topaz.local.dev:8899) |
resource_provider_registrations | azurerm | Strongly recommended | Use none with Topaz |
endpoint | azapi | Yes | List with resource_manager_endpoint, active_directory_authority_host, resource_manager_audience all pointing at https://topaz.local.dev:8899/ |
disable_instance_discovery | azapi | Yes | Set true to prevent the provider contacting Microsoft login discovery |
ARM_SUBSCRIPTION_ID / TF_VAR_subscription_id | azurerm, azapi | Recommended | Keep stable across runs |
Common issues
| Symptom | Likely cause | Fix |
|---|---|---|
https://https://... endpoint errors | metadata_host set with scheme | Use host:port only |
SubscriptionNotFound | Subscription missing in emulator | Start Topaz with --default-subscription or create one first |
azapi endpoint block syntax error | endpoint is an attribute list, not a block | Use endpoint = [{ ... }] syntax, not endpoint { ... } |
| azapi TLS / instance discovery failure | disable_instance_discovery not set | Add disable_instance_discovery = true to the azapi provider block |
CERTIFICATE_VERIFY_FAILED / TLS errors | Certificate trust not configured in tool runtime | Follow cert setup in Getting started and Azure CLI integration |