Skip to main content

Native CI

Atmos brings first-class CI/CD support directly into the CLI. Run Atmos commands in GitHub Actions and get rich job summaries, status checks, output variables, and stored planfile verification — no extra actions required.

Experimental

Quick Start

atmos.yaml
ci:
enabled: true
summary:
enabled: true
output:
enabled: true
variables:
- has_changes
- has_additions
- has_destructions
- plan_summary
checks:
enabled: true
GitHub Actions
- name: Plan
run: atmos terraform plan vpc -s prod
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Deploy
run: atmos terraform deploy vpc -s prod
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CI Configuration

Configure CI providers, job summaries, output variables, status checks, planfile storage, and templates in your atmos.yaml.

GitHub Actions Workflows

Running Atmos in GitHub Actions reduces to two steps: check out the repository, then run an atmos command. Atmos detects the CI environment automatically and produces job summaries, output variables, status checks, and stored planfiles without any wrapper actions.

The examples below pin the Atmos version via a repository variable named ATMOS_VERSION (e.g. set to 1.200.0). We don't publish a latest tag, so always pin to a specific release.

Permissions

Atmos's native CI features rely on the standard GitHub Actions permission scopes — grant only what each workflow's triggers require:

FeaturePermission
Job summaries ($GITHUB_STEP_SUMMARY)none required
Output variables ($GITHUB_OUTPUT)none required
Commit status checksstatuses: write
Check runs (modern Checks API)checks: write
PR commentspull-requests: write
Checkoutcontents: read
OIDC token issuanceid-token: write

There is no comments: write scope — PR comment writes use pull-requests: write (PR comments are issue comments under the hood). If you disable a feature in atmos.yaml (e.g. ci.checks.enabled: false), you can drop the matching permission.

Plan on Pull Request

.github/workflows/plan.yml
name: Plan
on:
pull_request:

permissions:
id-token: write
contents: read
statuses: write
checks: write
pull-requests: write

jobs:
plan:
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
steps:
- uses: actions/checkout@v6

- run: atmos terraform plan vpc -s prod

Apply on Merge

.github/workflows/apply.yml
name: Apply
on:
push:
branches: [main]

permissions:
id-token: write
contents: read
statuses: write
checks: write

jobs:
apply:
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
steps:
- uses: actions/checkout@v6

- run: atmos terraform deploy vpc -s prod

atmos terraform deploy runs a fresh plan and applies it with -auto-approve. Pass --verify-plan (or set ATMOS_TERRAFORM_VERIFY_PLAN=true) to additionally download the planfile uploaded during the PR run, generate a fresh plan, and perform a semantic comparison before applying — this opt-in verification is experimental. You can also run atmos terraform apply directly. See Planfile Storage for details.

Deploy Affected

Fan out across only the components that changed in the PR using atmos describe affected --format=matrix. When ci.enabled: true is set in atmos.yaml, the matrix is automatically written to $GITHUB_OUTPUT — no --output-file flag needed.

.github/workflows/deploy-affected.yml
on:
pull_request:

permissions:
id-token: write
contents: read
statuses: write
checks: write
pull-requests: write

