strata

Getting Started:

  • strata
  • Strata Glossary

How-To Guides:

  • How To: Get a TLS Certificate onto an Azure Application Gateway / WAF
  • How To: Model Azure Private DNS Zones (VNet Links + Externally-Owned Zones)
    • The short answer
    • Why not a dedicated private/vnet_links/external_id schema?
    • Worked example 1 — zone strata owns, linked to a VNet
    • Worked example 2 — externally-owned hub zone, records only
    • Mapping this onto strata
  • How To: Compose One Variable’s Value From Several Files (Per-Customer/Per-Ring Fragments)
  • How To: Generate Repeated Per-Instance Terraform Blocks (Providers/Modules) From One Composite Value
  • How To: Migrate a v1 workspace.yaml (Topology/Provisioning Decoupling)
  • How To: Use the SBOM Feature (CycloneDX)

Configuration Files:

  • Configuration Files
  • Solution (kind: solution)
  • Configuration (kind: configuration)
  • Provider (kind: provider)
  • ProviderConfig (kind: providerconfig)
  • Resource (kind: resource)
  • Network (kind: network)
  • Firewall (kind: firewall)
  • DNS (kind: dns)
  • TopologyConfig (kind: topologyconfig)
  • Workspace (kind: workspace)
  • Module (kind: module)
  • Namespace (kind: namespace)
  • Tenant (kind: tenant)
  • Environment (kind: environment)
  • Deployment (kind: deployment)
  • Version (kind: version)
  • Artifact (kind: artifact)
  • Integration (kind: integration)

Integrations:

  • Integrations — How to Configure Each One
  • Infisical (store: infisical)
  • Azure Key Vault (store: azure-keyvault)
  • Azure App Configuration (store: azure-appconfig)
strata
  • How To: Model Azure Private DNS Zones (VNet Links + Externally-Owned Zones)
  • View page source

How To: Model Azure Private DNS Zones (VNet Links + Externally-Owned Zones)

You are driving an Azure private DNS Terraform component (e.g. AKS’s private cluster zone, an application zone linked to a VNet, or a hub-owned zone a spoke only writes a few records into) from strata, and kind: dns’s schema has no vnet_id/private/external_id-shaped fields. This guide covers why that’s intentional and how to model both patterns today with spec.zones[].configuration — strata’s provisioner passthrough field — instead of a dedicated schema.

The short answer

spec.configuration is not a stopgap — it’s the correct, permanent place for this. A real Azure private-DNS Terraform component’s actual contract (checked directly against a live consumer) is just a few named inputs on top of kind: dns’s existing records[]:

Concept

Real Terraform input shape

Where it goes in strata

VNet to link the zone to

vnet_id (string, one ARM ID)

spec.zones[].configuration.vnet_id

Zone created in this subscription

local_zone = { name }

the zone’s own name + records[] (already modeled)

Zone owned elsewhere, records written into it

hub_zone = { resource_id }

spec.zones[].configuration.hub_zone.resource_id

No new top-level fields needed, and nothing is lost by using configuration instead: ${var:}/${secret:}/${feature:}/${output:...} tokens inside it resolve exactly the same way they would in a dedicated field — resolve_value_tokens_in_mapping() and the ${output:...}-ownership safety check (_contains_output_token()) both recurse generically into every nested dict/list, not just known top-level keys.

Why not a dedicated private/vnet_links/external_id schema?

Checked against the real, named consumer’s Terraform component before deciding: its actual variables.tf takes a single vnet_id string and a hub_zone = { resource_id } object — not the generalized vnet_links: list[str]/external_id: str shape a first pass at this schema proposed. Minting fields that don’t match any real module’s actual contract would mean more translation work for whoever owns that Terraform module, not less — they’d have to map strata’s reshaped names back onto their own variables.tf by hand, in both directions.

Every other “promote this to a real schema field” decision in strata’s model layer (see ADR-0006 on output_key, or the chart_repository → named remotes consolidation) was made only after finding two or more independently-written real consumers converging on the same shape. This has exactly one real consumer so far — the bar isn’t met yet. Revisit if a second, independently-authored Azure private-DNS Terraform component needs the same shape.

Worked example 1 — zone strata owns, linked to a VNet

A zone created and fully owned by this deployment (e.g. an application domain for an AKS cluster), linked to the spoke’s own VNet:

apiVersion: strata.huybrechts.xyz/v2
kind: dns
meta:
  name: spoke-app-dns
spec:
  zones:
    - name: apps.internal.example.com
      ttl: 3600
      configuration:
        vnet_id: "${output:vnet.vnet_id}"
      records:
        - name: grafana
          type: A
          value: "${var:APP_LB_IP}"
      default_tags:
        environment: prd

vnet_id here is a ${output:...} token because, in this mapping, the VNet is created by a different strata execution step (vnet) than the one that applies this dns document — dns’s step must depends_on: [vnet] and list this document under its own targets so the existing ownership-claiming check (gap #12) accepts the token. If your workspace instead provisions the VNet and the DNS zone in the same Terraform root/step (as the real reference consumer actually does — module.vnet and module.dns wired together in one main.tf), there is no cross-step reference at all: vnet_id is pure intra-root HCL the Terraform module itself resolves, and configuration doesn’t need to carry it — don’t invent a token where the real module has none.

Worked example 2 — externally-owned hub zone, records only

A zone owned by a separate, centrally-managed hub subscription. This deployment doesn’t create it — it only links its own VNet to it and writes a couple of its own records:

apiVersion: strata.huybrechts.xyz/v2
kind: dns
meta:
  name: spoke-hub-dns
spec:
  zones:
    - name: hub.internal.example.com   # documentation only — nothing is created with this name
      configuration:
        vnet_id: "${var:SPOKE_VNET_ID}"
        hub_zone:
          resource_id: "${secret:HUB_DNS_ZONE_RESOURCE_ID}"
      records:
        - name: grafana
          type: A
          value: "${var:APP_LB_IP}"
      # No default_tags/custom_tags — a zone this deployment doesn't own has no cloud-resource
      # identity here to tag. ttl is still fine to set: it's a default for the records THIS
      # document writes, not a property of the zone resource itself.

vnet_id/hub_zone.resource_id are ${var:}/${secret:} tokens here, not ${output:...} — the hub’s VNet and zone are long-lived infrastructure created entirely outside this workspace’s own execution graph, so from strata’s point of view they’re just already-known constants, the same as environment_info.subscription_id already is. A Value token is opaque: it never needs to understand that the string happens to be an ARM resource ID.

Mapping this onto strata

  • spec.zones[].configuration — raw, provisioner-specific passthrough (kind: dns’s own schema reference: config/dns.md). Not validated by strata beyond Value-token syntax; the Terraform module’s own variables.tf/precondition blocks are the real contract and the real guardrails.

  • ${output:step.key} vs. ${var:}/${secret:} — use ${output:...} only when the value is produced by a different strata execution step in the same workspace that this document’s own step depends_on; use ${var:}/${secret:} for anything externally-known (hub infrastructure, values supplied via environment/secret stores). See value-token-resolution.md for the full token model.

  • Nothing about this pattern is Azure-specific at the strata-schema level — the same configuration passthrough convention applies equally to a Cloudflare/Route53 provider’s own zone-association concepts, whatever shape those turn out to need.

Previous Next

© Copyright 2026, Huybrechts XYZ.

Built with Sphinx using a theme provided by Read the Docs.