workflows

workflows sets the steps that Stategraph Orchestration runs to plan and apply a directory and workspace, in place of the default workflow. An entry can also set the engine, the GitHub environment, the runner, the lock policy, and the plan storage.

Default Configuration

workflows:
  - tag_query: ""
    plan:
      - type: init
      - type: plan
    apply:
      - type: init
      - type: apply

Keys

workflows is a list. Orchestration uses the first entry whose tag_query matches the directory and workspace. Each entry has these keys:

Key Type Description
tag_query string A tag query that must match the tag set of a directory and workspace. Required. An empty string matches everything.
plan list The plan steps. Default is init, then plan.
apply list The apply steps. Default is init, then apply.
engine engine The engine for this workflow, in place of the top-level one.
environment string The GitHub environment for the operation. Not set by default.
runs_on object The runs-on configuration. Default is ubuntu-latest on GitHub. On GitLab, set it to a list of runner tags. Orchestration passes it to the pipeline as the RUNS_ON input, which is empty by default.
lock_policy string When Orchestration locks the directory: strict (when it is merged or applied), apply (only when it is applied), merge (only when it is merged), or none (never). A value other than strict is less safe, but can be useful in the right situation. Default is the top-level lock_policy, which defaults to strict.
storage storage Plan storage for this workflow. Default is the top-level storage.
terragrunt boolean Legacy. true is equivalent to engine: { name: terragrunt }. Default is false. Use engine.
cdktf boolean Legacy. true is equivalent to engine: { name: cdktf }. Default is false. Use engine.
terraform_version string Legacy. Equivalent to engine: { name: terraform, version: <value> }. Use engine.

Workflow Steps

Steps run in order. Both the plan and the apply list accept each step type:

  • init, plan, apply: run that command of the engine, for example terraform plan.
  • env: sets environment variables.
  • run: runs a custom command.
  • oidc: gets cloud credentials through OIDC.
  • checkov: scans the plan with Checkov.
  • conftest: checks the plan against Conftest policies.
  • opa: evaluates an OPA policy against the plan.
  • gates: runs a command that creates Gatekeeper gates.

Every step list needs its own step

If a workflow sets plan steps, at least one of them must be a plan step. If it sets apply steps, at least one must be an apply step. Orchestration rejects any other configuration with a repository configuration error.

A run step that calls terraform plan or terraform apply does not count: the plan step reports the plan result that Orchestration tracks, and the apply step takes part in the apply handling of Orchestration.

Without a plan or apply key, the workflow uses the default steps, which meet this rule. If you remove a default step, such as init, errors or unexpected behavior can occur.

Init, plan, and apply steps

Key Type Description
type string init, plan, or apply.
extra_args list Extra command line arguments for the command.
visible_on string When the output shows in a pull request comment or the console: always, failure, or success. Default is failure for init, and always for plan and apply.

Env

The env step sets environment variables for the steps after it, so put it before the steps that use them. It has two methods: a command, or a script with method: source.

Command

Sets the variable name to the output of cmd.

Key Type Description
name string The environment variable name.
cmd list The command that prints the value of the environment variable.
trim_trailing_newlines boolean Trim trailing newlines. Default is true.
sensitive boolean true masks the value in the output that Orchestration posts, and on GitHub Actions also in the job log. GitLab CI does not mask it in the job log. Default is false.

Source Method

Runs cmd and keeps the environment variables that it exports. The script must export them, so test it on its own first.

Key Type Description
method string Must be source.
cmd list The command or script that exports environment variables.
sensitive boolean true masks the variables that the script changes in the output that Orchestration posts, and on GitHub Actions also in the job log. GitLab CI does not mask them in the job log. Default is false.

Run

The run step runs a custom command from the directory that Orchestration works on. The command must be available and executable on the runner: use absolute paths, or install what it needs. It can read the runner environment variables, such as TERRATEAM_DIR and TERRATEAM_WORKSPACE, so one workflow can serve many directories and workspaces.

