Skip to main content

CloudFormation Components

Configure CloudFormation templates, parameters, and delivery targets in Atmos stack manifests. Components use the same inheritance, authentication, and dependencies as other Atmos component types.

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 hooks for diff/plan, apply/deploy, delete, drift detect, and drift describe. Other CloudFormation commands do not fire hooks. See CloudFormation lifecycle hooks.
source

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

provision
Delivery targets, automatic artifact-bucket provisioning, and source workdirs. See Delivery Targets and Workdirs.
auth
Component-level Atmos Auth providers, identities, and integrations.
dependencies
Cross-component ordering for --all and --affected runs.

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, Atmos renders it with Go templates (when templates.settings.enabled: true) and evaluates YAML functions in it before sending it to CloudFormation. See Inline Templates and Templating.
  • path — a file reference: the path to a template file, relative to the component directory under the base path. Atmos uses the file verbatim: it does not render Go templates or YAML functions in it. A file that still contains Rain directives (!Rain::Env, !Rain::Embed, ${Rain::Constant}) is rejected before any AWS call, with the Atmos-native replacement named for each directive; see Migrating from Rain. To keep the file but use YAML functions inside it, use template: !include <file> | eval instead.

Setting both is an error.

stack_name
The CloudFormation stack name. Supports Go templates, such as "{{ .vars.stage }}-vpc". Set the existing stack name when migrating a deployment.
parameters
Template parameters as a YAML map or a list of ParameterKey/ParameterValue entries. See Parameters for value conversion, file imports, and update behavior.
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). tags must be a map of tag names to string values. A list is rejected before any AWS call, whether it is the AWS CLI [{"Key": ..., "Value": ...}] form or the string list that !tags produces, so a stack is never deployed silently untagged. To reuse an existing AWS CLI tags file, reshape it into a map with !include:

tags: !include 'tags.json ".[] as $t ireduce ({}; .[$t.Key] = $t.Value)"'

To carry Atmos metadata.labels onto the stack, set tags: !labels. The values of metadata.tags and metadata.labels never flow into stack tags automatically; each stack tag has to be declared here.

stack_policy

Protects specific resources from update during UpdateStack. Provide the policy one of two mutually exclusive ways; setting both file and body is a validation error:

  • stack_policy.file — the path to a stack policy JSON document, relative to the component directory under the base path. Atmos uses the file verbatim.
  • stack_policy.body — an inline stack policy, either a JSON string or a YAML map. A map is serialized to JSON; a string is used as written. An inline body needs no component directory, and Atmos renders it with Go templates and YAML functions the same way it renders an inline template.

Atmos installs the policy before it executes an update, so the policy governs that update. For a new stack, it installs the policy after the create succeeds.

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

Parameters​

Use a map for named parameter values:

parameters:
Environment: dev
AvailabilityZones: [us-east-1a, us-east-1b]

Atmos converts scalar values to strings and joins list values with commas. An invalid parameter shape or a null value is an error. Use "" for an intentional empty string. If an output dependency is unavailable, deploy its component first or supply an explicit fallback.

You can also import an AWS CLI parameter array or the Parameters section of a Rain configuration file:

parameters: !include dev-parameters.json
# For a Rain configuration file:
# parameters: !include rain-config.yaml .Parameters

Atmos uses the configured values on each deployment. To retain a deployed value on an update, use the list form with UsePreviousValue: true and omit ParameterValue for that entry:

parameters:
- ParameterKey: Environment
ParameterValue: dev
- ParameterKey: DatabasePassword
UsePreviousValue: true

Lifecycle Hooks​

These commands fire component hooks:

CommandsEvent suffix
diff, planaws/cloudformation.diff
apply, deployaws/cloudformation.apply
deleteaws/cloudformation.delete
drift detectaws/cloudformation.drift-detect
drift describeaws/cloudformation.drift-describe
changeset createaws/cloudformation.changeset-create
changeset executeaws/cloudformation.changeset-execute

