This repository has been archived on 2026-08-17. You can view files and clone it. You cannot open issues or pull requests or push a commit.

terraform-provider-dynu

A standalone Terraform provider for Dynu DNS.

Status: early CRUD milestone. This provider includes read-only data sources plus one writable resource (dynu_dns_record) to establish CRUD foundations.

Quick start (local dev with dev_overrides)

This provider is not published to the Terraform Registry yet. For local development, use Terraform CLI dev_overrides and your local provider binary.

  1. Build provider binary in repo root:
go build -o terraform-provider-dynu

By default, local builds use embedded metadata values:

  • version=dev
  • commit=none
  • built=unknown
  1. Configure ~/.terraformrc:
provider_installation {
  dev_overrides {
    "dynu/dynu" = "/path/to/terraform-dynu-provider"
  }

  direct {}
}
  1. Run the runnable read-only example:
cd examples/read_only
cp terraform.tfvars.example terraform.tfvars
terraform validate
terraform plan
  1. Run the live-safe write example (opt-in, creates a disposable DNS record only):
cd examples/live_safe_dns_record
cp terraform.tfvars.example terraform.tfvars
# set only dynu_root_domain to a Dynu-managed root zone you control
terraform validate
terraform plan

Warning

Do not run terraform init as part of the normal Codex/local test loop for this repo. Because the provider is not yet published to the Terraform Registry, init may attempt registry/network resolution and fail or give misleading results. Use dev_overrides, rebuild the local binary, then run terraform validate and terraform plan.

With dev_overrides, Terraform uses your local binary for dynu/dynu.

Codex/local validation loop

Use this expected loop for local verification and Codex-driven validation:

go build -o terraform-provider-dynu
cd examples/read_only
terraform validate
terraform plan

When provider configuration or code changes, rebuild the provider binary first, then re-run terraform validate and terraform plan.

Version metadata in binaries

The provider embeds build metadata (version, commit, date) in the binary.

  • Local builds (no -ldflags) default to:
    • version=dev
    • commit=none
    • built=unknown
  • You can inspect metadata from the binary with either:
    • ./terraform-provider-dynu --version
    • ./terraform-provider-dynu version

Primary build method (recommended):

./build.sh v0.2.0

build.sh takes exactly one argument (the version string), then derives commit and date automatically:

  • COMMIT=$(git rev-parse --short HEAD)
  • DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)

Manual equivalent (same stamped build command used by build.sh):

VERSION=v0.2.0
COMMIT=$(git rev-parse --short HEAD)
DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)

go build -o terraform-provider-dynu \
  -ldflags="-X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${DATE}"

Release tag/version mapping:

  • Git tag v0.1.0 -> build with VERSION=v0.1.0
  • Git tag v0.2.0 -> build with VERSION=v0.2.0

Copy/paste starter configuration

terraform {
  required_providers {
    dynu = {
      source = "dynu/dynu"
    }
  }
}

provider "dynu" {
  api_key = var.dynu_api_key # optional if DYNU_API_KEY is set
}

variable "dynu_api_key" {
  type      = string
  default   = null
  sensitive = true
}

data "dynu_domains" "all" {}

# Use a real hostname from your Dynu account.
data "dynu_domain" "selected" {
  hostname = "www.example.com"
}

data "dynu_dns_records" "selected" {
  hostname = "www.example.com"
}

Provider schema reference

Provider: dynu

Optional arguments:

  • api_key (String, Sensitive)
    • Falls back to DYNU_API_KEY environment variable when omitted.
  • base_url (String)
    • Test/dev override for Dynu API base URL.

Provider resources:

  • dynu_dns_record (first writable resource)

dynu_dns_record

Arguments:

  • hostname (String, required)
  • record_type (String, required)
  • content (String, optional/computed)
    • For A and AAAA, omitted/blank content means Dynu dynamic-IP intent.
    • For non-A/AAAA record types, content is required.
  • dynamic (Bool, optional/computed)
    • Explicit dynamic-mode toggle for A/AAAA. Existing omitted content behavior remains backward compatible.
  • ttl (Number, optional)
  • state (Bool, optional)
  • group (String, optional)
  • host (String, optional)
  • node_name (String, optional)

Attributes:

  • id (String) in domain_id/record_id format
  • domain_id (Number)
  • domain_name (String)
  • updated_on (String)
  • plus all configurable arguments

Example:

resource "dynu_dns_record" "txt" {
  hostname    = "api.example.com"
  record_type = "TXT"
  content     = "hello-from-terraform"
  ttl         = 300
  state       = true
}

