Skip to main content

terraflow.yaml

Every consumer repository declares a terraflow.yaml file at the root that describes its infrastructure stack. The producer's orchestrator parses this file to determine which layers to run, in what order, and with what configuration.

Quick start

Here is a minimal terraflow.yaml for a new stack with a single layer targeting GCP:

stack:
name: my-service
owner: platform
tier: standard
cloud: gcp

terraform:
min_version: "1.14.9"

environments:
- name: dev
is_default: true
auto_deploy: true
tier: standard
project: eigenoid-dev

- name: prd
approval_required: true
depends_on: [dev]
tier: critical
project: eigenoid-prd

layers:
- name: compute
path: compute
yaml

With this file in place, opening a PR will automatically run terraform plan against the dev environment. After review, a reviewer comments /terraflow apply to apply the changes.

To add a second layer that depends on the first:

layers:
- name: compute
path: compute
- name: monitoring
path: monitoring
depends_on: [compute]
yaml

The orchestrator will run compute first (wave 0), then monitoring (wave 1).


Schema

# -- Stack metadata ---------------------------------------------------
stack:
name: <string> # Unique stack name (used in state bucket and labels)
owner: <string> # Owning team (e.g., "platform", "product")
tier: <string> # foundation | standard | experimental
cloud: <string> # gcp | aws | azure

# -- Terraform settings -----------------------------------------------
terraform:
min_version: "<semver>" # Minimum required version (e.g., "1.14.9")

# -- Environments ------------------------------------------------------
environments:
- name: <string> # Environment name (must match config/)
is_default: <bool> # Only one can be default (used for PR plans)
auto_deploy: <bool> # true = automatic plan+apply on push to main
approval_required: <bool> # true = requires approval (GitHub Environment)
depends_on: [<string>] # Environments that must be applied first
tier: <string> # standard | critical (affects gates)
project: <string> # GCP project ID (informational; real config lives in producer)

# -- Layers ------------------------------------------------------------
layers:
- name: <string> # Layer name (e.g., "wif", "network", "compute")
path: <string> # Relative path to the Terraform directory
depends_on: [<string>] # Other layers that must be applied first (for waves)
yaml

Full example

# iac-foundation/terraflow.yaml
stack:
name: foundation
owner: platform
tier: foundation
cloud: gcp

terraform:
min_version: "1.14.9"

environments:
- name: dev
is_default: true
auto_deploy: true
tier: standard
project: eigenoid-dev

- name: qa
approval_required: true
depends_on: [dev]
tier: critical
project: eigenoid-qa

- name: prd
approval_required: true
depends_on: [qa]
tier: critical
project: eigenoid-prd

layers:
- name: platform-wif
path: platform-wif
- name: service-wif
path: service-wif
depends_on: [platform-wif]
- name: governance-sa
path: governance-sa
depends_on: [platform-wif]
yaml

Waves (dependency ordering)

The orchestrator groups layers into waves based on depends_on. Layers with no dependencies go in wave 0, layers that depend on wave 0 go in wave 1, and so on.

Wave 0: [wif] ← no dependencies, runs first
Wave 1: [network] ← depends_on: [wif]
Wave 2: [compute] ← depends_on: [network]

Within each wave, layers run in parallel (each one is an independent GitHub Actions job).

Multi-layer example

layers:
- name: wif
path: wif

- name: network
path: network
depends_on: [wif]

- name: dns
path: dns
depends_on: [wif]

- name: compute
path: compute
depends_on: [network, dns]
yaml

Result:

Wave 0: [wif] ← runs first
Wave 1: [network, dns] ← parallel, both depend on wif
Wave 2: [compute] ← after both network AND dns

Validation

The orchestrator validates the spec at parse time:

  1. terraflow.yaml exists at the repo root.
  2. Required fields (stack.name, stack.cloud, at least one layer).
  3. depends_on references point to layers that actually exist.
  4. Layer paths exist as directories in the repo.
  5. No cycles in the dependency graph.

If validation fails, the pipeline stops with a clear error in the Setup step.

Conventions

FieldConvention
stack.namekebab-case, max 30 characters (used in bucket names and labels)
layer.namekebab-case, singular (e.g., wif, not wif-layer)
layer.pathRelative to the repo root, no trailing slash
environments[].nameLowercase, no hyphens (e.g., dev, qa, prd)
terraform.min_versionExact semver, no v prefix

State backend

The state bucket name is derived automatically:

{state_bucket_prefix}-{stack_name}-tfstate-{environment}

For example, with state_bucket_prefix: eigenoid-2cea55 and stack.name: shared-resources:

  • dev: eigenoid-2cea55-shared-resources-tfstate-dev
  • qa: eigenoid-2cea55-shared-resources-tfstate-qa

The state_bucket_prefix is configured in platform-actions/config/environments.yaml, not in the consumer.