jobs:
affected:
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
outputs:
matrix: ${{ steps.affected.outputs.matrix }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- id: affected
run: atmos describe affected --format=matrix

deploy:
needs: affected
if: ${{ needs.affected.outputs.matrix != '' }}
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
strategy:
matrix: ${{ fromJson(needs.affected.outputs.matrix) }}
fail-fast: false
steps:
- uses: actions/checkout@v6

- env:
COMPONENT: ${{ matrix.component }}
STACK: ${{ matrix.stack }}
run: atmos terraform deploy "$COMPONENT" -s "$STACK"

The affected job emits a matrix of {component, stack} pairs; the deploy job spreads across them in parallel.

Deploy All

Fan out across every instance defined in your stacks using atmos list instances --format=matrix. Use this for full deploys, drift sweeps across the whole estate, or initial bootstraps. Like describe affected, it auto-routes to $GITHUB_OUTPUT when CI is enabled.

.github/workflows/deploy-all.yml
on:
workflow_dispatch:
schedule:
- cron: '0 6 * * *' # Daily drift sweep

permissions:
id-token: write
contents: read
statuses: write
checks: write

jobs:
inventory:
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
outputs:
matrix: ${{ steps.list.outputs.matrix }}
steps:
- uses: actions/checkout@v6

- id: list
run: atmos list instances --format=matrix

deploy:
needs: inventory
if: ${{ needs.inventory.outputs.matrix != '' }}
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
strategy:
matrix: ${{ fromJson(needs.inventory.outputs.matrix) }}
fail-fast: false
steps:
- uses: actions/checkout@v6

- env:
COMPONENT: ${{ matrix.component }}
STACK: ${{ matrix.stack }}
run: atmos terraform deploy "$COMPONENT" -s "$STACK"

Authentication

The shape of the story: define a CI profile in atmos.yaml, point the workflow at it, done. Atmos exchanges the GitHub OIDC token for cloud credentials transparently — there is no atmos auth login step in CI.

1. Define a github profile. The profile name is arbitrary; we use github to match the example repos. It holds the github/oidc provider plus the identity CI should use:

atmos.yaml
auth:
providers:
github-oidc:
kind: github/oidc
region: us-east-1
identities:
plat-dev/terraform:
provider: github-oidc
role_arn: arn:aws:iam::111122223333:role/atmos-terraform
default: true # Use this identity unless a component overrides it

The IAM role's trust policy must allow GitHub's OIDC issuer for your repo — see Configuring OpenID Connect in AWS for the trust-policy template. Other clouds work the same way: see azure/oidc and gcp/workload-identity-federation.

2. Pick how identities are selected. All three work under the same profile:

One identity for everything
Mark one identity default: true (as above), or set the ATMOS_IDENTITY env var. Simplest setup — works when CI talks to one cloud account/role.
Per-component identity
Set settings.identity on a component or stack to pick a different identity for that scope. Useful when prod components need a different role than dev.
Inheritance
Identities flow through the stack inheritance chain like everything else, so you can set the identity on a base stack and let descendants inherit (or override) it.

3. Wire the workflow. Two pieces: the id-token: write permission, and ATMOS_PROFILE set to the profile name.

.github/workflows/apply.yml
permissions:
id-token: write # Required for GitHub to mint the OIDC token
contents: read
statuses: write
checks: write

env:
ATMOS_PROFILE: github

jobs:
deploy:
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
steps:
- uses: actions/checkout@v6

- run: atmos terraform deploy vpc -s prod

id-token: write lets GitHub issue the OIDC JWT; ATMOS_PROFILE: github activates the profile defined above. No atmos auth login step is needed — Atmos exchanges the OIDC token for cloud credentials when it runs the terraform command. (atmos auth login exists for interactive/local use; in CI it's redundant.)

Gating Production with Environments

GitHub Actions environments buy you three things: per-environment secrets and variables, required manual approvals before the job runs, and deployment history visible in the GitHub UI. Wire one in by adding environment: to the deploy job:

.github/workflows/apply.yml
jobs:
deploy-prod:
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
environment: prod # Requires approval if configured in repo settings
permissions:
id-token: write
contents: read
statuses: write
checks: write
env:
ATMOS_PROFILE: github
steps:
- uses: actions/checkout@v6

- run: atmos terraform deploy vpc -s prod

The GitHub environment (prod) and the Atmos stack (-s prod) are independent concepts — one gates the workflow run, the other selects the stack configuration. Many teams happen to name them the same; nothing requires it.

For deeper auth reference: Profiles, Auth concepts, Providers, Identities.

Working Examples

Two reference repositories you can clone and adapt — both include atmos.yaml, stack configuration, components, and full workflows. They build on the patterns above with use-case-specific design patterns (preview environments, image promotion, label gating).

Features

Job Summaries

Rich Markdown summaries with resource counts, inline badges, and collapsible diffs written to $GITHUB_STEP_SUMMARY. Includes separate templates for plan and apply results. Templates are fully customizable with Go template syntax.

Outputs

Terraform plan and apply results exported as CI output variables for use in downstream jobs. On GitHub Actions, these are written to $GITHUB_OUTPUT.

Checks

Live commit status checks showing real-time operation progress — "Plan in progress" while running and "3 to add, 1 to change, 0 to destroy" when complete.

Planfile Storage

Store and retrieve planfiles across CI pipeline stages using S3, GitHub Artifacts, or local filesystem. The deploy command downloads stored planfiles, generates a fresh plan, and performs a semantic comparison to detect drift before applying.

Commands

CI features are activated with the --ci flag on existing Terraform commands or automatically when running in a CI environment (e.g. GitHub Actions):

atmos terraform plan [--ci]
Run plan with job summary, output variables, status checks, and planfile upload.
atmos terraform apply [--ci]
Run apply with job summary, output variables, and status checks.
atmos terraform deploy [--ci]
Deploy with stored planfile verification, drift detection, and full CI reporting.
atmos terraform planfile
Manage stored planfiles: upload, download, list, delete, and show.
atmos describe affected --format=matrix
Generate GitHub Actions matrix strategy from affected components.

Providers

Atmos auto-detects the CI environment and selects the appropriate provider:

GitHub Actions
Integrates with GitHub job summaries, commit status checks, and output variables. Requires GITHUB_TOKEN for checks and PR features.
Generic CI
Prints summaries, checks, and outputs to stdout. Useful for local development and testing, or any CI provider without native integration.
Looking for our old GitHub Actions?

The Cloud Posse cloudposse/github-action-atmos-terraform-* actions have been deprecated in favor of native CI. Existing workflows still function, but new projects should use the patterns above.