Terraform/OpenTofu provider for registering domains and managing DMARC/SPF/DKIM/MTA-STS records on the dmarc.ing platform.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
alice 8002176be0
All checks were successful
Bump Tag on Main / bump-tag (push) Successful in 8s
Go lint / lint (push) Successful in 2m43s
Release / release (push) Successful in 15m7s
Merge pull request 'Convert dmarcing_dmarc_policy to structured attributes' (#13) from feat/spf-netblocks-dns-records into main
Reviewed-on: #13
2026-08-28 09:48:28 +00:00
.forgejo/workflows Merge pull request 'fix(release): use an absolute path for --filename-sha256sums' (#11) from fix/release-absolute-sums-path into main 2026-08-28 05:38:16 +00:00
examples Convert dmarcing_dmarc_policy to structured attributes 2026-08-28 19:11:34 +10:00
instructions Initial provider: dmarc.ing domain and DNS policy resources 2026-08-24 17:06:12 +10:00
internal/provider Convert dmarcing_dmarc_policy to structured attributes 2026-08-28 19:11:34 +10:00
.gitignore Initial provider: dmarc.ing domain and DNS policy resources 2026-08-24 17:06:12 +10:00
.goreleaser.yml Publish to boring-registry instead of a hand-rolled network mirror 2026-08-26 15:49:17 +10:00
catalog-info.yaml chore: update Backstage catalog source location to Forgejo 2026-08-28 04:28:52 +10:00
go.mod Initial provider: dmarc.ing domain and DNS policy resources 2026-08-24 17:06:12 +10:00
go.sum Initial provider: dmarc.ing domain and DNS policy resources 2026-08-24 17:06:12 +10:00
main.go feat: move provider registry namespace from failurezone to dmarc-ing 2026-08-28 14:52:21 +10:00
README.md feat: move provider registry namespace from failurezone to dmarc-ing 2026-08-28 14:52:21 +10:00
renovate.json refactor: extend shared Renovate preset 2026-08-28 04:06:12 +10:00

terraform-provider-dmarcing

lint release

Terraform/OpenTofu provider for registering domains and managing DMARC/SPF/DKIM/MTA-STS records on the dmarc.ing platform, backed by service-dmarc-api.

Usage

terraform {
  required_providers {
    dmarcing = {
      source  = "tf.stacktonic.au/dmarc-ing/dmarcing"
      version = "~> 0.1"
    }
  }
}

provider "dmarcing" {
  endpoint  = "https://api.dmarc.ing"   # or DMARCING_ENDPOINT env var
  api_token = var.dmarcing_api_token     # or DMARCING_API_TOKEN env var
}

resource "dmarcing_domain" "example" {
  org_id = 1
  domain = "example.com"
}

Generate an API token from the dmarc.ing dashboard's API Tokens page (/tokens).

See examples/ for every resource. Full schema docs can be generated with tfplugindocs (tfplugindocs generate), not yet run in this repo.

Resources

Resource Purpose Delete behavior
dmarcing_domain Register a domain under an org Unregisters the domain and cascades to every DNS record stored for it
dmarcing_domain_verification Trigger DNS ownership verification No-op (no API to unverify); removed from state only
dmarcing_dmarc_policy Set the DMARC TXT record No-op (no delete API); removed from state only
dmarcing_spf_policy Set the SPF TXT record No-op (no delete API); removed from state only
dmarcing_sts_policy Set the MTA-STS policy No-op (no delete API); removed from state only
dmarcing_dkim_record Set a DKIM selector's record Deletes the selector
dmarcing_spf_service Add a third-party ESP's SPF include Removes the service

dmarcing_domain_verification depends on a DNS TXT record (from dmarcing_domain's verification_host/verification_value) already being live — typically created with your DNS provider's own Terraform resource. If it hasn't propagated yet, applying dmarcing_domain_verification fails with a clear error and can simply be retried.

Development

go build ./...
go test ./...

Acceptance tests exercise a full Terraform plan/apply/destroy cycle against a local fake API server and are gated on TF_ACC=1 (requires a terraform or tofu binary on PATH, or network access for terraform-plugin-testing to download one):

TF_ACC=1 go test ./internal/provider/... -v

To try the provider against a real dmarc.ing deployment without publishing anything, point Terraform at the locally built binary with a dev_overrides block:

go build -o terraform-provider-dmarcing .
# ~/.terraformrc or ~/.tofurc
provider_installation {
  dev_overrides {
    "tf.stacktonic.au/dmarc-ing/dmarcing" = "/path/to/this/repo"
  }
  direct {}
}

Distribution

This provider is published to our self-hosted boring-registry instance, which implements the real Terraform Provider Registry Protocol (not just a network mirror) against S3-compatible storage. No GitHub, no public Terraform/OpenTofu Registry account.

.forgejo/workflows/release.yml, triggered on a vX.Y.Z tag (auto-created by bump-tag.yaml from commit messages):

  1. Runs GoReleaser to build checksummed, multi-platform archives into dist/ and GPG-sign the checksums file (release.disable: true in .goreleaser.yml — GoReleaser itself doesn't publish anywhere; only boring-registry's own upload does).
  2. Authenticates to OpenBao via Forgejo OIDC and fetches the S3 credentials for boring-registry's storage backend (see infra-k8s-vultr/infrastructure/cloud/object-storage.tf/boring-registry.tf — the bucket and its Vultr Object Storage credentials are provisioned there, not in this repo).
  3. Installs the boring-registry CLI and runs boring-registry upload provider --namespace dmarc-ing --filename-sha256sums dist/..._SHA256SUMS against that S3 storage backend.

One-time manual setup (managed in infra-k8s-vultr, not this repo):

  1. A GPG key (RSA, not ECC) whose public half is registered as dmarc-ing's signing key — boring-registry expects this as a signing-keys.json file placed directly in the storage backend at providers/dmarc-ing/signing-keys.json (see boring-registry's publish-providers docs — there's no CLI command for this step, it's provisioned via infra-k8s-vultr's aws_s3_object resource instead).
  2. This repo's GPG_PRIVATE_KEY/GPG_PASSPHRASE secrets (the private half of that same key) plus the VAULT_ADDR org variable and the terraform-provider-dmarcing-release OpenBao JWT role, which the release workflow uses to fetch the storage credentials at runtime.

Consumers reference this provider with an explicit registry hostname, since it's a real registry (not a mirror substituted in via CLI config) — no provider_installation override needed:

required_providers {
  dmarcing = {
    source  = "tf.stacktonic.au/failurezone/dmarcing"
    version = "~> 0.1"
  }
}

If boring-registry requires an API token to install from (per its own auth config), add a credentials block for its hostname in ~/.terraformrc/~/.tofurc.

Note: the exact boring-registry upload provider flags in release.yml are written from its published docs and CLI source, which weren't fully consistent with each other at the time — verify against boring-registry upload provider --help for the version actually installed before relying on this in production. This hasn't been exercised against a real boring-registry instance or terraform init/tofu init yet (no network access in the environment this was built in).