Orchestration records the output of the command and masks secrets with ***. When the command fails, the output shows in the pull request comment by default, so remove or mask sensitive data in it.

Key Type Description
cmd list The command to run.
run_on string When the step runs, from the state of the workflow: success, failure, or always. Default is success.
env object Environment variables for this command. Each key is a variable name, and each value is a string.
ignore_errors boolean true ignores a failure. Default is false, or true when on_error contains a gate.
visible_on string When the output shows in a pull request comment or the console: always, failure, or success. Default is failure.
format string or object How the output shows in pull request comments: code (the default, a fenced code block), raw (as it is), or markdown (as Markdown). The object form { type: code, lang: <language> } gives a fenced code block with syntax highlighting for <language>.
on_error list Actions when the command fails. Each entry is an object with a type. The only type is gate, which creates a Gatekeeper gate with the keys in Gate.

Checkov

The checkov step runs Checkov on the JSON form of the plan, to find misconfigurations and security issues. The output goes into the results. The step fails when Checkov exits with an error.

Key Type Description
type string Must be checkov.
extra_args list Extra command line arguments for checkov. They replace the default --quiet --compact.
gate object A gate that asks for approval when Checkov fails.
ignore_errors boolean true ignores a failure. Default is false, or true when gate is set.
run_on string When the step runs, from the state of the workflow: success, failure, or always. Default is success.

Checkov reads its configuration from environment variables. Set them with an env step before the checkov step. Common variables:

  • CKV_SKIP_CHECK: comma-separated list of check IDs to skip.
  • CKV_CHECK: comma-separated list of specific check IDs to run.
  • CKV_FRAMEWORK: framework to scan (default: terraform_plan).

For all options, see the Checkov documentation.

Conftest

The conftest step runs Conftest with Rego policies on the JSON form of the plan. The output goes into the results. The step fails when a policy is violated.

Key Type Description
type string Must be conftest.
extra_args list Extra command line arguments for conftest.
gate object A gate that asks for approval when Conftest fails.
ignore_errors boolean true ignores a failure. Default is false, or true when gate is set.
run_on string When the step runs, from the state of the workflow: success, failure, or always. Default is success.

By default, Conftest reads policies from the policy/ directory, relative to the Terraform configuration. To change Conftest, set these environment variables with an env step:

  • CONFTEST_POLICY: path to the directory containing Rego policy files.
  • CONFTEST_NAMESPACE: namespace to use for policy evaluation (default: main).
  • CONFTEST_OUTPUT: output format (json, table, tap, junit, github).

For policies and other options, see the Conftest documentation.

OPA

The opa step runs opa eval on the JSON form of the plan. Pass the policy and the query in extra_args, for example ['-d', 'policy.rego', 'data.terraform.deny'].

Key Type Description
type string Must be opa.
extra_args list Extra command line arguments for opa eval, usually the policy file (-d) and the query.
fail_on string undefined fails the step when the query result is undefined. defined fails the step when the query result is defined. Default is undefined.
gate object A gate that asks for approval when the step fails.
ignore_errors boolean true ignores a failure. Default is false, or true when gate is set.
run_on string When the step runs, from the state of the workflow: success, failure, or always. Default is success.

Gate

A gate asks for manual approval when a step fails, for example a security scan, a policy check, or a custom validation script. When a gated step fails, Orchestration records a gate instead of failing the plan, and blocks the apply until authorized users approve the gate. See Gatekeeper.

The checkov, conftest, and opa steps accept gate. The run step does not: use on_error with an entry of type: gate and the same keys.

Key Type Description
token string A unique identifier for this gate request. Gates without a token are approved by approving the pull request.
name string A name for the gate.
all_of list Users, teams, or roles that must all approve the gate.
any_of list Users, teams, or roles from which any_of_count approvals are required.
any_of_count integer The number of approvals required from the any_of list. Default is 0, which requires no approval from any_of. Set it to 1 or more when you use any_of.

To approve a gate with a token, an authorized user comments:

stategraph gate approve <token>

