# Atmos Hooks

> Atmos hooks: lifecycle events, hook kinds, command/store/git/security hooks, step/steps hooks, when: conditions, scoping and overrides, toolchain integration, --skip-hooks, and Atmos Pro/local output

- Skill: `cloudposse/atmos-hooks` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cloudposse/atmos-hooks`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cloudposse/atmos-hooks/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: cloudposse (https://skillmd.com/u/cloudposse)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cloudposse/atmos-hooks

---


# Atmos Hooks

Use this skill for lifecycle hooks that run before or after component operations,
or for generation hooks declared in a scaffold template.

Hooks can run scanners, policy checks, store writes, Git actions, custom commands, or other
toolchain-aware automation around Terraform, Helm, Kubernetes, and other component commands.

## Related Skills

| Need | Load |
|---|---|
| Store output hooks | [atmos-stores](../atmos-stores/SKILL.md) |
| Shared step fields and `kind: step` payloads | [atmos-steps](../atmos-steps/SKILL.md) |
| Post-deployment smoke tests and integration checks | [atmos-tests](../atmos-tests/SKILL.md) |
| Git hooks and GitOps repositories | [atmos-git](../atmos-git/SKILL.md) |
| Tool installation for hook commands | [atmos-toolchain](../atmos-toolchain/SKILL.md) |
| CI summaries and Atmos Pro upload | [atmos-ci](../atmos-ci/SKILL.md) and [atmos-pro](../atmos-pro/SKILL.md) |

## Hook Shape

Hooks are configured in stack manifests at global, component-type, or component scope.

```yaml
hooks:
  store-vpc-outputs:
    events:
      - after.terraform.apply
    kind: store
    name: prod/ssm
    outputs:
      vpc_id: .vpc_id

components:
  terraform:
    vpc:
      hooks:
        scan-plan:
          events:
            - after.terraform.plan
          kind: trivy
```

Modern dotted event names such as `after.terraform.plan` are preferred. Legacy hyphenated event
names may appear in older stacks; modernize them when editing nearby config.

## Lifecycle Events

Use before/after events for component operations, for example:

- `before.terraform.init`, `after.terraform.init`
- `before.terraform.plan`, `after.terraform.plan`
- `before.terraform.apply`, `after.terraform.apply`
- `before.terraform.deploy`, `after.terraform.deploy`
- `before.terraform.test`, `after.terraform.test`
- `before.terraform.output`, `after.terraform.output` — fires for `atmos terraform output`,
  useful for backfilling a store from already-deployed infrastructure without an `apply`
- `before.terraform.refresh`, `after.terraform.refresh`

Kubernetes provides `before`/`after` events for `render`, `diff`/`plan`, `apply`/`deploy`,
`delete`, and `validate`. Native Helm provides `template`, `diff`, `apply`/`deploy`, and
`delete`; Helmfile provides `template`, `diff`, `apply`/`sync`/`deploy`, and `destroy`.
Use the canonical dotted events and remember that command aliases normalize to their execution
event (`deploy` to `apply`, Kubernetes `plan` to `diff`, and Helmfile `sync` to `apply`).

Scaffold templates use the separate `before.scaffold.generate` and
`after.scaffold.generate` events. They reuse the condition vocabulary but can run only
`kind: step` and `kind: steps`; do not configure stack-only kinds in `spec.hooks`.

Multi-component DAG runs (e.g. `--affected`, `--query`, or workflows that fan out across several
components) also fire aggregate events once for the whole run, in addition to the per-component
events fired for each individual component: `after.terraform.plan.aggregate`,
`after.terraform.apply.aggregate`, and `after.terraform.destroy.aggregate`. Use a per-component event
for component-specific behavior (scans, store writes) and an aggregate event for run-level summaries
or notifications that should fire only once.

## Conditional Execution with `when`

Hooks share the same `when:` condition engine as workflow steps: predicate keywords (`ci`, `local`,
`always`, `never`, `success`, `failure`) or a CEL expression built from runtime facts such as `stack`
and `component`. For example, restrict a hook to CI runs against the `prod` stack:

```yaml
hooks:
  prod-ci-scan:
    events:
      - after.terraform.plan
    kind: trivy
    when: stack == "prod" && ci
