Files
docker/infrastructure/terraform/dynu/README.md
T

4.0 KiB

Dynu Terraform Layer (Brownfield DNS Reconciliation)

This Terraform root is for Dynu DNS brownfield reconciliation. The intended pattern is:

  1. Import the existing root domain object.
  2. Read inventory through data.dynu_dns_records.root.
  3. Generate reviewable dynu_dns_record resources and import commands.
  4. Import every existing DNS record into matching Terraform resources.
  5. Use terraform plan as the reconciliation check before any apply.

Provider behavior to keep in mind

  • Source: beatz174-bit/dynu
  • dynu_domain import requires a numeric Dynu domain ID.
  • Importing dynu_domain imports only the root domain object.
  • It does not import DNS records/subdomains.
  • dynu_dns_record imports require <domain_id>/<record_id>.

Variables

  • dynu_root_domain (default: lan.ddnsgeek.com)
  • dynu_api_key (sensitive)
  • dynu_username / dynu_password (optional)

Safe validation commands

cd infrastructure/terraform/dynu
terraform fmt -check -recursive
terraform init -backend=false -input=false
terraform validate
python3 -m py_compile scripts/generate-brownfield-records.py

Brownfield workflow

cd infrastructure/terraform/dynu

terraform init
terraform import dynu_domain.lan_ddnsgeek_com '<numeric-dynu-domain-id>'

terraform apply -refresh-only
terraform output -json dynu_dns_records > /tmp/dynu-records.json

python3 scripts/generate-brownfield-records.py --dry-run
python3 scripts/generate-brownfield-records.py --overwrite

# Review generated/dynu_dns_records.generated.tf
# Review generated/import-dynu-dns-records.sh

bash generated/import-dynu-dns-records.sh

terraform plan

What each component means

  • data.dynu_dns_records.root: read-only live inventory from Dynu.
  • generated/dynu_dns_records.generated.tf: generated management-intent resources; includes prevent_destroy = true on each record.
  • generated/import-dynu-dns-records.sh: imports each discovered record to its generated dynu_dns_record address using <domain_id>/<record_id>.
  • terraform plan after imports: reconciliation checkpoint. Any create/update/delete must be reviewed manually before apply.

Generated artifacts

The helper script writes these files under generated/:

  • generated/dynu_dns_records_inventory.json
  • generated/dynu_dns_records.generated.tf
  • generated/import-dynu-dns-records.sh

These are generated outputs meant for operator review before use in production.

Troubleshooting

Plan shows a large wall of + values under outputs

Cause:

Terraform is planning to save new output values to state (for example, live records from data.dynu_dns_records.root). This is not creating DNS records by itself.

How to verify:

  • Output-only changes appear under Changes to Outputs.
  • Real DNS changes appear as dynu_dns_record resource create/update/delete actions.

Use:

terraform apply -refresh-only

to persist refreshed data source and output values only.

Error: There is no function named "regexreplace"

Cause:

regexreplace is not a Terraform function. Resource-name slugification should not be implemented in Terraform HCL for this workflow.

Fix:

  • Keep inventory.tf focused on reading live records via data.dynu_dns_records.root.
  • Keep Terraform outputs simple (for example, <domain_id>/<record_id> mappings).
  • Let scripts/generate-brownfield-records.py generate Terraform-safe resource names with Python tf_name(record).

Error: '"'"'dynu_dns_records'"'"'

Cause:

The helper script reads terraform output -json and expects an output named dynu_dns_records.

Fix:

cd infrastructure/terraform/dynu
terraform init
terraform apply -refresh-only
terraform output -json | jq 'keys'

Confirm dynu_dns_records appears in the key list.

If it does not, check that the Terraform config contains:

data "dynu_dns_records" "root" {
  hostname = var.dynu_root_domain
}

output "dynu_dns_records" {
  value = data.dynu_dns_records.root.records
}

Then rerun:

python3 scripts/generate-brownfield-records.py --dry-run