Choosing an Engine

The engine is the tool that Stategraph Orchestration runs for each plan and apply: Terraform, OpenTofu, the stategraph CLI, Terragrunt, Pulumi, CDKTF, or a custom command. The default is the Terraform CLI. Set the engine key at the top level of .stategraph/config.yml, or override it per workflow.

Built-in engines

Terraform (default)

engine:
  name: terraform
  version: '1.5.7'

OpenTofu

engine:
  name: tofu
  version: '1.9.0'

See OpenTofu.

Terragrunt

engine:
  name: terragrunt
  tf_version: '1.5.7'

tf_cmd selects the tool under Terragrunt (terraform by default, or tofu), and tf_version pins its version. See Terragrunt.

CDKTF

engine:
  name: cdktf
  tf_cmd: tofu

See CDKTF.

Pulumi

engine:
  name: pulumi

The pulumi engine takes no other keys. See Pulumi.

Stategraph

engine:
  name: stategraph
  tf_cmd: tofu

The stategraph engine runs the stategraph CLI and keeps your state in Infrastructure as a Database, in place of a Terraform backend. The runner calls stategraph tf plan and stategraph tf apply. These commands run Terraform or OpenTofu and store the state in your Stategraph server. The engine does not collect outputs. For scoped plans, resource locks, and transactions, see Use with Orchestration.

Setting up the Stategraph engine

Prerequisites

  • A Stategraph server that your runners can reach: Stategraph Cloud or a self-hosted deployment.
  • An API key with permission to plan and apply the states of the repository.
  • The tenant ID. stategraph info lists your tenants.
  • The stategraph CLI on a developer machine, for the one-time onboarding:
curl -sSL https://get.stategraph.com/install.sh | sh

Onboard each directory once

Each directory (with its workspace) needs a Stategraph state before its first run. On a developer machine, export STATEGRAPH_API_BASE, STATEGRAPH_API_KEY, and STATEGRAPH_TENANT_ID. Then:

  1. Go to the directory that holds the *.tf files.
  2. If the directory has no terraform.tfstate, create a new state:
stategraph states create --name my-app
  1. If a terraform.tfstate exists, import it:
stategraph import tf --name my-app terraform.tfstate
  1. Both commands write stategraph.json in the directory. Commit it next to the Terraform code.
  2. Do these steps for each directory that uses this engine.

For all options, see states and import.

Enable the engine

After you commit stategraph.json in each directory, set the engine:

engine:
  name: stategraph

To pin the CLI version that the runner uses, set version:

engine:
  name: stategraph
  version: '2.5.7'

To run OpenTofu in place of Terraform, set tf_cmd and tf_version:

engine:
  name: stategraph
  tf_cmd: tofu
  tf_version: '1.9.0'

override_tf_cmd sets the Terraform or OpenTofu program that the CLI calls (TF_CMD), in place of terraform or tofu.

Runner secrets

The runner needs three environment variables. Set them as GitHub Actions secrets on GitHub, as masked CI/CD variables on the GitLab project, or in your self-hosted runner configuration. See Variables.

Variable Purpose
STATEGRAPH_API_BASE URL of your Stategraph server, for example https://stategraph.example.com
STATEGRAPH_API_KEY API key with permission to plan and apply the relevant states
STATEGRAPH_TENANT_ID The tenant UUID

All three are required. The engine's preflight fails the run if one is missing, or if the directory has no stategraph.json.

Plan and apply

  1. Open a pull request that changes a directory with engine: stategraph.
  2. Orchestration runs a plan and posts the diff on the pull request.
  3. Review the plan, get the required approvals, and comment stategraph apply.
  4. Orchestration applies against your Stategraph server. The Stategraph database records the state changes.

Custom engine

Use a custom engine for tools outside the Terraform family, for wrappers, or for legacy systems. Each key is one step, and every step is optional. Set only the steps that your workflow needs.

engine:
  name: custom
  # The command to run during the init step (optional)
  init: ['echo', 'init']
  # The command to run during the plan step (optional)
  plan: ['my-custom-plan']
  # The command to produce a human-readable diff of the plan output (optional)
  diff: ['printf', '+ added foo\n- removed bar\n~ updated bar\n']
  # The command to print the plan's resource counts as JSON (optional)
  resource_summary: ['echo', '{"created": 1, "updated": 0, "replaced": 0, "deleted": 0}']
  # The command to run during the apply step (optional)
  apply: ['my-custom-apply']
  # The command to run for stategraph apply-autoapprove, which has no stored plan (optional)
  unsafe_apply: ['my-custom-apply', '--no-plan']
  # The command to return output values as a JSON string (optional)
  outputs: ['echo', '{"foo": "bar"}']
  • resource_summary prints a JSON object with the integer keys created, updated, replaced, and deleted. It runs after a plan that reports changes, and the counts appear in the summary comment.
  • unsafe_apply runs for stategraph apply-autoapprove.
  • To pass data from the plan step to the apply step, write it to the path in TERRATEAM_PLAN_FILE. The apply step can read it there.

Plan exit codes

The exit code of the plan command sets the result. 0 means that the plan succeeded and there are changes. 2 means that the plan succeeded and there are no changes. Any other exit code is a failure. Terraform and OpenTofu use the reverse with -detailed-exitcode, where 2 means changes. Wrap those tools if you pass their exit code through.

Per-workflow engines

Different workflows can use different engines, for example when environments need different tools. Put the engine block in the workflow entry:

workflows:
  - tag_query: "development"
    engine:
      name: tofu
  - tag_query: "production"
    engine:
      name: terraform

Best practices

  • Pin versions. Use version on the terraform and tofu engines. Use tf_cmd and tf_version on the terragrunt, cdktf, and stategraph engines to control which Terraform-compatible CLI runs.
  • Test custom engines locally with the environment variables that the runner sets, especially TERRATEAM_PLAN_FILE.

Next steps