Gates

The gates step runs a command, and creates a Gatekeeper gate for each entry of the gates list in the JSON that the command prints to standard output. The step never fails the workflow.

Key Type Description
type string Must be gates.
cmd list The command to run. Its standard output must be a JSON object of the form {"gates": [ ... ]}. Each gate accepts token, name, all_of, any_of, any_of_count, and add_reviewers (boolean, default true: request a review from the user: and team: entries).
run_on string When the step runs, from the state of the workflow: success, failure, or always. Default is success.

OIDC

The oidc step gets short-lived credentials from AWS, Azure, or GCP through OpenID Connect. For AWS, it sets AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, and AWS_REGION. Each string value except type and provider can come from a GitHub secret or an environment variable, as ${ENV_VAR}.

Key Type Provider Description
type string Must be oidc.
provider string aws, azure, or gcp. Required for azure and gcp. Default is aws.
role_arn string aws The ARN of the IAM role to use. Required.
assume_role_arn string aws The ARN of an IAM role to assume into, after role_arn. Default is the value of role_arn.
assume_role_enabled boolean aws false skips the assume into assume_role_arn. Default is true.
audience string aws The AWS audience name. Default is sts.amazonaws.com.
region string aws The AWS region, set as AWS_REGION. Default is us-east-1.
session_name string aws The AWS session name. Default is terrateam.
duration integer aws The AWS session duration in seconds. Default is 3600.
service_account string gcp The email address or unique identifier of the Google Cloud service account to generate credentials for. Required.
workload_identity_provider string gcp The full identifier of the Workload Identity Provider, with the project number, pool name, and provider name. Required.
access_token_lifetime integer gcp The lifetime of the access token, in seconds. Default is 3600.
audience string gcp The GCP audience name. Default is https://iam.googleapis.com/ followed by workload_identity_provider.
access_token_subject string gcp The email address of a user to impersonate for Domain-Wide Delegation.
client_id string azure The App Registration client ID. Required.
tenant_id string azure The Azure AD (Entra ID) tenant ID. Required.
subscription_id string azure The default Azure subscription ID.
audience string azure The Azure audience name. Default is api://AzureADTokenExchange.

Examples

Setting an environment variable with a command

workflows:
  - tag_query: "dir:prod"
    plan:
      - type: env
        name: MY_CUSTOM_VAR
        cmd: ['echo', 'Hello, World!']
      - type: run
        cmd: ['echo', 'The value of MY_CUSTOM_VAR is: $MY_CUSTOM_VAR']
      - type: init
      - type: plan

Setting environment variables with the source method

workflows:
  - tag_query: "dir:prod"
    plan:
      - type: env
        method: source
        cmd: ['$TERRATEAM_ROOT/scripts/environment']
      - type: init
      - type: plan
    apply:
      - type: env
        method: source
        cmd: ['$TERRATEAM_ROOT/scripts/environment']
      - type: init
      - type: apply

For directories that match dir:prod, the environment script exports environment variables before the plan, and again before the apply. Then terraform init runs, and terraform plan or terraform apply runs with these variables.

Custom plan and apply steps

workflows:
  - tag_query: "dir:production"
    plan:
      - type: init
      - type: run
        cmd: ['echo', 'Running custom plan step']
      - type: plan
        extra_args: ['-input=false']
    apply:
      - type: init
      - type: apply
        extra_args: ['-auto-approve']
      - type: run
        cmd: ['./post_apply_script.sh']

For directories that match dir:production, this workflow runs terraform init for both plan and apply. It prints a message before terraform plan, plans with -input=false, applies with -auto-approve, and runs a custom script after the apply.

Blocking the apply path

Some teams plan with Orchestration but apply somewhere else. To enforce this, add a run step to apply that exits non-zero, so that the apply fails before Terraform runs. The apply steps must still contain an apply step.

