CloudFormation change sets, drift, import and cross-stack references
Read a change set (Action, Replacement True/Conditional/False, property-level before and after), detect drift and what it can't see, revert drift with drift-aware change sets (REVERT_DRIFT), import existing resources (DeletionPolicy required, no other changes), how DeletionPolicy behaves on delete versus replacement, and exports versus Fn::GetStackOutput for cross-stack, cross-account and cross-Region references.
markdownreferencev4M64Q-6G5Mstage: buildtrust: officialservice: cloudformationinterface: cliinterface: sdkinterface: iacchange setdescribe-change-setinclude-property-valuesReplacement ConditionalRequiresRecreationEvaluation Dynamicdrift detectionDRIFTEDMODIFIEDNOT_CHECKEDdrift limitationsdrift-aware change setREVERT_DRIFTSyncWithActualresource importIMPORT change setDeletionPolicyRetainSnapshotRetainExceptOnCreatereplacement deletes old resourceExportFn::ImportValuelist-importsFn::GetStackOutputcross-account stack outputcross-region reference
Change sets, drift, import and cross-stack references
Before executing a change set, read its Replacement column. True means CloudFormation creates a new resource and deletes the old one, whatever its DeletionPolicy says. For a database or a bucket, that is data loss.
Reading a change set
aws cloudformation describe-change-set --stack-name s --change-set-name c --include-property-values
| Field | Values and meaning |
|---|---|
Action | Add, Modify (may interrupt or replace), Remove, Import, Dynamic (nested stacks only: the action can't be known in advance), SyncWithActual (drift-aware change sets: drift status reset, resource unchanged) |
Replacement (Modify only) | True: always replaced. Conditional: replacement depends on a value only known at deploy time (RequiresRecreation: Always with Evaluation: Dynamic). False: updated in place. When several changes apply, the one with the most impact wins (Always > Conditional > Never) |
Scope | What changes: Properties, Tags, … |
Details[].Target | Path (e.g. /Properties/Description), BeforeValue, AfterValue, AttributeChangeType (Add, Modify, Remove) with --include-property-values |
A change set is a preview, not a promise. It runs pre-deployment checks (syntax, name conflicts, quotas), but runtime failures such as custom resource logic still happen at execution. Executing one change set deletes the stack's other change sets.
DeletionPolicy
| Value | On stack delete, or when the resource is removed from the template |
|---|---|
Delete (default) | Deleted |
Retain | Kept, no longer managed, and still billed |
Snapshot | Snapshot, then delete. Supported on EBS volumes, RDS instances and clusters, DocumentDB, Neptune, ElastiCache clusters and replication groups, and Redshift |
RetainExceptOnCreate | Kept, except when the operation that created it rolls back |
- Defaults that differ:
AWS::RDS::DBCluster, and anAWS::RDS::DBInstancewithoutDBClusterIdentifier, default toSnapshot. - None of these apply on replacement. The old physical resource is always deleted then.
- Retained resources with fixed names cause the next deploy to fail with
already exists. Import them or rename (reference/cloudformation/errors.md).
Drift detection
aws cloudformation detect-stack-drift --stack-name s starts it, describe-stack-drift-detection-status --stack-drift-detection-id <id> reports progress, and describe-stack-resource-drifts --stack-name s lists the drifted resources. (The command names come from the SDK operations; the user-guide page shows only the console.)
- Stack:
DRIFTED,IN_SYNCorNOT_CHECKED. Resource:MODIFIED,DELETED,IN_SYNCorNOT_CHECKED. Property:ADD,REMOVEorNOT_EQUAL. - Stack-level tags are checked. Nested stacks are not: run detection on each one.
What drift detection can't see:
- Properties you didn't set in the template, even if they changed. Set a property explicitly, even to its default, if you want it watched.
KMSKeyIdon any resource, Lambda function code, and write-only values such as an IAM login profile password.- Unsupported resource types, which show
NOT_CHECKED.
False alarms:
- Equal values in other units (1024 MB vs 1GB): align the template with the actual value.
- Default array items the service adds.
- Attachment resources, such as
AWS::EC2::SecurityGroupIngress, whose target is in another stack.
Fixing drift
| Option | How |
|---|---|
| Drift-aware change set (revert) | create-change-set … --deployment-mode REVERT_DRIFT (SDK DeploymentMode: "REVERT_DRIFT"), or console Drift aware change set. Three-way comparison: actual state, previous deployment, new template. Drifted resources are set back to the template. Where you change the template to match reality, only the drift status resets (SyncWithActual) |
| Accept the change | Edit the template to the actual value and deploy |
| Remove and re-import | Retain, remove from the template, import again (below) |
Drift-aware limits:
- Immutable properties aren't reconciled. Write-only properties compare against the previous deployment's values.
- Tag keys absent from the template aren't touched.
- AWS-managed values the template doesn't set, such as an Auto Scaling group's desired capacity, are left alone.
- Attachments from other stacks (for example
AWS::IAM::Policyattached to this stack's role) count as drift and may be reverted. - 27 resource types fall back to a two-way comparison, among them
AWS::RDS::DBProxy,AWS::MSK::ClusterandAWS::IAM::OIDCProvider.
CDK --hotswap deployments create drift on purpose. The CLI then prompts for cdk deploy --revert-drift (reference/cdk/bootstrap-roles-and-trust.md).
Importing existing resources
- Add each resource to the template with its identifier (for example
BucketName), its required properties, and aDeletionPolicy(any value). The template must describe the whole stack.aws cloudformation get-template-summary --template-url …lists the identifier properties. - Create a change set of type
IMPORTwith the resources to import, then execute it. Statuses areIMPORT_IN_PROGRESS→IMPORT_COMPLETE, orIMPORT_ROLLBACK_*. - Run drift detection afterwards. CloudFormation doesn't check that your template matches the real configuration.
Restrictions:
- An import can't create, delete or modify anything else.
- A resource can't be in two stacks in one Region. IAM roles, Route 53 zones and CloudFront distributions are global, so check other Regions too.
- Nesting is limited to one level.
- Restrict which types can be imported with
cloudformation:ImportResourceTypes.
Other paths include auto-import (cdk deploy --import-existing-resources adopts unmanaged resources whose custom names match), the IaC generator, and stack refactoring to move resources between stacks.
Cross-stack references
Export + Fn::ImportValue | Fn::GetStackOutput | |
|---|---|---|
| Scope | Same account and Region | Also cross-Region (Region) and cross-account (RoleArn); not cross-partition |
| Integrity | Strong. While imported, the exporter can't be deleted and the value can't change. aws cloudformation list-imports --export-name <n> shows who holds it | Weak. Resolved at create or update; the consumer isn't told when the producer changes and must be re-deployed |
| Names | Export names unique per account and Region | StackName + OutputName; no export needed |
VpcId:
Fn::GetStackOutput:
StackName: network
OutputName: VpcId
Region: us-west-2 # optional
RoleArn: arn:aws:iam::<producer-account>:role/ReadOutputs # optional, cross-account
- The cross-account role lives in the producer account. It allows
cloudformation:DescribeStackson that stack and trusts the consumer stack's execution role, which is used whenRoleArnis omitted. - Use it as a property value or inside
Fn::Join,Fn::IforFn::Select. Elsewhere it returnsInternalFailure(reference/cloudformation/errors.md). - Don't use the
!GetStackOutputshort form with other short forms inside it.