HCP Terraform run review and gating
v0.8.9Review HCP Terraform (formerly Terraform Cloud) and Terraform Enterprise runs and gate what happens next: list workspaces and runs, project a run's plan into a summary that carries no resource or output values but does identify the bounded attribute paths that force replacement, read why a run failed from a bounded tail of its plan or apply log, queue a speculative plan-only run, retry a failed run against its exact configuration version, and confirm, discard, or cancel a run through emisar's policy and approval path instead of the HCP UI.
curl
jq
bash
Install
emisar pack install
fetches this pack, re-validates it, and verifies its content hash against the
--hash
below — the exact bytes this page was rendered against, so a tampered copy is
rejected — before copying it into the runner's packs dir. The command reloads a
running daemon itself; no manual restart.
sudo emisar pack install hcp-terraform --hash sha256:7d808108fe995fbb94f0c44396a2df00f6bae257b1cccef204193063660d59c6
Setup
Calls the HCP Terraform JSON:API over HTTPS with curl, sending the token in an Authorization: Bearer header read from
TFE_TOKEN
, so allowlist that variable in
inherit_env
. Point
TFE_ADDRESS
at your Terraform Enterprise host to use this against a self-managed install.
Environment
Set these on the runner host, then add each name to
execution.inherit_env
so the value reaches the action.
-
TFE_TOKENrequiredAPI token. A user or team token scoped to the workspaces you want reviewable; see the notes for what each action needs.
-
TFE_ADDRESSdefault https://app.terraform.ioAPI host, for Terraform Enterprise or a private HCP region.
Notes
- Create a user token at app.terraform.io/app/settings/tokens , or a team token under Organization Settings → Teams → the team → Team API token. The organization token (Organization Settings → API tokens) is the one several actions here reject.
- plan_summary calls the plan's json-output endpoint, which HashiCorp does NOT allow organization tokens to reach: it needs a user or team token with admin access to the workspace. run_details works with any token that can read runs and still reports the plan's add/change/destroy counts, so prefer it when you do not want to hand out workspace admin.
- apply_run, discard_run, and cancel_run also reject organization tokens — HCP requires a user or team token with permission to apply runs for the workspace.
- The token can do everything these actions expose, so scope it to the workspaces you actually want reachable and let emisar policy decide who may reach the mutating actions.
- Needs curl 7.76 or newer: the actions use --fail-with-body so a rejected request reports the API's error document instead of failing with empty output.
- json-output answers with a one-minute presigned redirect. curl drops the Authorization header on that cross-host hop by design; the redirect target carries its own signature.
- run_diagnostics downloads the failed phase's log through the presigned URL HCP returns. That URL is a credential and never appears in output or audit, but the log text itself becomes part of the governed result — which is why this read is medium risk and policy-gated.
- retry_run creates a standard run, so HCP requires a user or team token that can queue plans in the workspace. The new run is created with auto-apply off and still needs tfc.apply_run before anything changes.
- create_plan_only_run needs a remote- or agent-execution workspace and a usable configuration already associated with it. Local-execution workspaces and never-configured workspaces cannot plan through HCP runners.
Verify it works
Runs tfc.list_organizations, a low-risk
read that confirms the pack can reach its target. Run it on the host once the pack
is installed; pack install runs it for
you.
sudo emisar pack verify hcp-terraform
Install and configure a pack walks through the whole sequence on a host.
Actions 15 total
View on GitHub-
tfc.apply_run script high High — service-affecting
POST /runs/<id>/actions/apply
Confirm a planned run so HCP Terraform applies it. This is the real apply — it creates, changes, and destroys whatever the plan says, and it cannot be undone from here. Review the plan first with tfc.plan_summary or tfc.run_details. Returns the run's state after confirmation.
View source on GitHub -
tfc.cancel_run script medium Medium — changes state, easily reversible
POST /runs/<id>/actions/cancel
Interrupt a run that is currently planning or applying. Cancelling mid-apply stops Terraform partway, so the workspace can be left with some changes made and others not; prefer discard_run for a run that has only planned. Returns the run's state afterwards.
View source on GitHub -
tfc.create_plan_only_run script medium Medium — changes state, easily reversible
POST /runs (plan-only)
Queue a speculative plan-only run against a workspace's usable current or previously uploaded configuration. The workspace must be configured for remote or agent execution; a local-execution workspace has no remote plan environment for this action to run in. A plan-only run can never be applied, so this asks "what would change?" without putting an applyable plan in front of anyone. Review the result with tfc.plan_summary once it finishes.
View source on GitHub -
tfc.discard_run script medium Medium — changes state, easily reversible
POST /runs/<id>/actions/discard
Discard a run awaiting confirmation so its plan is never applied and the workspace unlocks for the next run. Discards no infrastructure — it throws away the pending plan. Returns the run's state afterwards.
View source on GitHub -
tfc.force_unlock_workspace script high High — service-affecting
POST /workspaces/<id>/actions/force-unlock
Force-unlock a workspace, breaking a lock held by a run, another user, or a team. This is the recovery for a dead run that never released its lock — but if that run is in fact still applying, breaking the lock lets a second writer at the state and can corrupt or lose it. First read the holder from tfc.workspace_details and confirm with tfc.run_details that it is finished, and when a plain unlock answers 503 (state still finalizing), retry tfc.unlock_workspace instead of escalating to force. Returns the workspace's lock state afterwards.
View source on GitHub -
tfc.list_organizations script low Low — read-only or trivially reversible
GET /organizations
List the HCP Terraform organizations this token can see, with the contact email and creation date of each. Use it to confirm the token authenticates and to find the organization name the workspace actions need.
View source on GitHub -
tfc.list_runs script low Low — read-only or trivially reversible
GET /workspaces/<id>/runs
List a workspace's runs with status, the operator's message (clipped to 100 characters), whether the run destroys or is plan-only, and which of confirm/discard/cancel the run will currently accept. Use it to find a run awaiting confirmation; page on next_page for older runs.
View source on GitHub -
tfc.list_workspaces script low Low — read-only or trivially reversible
GET /organizations/<name>/workspaces
List an organization's workspaces with their execution mode, Terraform version, auto-apply setting, lock state, and resource count. Use it to find the workspace ID the run actions need, or to see which workspaces apply without review.
View source on GitHub -
tfc.lock_workspace script medium Medium — changes state, easily reversible
POST /workspaces/<id>/actions/lock
Lock a workspace and record why, so queued and new runs hold before planning or applying and nothing can write its state until it is unlocked. Speculative plan-only runs still work. Reversible with tfc.unlock_workspace; a workspace that is already locked answers 409. Returns the workspace's lock state afterwards.
View source on GitHub -
tfc.plan_summary script low Low — read-only or trivially reversible
Project an HCP Terraform plan into a reviewable summary
List what a run's plan would create, update, delete, or replace, with per-action counts, drift, and planned output changes. The summary counts always cover the whole plan; the change, drift, and output lists keep a bounded sample — most destructive first — and `truncated` reports how many entries each list dropped, so the result fits the runner's structured-output cap on plans of any size. Attribute and output VALUES are never emitted — only addresses, types, actions, names, and the bounded `replace_paths` that identify replacement-forcing attributes — because the structured plan HCP Terraform serves carries every value in cleartext, sensitive ones included. Needs a user or team token with admin access to the workspace; tfc.run_details gives counts on a read-only token.
View source on GitHub -
tfc.retry_run script medium Medium — changes state, easily reversible
POST /runs (retry a failed run's exact configuration)
Retry a failed run by creating a new standard run from the source run's exact workspace and configuration version. Only an errored, canceled, force-canceled, or discarded source can be retried, and both IDs are taken from the fetched source run, never from the caller. The new run is created with plan-only and auto-apply explicitly false, so nothing is applied until a human confirms it through tfc.apply_run. Only the configuration is pinned: workspace variables and provider state are re-read when the new run plans.
View source on GitHub -
tfc.run_details script low Low — read-only or trivially reversible
GET /runs/<id>?include=plan
Show one run with its plan's add / change / destroy / import counts. This is the review a token holding only "read runs" can perform — tfc.plan_summary returns the per-resource detail but requires workspace admin. Use it to size a run's blast radius before confirming it.
View source on GitHub -
tfc.run_diagnostics script medium Medium — changes state, easily reversible
Diagnose a failed run from its plan or apply log
Show why a run failed: picks the phase that failed — the apply when it errored or was canceled, otherwise the plan — and returns that phase's status and timestamps with a bounded tail of its log, the last 60 lines capped at 2 KiB with control codes stripped. Provider logs can carry sensitive operational values, so this read is medium risk and policy-gated even though it changes nothing. The presigned log URL HCP returns is fetched but never emitted, and a missing or unreadable log is reported explicitly instead of passing as an empty tail.
View source on GitHub -
tfc.unlock_workspace script medium Medium — changes state, easily reversible
POST /workspaces/<id>/actions/unlock
Unlock a workspace so pending runs can proceed again. Releases a lock the token's own user placed; a lock held by a run or by a different user answers 409 and takes tfc.force_unlock_workspace instead. A 503 means HCP Terraform is still finalizing the latest state version — retry the unlock rather than escalating to force. Returns the workspace's lock state afterwards.
View source on GitHub -
tfc.workspace_details script low Low — read-only or trivially reversible
GET /organizations/<name>/workspaces/<workspace>
Show one workspace by name — its execution mode, Terraform version, auto-apply setting, VCS repository and working directory, resource count, and its lock state including who holds the lock and why. This is the read that answers "why is this workspace locked, and whose lock is it" before anyone reaches for force-unlock, and the id it returns is what the run and lock actions take.
View source on GitHub