github mattrobinsonsre/terrapod v1.7.0

4 hours ago

Terrapod is a free, open-source platform replacement for Terraform Enterprise. This release makes the module registry find its own modules, adds OpenBao (or HashiCorp Vault) as a first-class source for variable values, and makes long plan logs navigable.

Highlights

  • OpenBao (or HashiCorp Vault) as a variable value source — a variable can hold a reference ({mount, path, field}) instead of a literal, resolved per run and delivered through the run's Secret, so the secret stays in the server and nothing is copied into Terrapod's database. Static kv-v2 and dynamic engines both work, so a fresh short-lived credential is minted for each run. Kubernetes auth by default, with JWT, AppRole and token available, a private CA and a path allow-list per instance, and an unresolvable reference fails the run rather than silently delivering nothing.
  • Secrets delivered as files — a reference can name a file (gcp/adc.json, ~/.aws/credentials), and the variable then holds the file's path, so the secret never appears in the environment or in tfvars. One file can carry several fields of a single read: a logic-less template, a whole-secret json or env format, or a base64-decoded field. Variables reading the same secret share one read per run.
  • Lease revocation and read auditing — opt in per instance, and a run phase's dynamic-secret leases are revoked once its Job ends rather than waiting out their TTL. Every read is audited, naming the variables, path, phase and outcome, and never a value.
  • Diagnostics for the value source — an admin page and API showing each instance's health, login, token TTL and TLS trust, plus a reference Check that tells you whether a reference would resolve without minting a credential.
  • Org-wide module autodiscovery — a module autodiscovery rule's repository URL can now name one repository, a whole org or group, or a pattern over one namespace (owner/terraform-*). Repositories created after the rule register automatically; the ones that existed when it was made are a baseline you register from explicitly, all of them or a chosen subset. Renames are reported, not acted on.
  • Module autodiscovery rules — saved rules that find the modules in a repository and register them, with a preview before you commit to anything.
  • Registry submodules — a module published from a subdirectory of its repository, rather than requiring a repository per module.
  • Jump to any change in a plan log — once a plan log is complete, the viewer offers a picker listing every resource change the plan announces, the summary, and every error and warning. Choosing one scrolls to that line, so the reason a plan failed no longer has to be hunted for.
  • Ids work with or without their type prefixws-{uuid} and the bare uuid are accepted interchangeably across the API, so an id copied out of the UI or an MCP response works against any endpoint. Emitted ids are unchanged.
  • A catalog item's module interface — readable through the API, the SDK, the provider, MCP and the UI.
  • terrapod_run_retry — retrying a run takes what creating one takes, and keeps the destroy flag.
  • OpenBao first — OpenBao is named first throughout the documentation and the UI, with HashiCorp Vault equally supported; both speak the same API. Identifiers, config keys and API attributes are unchanged.

Bug Fixes

  • A malformed id returns a client error instead of 500 Internal server error. Forty-six request paths reported a bad id as a server fault; each now answers with the status its own endpoint already uses for the mistake.
  • A failed run says why, instead of only "Runner exited with code 1".
  • The plan log keeps following a finished phase until the whole log has arrived, so its tail no longer goes missing until you reload.
  • The AI plan summary rates the risk of the change, not of the estate.
  • A Vault outage during an apply claim returns the run to confirmed instead of wedging it.
  • A configuration archive that is truncated, or that cannot be written out, fails the run with the real cause instead of being stored.
  • Each configuration download gets its own temporary file, removed on return.
  • The outbound-URL guard no longer resolves a host that an egress proxy will carry.
  • The workspace page shows why a request failed, not just that it did.
  • terrapod_run_plan_json validates its input and defaults to a compact view of what changes.
  • The Terraform provider loads again: the module autodiscovery rule's reserved provider attribute is renamed.
  • Registry module subdirectories converge, and retry and destroy follow can-queue-destroy.
  • The module interface summary pluralises its counts.
  • Module autodiscovery rules and their per-repository scan state now replicate, so a follower can apply a module an autodiscovery rule registered.
  • An org-wide "register all" scan, and its rebaseline, are bounded and resume from where they stopped, rather than walking every repository in one request.
  • Over-length module autodiscovery rule fields return 422 instead of 500.
  • The Vault lease watcher reads its pending set in batches instead of walking all of it every reconcile cycle.
  • The per-run Secret is sized as a whole before it is created, so a Vault file that fits its own cap but pushes the Secret past Kubernetes' limit fails with a clear message.
  • terrapod_module_autodiscovery_rule_repositories returns the candidates, skips, previous locations and next-check time the API already provides.
  • Setting a module autodiscovery limit to 0 in Helm values now takes effect; the chart previously replaced a zero with the default.
  • Module autodiscovery's glob walk runs off the event loop in preview, polling and scans.

Security

  • The outbound-URL guard unwraps IPv4 embedded in NAT64 (64:ff9b::/96) and 6to4 addresses, so a forbidden address cannot be reached through its IPv6 spelling.
  • The Vault reference check writes a vault.read audit entry, like every other read.
  • The reference check no longer lists a secret's key names for a workspace using local execution, where a Vault-sourced variable could never have been delivered anyway.

Upgrade notes

Three changes alter behaviour for an existing configuration. Two are deliberate uses of the one MINOR exception — tightening a permission that was wrongly open — and they are named here because an operator should meet them before their next apply fails rather than after.

  • Retrying a run now needs what creating that run needs. It used to need run:cancel, which is a plan-tier capability; on an auto-applying workspace a retry is an apply. It now needs run:plan for a plan-only run, and run:apply (plus run:apply-destroy for a destroy) for an apply-capable one. A role holding run:cancel without run:apply will get a 403 where it previously succeeded — grant the capability the run's kind requires. See RBAC capabilities.
  • Notification webhooks and run-task callbacks are no longer delivered to loopback or link-local addresses. Neither has a legitimate receiver on it and both are a request-forgery primitive, so they are refused whatever the configuration. A webhook pointing at 127.0.0.1, a localhost name or 169.254.x stops delivering on upgrade; name the host in api.config.outbound_requests.allowed_hosts, or its network in allowed_cidrs, to keep it. Private space (RFC1918) is unaffected by default — delivering to an in-cluster service stays the ordinary case. The refusal is an ordinary failed delivery whose message names the address and the allow-list that would permit it. See the runbook.
  • Provider: terrapod_registry_module.vcs_tag_pattern becomes Optional+Computed, fixing a perpetual plan diff for anyone who left it unset. The one consequence: removing it from configuration no longer resets it to the server default, it keeps the current value.

Otherwise:

  • One expand-only migration per change, and the release line stays a prefix of main. Upgrading from 1.6 runs only the migrations it has not seen. A revision-graph test now guards that property.
  • Nothing else in this release is on by default that was not before. The value source is off until api.config.vault.enabled and at least one instance are set; lease revocation is opt-in per instance.
  • File delivery needs listeners on 1.7. An older listener ignores the run's vault-files, and the run then fails naming a file that was never written. Upgrade listeners before using it.
  • v1.5 is out of support from this release. v1.6 is now the supported previous minor and receives security fixes.

Status

Stable — the seventh minor release.

Full Changelog: v1.6.4...v1.7.0

Don't miss a new terrapod release

NewReleases is sending notifications on new releases.