Skip to main content

Terraform / OpenTofu read-only ops

v0.7.9

Read-only Terraform or OpenTofu CLI actions for inspecting a workspace. NO apply, NO destroy, NO state mutation — those should happen in CI, not via a runner. Operates in the directory given by TF_DIR env var, running whichever CLI TF_BIN names — `terraform` by default, `tofu` on an OpenTofu host, so one of those two must be on PATH.

10 allowed by default 3 need approval by default
Pack ID
terraform-readonly
Vendor
emisar
OS
linux
Actions
13
Required binaries. Install these on the host before relying on the pack — an action that calls a missing one fails at run time.
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.

on the runner host
sudo emisar pack install terraform-readonly --hash sha256:4ce1f429f60d5383f14ac2c067cd17769c5ffdc881cc2cc14394d7f0296971cc

Setup

Every action runs cd "$TF_DIR" && terraform … , so TF_DIR must point at an initialized working directory on the runner host. Set TF_BIN =tofu to drive OpenTofu instead. Provider and backend credentials come from that config, not from this pack.

Environment

Set these on the runner host, then add each name to execution.inherit_env so the value reaches the action.

  • TF_DIR required

    Path to an initialized working directory (already terraform init -ed, with .terraform and a backend configured).

  • TF_BIN default terraform

    CLI that runs every action. Set it to tofu on an OpenTofu host; the two are command-compatible for everything this pack calls.

  • TF_STATE_CANDIDATE_DIR

    Optional restricted directory containing candidate .tfstate files for metadata inspection and comparison.

  • TF_PLAN_DIR

    Optional restricted directory where CI drops saved plan files for plan_file_summary to review.

Host access

Run these commands yourself on the runner host. Emisar shows and copies setup recipes; it never runs them.

Read the Terraform workspace and create its local lock or working files while planning.

tf.versiontf.providerstf.state_listtf.state_showtf.show_jsontf.outputtf.validatetf.plan_file_summarytf.state_metadatatf.state_file_metadatatf.state_compare_metadatatf.plan_no_savetf.plan_summary

Run the Emisar service as root

Grant access
sudo install -d -m 0755 /etc/systemd/system/emisar.service.d
printf '%s\n' '[Service]' 'User=root' 'Group=root' | sudo tee /etc/systemd/system/emisar.service.d/10-terraform-host-access.conf >/dev/null
sudo systemctl daemon-reload
sudo systemctl restart emisar
Verify access
test "$(systemctl show emisar --property=User --value)" = root
sudo test -r /srv/infra/prod
sudo test -w /srv/infra/prod

Impact: Every Emisar action on this runner executes as root and can read or modify local Terraform files, including state and embedded secrets. Pack policy still limits commands.

Notes

  • Provider and backend auth depend entirely on what the config uses: an AWS provider/backend reads AWS_* (or an EC2/role profile), GCP reads GOOGLE_APPLICATION_CREDENTIALS, and so on — allowlist whatever your config needs.
  • state_list, state_show, show_json, output, state_metadata, and state_compare_metadata read the backend state, so the backend credentials must be reachable; validate, version, state_file_metadata, and plan_file_summary do not.
  • plan_no_save and plan_summary (both risk medium) call every provider's read APIs (quota usage) and briefly hold the state lock, but never write state or save an applyable plan.
  • For review, prefer plan_summary over plan_no_save and show_json: it returns addresses, actions, counts, drift, and diagnostics as JSON while emitting no attribute or output values. plan_file_summary returns the same shape for a plan CI already saved, without contacting a provider.
  • plan_summary needs a workspace that plans ON THIS HOST. What decides that is the HCP Terraform / Terraform Enterprise workspace's execution mode, not the backend: a cloud block with execution mode local works fine, while remote and agent stream the run's human output instead of machine-readable messages. The action fails closed there rather than reporting an empty plan; use the hcp-terraform pack for those workspaces. plan_no_save still works everywhere, and the state-reading actions are unaffected.
  • state pull may upgrade state to the local CLI's readable format; terraform_version in projected metadata can therefore reflect the runner's CLI rather than the writer of the remote snapshot.
  • TF_BIN only reaches the action when the runner's inherit_env allowlists it, exactly like TF_DIR — an unset TF_BIN runs terraform .

