Skip to main content

CloudFormation Components

Define CloudFormation stacks in stack manifests when you want Atmos to deploy them alongside Terraform, Helm, Kubernetes, Helmfile, Packer, and Ansible components. A CloudFormation component describes which template to deploy, which parameters and capabilities to pass, and how the stack should behave for each environment — all deployed directly through the AWS SDK for Go v2, with no aws CLI or cfn/sam/Rain binary dependency.

Experimental

Available Configuration Sections​

CloudFormation components use the same stack sections as other Atmos components, so the stack can inherit values, run hooks, use Auth, declare dependencies, and be included in affected runs.

metadata
Component behavior, inheritance, and base component selection.
vars
Variables available to stack template rendering.
env
Environment variables applied before CloudFormation operations run.
settings

Integration metadata and legacy dependency settings, including settings.aws_cloudformation.region (see Region Resolution).

hooks

Lifecycle event handlers. diff/plan, apply/deploy, delete, drift detect, and drift describe fire before/after events (before.aws/cloudformation.diff, after.aws/cloudformation.apply, and so on) — every other verb, including render, validate, output, fmt, tree, logs, watch, changeset *, get *, stackset *, list, backend *, and source *, does not fire hook events.

source

JIT provisioning of a remote template before operations run — see Source Provisioning below.

provision

Delivery targets: the account/region direct-deploy default, kind: aws/s3 (template packaging), or kind: git (a Git deployment repository) — all selectable via apply --target. A fourth kind, kind: aws/stackset, is not selected through apply --target; it's used exclusively by the stackset verb group. See Delivery Targets below for the full picture, including provision.backend.enabled auto-provisioning.

auth
Component-level Atmos Auth providers, identities, and integrations.
dependencies
Cross-component ordering for --all and --affected runs.

CloudFormation components do not support a generate: section — there is no codegen-artifact output the way Terraform generates backend/provider files. They also do not support a plugins: section — there is no chart-style plugin system, unlike native Helm.

CloudFormation-Specific Sections​

template / path (one required)

The CloudFormation template, provided one of two mutually exclusive ways:

  • template — an inline template body, either a literal string (a | block scalar, YAML or JSON) or a structured YAML map. Because it's inline stack config, it flows through Atmos's own {{ }} templating pipeline before being sent to CloudFormation — file-based templates (path:) do not get this.
  • path — a file reference: the path to a template file, relative to the component directory under the base path.

Setting both is an error.

stack_name
The explicit CloudFormation stack name. There is no legacy name-pattern interpolation — set the name you want directly (Go templates are supported, like any other stack field).
parameters

CloudFormation template parameters, as either a YAML map of name to value or a list of ParameterKey/ParameterValue entries (the AWS CLI and Rain parameter-file format, which also works through !include). Values are normalized at the API boundary: scalars are stringified, and list values are comma-joined to match CloudFormation's List<Type>/CommaDelimitedList wire format (the API only accepts strings). Any other shape, or a malformed list entry, is an error that names the problem; Atmos never falls back to the template defaults. Atmos config is the source of truth for every parameter on every deploy, the same declarative stance the Terraform component takes toward variables, so UsePreviousValue is not needed in the map form. The list form accepts UsePreviousValue: true (without a ParameterValue) for the rare update that must keep a stack's current value. To use only the Parameters section of a Rain config file, select it with !include rain-config.yaml .Parameters.

capabilities

Acknowledged IAM capabilities: CAPABILITY_IAM, CAPABILITY_NAMED_IAM, or CAPABILITY_AUTO_EXPAND. Atmos checks each value locally, and an unknown value fails before any AWS call, naming the value and the valid set.