Prefix the suffix with before. or after., such as before.aws/cloudformation.apply. The changeset events are separate from apply, so a hook listed only for apply does not fire around changeset create or changeset execute. Other CloudFormation commands, including changeset list and changeset delete, do not fire hooks. Use --skip-hooks to skip all hooks, or --skip-hooks=name1,name2 to skip selected hooks. See Hooks.

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

Inline Templates and Templating​

An inline template: is stack configuration, so Atmos renders it before it sends the body to CloudFormation. Two mechanisms apply:

  • Go templates — values such as {{ .vars.stage }} and the rest of the template context, when templates.settings.enabled: true in atmos.yaml.
  • YAML functions — !env, !store, !aws.cloudformation.output, and the other YAML functions, evaluated like in any other stack value.

A template file referenced by path: is used verbatim. Atmos does not render Go templates or YAML functions in it, so a {{ }} sequence in a file stays literal. Use an inline template: whenever a template needs per-stack values. Go templates work in both the string and map forms; YAML functions apply to values in the map form. CloudFormation's short-form intrinsics (!Ref, !Sub, !GetAtt, !Join, and the rest) are accepted in the map form and rewritten to their long form (Ref:, Fn::Sub:, Fn::GetAtt:) before the template is sent, so you can write either spelling:

stacks/catalog/marker.yaml
components:
aws/cloudformation:
marker:
stack_name: "{{ .vars.stage }}-marker"
template:
AWSTemplateFormatVersion: "2010-09-09"
Resources:
Marker:
Type: AWS::SSM::Parameter
Properties:
Name: "/atmos/{{ .vars.stage }}/marker"
Type: String
Value: !env MARKER_VALUE
Outputs:
ParameterName:
Value: !Ref Marker
stack_policy:
body:
Statement:
- Effect: Allow
Action: "Update:*"
Principal: "*"
Resource: "*"

Keep the template in a file and still use YAML functions​

To keep a template as a file on disk and still use Atmos YAML functions inside it, include the file into template: with the | eval option. The file keeps its CloudFormation short-form intrinsics, and every Atmos function in it is evaluated as if it were written in the manifest:

stacks/catalog/app.yaml
components:
aws/cloudformation:
app:
stack_name: "{{ .vars.stage }}-app"
template: !include ../../components/cloudformation/app/template.yaml | eval
components/cloudformation/app/template.yaml
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "app-${Stage}"
Role: !GetAtt Role.Arn
Code:
ZipFile: !include ./src/handler.py
Tags: !include ./tags.yaml
Marker:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub "/app/${Stage}/owner"
Type: String
Value: !env DEPLOY_OWNER

A nested !include inside the evaluated template resolves ./ and ../ relative to the template file itself (bare paths relative to base_path), so the template can reference files next to it the way Rain's !Rain::Embed did. A non-YAML file loads as a string, which is how a script lands in ZipFile. Without | eval, an included file is data: its CloudFormation intrinsics are still preserved, but an Atmos function tag inside it is dropped and only its argument remains, per the !include contract. The | eval option is a deliberate opt-in because it lets the included file run any YAML function, including !exec.

CloudFormation has its own dynamic references that use the same {{ }} syntax, such as {{resolve:ssm:/my/param}}. Escape the opening braces once so that Atmos emits them literally and CloudFormation receives the reference:

template: |
Resources:
Param:
Type: AWS::SSM::Parameter
Properties:
Type: String
Value: '{{ "{{" }}resolve:ssm:/my/param}}'

Asset Packaging Hooks​

Use the archive step in a kind: steps hook to package local code, followed by a publish step to upload it. The example requires an existing S3 bucket accessible to the component's credentials.

Add these fields to the CloudFormation component. Its template must declare ArtifactBucket and ArtifactKey parameters and use them for the Lambda resource's Code.S3Bucket and Code.S3Key:

