Skip to content

How TerraDart is built

If you are deciding whether to depend on TerraDart, two questions matter more than any feature list: why should I trust hundreds of generated factory classes? and why should I expect this project to keep up with its providers? This page answers both with mechanisms you can inspect in the repository — every claim below names something that actually runs, not an aspiration.

Curated factories are not written by hand. They are generated by a maintainer CLI (terradart-codegen wrap) from pinned inputs: the Terraform provider schema of each provider (hashicorp/google, hashicorp/google-beta, hashicorp/aws, cloudflare/cloudflare, appwrite/appwrite, hashicorp/azurerm), plus what the schema leaves out. For Google that is Magic Modules metadata — the upstream YAML Google uses to generate the provider itself, synced per-resource with provenance recorded for every file. For AWS, Cloudflare, Appwrite and Azure it is validator hints extracted from the provider’s Go source at the pinned release: the value sets that become enums and the exclusive groups that become sealed types.

Generation is byte-deterministic: CI re-runs the generator against the pinned inputs on every pull request and fails if a single generated file differs from what is committed. There is no “mostly generated, occasionally hand-patched” gray zone — generated files carry a do-not-edit banner, repo tooling blocks direct edits, and the parser that extracts enum values from schema descriptions is the same code the coverage checker runs, so the generator and its auditor cannot drift apart.

Google generates its own Terraform provider from Magic Modules, and TerraDart reads both ends of that pipeline:

The MM YAML overlay carries metadata the provider schema does not surface — enum values, exactly-one-of groups, sensitive fields, force_new, free-form description text — read at the Magic Modules commit the provider release was generated from. Google tracking is pinned to GA ~> 8.0; beta-only types live in terradart_google_beta, and names GA also has stay in terradart_google.

The weekly loop under Sustainability refreshes these fixtures. You don’t need to track it to use TerraDart; it is documented so that, when a release adds factories or shifts an existing one, you can see which upstream change drove it.

The rule is simple: facts are derived, judgment is declared, and both are auditable.

  • Anything the schema, Magic Modules or the provider’s validators can state — parameters, enum values, output getters, nested block shapes, documentation — is derived, so it moves automatically when upstream moves.
  • Human decisions — how to model an exactly-one-of block as a sealed class hierarchy, what to name it, and which surfaces to freeze deliberately — live in thin, reviewable override files.
  • Every escape hatch is a fail-closed ledger: frozen surfaces, coverage debts, and migrator round-trip exceptions are recorded lines with reasons, and CI fails when an entry goes stale. Forgetting is impossible to do silently.

This is also why the API is opinionated: IAM adjuncts default to additive *_iam_member variants, and mutually-exclusive nested blocks are enforced at compile time with sealed classes instead of documented conventions.

Type-checking generated code proves it compiles, not that it works. Verification is layered from cheap to expensive:

The second layer asks Terraform itself. On every pull request, each example’s synthesized JSON is checked with terraform validate against the real provider plugin, and the migrator round-trips every example — synthesize, migrate the JSON back to Dart, synthesize again — and requires identical output. CI does not apply examples to a live project; real-apply evidence is tracked through the cookbook (see Status). When you upgrade TerraDart and your synthesized JSON is unchanged, that is not luck; it is the property this harness exists to protect.

TerraDart is a single-maintainer project, and you should weigh that honestly. Here is what makes it a smaller risk than the label suggests: the routine work does not depend on the maintainer being present.

Bumps run on different weekdays, so one can merge before the next opens. This table is rendered from tool/providers.yaml, and the docs consistency check fails when the two disagree:

ProviderDay (UTC)Routine bump
Google Cloud (with google-beta)Sundayauto-merges when CI is green
AWSMondayauto-merges when CI is green
CloudflareTuesdaywaits for review
AppwriteWednesdayauto-merges when CI is green
AzureThursdayauto-merges when CI is green
  • The update loop runs on a schedule. Provider schema updates arrive as automatic weekly pull requests on the days above. A new resource type arrives as a generated factory queued for API polish. A routine bump, one with no removed type and no breaking change to the generated Dart API, enables auto-merge and lands once the full CI battery is green, without waiting on anyone’s availability. A provider set to bump.mode: pr-only in tool/providers.yaml always waits for review.
  • The repository is deliberately maintainable by others — human or AI. A single agent guide is the source of truth for how to work on the codebase; the ledgers make every standing decision explicit; determinism makes every regeneration verifiable. A successor does not need tribal knowledge, and routine maintenance runs under the gates above.
  • Lock-in is shallow by design. The output is standard Terraform JSON. If the project stopped tomorrow, your existing configurations would keep working with plain OpenTofu or Terraform — and migrating away is a file-format decision, not a re-architecture.

What is not promised: there is no SLA. Issue triage and reviews are best-effort, and security response targets are documented in SECURITY.md. The status page states the current release phase and change policy in the same spirit.

  • How TerraDart works — what a Stack becomes: synth, plan and apply, and the constants and outputs your app reads.
  • Coverage — every factory of every provider package and the examples that exercise it; the provider pages cover the rest.
  • Contributing or pointing an agent at the repo? Start with AGENTS.md.