Skip to content

projects.yml schema

projects.yml at the repo root tells the engine what to discover and which hooks to run. There are three discovery modes. Terragrunt mode is exclusive; the two plain-Terraform modes can be combined.

The engine's vocabulary is project; in Terragrunt mode a project is exactly a unit, and this site uses the two words interchangeably.

Terragrunt mode

terragrunt:
  root: live               # directory that holds root.hcl
  include_dependents: true # re-plan a changed unit's downstream dependents
  exclude:                 # optional globs, relative to root
    - "**/scratch/**"
  tool: terragrunt         # optional

The shipped configuration has no excludes, on purpose: the foundation units are the highest-impact part of the tree, and excluding anything from discovery hides it from the plan, the gates, and the merge gate.

Discovery uses native Terragrunt (terragrunt find with dependencies and the DAG). A changed unit impacts that unit plus every downstream dependent, in topological order, so apply runs in the right sequence.

Explicit projects (plain Terraform)

projects:
  - name: prod
    dir: environments/prod
    tool: terraform

The label is <dir>/<name>.

Generated projects (plain Terraform)

generate_projects:
  include:
    - "environments/**"
  exclude:
    - "**/modules/**"

Every folder with *.tf under an include glob (minus excludes and shared modules) becomes a project, labelled by its path. Globs support **.

The drift block

drift:
  enabled: true    # default true
  include:
    - "live/**"
  exclude:
    - "sandbox/**"

Scopes or disables the daily drift sweep. With no drift: block, every discovered unit is swept.

The steps block

steps:
  post_plan:
    - bash "$TFPR_ENGINE_DIR/scripts/conftest-gate.sh"
    - CHECKOV_SOFT_FAIL=1 bash "$TFPR_ENGINE_DIR/scripts/checkov-gate.sh"
    - bash "$TFPR_ENGINE_DIR/scripts/infracost-gate.sh"

$TFPR_ENGINE_DIR points at the engine checkout the reusable workflow provides. It defaults to the workspace, so the same paths resolve in vendored mode.

Hooks run at fixed seams around init, plan, and apply. A global steps: applies to every unit. An individual project may set its own steps:, which fully replaces the global one (there is no merge). See the hook and step model.

Discovery needs PyYAML

Project selection runs on the hosted runner, which has Python. A steps: or drift: block requires PyYAML for parsing and the selector fails loudly if it is missing, rather than silently ignoring your config.