vars:
artifact_bucket: acme-lambda-artifacts
artifact_key: lambda/releases/v1/handler.zip
parameters:
ArtifactBucket: "{{ .vars.artifact_bucket }}"
ArtifactKey: "{{ .vars.artifact_key }}"
hooks:
package-lambda:
events: [before.aws/cloudformation.diff, before.aws/cloudformation.apply]
kind: steps
on_failure: fail
with:
- type: archive
source: src/
destination: handler.zip
mtime: epoch
- type: publish
source: handler.zip
destination: "{{ .vars.artifact_key }}"
target:
kind: aws/s3
bucket: "{{ .vars.artifact_bucket }}"
region: us-east-1

Enable Go templating in atmos.yaml for the expressions above. Relative paths resolve from the component directory. Use a new artifact key for each release so a code change also changes the CloudFormation parameter.

These hooks run for diff/plan and apply/deploy, so a real preview also builds and uploads the archive. Dry-run skips hooks. To package the artifact when you create or execute a named changeset, add the changeset events to the same hook so the changeset never references an artifact that was not uploaded:

hooks:
package-lambda:
events:
- before.aws/cloudformation.diff
- before.aws/cloudformation.apply
- before.aws/cloudformation.changeset-create

List before.aws/cloudformation.changeset-execute as well if the changeset might be created by a different run and you want the archive rebuilt on execute.

The upload uses the component's resolved AWS identity. An S3 delivery target's identity applies to template delivery, so grant the component identity access to the artifact bucket as well. Unchanged artifacts are skipped; template asset references must still be configured explicitly.

For a runnable build, test, publish, and deploy pipeline, see the advanced CloudFormation example. Its after.aws/cloudformation.apply hook uses the http step to verify the deployed Lambda's response in Floci.

Source Provisioning​

Use source to download a template or component directory before an operation needs its local files. Manage downloads with the source commands.

Source Formats​

A source accepts a URI string or a map:

source: github.com/acme/cfn-templates.git//vpc?ref=1.2.0
stacks/catalog/vpc.yaml
components:
aws/cloudformation:
vpc:
stack_name: vpc-dev
source:
uri: github.com/acme/cfn-templates.git//vpc
version: 1.2.0
path: template.yaml

A directory source downloads the template, stack policy, and other files. The component's path is relative to that directory.

A single-file source can omit path:

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

Atmos uses the URI's filename for a single-file download. After provisioning, it infers path only when the result contains one regular file at its root, excluding provisioner metadata. Set path explicitly for nested or ambiguous contents. Dry-run does not download files or infer a path.

Source Fields​

uri (required in map form)
Source location. Supports Git, HTTP archives or files, S3, and OCI sources.
version
Git tag, branch, or commit. Appended as ?ref= when the URI has no ref. For HTTP, S3, or OCI sources, put the version in the URI instead.
included_paths
Glob patterns selecting files to copy. Omit to include all files.
excluded_paths
Glob patterns excluding files after inclusion filters are applied.
ttl
Cache duration, such as "0s", "1h", "7d", or "daily". See Caching and Refresh.
retry
Download retry settings: max_attempts, initial_delay, max_delay, backoff_strategy (exponential, linear, or constant), multiplier, random_jitter, and max_elapsed_time.

Private S3 Sources​

Use an S3 URI for authenticated downloads:

source:
uri: s3://my-cfn-templates/vpc.yaml?region=us-east-2

The s3::https://s3.us-east-2.amazonaws.com/bucket/key form also supports S3 authentication. A plain https:// URI uses HTTP retrieval.

Downloads use the component's resolved AWS identity, including downloads needed by render and fmt. Credentials are scoped to the download request. Configure credentials through Atmos Auth; source URLs reject the aws_profile, aws_access_key_id, aws_access_key_secret, and aws_access_token query parameters.

Bucket Region and Access Errors​

