---
cuppy_hub: aws
cuppy_path: reference/cloudformation/change-sets-drift-and-import.md
cuppy_section: docs
cuppy_doc: M64Q-6G5M
cuppy_doc_uuid: 1261a38f-9745-4eb8-a3db-5d07b4725bc8
cuppy_permalink: https://cuppy.io/aws/d/M64Q-6G5M
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/reference/cloudformation/change-sets-drift-and-import
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: ffe430cb2cfe8c85f4f480b82d3f12646ee2cbf7283ab4a89652dde133364ad0
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# 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 an `AWS::RDS::DBInstance` without `DBClusterIdentifier`, default to **`Snapshot`**.
- **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_SYNC` or `NOT_CHECKED`. **Resource:** `MODIFIED`, `DELETED`, `IN_SYNC` or `NOT_CHECKED`. **Property:** `ADD`, `REMOVE` or `NOT_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.
- `KMSKeyId` on 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::Policy` attached 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::Cluster` and `AWS::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

1. Add each resource to the template with its **identifier** (for example `BucketName`), its required properties, and **a `DeletionPolicy` (any value)**. The template must describe the whole stack. `aws cloudformation get-template-summary --template-url …` lists the identifier properties.
2. Create a change set of type `IMPORT` with the resources to import, then execute it. Statuses are `IMPORT_IN_PROGRESS` → `IMPORT_COMPLETE`, or `IMPORT_ROLLBACK_*`.
3. **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 |

```yaml
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:DescribeStacks` on that stack and trusts the consumer stack's **execution role**, which is used when `RoleArn` is omitted.
- Use it as a property value or inside `Fn::Join`, `Fn::If` or `Fn::Select`. Elsewhere it returns `InternalFailure` (`reference/cloudformation/errors.md`).
- Don't use the `!GetStackOutput` short form with other short forms inside it.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 131 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
