# Atmos Pro

Configure Atmos Pro integration for stack locking, status reporting, and workspace management across your organization.

## Overview

Atmos Pro provides centralized management capabilities for teams using Atmos at scale:

- **Stack Locking** — Prevent concurrent modifications to the same stack
- **Status Reporting** — Track deployment status and history
- **Workspace Management** — Organize teams and projects

## Configuration

The only required setting is your `workspace_id`. All other settings have sensible defaults and can be left unset.

### Minimal

**File:** `atmos.yaml`

```yaml
settings:
  pro:
    workspace_id: "your-workspace-id"
```

### Advanced

Override defaults only if needed. All fields shown below are optional except `workspace_id`.

**File:** `atmos.yaml`

```yaml
settings:
  pro:
    base_url: "https://app.cloudposse.com"
    endpoint: "api/v1"
    workspace_id: "your-workspace-id"
    max_payload_bytes: 4194304
    github_oidc:
      request_url: ""
      request_token: ""
```

## Configuration Reference

- **`settings.pro.enabled`**

  Master switch for Atmos Pro on a component or stack. When `atmos list instances --upload`
  sends the inventory to Atmos Pro, this value controls whether the instance is tracked as
  active or shown as disabled.

  Atmos collapses an enabled hierarchy before upload, so an outer disable always wins:
  ```
  metadata.enabled  >  settings.pro.enabled  >  settings.pro.drift_detection.enabled
  ```
  Disabling a component with [`metadata.enabled: false`](/stacks/components/component-metadata#enabled)
  forces `settings.pro.enabled` (and therefore `settings.pro.drift_detection.enabled`) off in the
  uploaded payload — a disabled component is never dispatched for drift detection or workflow runs,
  regardless of its `pro` settings.
  - **Type:** `boolean`
  - **Default:** `true` (when a `settings.pro` block is present)
- **`settings.pro.base_url`**

  Base URL for the Atmos Pro API.
  - **Type:** `string`
  - **Default:** `https://app.cloudposse.com`
  - **Environment Variable:** `ATMOS_PRO_BASE_URL`
- **`settings.pro.endpoint`**

  API endpoint path appended to the base URL.
  - **Type:** `string`
  - **Default:** `api/v1`
  - **Environment Variable:** `ATMOS_PRO_ENDPOINT`
- **`settings.pro.workspace_id`**

  Atmos Pro workspace identifier. Required for authentication. This value is not a secret and is safe to commit to version control.
  - **Type:** `string`
  - **Default:** (none)
  - **Environment Variable:** `ATMOS_PRO_WORKSPACE_ID`
- **`settings.pro.token`**

  Bearer token for Atmos Pro API authentication. Atmos Pro does not issue API keys or personal access tokens — the only way to obtain a bearer token is by performing an OIDC token exchange against the Atmos Pro API. This is intended for advanced integrations outside of GitHub Actions.
  - **Type:** `string`
  - **Default:** (none)
  - **Environment Variable:** `ATMOS_PRO_TOKEN`
- **`settings.pro.max_payload_bytes`**

  Maximum payload size (in bytes) before Atmos automatically chunks upload requests to Atmos Pro. When `atmos describe affected --upload` or `atmos list instances --upload` produces a payload larger than this threshold, it is split into multiple smaller requests with batch metadata for server-side reassembly.
  - **Type:** `integer`
  - **Default:** `4194304` (4 MB)
- **`settings.pro.github_oidc.request_url`**

  GitHub Actions OIDC token request URL. Automatically set in GitHub Actions via the `ACTIONS_ID_TOKEN_REQUEST_URL` environment variable.
  - **Type:** `string`
- **`settings.pro.github_oidc.request_token`**

  GitHub Actions OIDC request token. Automatically set in GitHub Actions via the `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variable.
  - **Type:** `string`

## Environment Variables

- **`ATMOS_PRO_BASE_URL`**
  Override the Atmos Pro API base URL. Maps to 
  `settings.pro.base_url`
  .
- **`ATMOS_PRO_ENDPOINT`**
  Override the API endpoint path. Maps to 
  `settings.pro.endpoint`
  .
- **`ATMOS_PRO_WORKSPACE_ID`**
  Workspace identifier for authentication. Not a secret. Maps to 
  `settings.pro.workspace_id`
  .
- **`ATMOS_PRO_TOKEN`**
  Bearer token obtained via OIDC token exchange (advanced). Maps to 
  `settings.pro.token`
  .
- **`ATMOS_PRO_RUN_ID`**
  CI/CD run identifier. Set automatically in CI environments for tracking.

## Authentication

### GitHub OIDC Token Exchange

For GitHub Actions workflows, use OIDC token exchange for secure, token-less authentication. This is the recommended approach.

**File:** `atmos.yaml`

```yaml
settings:
  pro:
    workspace_id: "your-workspace-id"
    github_oidc:
      request_url: !env ACTIONS_ID_TOKEN_REQUEST_URL
      request_token: !env ACTIONS_ID_TOKEN_REQUEST_TOKEN
```

In your GitHub Actions workflow:

```yaml
name: Deploy Infrastructure
on: push

permissions:
  id-token: write  # Required for OIDC token request
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Deploy with Atmos
        run: |
          atmos pro lock vpc -s prod/us-east-1
          atmos terraform apply vpc -s prod/us-east-1
          atmos pro unlock vpc -s prod/us-east-1
```

Advanced: Bearer Token Authentication

Atmos Pro does not issue API keys or personal access tokens. The only way to obtain a bearer token is by
performing your own OIDC token exchange against the Atmos Pro API. This is intended for advanced integrations
outside of GitHub Actions, such as custom CI/CD pipelines that handle OIDC flows independently.

**File:** `atmos.yaml`

```yaml
settings:
  pro:
    token: !env ATMOS_PRO_TOKEN
```

Pass the bearer token via environment variable:

```bash
export ATMOS_PRO_TOKEN="<bearer-token-from-oidc-exchange>"
atmos pro lock vpc -s prod/us-east-1
```

## Event Integration

Atmos Pro reacts to GitHub webhook events on your repositories and dispatches
workflows on a per-stack, per-event basis. The contract is defined under
`settings.pro` on each stack (or component): for every event activity you want
Atmos Pro to handle, declare which workflow file to dispatch and which inputs
to pass.

The CLI does not interpret these blocks — they are passed through unchanged in
the payload uploaded by `atmos describe affected --upload` (and `atmos list
instances --upload`). Atmos Pro reads the blocks server-side when the matching
GitHub webhook arrives and dispatches the configured workflow against the
correct SHA.

### Supported events

- **`settings.pro.pull_request`**
  Plan and apply workflows for pull-request lifecycle activities (
  `opened`
  , 
  `synchronize`
  , 
  `reopened`
  , 
  `merged`
  ).
- **`settings.pro.release`**
  Workflows triggered when a GitHub release is published.
- **`settings.pro.drift_detection`**
  Per-stack opt-in for drift detection. Atmos Pro periodically inspects enabled stacks and surfaces drift.
- **`settings.pro.merge_group`**
  Workflows for GitHub merge-queue checks. New in this release — see 
  [Merge Queue Support](#merge-queue-support)
  .

### `settings.pro.pull_request`

Configure workflow dispatch for pull-request activities. Each activity maps a
GitHub PR action to a list of workflows to dispatch. The `merged` activity is
synthesized by Atmos Pro from `pull_request.closed` events with `merged: true`
and is the conventional place to wire your apply workflow.

**File:** `stacks/mixins/atmos-pro.yaml`

```yaml
settings:
  pro:
    enabled: true
    pull_request:
      opened:
        workflows:
          atmos-terraform-plan.yaml:
            inputs:
              component: "{{ .atmos_component }}"
              stack: "{{ .atmos_stack }}"
      synchronize:
        workflows:
          atmos-terraform-plan.yaml:
            inputs:
              component: "{{ .atmos_component }}"
              stack: "{{ .atmos_stack }}"
      reopened:
        workflows:
          atmos-terraform-plan.yaml:
            inputs:
              component: "{{ .atmos_component }}"
              stack: "{{ .atmos_stack }}"
      # `merged` is synthesized from pull_request.closed{merged:true}.
      merged:
        workflows:
          atmos-terraform-apply.yaml:
            inputs:
              component: "{{ .atmos_component }}"
              stack: "{{ .atmos_stack }}"
```

- **`settings.pro.pull_request.opened`**
  Activity fired when a pull request is opened.
- **`settings.pro.pull_request.synchronize`**
  Activity fired when new commits are pushed to a pull request. This is the canonical "plan on every push" trigger and is also the fallback target for merge-queue checks (see 
  [Resolution order](#resolution-order)
  ).
- **`settings.pro.pull_request.reopened`**
  Activity fired when a closed pull request is reopened.
- **`settings.pro.pull_request.merged`**
  Activity fired when a pull request is closed with 
  `merged: true`
  . This is the conventional place to wire the apply workflow. Apply continues to fire here even when the PR is merged through a merge queue.

### `settings.pro.release`

Configure workflow dispatch for GitHub release activities.

**File:** `atmos.yaml`

```yaml
settings:
  pro:
    enabled: true
    release:
      published:
        workflows:
          atmos-terraform-apply.yaml:
            inputs:
              component: "{{ .atmos_component }}"
              stack: "{{ .atmos_stack }}"
```

- **`settings.pro.release.published`**
  Activity fired when a GitHub release is published. Typically wired to an apply workflow scoped to a production stack.

### `settings.pro.drift_detection`

Per-stack opt-in for Atmos Pro's drift detection. Configure once per stack
(usually in `_defaults.yaml`) and override per environment as needed.

**File:** `stacks/deploy/_defaults.yaml`

```yaml
settings:
  pro:
    enabled: true
    drift_detection:
      enabled: true
```

- **`settings.pro.drift_detection.enabled`**

  Enable Atmos Pro drift detection for this stack.

  Effective drift detection also requires the component to be effectively Pro-enabled. An outer
  [`metadata.enabled: false`](/stacks/components/component-metadata#enabled) or
  `settings.pro.enabled: false` disables drift detection regardless of this value — Atmos
  collapses the hierarchy (`metadata.enabled` > `pro.enabled` > `drift_detection.enabled`)
  before upload so a disabled component is never dispatched for drift.
  - **Type:** `boolean`
  - **Default:** `false`

### Merge Queue Support

Atmos Pro reports check status on GitHub merge-queue synthetic commits the same way it does on regular pull-request commits. When a PR enters the merge queue, GitHub creates a synthetic merge commit on a temporary `gh-readonly-queue/<base>/pr-<N>-<sha>` branch and re-runs all required status checks against that new SHA. Atmos Pro creates an "Atmos Pro" check run on that synthetic SHA, watches for the configured workflow to complete, and concludes the check (success / failure / no-affected-stacks neutral). Comment updates are posted to the originating PR so the queue check resolution appears on the same thread.

> **Note**
>
> **Prerequisites.** Full merge-queue support requires **Atmos CLI ≥ 1.218.0**. Earlier CLI versions still work for the empty-diff case (Atmos Pro concludes the check as "No affected stacks workflow detected"), but PRs that actually touch stacks will be reported incorrectly until the CLI is upgraded.

#### How it works

1. **PR opened or synchronized** → Atmos Pro dispatches `pull_request.synchronize.workflows` against the PR head SHA.
2. **PR added to the merge queue** → GitHub creates a synthetic merge commit on `gh-readonly-queue/<base>/pr-<N>-<sha>`. Atmos Pro creates a check run on that synthetic SHA and dispatches the workflow resolved from `merge_group` (see below).
3. **Queue ultimately merges the PR** → GitHub fires `pull_request.closed` with `merged: true` on the originating PR. The existing `pull_request.merged.workflows` (apply) flow runs unchanged. Apply does **not** fire on `merge_group` events.

#### Configure `settings.pro.merge_group`

To get the right outcome on merge-queue synthetic commits, declare a
`merge_group` block alongside `pull_request` and `release` and point it at the
workflow you want dispatched in the queue. Workflow filenames are arbitrary —
Atmos Pro has no way to tell from the filename whether a workflow plans,
applies, or does something else — so the recommended pattern is to be
explicit about which workflow runs in the queue rather than relying on Atmos
Pro to infer one from your `pull_request.synchronize` config.

In most cases the queue should run the **same plan workflow** as
`pull_request.synchronize`, optionally with stricter inputs (fail-on-drift,
required approvals, etc.):

**File:** `stacks/mixins/atmos-pro.yaml`

```yaml
settings:
  pro:
    enabled: true
    pull_request:
      synchronize:
        workflows:
          atmos-terraform-plan.yaml:
            inputs:
              component: "{{ .atmos_component }}"
              stack: "{{ .atmos_stack }}"
    merge_group:
      checks_requested:
        workflows:
          atmos-terraform-plan.yaml:
            inputs:
              component: "{{ .atmos_component }}"
              stack: "{{ .atmos_stack }}"
              # Example: stricter inputs in the queue.
              fail_on_drift: "true"
```

- **`settings.pro.merge_group.checks_requested`**

  Activity fired when GitHub requests checks on a merge-queue synthetic
  commit (the only meaningful merge\_group activity for Atmos Pro). The
  `workflows` map has the same shape as `pull_request.<activity>.workflows`.

#### Resolution order

When Atmos Pro receives a merge-queue trigger for a stack, it resolves the
workflow to dispatch in this order:

1. `settings.pro.merge_group.checks_requested.workflows` if defined — **this
   is the recommended path.**
2. Otherwise, `settings.pro.pull_request.synchronize.workflows` is used as a
   backstop so existing customers do not regress when a queue is enabled.
   Because workflow filenames are arbitrary, this backstop can dispatch a
   workflow that is not appropriate for the queue (e.g. an apply step). Treat
   it as a transitional behavior, not a recommended configuration.
3. Otherwise, no workflow is dispatched (same null behavior as a stack with no
   per-event configuration).

#### Schema notes

- `merge_group` is a **sibling** of `pull_request` / `release` /
  `drift_detection`, not nested under `pull_request`. The events fire on
  different SHAs and have a different action vocabulary, matching GitHub's
  webhook event model.
- The only valid activity under `merge_group` today is `checks_requested`.
  GitHub also fires `merge_group.destroyed`, but Atmos Pro relies on its
  existing stale-uploads reconciler for cleanup, so users do not configure
  anything for it.

#### Migration note

If you are enabling GitHub merge queue on a repository that already uses
Atmos Pro, the right migration is to add a `settings.pro.merge_group` block
to every stack mixin that already has `settings.pro.pull_request`. In most
cases the queue should dispatch the same plan workflow as
`pull_request.synchronize` — declare it explicitly so the queue's check
outcome is deterministic and reviewable from your stack config rather than
inferred from a backstop.

#### See also

- [Managing a merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue) — GitHub documentation
- [`atmos describe affected --upload`](/cli/commands/describe/affected) — uploads affected stacks under `pull_request`, `pull_request_target`, and `merge_group` events

### Workflow input templating

Inputs to dispatched workflows are rendered as Go templates against the
component context. The most common variables:

- `{{ .atmos_component }}` — the affected component name
- `{{ .atmos_stack }}` — the affected stack name
- `{{ .vars.<name> }}` — any value from the component's `vars` section
- `{{ .settings.<name> }}` — any value from the component's `settings` section

Use these to keep workflow definitions DRY across stacks.

## Related Commands