tags
A map of key: value tags applied to the CloudFormation stack (not to be confused with Atmos's own component tags/--tags selection, which is separate).
stack_policy

Protects specific resources from update during UpdateStack. Set stack_policy.file to a stack policy JSON document path, relative to the component directory under the base path. Applied after a successful apply.

role_arn
IAM role ARN that CloudFormation assumes to deploy the stack.
notification_arns
SNS topic ARNs that CloudFormation publishes stack events to.
disable_rollback
Prevents automatic rollback on stack creation failure.
termination_protection

Prevents the stack from being deleted. atmos aws cloudformation delete respects this and fails with an actionable hint instead of silently disabling it — pass --disable-termination-protection to delete anyway. Setting this to false and re-applying does not disable protection: apply only ever turns protection on, never off.

timeout_in_minutes

Accepted for compatibility, but ignored with a warning: the CloudFormation changeset APIs do not support a stack timeout. Atmos stops watching after 60 minutes; this does not cancel the AWS operation.

Example​

stacks/catalog/vpc.yaml
components:
"aws/cloudformation":
vpc:
path: template.yaml
stack_name: "{{ .vars.stage }}-vpc"
parameters:
CidrBlock: "10.0.0.0/16"
AvailabilityZones:
- us-east-1a
- us-east-1b
capabilities:
- CAPABILITY_IAM
tags:
team: platform
stack_policy:
file: stack-policy.json
termination_protection: true
timeout_in_minutes: 30
dependencies:
components:
- vpc-flow-logs

Inline templates skip the on-disk file entirely and support two forms — a literal string body, or a structured map:

stacks/catalog/bucket.yaml
components:
"aws/cloudformation":
bucket-inline-string:
stack_name: "{{ .vars.stage }}-bucket"
template: |
AWSTemplateFormatVersion: '2010-09-09'
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: "{{ .vars.stage }}-bucket"

bucket-inline-map:
stack_name: "{{ .vars.stage }}-bucket-map"
template:
AWSTemplateFormatVersion: "2010-09-09"
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: "{{ .vars.stage }}-bucket-map"

Component Directory Structure​

CloudFormation components are located under components."aws/cloudformation".base_path from atmos.yaml (defaults to components/cloudformation):

components/cloudformation/
└── vpc/
├── template.yaml
└── stack-policy.json

Source Provisioning​

Point a component at a remote template through the top-level source: section — the same JIT vendoring used by other component types — instead of committing the template to your infrastructure repository. Two shapes are supported:

Directory/subdirectory source

Any go-getter URI (Git, HTTP archive, S3, OCI) pointing at a directory. The component directory (template, stack policy, and any local assets) is vendored, and path: resolves relative to it, exactly like other component types' source: behavior.

components:
"aws/cloudformation":
vpc:
source:
uri: github.com/acme/cfn-templates.git//vpc?ref={{ .Version }}
version: 1.2.0
path: template.yaml
Single-file source

When the source.uri resolves to exactly one file — a bare template URI, with no surrounding directory structure — Atmos fetches it directly as the component's path: file. A CloudFormation component is often exactly one file, and demanding a directory structure around it would be ceremony.

components:
"aws/cloudformation":
dns:
stack_name: dns-dev
source:
uri: https://raw.githubusercontent.com/acme/cfn-templates/v1.2.0/dns.yaml

path: does not need to be set in the single-file case — Atmos names the vendored file after the source URI's basename and inspects the provisioned result. Exactly one regular root file is inferred; ambiguous or nested contents require an explicit path. Provisioner metadata files are ignored. Deployed reads and deletion do not fetch the source; dry-run defers inference.

Manifest-driven vendoring is not supported

aws/cloudformation components cannot be vendored through a vendor.yaml manifest (atmos vendor pull) — only the source:-based JIT provisioning described above, the same restriction native Helm and Kubernetes components have. Use source: for every CloudFormation component that isn't authored directly in your infrastructure repository.

Delivery Targets​

By default, apply/deploy deploy directly to the account/region resolved for the component (see Region Resolution) via CreateChangeSet/ExecuteChangeSet — this is the implicit behavior when --target is omitted and no provision.default is set. A component can declare additional named targets under provision.targets, selected with --target:

kind: aws/cloudformation

The direct-deploy target. It is implicit — named default, with no configuration — whenever nothing else is selected, so most components never declare it. Declare it explicitly when it needs configuration, which today means the packaging: field described in Choosing the Packaging Bucket, or when you want a name to select with --target or provision.default.

components:
"aws/cloudformation":
vpc:
provision:
default: direct
targets:
direct:
kind: aws/cloudformation
packaging: artifacts
artifacts:
kind: aws/s3
bucket: my-cfn-artifacts
region: us-east-1

The provision.default field names the target used when --target is omitted, and --target overrides it for one run.

kind: aws/s3

Uploads the template to S3 without deploying a stack. Selecting the packaged target below with --target packaged only uploads the template, for example for a review step.

During a direct deployment, Atmos uses the S3 target as a packaging destination when the template exceeds CloudFormation's 51,200-byte inline size limit, then deploys the uploaded template. To deploy, omit --target when no provision.default is set, or select a direct kind: aws/cloudformation target. bucket and region are both required: region builds the https:// S3 URL passed as CreateChangeSet's TemplateURL (AWS rejects a bare s3:// URI there), so it can't be left to be inferred later.

components:
"aws/cloudformation":
vpc:
provision:
targets:
packaged:
kind: aws/s3
bucket: my-cfn-artifacts
prefix: templates
region: us-east-1

Atmos uploads each template to a content-addressed key, <prefix>/<stack>/<component>/template-<digest>.yaml, next to a .metadata.json sidecar. Leading and trailing slashes on prefix are trimmed, so prefix: /templates/ and prefix: templates produce the same keys. When an object with the same key and digest already exists, Atmos reuses it instead of uploading again, so repeated runs on a versioned bucket do not accumulate object versions. A publish-only apply --target <name> reports the uploaded location as an s3:// URI and an https:// URL.

The bucket must exist before apply/deploy uploads to it — either provision it explicitly with atmos aws cloudformation backend create, or set provision.backend.enabled: true (a sibling of targets, not nested under a target) to have apply/deploy create it automatically the first time it's missing. Auto-provisioning only runs the existence check up front — it never reconciles a bucket that already exists, unlike backend create/update run explicitly, which always re-applies the secure defaults (versioning, encryption, public access blocking, tags).

components:
"aws/cloudformation":
vpc:
provision:
backend:
enabled: true
targets:
packaged:
kind: aws/s3
bucket: my-cfn-artifacts
region: us-east-1

Only apply and deploy create the bucket. The validate, diff, and changeset create verbs also upload an oversized template, but they never create infrastructure: when the bucket is missing they fail with a hint to run backend create or apply.

kind: git

Commits the template YAML to a Git repository (declared in git.repositories) instead of deploying it — for example, a review or GitOps-style pipeline that applies from the committed template separately. Follows the same provision.targets shape used by native Helm and Kubernetes components.

kind: aws/stackset

Delivers to a multi-account/multi-region CloudFormation StackSet instead of a single-account/region stack. Unlike kind: aws/s3 and kind: git, an aws/stackset target is not selected through apply --target — it is used exclusively by the atmos aws cloudformation stackset verb group, and only by create/update, which resolve it to learn the target accounts/regions/permission model. delete and instances act directly on stack_name (the StackSet's own name) and don't resolve or require a provision.targets entry.

components:
"aws/cloudformation":
vpc:
provision:
targets:
multi-account:
kind: aws/stackset
accounts:
- "111111111111"
- "222222222222"
regions:
- us-east-1
- us-west-2
permission_model: SELF_MANAGED
administration_role_arn: arn:aws:iam::111111111111:role/AWSCloudFormationStackSetAdministrationRole
execution_role_name: AWSCloudFormationStackSetExecutionRole
accounts
Target AWS account IDs for stack instances.
regions
Target AWS regions for stack instances.
permission_model
SELF_MANAGED (default) or SERVICE_MANAGED. Atmos supports instance targeting only with SELF_MANAGED. A SERVICE_MANAGED target with both accounts and regions is rejected before the StackSet is created. Empty service-managed StackSets can be created and deleted; manage their instances with AWS CloudFormation.
administration_role_arn
IAM role ARN CloudFormation assumes to create/manage the StackSet (self-managed permissions).
execution_role_name
IAM role name CloudFormation assumes in each target account (self-managed permissions).

stackset create creates the StackSet's initial stack instances only when both accounts and regions are set; stackset update updates the template/parameters/capabilities and propagates the change to every existing instance, without changing which accounts/regions have one.

Choosing the Packaging Bucket​

Packaging uploads an oversized template through a kind: aws/s3 target. Atmos picks that target as follows:

  • When the selected target is itself kind: aws/s3, that target is used.
  • When the component declares exactly one kind: aws/s3 target, it is used automatically.
  • When the component declares two or more, the selected deploy target must name one with packaging: <target-name>. Atmos never guesses.

The implicit direct-deploy target has no configuration, so it cannot carry packaging:. With two or more kind: aws/s3 targets, declare the deploy target explicitly and make it the default:

components:
"aws/cloudformation":
vpc:
provision:
default: direct
targets:
direct:
kind: aws/cloudformation
packaging: artifacts
artifacts:
kind: aws/s3
bucket: my-cfn-artifacts
region: us-east-1
archive:
kind: aws/s3
bucket: my-cfn-archive
region: us-east-1

Naming the explicit target default works too and replaces the implicit one, so provision.default is not needed. A kind: git deploy target takes the same packaging: field.

Dry Run​

A dry run (--dry-run) validates the configuration statically and makes no AWS calls. It applies the same rules the real run enforces before it changes anything: an unknown --target, a kind: aws/s3 target without bucket or region, a template over the 51,200-byte inline limit with no packaging target, and for StackSets an unknown permission_model or a SERVICE_MANAGED target that sets both accounts and regions. Values that come from templates or YAML functions are not evaluated, so they are not judged. The output names the verb, component, and stack, for example Dry run: apply vpc in plat-ue2-dev: configuration validated; no AWS calls made.