Skip to main content

tf-apply

info

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.

tip

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

NameTypeRequiredDescription
dirstringYDirectory containing OpenTofu configuration files. This path is relative to the temporary workspace that Kargo provisions for use by the promotion process.
planstringNPath 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[]objectNVariables to pass to OpenTofu. Ignored when applying a saved plan file.
vars[].filestringNPath to a variables file (.tfvars), relative to the dir directory. Mutually exclusive with name/value.
vars[].namestringNVariable name. Required when not using file. Mutually exclusive with file.
vars[].valuestringNVariable value. Required when not using file. Mutually exclusive with file.
env[]objectNEnvironment variables to set during OpenTofu execution.
env[].namestringYEnvironment variable name. Must match the pattern ^[a-zA-Z_][a-zA-Z0-9_]*$.
env[].valuestringYEnvironment variable value.

Output

NameTypeDescription
resultstringThe 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 }}