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.

info

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