tf-apply
This promotion step is only available in Kargo on the Akuity Platform, versions v1.9 and above.
Additionally, it requires enabling of the Promotion Controller to allow for Pod-based promotions.
tf-apply applies an OpenTofu configuration to create, update, or delete
infrastructure. This step can apply changes directly from configuration files or
from a saved plan file generated by the tf-plan step.
For a safer workflow, use tf-plan with the out option to
generate a plan file, then apply that specific plan with tf-apply. This
ensures that exactly the changes previewed in the plan are applied.
Configuration
| Name | Type | Required | Description |
|---|---|---|---|
dir | string | Y | Directory containing OpenTofu configuration files. This path is relative to the temporary workspace that Kargo provisions for use by the promotion process. |
plan | string | N | Path to a plan file to apply, relative to the dir directory. Typically generated by the tf-plan step. When not specified, the apply runs directly against the configuration. |
vars | []object | N | Variables to pass to OpenTofu. Ignored when applying a saved plan file. |
vars[].file | string | N | Path to a variables file (.tfvars), relative to the dir directory. Mutually exclusive with name/value. |
vars[].name | string | N | Variable name. Required when not using file. Mutually exclusive with file. |
vars[].value | string | N | Variable value. Required when not using file. Mutually exclusive with file. |
env | []object | N | Environment variables to set during OpenTofu execution. |
env[].name | string | Y | Environment variable name. Must match the pattern ^[a-zA-Z_][a-zA-Z0-9_]*$. |
env[].value | string | Y | Environment variable value. |
Output
| Name | Type | Description |
|---|---|---|
result | string | The apply output as text, showing what changes were made. |
Examples
Common Usage
The most common usage of this step is to apply Stage-specific OpenTofu configuration directly, such as deploying an AWS Lambda function. This example shows a complete direct-apply workflow where configuration is updated, applied, and state changes are committed back to Git.
vars:
- name: repoURL
value: https://github.com/example/infra.git
- name: image
value: 123456789.dkr.ecr.us-west-2.amazonaws.com/my-app
steps:
- uses: git-clone
config:
repoURL: ${{ vars.repoURL }}
checkout:
- branch: main
path: ./src
- uses: hcl-update
config:
path: ./src/opentofu/${{ ctx.stage }}/env.auto.tfvars
updates:
- key: image_uri
value: ${{ vars.image }}:${{ imageFrom(vars.image).Tag }}
- uses: tf-apply
config:
dir: ./src/opentofu/${{ ctx.stage }}
env:
- name: AWS_REGION
value: us-west-2
- name: AWS_ACCESS_KEY_ID
value: ${{ secret('aws-creds').awsAccessKeyID }}
- name: AWS_SECRET_ACCESS_KEY
value: ${{ secret('aws-creds').awsSecretAccessKey }}
- uses: git-commit
config:
path: ./src
message: Update ${{ ctx.stage }} to ${{ imageFrom(vars.image).Tag }}
- uses: git-push
config:
path: ./src
Post-PR Merge Apply
This example shows the second phase of a PR-based workflow. After a pull request containing updated configuration has been reviewed and merged, a fresh clone of the merged branch is made and then applied. This pattern ensures that production changes are reviewed before being applied.
vars:
- name: repoURL
value: https://github.com/example/infra.git
- name: image
value: 123456789.dkr.ecr.us-west-2.amazonaws.com/my-app
steps:
# First phase: Create and wait for PR (see tf-plan examples)
# ...
- uses: git-wait-for-pr
config:
prNumber: ${{ task.outputs['open-pr'].prNumber }}
repoURL: ${{ vars.repoURL }}
# Second phase: Clone merged main and apply
- uses: git-clone
config:
repoURL: ${{ vars.repoURL }}
checkout:
- branch: main
path: ./merged
- uses: tf-apply
config:
dir: ./merged/opentofu/${{ ctx.stage }}
env:
- name: AWS_REGION
value: us-west-2
- name: AWS_ACCESS_KEY_ID
value: ${{ secret('aws-creds').awsAccessKeyID }}
- name: AWS_SECRET_ACCESS_KEY
value: ${{ secret('aws-creds').awsSecretAccessKey }}
- uses: git-commit
config:
path: ./merged
message: "State files for: Update ${{ ctx.stage }} to ${{ imageFrom(vars.image).Tag }}"
- uses: git-push
config:
path: ./merged
Applying a Saved Plan
This example demonstrates applying a saved plan file. By saving the plan to a file, you ensure that exactly the changes shown in the plan are applied, with no possibility of drift between planning and applying.
steps:
# Clone, update configuration, etc...
- uses: tf-plan
as: plan
config:
dir: ./src/opentofu/${{ ctx.stage }}
out: tfplan
env:
- name: AWS_REGION
value: us-west-2
- name: AWS_ACCESS_KEY_ID
value: ${{ secret('aws-creds').awsAccessKeyID }}
- name: AWS_SECRET_ACCESS_KEY
value: ${{ secret('aws-creds').awsSecretAccessKey }}
- uses: tf-apply
config:
dir: ./src/opentofu/${{ ctx.stage }}
plan: tfplan
env:
- name: AWS_REGION
value: us-west-2
- name: AWS_ACCESS_KEY_ID
value: ${{ secret('aws-creds').awsAccessKeyID }}
- name: AWS_SECRET_ACCESS_KEY
value: ${{ secret('aws-creds').awsSecretAccessKey }}