Describe the bug
cdk import fails with
You have modified resources [...] in your template that are not being imported.
Update, create or delete operations cannot be executed during import operations.
even when the user did not modify those resources, as long as the stack already contains a resource whose DependsOn array is not in alphabetical order.
The resources reported as "modified" are exactly the ones whose DependsOn arrays get re-sorted by the CLI before the IMPORT change set is submitted.
Root cause
ResourceImporter caches the deployed template and reuses the same object to build the IMPORT change-set template. However, cloudformation-diff's fullDiff() mutates its input in place via normalize(), which sorts DependsOn arrays. Because the cached "current template" is first passed to fullDiff() (during import discovery) and later reused as the base for the change-set template, the sorted DependsOn leaks into the submitted IMPORT change set.
CloudFormation's IMPORT change set forbids any modification to non-imported resources, so this purely cosmetic reorder is rejected.
Code references (current main):
packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts
fullDiff() calls normalize(currentTemplate) / normalize(newTemplate) — mutates the caller's objects (L48–L55)
normalize() sorts DependsOn in place: template[key] = template[key].sort(); (L221–L231)
packages/@aws-cdk/toolkit-lib/lib/api/resource-import/importer.ts
currentTemplate() caches the deployed template into _currentTemplate (L297–L301)
discoverImportableResources() passes that cached object to cfnDiff.fullDiff(currentTemplate, this.stack.template) (L249–L251) → mutates it
currentTemplateWithAdditions() reuses the same cached (now-mutated) object to build the import template (L307–L308)
importResourcesFromMap() submits it as the IMPORT change set (L198–L202)
So fullDiff() should be side-effect free (normalize a copy), and/or ResourceImporter should not reuse the diffed template object as the change-set base.
Expected behavior
cdk import submits the already-deployed (non-imported) resources byte-for-byte unchanged, so importing new resources never trips the "modified resources ... not being imported" guard for resources the user did not touch.
Current behavior
The submitted change-set template has the affected resource's DependsOn sorted alphabetically, while the deployed template has the natural (synth) order. For example a CDK L2 Lambda produces:
- deployed /
cdk synth: DependsOn: [ <Fn>ServiceRoleDefaultPolicy<hash>, <Fn>ServiceRole<hash> ]
- submitted by
cdk import: DependsOn: [ <Fn>ServiceRole<hash>, <Fn>ServiceRoleDefaultPolicy<hash> ] (alphabetical)
CloudFormation then reports that resource (and the resources depending on it) as modified and aborts the import.
Reproduction steps
- Deploy a stack that contains a resource with a multi-element
DependsOn whose natural order is not alphabetical. A CDK L2 lambda.Function reliably triggers this: it generates Function -> [ ...ServiceRoleDefaultPolicy..., ...ServiceRole... ], whose natural order differs from the alphabetical order (...ServiceRole<hash> sorts before ...ServiceRoleDefaultPolicy<hash>).
- Add a new resource to the stack that you intend to import by identifier (e.g. an existing resource of an import-supported type).
- Run
cdk import (with --force if there are other non-import diffs).
- Import is rejected with
You have modified resources [<the Function and its dependency graph>] in your template that are not being imported, despite no change to those resources.
Inspecting the CreateChangeSet request (e.g. via -vvv) shows the only difference for the flagged resources is the order of the DependsOn array.
Workaround
Force the affected resource's DependsOn to a single element (sorting a one-element array is a no-op) via an escape hatch, e.g.:
(fn.node.defaultChild as lambda.CfnFunction).addOverride('DependsOn', [<defaultPolicyLogicalId>]);
Environment
- CDK CLI Version: 2.1125.0
- Framework Version (aws-cdk-lib): 2.93.0 or 2.201.0
- Node.js Version: v20.6.0 or v22.11.0
- OS: macOS
This is 🐛 Bug Report
Describe the bug
cdk importfails witheven when the user did not modify those resources, as long as the stack already contains a resource whose
DependsOnarray is not in alphabetical order.The resources reported as "modified" are exactly the ones whose
DependsOnarrays get re-sorted by the CLI before the IMPORT change set is submitted.Root cause
ResourceImportercaches the deployed template and reuses the same object to build the IMPORT change-set template. However,cloudformation-diff'sfullDiff()mutates its input in place vianormalize(), which sortsDependsOnarrays. Because the cached "current template" is first passed tofullDiff()(during import discovery) and later reused as the base for the change-set template, the sortedDependsOnleaks into the submitted IMPORT change set.CloudFormation's IMPORT change set forbids any modification to non-imported resources, so this purely cosmetic reorder is rejected.
Code references (current
main):packages/@aws-cdk/cloudformation-diff/lib/diff-template.tsfullDiff()callsnormalize(currentTemplate)/normalize(newTemplate)— mutates the caller's objects (L48–L55)normalize()sortsDependsOnin place:template[key] = template[key].sort();(L221–L231)packages/@aws-cdk/toolkit-lib/lib/api/resource-import/importer.tscurrentTemplate()caches the deployed template into_currentTemplate(L297–L301)discoverImportableResources()passes that cached object tocfnDiff.fullDiff(currentTemplate, this.stack.template)(L249–L251) → mutates itcurrentTemplateWithAdditions()reuses the same cached (now-mutated) object to build the import template (L307–L308)importResourcesFromMap()submits it as the IMPORT change set (L198–L202)So
fullDiff()should be side-effect free (normalize a copy), and/orResourceImportershould not reuse the diffed template object as the change-set base.Expected behavior
cdk importsubmits the already-deployed (non-imported) resources byte-for-byte unchanged, so importing new resources never trips the "modified resources ... not being imported" guard for resources the user did not touch.Current behavior
The submitted change-set template has the affected resource's
DependsOnsorted alphabetically, while the deployed template has the natural (synth) order. For example a CDK L2 Lambda produces:cdk synth:DependsOn: [ <Fn>ServiceRoleDefaultPolicy<hash>, <Fn>ServiceRole<hash> ]cdk import:DependsOn: [ <Fn>ServiceRole<hash>, <Fn>ServiceRoleDefaultPolicy<hash> ](alphabetical)CloudFormation then reports that resource (and the resources depending on it) as
modifiedand aborts the import.Reproduction steps
DependsOnwhose natural order is not alphabetical. A CDK L2lambda.Functionreliably triggers this: it generatesFunction -> [ ...ServiceRoleDefaultPolicy..., ...ServiceRole... ], whose natural order differs from the alphabetical order (...ServiceRole<hash>sorts before...ServiceRoleDefaultPolicy<hash>).cdk import(with--forceif there are other non-import diffs).You have modified resources [<the Function and its dependency graph>] in your template that are not being imported, despite no change to those resources.Inspecting the
CreateChangeSetrequest (e.g. via-vvv) shows the only difference for the flagged resources is the order of theDependsOnarray.Workaround
Force the affected resource's
DependsOnto a single element (sorting a one-element array is a no-op) via an escape hatch, e.g.:Environment
This is 🐛 Bug Report