Verify it works

Runs tf.version, 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.

on the runner host
sudo emisar pack verify terraform-readonly

Install and configure a pack walks through the whole sequence on a host.

Actions 13 total

View on GitHub
  • tf.output exec high High — service-affecting

    terraform output -json

    Show the workspace's output values (terraform output -json). The -json form deliberately un-masks outputs marked `sensitive`, so the values routinely carry secrets (a db_password, a kubeconfig, provider credentials) in cleartext; scope it by policy. The runner's redaction is a fail-closed backstop, not a guarantee — it is pattern-bound and can't reliably mask output values whose arbitrary names and shapes match no rule.

    View source on GitHub
  • tf.plan_file_summary script low Low — read-only or trivially reversible

    Project a saved plan into a reviewable summary

    List what a plan file already on disk would create, update, delete, or replace, in the same shape as tf.plan_summary but without contacting any provider — this reads a plan CI has already computed. 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. The filename must resolve beneath TF_PLAN_DIR; symlink and traversal escapes fail closed. A saved plan stores every attribute and output value in cleartext, sensitive ones included; this projection reads none of them.

    View source on GitHub
  • tf.plan_no_save exec medium Medium — changes state, easily reversible

    terraform plan

    Compute a plan but does NOT save it (no -out). Cannot be applied from this run. May call out to providers (read API quotas).

    View source on GitHub
  • tf.plan_summary script medium Medium — changes state, easily reversible

    Project a fresh plan into a reviewable summary

    List what a fresh plan would create, update, delete, or replace, with per-action counts, resources that drifted outside Terraform, planned output changes, and diagnostics. The summary counts always cover the whole plan; the change, drift, output, and diagnostic 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, and names — so a plan can be reviewed without dumping the secrets a full plan carries in cleartext. Saves no plan file, so nothing this returns can be applied. Needs a workspace that plans on this host: HCP Terraform and Terraform Enterprise workspaces set to remote or agent execution stream no structured output, and this fails rather than reporting an empty plan — review those with the hcp-terraform pack. A `cloud` block alone is fine; what decides it is the workspace's execution mode.

    View source on GitHub
  • tf.providers exec low Low — read-only or trivially reversible

    terraform providers

    List all providers declared in the workspace + their constraints.

    View source on GitHub
  • tf.show_json exec high High — service-affecting

    terraform show -json

    Show the full workspace state as JSON (terraform show -json), for programmatic queries. This dumps every resource's attributes, including ones marked `sensitive` (passwords, private keys, tokens), in cleartext; scope it by policy. The runner's redaction is a fail-closed backstop, not a guarantee — it is pattern-bound and can't reliably mask secrets whose names and shapes match no rule.

    View source on GitHub
  • tf.state_compare_metadata script low Low — read-only or trivially reversible

    Compare live and candidate Terraform state metadata

    Compare live backend metadata with one restricted candidate state and report an explicit lineage mismatch, older or newer candidate, or equal serial. The result never recommends or performs a state mutation.

    View source on GitHub
  • tf.state_file_metadata script low Low — read-only or trivially reversible

    Project candidate Terraform state metadata

    Read one restricted candidate .tfstate file and return metadata only. The filename must resolve beneath TF_STATE_CANDIDATE_DIR; symlink and traversal escapes fail closed.

    View source on GitHub
  • tf.state_list exec low Low — read-only or trivially reversible

    terraform state list

    List all resource addresses currently in state.

    View source on GitHub
  • tf.state_metadata script low Low — read-only or trivially reversible

    Project live Terraform state metadata

    Show only the current backend state's metadata — format version, lineage, serial, Terraform version, and resource instance count. Resource values and outputs are never emitted or written to disk.

    View source on GitHub
  • tf.state_show exec high High — service-affecting

    terraform state show <address>

    Show attributes of one resource in state. May contain secrets — relies on audit redaction.

    View source on GitHub
  • tf.validate exec low Low — read-only or trivially reversible

    terraform validate

    Validate the workspace's HCL files. No state read or write.

    View source on GitHub
  • tf.version exec low Low — read-only or trivially reversible

    terraform version

    Show the CLI version + provider versions.

    View source on GitHub