Plan file storage
Stategraph Orchestration applies the exact plan file that was reviewed in the pull request, so it stores the file between the plan run and the apply run. The storage key in .stategraph/config.yml sets where: the Orchestration server, an S3 bucket, your own commands, or nowhere. A workflow can override the top-level storage for its directories.
Plan files can contain sensitive values from your configuration and state. Restrict access to the store.
Configuring plan file storage
storage:
plans:
method: s3
bucket: my-terraform-plans
region: us-east-1
This example stores plan files in the AWS S3 bucket that bucket and region set.
Storage methods
The method key selects one of:
terrateam(default): the Orchestration server keeps plan files in its database. It takes no other keys.s3: an S3-compatible bucket keeps the plan files.bucketandregionare required.cmd: commands that you supply store and fetch the plan files.storeandfetchare required.deleteis optional.none: plan files are not kept after the plan run. The apply fails, unless the same storage block also setsunsafe_apply_without_plan: true. Then the apply runs a new non-interactive apply, not the reviewed plan.
The template variables $dir, $workspace, $date, $time, and $token build a unique path for each plan. See the storage reference for the full list.
S3 storage
The credentials on the runner need permission to put, get, and delete objects in the bucket.
| Key | Description |
|---|---|
bucket |
Bucket name. Required. |
region |
Region of the bucket. Required. |
path |
Object path in the bucket. Defaults to terrateam/plans/$dir/$workspace/$date-$time-$token. |
access_key_id |
Access key ID. Optional. Default: the AWS credential chain of the runner. |
secret_access_key |
Secret access key. Optional. Default: the AWS credential chain of the runner. |
delete_used_plans |
Delete the stored plan file when the apply run fetches it. Default true. |
store_extra_args, fetch_extra_args, delete_extra_args |
Extra arguments for aws s3 cp when storing and fetching, and for aws s3 rm when deleting. |
Custom command storage
The commands need access to the storage system and its credentials.
| Key | Description |
|---|---|
store |
The command that stores the plan file. $plan_path is the path of the file to store. |
fetch |
The command that fetches the plan file. $plan_dst_path is the path that the command must write the file to. Orchestration reads the file from that path, not from standard output. |
delete |
Optional. The command that deletes the stored plan file when the apply run fetches it. $plan_dst_path is not substituted in it. |
Google Cloud Storage with gsutil:
storage:
plans:
method: cmd
store: ["gsutil", "cp", "$plan_path", "gs://my-terraform-plans/$dir/$workspace/$date-$time-$token"]
fetch: ["gsutil", "cp", "gs://my-terraform-plans/$dir/$workspace/$date-$time-$token", "$plan_dst_path"]
delete: ["gsutil", "rm", "gs://my-terraform-plans/$dir/$workspace/$date-$time-$token"]
An HTTP store with curl:
storage:
plans:
method: cmd
store: ["curl", "-X", "PUT", "--data-binary", "@$plan_path", "https://plan-storage.example.com/$dir/$workspace/$date-$time-$token"]
fetch: ["curl", "-X", "GET", "https://plan-storage.example.com/$dir/$workspace/$date-$time-$token", "-o", "$plan_dst_path"]
delete: ["curl", "-X", "DELETE", "https://plan-storage.example.com/$dir/$workspace/$date-$time-$token"]
Disabling plan storage for one workflow
storage:
plans:
method: s3
bucket: my-terraform-plans
region: us-east-1
workflows:
- tag_query: "engine:terragrunt"
engine:
name: terragrunt
storage:
plans:
method: none
unsafe_apply_without_plan: true
Use method: none for a workflow whose saved plan would hold values that the apply must calculate again. The workflow then loses the reviewed-plan guarantee, because the apply evaluates the configuration again.
Using environment variables
bucket and region accept environment variables, so a workflow can choose the bucket:
workflows:
- tag_query: "dir:production"
plan:
- type: env
name: MY_PLAN_BUCKET
cmd: ['echo', 'production-bucket-name']
- type: init
- type: plan
apply:
- type: env
name: MY_PLAN_BUCKET
cmd: ['echo', 'production-bucket-name']
- type: init
- type: apply
- tag_query: ""
plan:
- type: env
name: MY_PLAN_BUCKET
cmd: ['echo', 'non-production-bucket-name']
- type: init
- type: plan
apply:
- type: env
name: MY_PLAN_BUCKET
cmd: ['echo', 'non-production-bucket-name']
- type: init
- type: apply
storage:
plans:
method: s3
bucket: $MY_PLAN_BUCKET
region: us-east-1
Also set each variable that the storage block uses in a catch-all workflow (tag_query: ""), so that each directory gets a value.
How plan files are used
- Orchestration plans a pull request that changes Terraform code, and stores the plan file, unless the matching workflow uses
method: none. - After review and approval, someone comments
stategraph apply. - Orchestration fetches the stored plan file and applies it. With
method: none, the apply runs without a stored plan. - If storage is set to delete used plans, Orchestration deletes the stored file after it fetches it.
Best practices
- Use one naming convention for stored plan files, so that they are easy to find.
- Set a retention policy on the store, so that old plan files do not pile up.
Next steps
- Storage reference: every key, type, and default.
- Apply after merge: how stored plans reach the apply run.