```

See [atmos-workflows](../atmos-workflows/SKILL.md#conditional-execution-with-when) for the full
`when:`/CEL syntax reference.

## Hook Kinds

Stack lifecycle hooks support `command`, `store`, `git`, `infracost`, `trivy`, `checkov`,
`kics`, and the step bridge. The legacy `ci.*` hook kinds still parse but are deprecated no-ops;
use the current CI provider bindings instead. Use a named kind when Atmos has one; use `command`
for a project-specific binary. The legacy `command:` discriminator and hyphenated events remain
compatibility input only; author new configuration with `kind:` and dotted events.

Hooks can use `dependencies.tools` so required scanners or CLIs are installed and placed on `PATH`
for the hook execution context.

When the hook declares the required binary in `dependencies.tools`, do not add a separate
`atmos toolchain install` step. Atmos resolves, installs, and injects the tool before the hook fires.

## Step-Backed Hook Kinds

Hooks can also delegate to the same step-type registry that workflows, custom commands, and cast
recordings use, instead of one of the named kinds above:

- `kind: step` runs **one** registered step type. Set the step type with the hook's `type:` field and
  configure it with `with:`, exactly like a workflow step.
- `kind: steps` runs an ordered list of registered step types, provided as a YAML list under `with:`.

Hook step lists run in order. A `type: test` group can contain `parallel` or
`matrix` checks; see [atmos-tests](../atmos-tests/SKILL.md).

The hook envelope owns `events`, `when`, `env`, `retry`, and `on_failure`; `with:` is
decoded and validated as the step's own configuration. `kind: step` supplies the one
step type through the hook's `type:`; `kind: steps` supplies type-bearing objects in its
ordered `with:` list.

```yaml
hooks:
  check-prereqs:
    events:
      - before.terraform.plan
    kind: step
    type: require
    with:
      tools:
        - kubectl
        - helm

  bring-up-and-plan:
    events:
      - before.terraform.plan
    kind: steps
    with:
      - type: emulator
        command: up
      - type: atmos
        command: terraform plan vpc
```

Use `kind: step`/`kind: steps` when you need a registered step type (`container`, `emulator`,
`require`, `atmos`, `shell`, and other types workflows support) inside a hook; use the older named
kinds (`trivy`, `checkov`, `kics`, `infracost`) when Atmos already ships a purpose-built scanner
integration for the job.

### Working directory in `kind: step`/`kind: steps`

A step's relative paths (`source`, `destination`, `path`, `files`, `context`, and other
step-specific fields) resolve against `with: { working_directory: ... }` when set, or the
component's own working directory when unset. This is the only surface where `working_directory`
is ever component-relative -- workflows and custom commands always resolve relative values against
the current working directory, since neither is scoped to a single component.

An explicit `working_directory:` value resolves differently depending on its shape:

| Value | Resolves against |
|---|---|
| Not set | The component's working directory (a provisioned/vendored working copy when one exists, otherwise the in-repo source directory) |
| `.`, `..`, `./foo`, `../foo` | The directory Atmos was run from |
| `foo`, `foo/bar` | The component's working directory + `foo` (or `foo/bar`) |
| `/absolute/path` | Used as-is |

A plain relative value (`foo`) behaves like the unset default -- component-relative. A `./`- or
`../`-prefixed value is an explicit signal to anchor to the directory Atmos was run from instead.

## Operational Guidance

- Use hooks for repeatable lifecycle behavior, not one-off local scripts.
- Scope hooks as narrowly as possible: component hooks for component-specific behavior, shared
  mixins/defaults for organization-wide checks.
- Use `--skip-hooks` to bypass all hooks for a diagnostic run, or `--skip-hooks=name1,name2` to skip
  specific hooks by name. This flag is registered on the `terraform` command only today; there is no
  helmfile or packer equivalent yet.
- Treat hook output as part of CI evidence. When Atmos Pro is connected and the hook kind supports
  upload, prefer structured upload; otherwise rely on local/CI summaries.
- Keep destructive hooks opt-in and visible in stack config.