resource "dynu_dns_record" "dynamic_a" {
  hostname    = "auth.example.com"
  record_type = "A"
  # content intentionally omitted for Dynu dynamic IPv4 behavior
}

Notes:

  • Dynu control-panel semantics treat blank A/AAAA values as dynamic/inherited records, not invalid static records.
  • Dynu can surface inherited/current values with parentheses in the UI (for example (203.0.113.10)); provider state preserves dynamic intent to avoid perpetual drift from changing live IPs.
  • The provider avoids sending empty-string IP payloads, and if Dynu rejects omitted IP fields in an API path, it uses a documented fallback emulation path based on the root domain current address/group metadata.

Data source schema reference

dynu_domains

Arguments:

  • none

Attributes:

  • domains (List(Object)):
    • id, name, unicode_name, token (sensitive), state, group
    • ipv4_address, ipv6_address, ttl
    • ipv4, ipv6, ipv4_wildcard_alias, ipv6_wildcard_alias
    • allow_zone_transfer, dnssec, created_on, updated_on

Example:

data "dynu_domains" "all" {}

dynu_domain

Arguments:

  • hostname (String, required)

Attributes:

  • domain (Object) with the same fields as dynu_domains.domains[*].

Example:

data "dynu_domain" "selected" {
  hostname = "www.example.com"
}

dynu_dns_records

Arguments:

  • hostname (String, required)

Attributes:

  • domain_id (Number)
  • domain_name (String)
  • records (List(Object)) with:
    • id, domain_id, domain_name, node_name, hostname, record_type
    • ttl, state, content, updated_on, group, host

Example:

data "dynu_dns_records" "selected" {
  hostname = "www.example.com"
}

Examples

  • Runnable local workflow: examples/read_only/
  • Live-safe write lifecycle workflow: examples/live_safe_dns_record/
  • Provider block example: examples/provider/provider.tf
  • Individual data source snippets:
    • examples/data-sources/dynu_domains/data-source.tf
    • examples/data-sources/dynu_domain/data-source.tf
    • examples/data-sources/dynu_dns_records/data-source.tf
  • Resource snippet:
    • examples/resources/dynu_dns_record/resource.tf

Troubleshooting local dev

  • Unsupported provider arguments

    • Symptom: errors such as Unsupported argument (for example username).
    • Fix: use only api_key and/or base_url in provider "dynu".
  • Bad API credentials

    • Symptom: diagnostics mention authentication failures.
    • Fix: verify api_key or DYNU_API_KEY and re-run terraform plan.
  • Unknown data source arguments

    • Symptom: unsupported argument errors in data blocks.
    • Fix: dynu_domain and dynu_dns_records require only hostname; dynu_domains takes no arguments.
  • Stale provider binary after code changes

    • Symptom: Terraform behavior doesn't reflect latest code.
    • Fix: rebuild binary (go build -o terraform-provider-dynu) and re-run terraform validate and terraform plan.

Development checks

Before committing, run:

./scripts/fix.sh
./scripts/check.sh

fix.sh applies standard formatting and module hygiene.
check.sh is the strict verification script used by CI.

Developer workflow

  • ./scripts/setup-dev.sh - validate local toolchain requirements
  • ./scripts/check.sh - formatting, vet, and unit tests
  • ./scripts/test-integration.sh - local mock-backed provider integration tests
  • ./scripts/testacc.sh - acceptance/integration test wrapper (live tests opt-in)

Live acceptance tests are opt-in and require:

  • TF_ACC=1
  • DYNU_API_KEY
  • optional DYNU_DOMAIN for domain-specific coverage

Live safe write testing

Use examples/live_safe_dns_record when you want to safely validate writable provider behavior against a real Dynu account.

  • This example creates a unique temporary subdomain in the form <prefix>-<random>.<dynu_root_domain>.
  • It creates exactly one disposable A record using a default IPv4 value accepted by Dynu.
  • It is designed so terraform destroy removes only the created disposable record from that run/state.

Safety guidance:

  • Set dynu_root_domain to a root Dynu-managed zone you control (for example example.com).
  • Do not supply or target an existing live full hostname you care about.
  • If apply is interrupted, rerun terraform destroy from the same directory/state to clean up.

Feature scope

Implemented:

  • Provider authentication via api_key or DYNU_API_KEY
  • Optional provider base_url override
  • Data sources: dynu_domains, dynu_domain, dynu_dns_records
  • Resource: dynu_dns_record (CRUD + import using domain_id/record_id)

Not implemented yet:

  • Additional Terraform resources beyond dynu_dns_record
  • Broader Dynu API coverage outside current DNS/domain scope
S
Description
No description provided
Readme Apache-2.0
43 MiB
Languages
Go 89.2%
Shell 10.8%