Set ?region= when the bucket is outside the identity's default region. A region-mismatch error identifies the bucket's region.

For an S3 403 response, check both object permissions and the object key. S3 can return 403 for a missing key when the identity lacks s3:ListBucket.

Caching and Refresh​

Set source.ttl on the component to refresh remote sources after an interval. Use "0s" to refresh on each invocation. A component value overrides the project default in components."aws/cloudformation".source.ttl:

atmos.yaml
components:
aws/cloudformation:
source:
ttl: 1h

A fresh cached source needs no download authentication. Without a TTL, shared component directories are reused until an explicit refresh. Workdirs also refresh when the source URI or version changes.

atmos aws cloudformation source pull vpc -s dev --force

Shared directories use the provisioning timestamp recorded by Atmos for TTL expiry. For an older download without that record, run source pull --force once to enable automatic refresh. Local shared directories are not refreshed by a remote-source TTL.

Operations that use a local template download the source when needed. Reads of a deployed stack and stack deletion do not download it. Named changeset execution may still need source files for a configured stack policy.

Component Directories​

Sources are downloaded under the configured CloudFormation base_path (default: components/cloudformation). The directory name comes from metadata.component, or the instance name when no base component is set. Instances using the same base component share that directory.

The component path must be relative and remain inside the components directory. The source pull and source delete commands use this same destination.

Archives containing symbolic or hard links produce empty regular files in place of those links. Atmos logs a warning naming the first link.

Workdirs​

Set provision.workdir.enabled: true to download a source into a separate workdir for each component instance:

source:
uri: github.com/acme/cfn-templates.git//vpc?ref=1.2.0
provision:
workdir:
enabled: true

CloudFormation workdirs require source. Enabling a workdir for a local component without a source fails validation. Remove the setting or add a source.

Vendoring manifests

CloudFormation components use source for remote templates. They do not support vendor.yaml manifests with atmos vendor pull.

Delivery Targets​

A delivery target controls where Atmos sends a template. Select a named target with --target, or set provision.default. Without either, Atmos deploys directly to CloudFormation using the implicit default target.

kind: aws/cloudformation
Deploy a stack. An explicit target accepts kind, auth, and packaging. Set the stack region through settings.aws_cloudformation.region.
kind: aws/s3
Upload the template without deploying a stack. Also supplies the packaging bucket for templates over the 51,200-byte inline limit.
kind: git
Commit the template to a repository declared in git.repositories. A separate pipeline can review and deploy the committed template.
kind: aws/stackset
Configure multi-account and multi-region StackSets. Use the stackset commands; this kind cannot be selected with apply --target.

Direct Deployment​

Declare a direct target when you need a target-specific identity or packaging bucket:

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

The --target flag overrides provision.default for one invocation. Naming a direct target default replaces the implicit target.

S3 Targets​

bucket (required)
Bucket to receive the template.
region (required for template uploads)
Bucket region. Atmos uses it to construct the HTTPS template URL passed to CloudFormation.
prefix
Object-key prefix. Leading and trailing slashes are removed.
provision:
targets:
artifacts:
kind: aws/s3
bucket: my-cfn-artifacts
region: us-east-1
prefix: templates

Uploads use <prefix>/<stack>/<component>/template-<digest>.yaml and a .metadata.json sidecar. Atmos reuses an object when its key and digest match, so repeated uploads do not create extra versions of identical templates. A publish-only apply --target artifacts reports both the S3 URI and HTTPS URL.

Packaging uploads the template body. It does not upload local Lambda archives or rewrite nested-template paths. Use asset-packaging hooks to build and upload them, and reference their S3 locations in the template.

Automatic Bucket Provisioning​

Create the bucket with backend create, or enable automatic creation:

provision:
backend:
enabled: true
targets:
artifacts:
kind: aws/s3
bucket: my-cfn-artifacts
region: us-east-1