workflows:
  - tag_query: ""
    plan:
      - type: init
      - type: plan
    apply:
      - type: run
        cmd: ['sh', '-c', 'echo "Applies for this repository are performed outside of Stategraph."; exit 1']
      # Required: the apply list must contain an apply step. The run step
      # above always fails first, so this step never runs.
      - type: apply

This workflow plans normally. On apply, the run step prints a message and exits non-zero. A failed step aborts the workflow, so the apply step never starts and terraform apply never runs. The pull request shows the message, because the output of a run step is visible on failure by default.

Order matters

The apply step must come after the run step that fails. If it comes first, terraform apply runs before the run step can block it.

AWS OIDC authentication

workflows:
  - tag_query: "dir:aws"
    plan:
      - type: oidc
        provider: aws
        role_arn: ${AWS_ROLE_ARN}
      - type: init
      - type: plan
    apply:
      - type: oidc
        provider: aws
        role_arn: ${AWS_ROLE_ARN}
      - type: init
      - type: apply

For directories that match dir:aws, the plan and the apply first get AWS credentials through OIDC, with the role ARN from the AWS_ROLE_ARN environment variable. Then terraform init runs, followed by terraform plan or terraform apply.

GCP OIDC authentication

workflows:
  - tag_query: "dir:gcp"
    plan:
      - type: oidc
        provider: gcp
        service_account: ${GCP_SERVICE_ACCOUNT}
        workload_identity_provider: ${GCP_WORKLOAD_IDENTITY_PROVIDER}
      - type: init
      - type: plan
    apply:
      - type: oidc
        provider: gcp
        service_account: ${GCP_SERVICE_ACCOUNT}
        workload_identity_provider: ${GCP_WORKLOAD_IDENTITY_PROVIDER}
      - type: init
      - type: apply

For directories that match dir:gcp, the plan and the apply first get GCP credentials through OIDC, with the service account and the workload identity provider from the GCP_SERVICE_ACCOUNT and GCP_WORKLOAD_IDENTITY_PROVIDER environment variables. Then terraform init runs, followed by terraform plan or terraform apply.

Security scanning with Checkov

workflows:
  - tag_query: ""
    plan:
      - type: init
      - type: plan
      - type: checkov

Checkov scans the plan for misconfigurations and security issues.

Policy validation with Conftest

workflows:
  - tag_query: ""
    plan:
      - type: init
      - type: plan
      - type: conftest

Conftest checks the plan against the policies of your organization.

Combined security and policy checks

workflows:
  - tag_query: "env:production"
    plan:
      - type: env
        name: CKV_SKIP_CHECK
        cmd: ['echo', 'CKV_AWS_20,CKV_AWS_23']
      - type: env
        name: CONFTEST_POLICY
        cmd: ['echo', '$TERRATEAM_ROOT/policies/production/']
      - type: init
      - type: plan
      - type: checkov
      - type: conftest

For production, this workflow makes Checkov skip two checks, points Conftest at the production policies, and runs both after the plan.

Using gates for manual approval

workflows:
  - tag_query: "env:production"
    plan:
      - type: init
      - type: plan
      - type: checkov
        gate:
          token: "checkov-override"
          any_of: ["team:security", "team:platform"]
          any_of_count: 1
      - type: conftest
        gate:
          token: "policy-override"
          all_of: ["team:compliance"]
          any_of: ["user:cto", "user:security-lead"]
          any_of_count: 1

A member of the security or platform team can approve a Checkov failure. A Conftest policy violation needs the approval of the compliance team, and of the CTO or the security lead.

Gating custom validation scripts

workflows:
  - tag_query: ""
    plan:
      - type: init
      - type: plan
      - type: run
        cmd: ['./scripts/cost-check.sh']
        on_error:
          - type: gate
            token: "cost-approval"
            any_of: ["team:finance", "user:budget-owner"]
            any_of_count: 1

A custom script checks the cost. When the cost exceeds the thresholds, a member of the finance team or the budget owner can approve.

Specifying a GitHub environment

workflows:
  - tag_query: production
    environment: production

The run uses the production GitHub environment, so it can read the secrets and variables of that environment.