skip to content

A DynamoDB table was created by hand and now has to be managed by an existing AWS CloudFormation stack, with no downtime and no data loss. How do you bring it under the stack's control, and what does that operation require?

level: middleimportance: should knowfreq 45%

answer

  1. adopt, do not recreate
  2. change set type IMPORT
  3. DeletionPolicy required on imported resources
  4. logical id mapped to physical identifier
  5. import does not verify properties

basics

~20 s

Use a CloudFormation import: create a change set with type IMPORT, supply a template entry for the resource carrying a DeletionPolicy, and map its logical id to the live resource's physical identifier. Executing it adopts the table without recreating it.

solid answer

~50 s

CloudFormation supports adopting existing resources through an **import** change set rather than recreating them. You add the resource to the stack's template — with a `DeletionPolicy` attribute, which import requires — then call `create-change-set` with `--change-set-type IMPORT` and a `--resources-to-import` list. Each entry gives the `ResourceType`, the `LogicalResourceId` in your template, and a `ResourceIdentifier` map holding the type's primary identifier, for a table its `TableName`. Executing the change set moves the stack through `IMPORT_IN_PROGRESS` to `IMPORT_COMPLETE`; nothing is created, replaced or deleted, so the table and its data are untouched. Two constraints matter: the rest of the template must be unchanged, because an import change set only imports, and the resource must not already belong to another stack. Critically, CloudFormation does **not** verify that your template's properties match the live table — so run drift detection immediately afterwards.

code

json · 9 lines
json
[
  {
    "ResourceType": "AWS::DynamoDB::Table",
    "LogicalResourceId": "OrdersTable",
    "ResourceIdentifier": {
      "TableName": "orders"
    }
  }
]

go deeper

for a junior

Know that CloudFormation can adopt an existing resource instead of recreating it, and that a plain stack update with the resource added would create a duplicate or fail rather than adopt anything.

for a middle

Be ready to walk the mechanics: change set type IMPORT, the resources-to-import mapping of logical id to physical identifier, the required DeletionPolicy, and the rule that nothing else in the template may change.

for a senior

Emphasise the safety order — read the live configuration, import, detect drift, reconcile the template, then deploy. Explain why import not validating properties is the trap, and how a wrong guess becomes a destructive update later.

for a principal

Frame adoption at estate scale: batching limits, which resources are worth importing versus rebuilding, and how you stop the account from accumulating hand-made resources faster than you can adopt them.

## Why import exists The naive way to bring a hand-made resource under CloudFormation is to write it into the template and update the stack — and that creates a *second*, brand-new table beside the original, or fails on a name clash. For anything stateful that is unacceptable. Resource import is the operation that says "this existing physical resource is now the thing my logical id refers to", with no create, no replace, and no delete. ## What you have to prepare **A template entry for the resource.** Written as you would write it normally, and carrying a `DeletionPolicy` attribute — CloudFormation requires one on every resource being imported. `Retain` is the usual choice for stateful resources, and it is a sensible default anyway for something you were careful enough not to recreate. ```yaml Resources: OrdersTable: Type: AWS::DynamoDB::Table DeletionPolicy: Retain Properties: TableName: orders BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: orderId AttributeType: S KeySchema: - AttributeName: orderId KeyType: HASH ``` **An identifier mapping.** A `ResourcesToImport` list, one entry per resource, each with three fields: `ResourceType`, the `LogicalResourceId` you just added to the template, and `ResourceIdentifier` — a map of the resource type's primary identifier property to the live value. Which property that is depends on the type: a DynamoDB table is identified by `TableName`, an S3 bucket by `BucketName`. Getting the identifier property wrong is the most common first failure. ## The operation ```bash aws cloudformation create-change-set \ --stack-name orders-prod \ --change-set-name adopt-orders-table \ --change-set-type IMPORT \ --template-body file://template.yaml \ --resources-to-import file://resources-to-import.json aws cloudformation execute-change-set \ --stack-name orders-prod \ --change-set-name adopt-orders-table ``` The stack moves through `IMPORT_IN_PROGRESS` to `IMPORT_COMPLETE`. If something goes wrong it rolls back through the `IMPORT_ROLLBACK_*` states, which unwind the adoption rather than deleting your table. The same change set type also creates a brand-new stack out of existing resources — the stack starts in `REVIEW_IN_PROGRESS` and the import is its first operation. ## What import deliberately does not do **It does not reconcile properties.** CloudFormation records your template as the expected configuration for the imported resource without checking it against the live one. If your template says `PAY_PER_REQUEST` and the real table is provisioned, the import still succeeds — and the stack is now lying about production. This is why the documented next step after any import is to run drift detection on the stack and reconcile whatever comes back `MODIFIED`. In an interview, saying "import then immediately detect drift" is the sentence that shows you have actually done this. **It does not change the resource.** No properties are applied to it during import. Making the live table match the template is a subsequent, ordinary stack update. ## Constraints worth naming - **The rest of the template must be unchanged.** An import change set imports; it will not also modify or add other resources in the same operation. Do your other changes in a separate update before or after. - **The resource cannot already belong to another stack.** CloudFormation will not let two stacks manage the same physical resource — which is precisely why moving a resource *between* stacks needs a retain-and-release step first, not a straight import. - **The resource type must support import.** Support is per type, like drift detection, and not universal. - **Imports are batched but bounded.** There is a limit on how many resources one import operation takes, so adopting a large hand-built estate is several passes. ## The order that keeps you safe Write the template entry from the live resource's actual configuration — read it from the service, do not guess. Import. Run drift detection. Fix the differences in the template, not in the resource, unless the difference is something you genuinely want changed. Only then let the stack take an ordinary update that touches it. Done in that order, the adoption is invisible to whatever is using the table.

  • Why is running drift detection right after an import considered mandatory rather than optional?
    Because import records your template as the expected configuration without ever comparing it to the live resource. Any property you guessed wrong is now a silent lie in the stack, and the first ordinary update that touches the resource may apply it for real. Detecting drift immediately turns those guesses into a MODIFIED report you can reconcile before anything is deployed.
  • Can an import change set also add a new resource or modify an existing one in the same operation?
    No. An import change set only imports; the rest of the template must match what the stack already has. Bundle other changes into a separate update before or after the import. This is a common first attempt — people write the whole intended end-state template, submit it as an import, and get rejected for the unrelated edits it contains.
  • What happens if the resource you are trying to import is already managed by another CloudFormation stack?
    The import is refused — CloudFormation will not let two stacks own the same physical resource. Moving one between stacks therefore needs the source stack to release it first: set DeletionPolicy Retain, remove it from the source template so the stack stops managing it while leaving it alive, and only then import it into the target stack.

saying these in an interview costs you the question

  • Adds the resource to the template and runs a normal stack update
  • Assumes import applies the template's properties to the live resource
  • Forgets the DeletionPolicy attribute required on imported resources
  • Bundles unrelated template edits into the import change set
  • Believes a successful import proves template and reality agree

context