Only apply and deploy create missing buckets automatically. This check does not change an existing bucket. Explicit backend create and backend update reapply versioning, encryption, public-access blocking, and tags.

The validate, diff/plan, and changeset create commands can upload oversized templates, but do not create buckets. Create the bucket before running them.

When the bucket does not exist and provision.backend.enabled is not set, every command that packages a template (plan, diff, validate, changeset create, apply, and deploy) stops before uploading. Each reports that the packaging bucket does not exist and suggests running atmos aws cloudformation backend create or setting provision.backend.enabled: true.

StackSet Targets​

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. Quote each ID as a 12-digit string.
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 fails validation. Manage service-managed instances through AWS CloudFormation.
administration_role_arn
Administration role ARN for self-managed StackSets.
execution_role_name
Role name assumed in each target account for self-managed StackSets.

The stackset create command creates instances when both accounts and regions are configured. The stackset update command updates existing instances without changing their account or region membership.

Only stackset create and stackset update resolve a target. The delete and instances subcommands use stack_name directly. See StackSet target resolution.

Choosing the Packaging Bucket​

Atmos selects the S3 packaging target in this order:

  1. Use the selected target when its kind is aws/s3.
  2. Use the only S3 target when exactly one is declared.
  3. With multiple S3 targets, use the selected deployment target's packaging field.

The implicit direct target cannot specify packaging. With multiple buckets, declare an explicit direct target:

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

Git targets also accept packaging. An unresolved or ambiguous packaging target is an error.

Dry Run​

Use --dry-run to validate static configuration without authentication, source downloads, hooks, template evaluation, or AWS calls.

Validation checks known values for:

  • Target names, kinds, and authentication settings.
  • Required S3 bucket and region settings.
  • Packaging for a template that exceeds the inline size limit.
  • StackSet permission models, account IDs, and regions.

Checks that require Go templates or YAML functions are deferred. See execution controls.

Target Authentication​

A target's auth block selects the identity for that destination. An explicit --identity or ATMOS_IDENTITY overrides target defaults for direct commands. Output references have a separate rule, described below.

provision:
targets:
artifacts:
kind: aws/s3
bucket: shared-cfn-artifacts
region: us-east-2
auth:
identity: shared-services

Define shared-services in global or component auth, or declare it fully under the target's auth.identities. You can also select an identity by marking it default: true in that map.

Identity by Operation​

OperationIdentity used
Source downloadComponent's resolved identity
S3 upload, automatic bucket creation, backend commandsS3 target's identity
Direct stack operationsSelected or default aws/cloudformation target's identity
Cross-component output referenceProducer's default direct target's declared identity; see Output references
stackset create, stackset updateSelected StackSet target's identity
stackset delete, stackset instancesComponent/CLI identity; pass --identity for another account
Git deliveryTarget identity, or repository identity when no target/CLI override is set

Target authentication is isolated from component hooks and secret lookups. An authentication failure stops the command; Atmos does not retry with the component's credentials.

Validation​

Atmos validates the selected target's auth block and every direct target's configuration keys before AWS calls, including during dry-run.

  • Use supported auth keys. A misspelling such as identitty is an error.
  • Select an identity with auth.identity, default: true, or --identity when declaring auth.identities.
  • Direct targets accept only kind, auth, and packaging. Set their region on the component, using region resolution.

Errors identify the affected target, component, stack, and identity and provide a correction hint.

Output references​

Both !aws.cloudformation.output and atmos.Component(...).outputs use the producer's default direct target identity. That declared identity takes precedence over the consumer's --identity or ATMOS_IDENTITY.

When the producer's target declares no auth, the lookup uses the caller's credentials. Authentication failures do not fall back to a same-named stack in another account. See the output function reference.

Disabling authentication​

Use --identity=false or ATMOS_IDENTITY=false to disable Atmos authentication. The AWS SDK default credential chain then applies. Atmos warns once per target when bypassing a declared identity.