Skip to content
cdkd

Importing existing resources

cdkd import adopts AWS resources that are already deployed (e.g. via cdk deploy, manual creation, or another tool) into cdkd state, so the next cdkd deploy updates them in-place instead of trying to CREATE duplicates.

It reads the CDK app to find logical IDs, resource types, and dependencies, then matches each logical ID to a real AWS resource in one of three modes.

All examples below assume cdkd reads the CDK app command from cdk.json (the typical case). Pass --app "<command>" only if you're running cdkd outside the CDK project directory or want to override cdk.json.

Mode 1: auto (default — no flags)

cdkd import MyStack

Attempts to import every resource in the synthesized template, without naming physical ids by hand. Per resource, cdkd tries two lookups in order:

  1. By physical name — the template's own name property (ManagedPolicyName, TopicName, TableName, ...).
  2. From CloudFormation — if a CloudFormation stack of the same name exists, its DescribeStackResources mapping. This is what makes adopting a cdk deploy-managed stack work without naming ids by hand.

Note

There is deliberately no aws:cdk:path tag lookup. aws:cdk:path is not a tag that exists on AWS resources: AWS rejects any aws:-prefixed tag write (Tag keys beginning with aws: are reserved for system use), and CloudFormation keeps the value in the template's resource Metadata without promoting it to a tag. cdkd once carried such a walk as a stage-3 fallback; because it could never match, it was removed from every provider. Stage 2 is what resolves a CFn-generated physical name — the usual CDK shape, which stage 1 alone cannot find.

Stage 2 is best-effort and never fatal: with no CloudFormation stack of that name it is skipped silently, and if the call fails (missing cloudformation:DescribeStackResources permission, throttling) cdkd warns and falls back to stage 1. A resource that neither names itself nor lives in a same-named CloudFormation stack comes back not found; adopt it with an explicit --resource <logicalId>=<physicalId>.

To adopt a cdk deploy-managed stack and retire the CloudFormation stack in one step, use --migrate-from-cloudformation. Plain auto mode adopts without touching the source stack. For a handful of resources, name them explicitly with --resource (Mode 2).

Mode 2: selective (CDK CLI parity — when explicit overrides are given)

# Import ONLY MyBucket; the other resources in the template are left alone.
cdkd import MyStack --resource MyBucket=my-bucket-name

# Several resources at once (--resource is repeatable).
cdkd import MyStack \
  --resource MyBucket=my-bucket-name \
  --resource MyFn=my-function-name

# Composite physical ids: some types are identified by several segments
# joined with a `|` pipe. QUOTE the argument — an unquoted `|` is the shell
# pipe operator and would truncate the id.
cdkd import MyStack \
  --resource 'MyGlueTable=my_database|my_table' \
  --resource 'MyGetMethod=a1b2c3d4e5|xy9z8w|GET'

# CDK CLI compat: read overrides from a JSON file.
cdkd import MyStack --resource-mapping mapping.json
# mapping.json: { "MyBucket": "my-bucket-name", "MyFn": "my-function-name" }

# CDK CLI compat: inline JSON (handy for non-TTY CI scripts).
cdkd import MyStack --resource-mapping-inline '{"MyBucket":"my-bucket-name"}'

# Capture cdkd's resolved logicalId→physicalId mapping for re-use.
# Combine with --auto (or no flags) to record the auto-mode resolutions.
cdkd import MyStack --record-resource-mapping ./mapping.json
# mapping.json after the run: { "MyBucket": "my-bucket-name", ... }
# Replay non-interactively in CI:
cdkd import MyStack --resource-mapping ./mapping.json --yes

When at least one --resource flag (or a --resource-mapping / --resource-mapping-inline payload) is supplied, only the listed resources are imported. Every other resource in the template is reported as out of scope and left out of state — the next cdkd deploy will treat them as new and CREATE them. This matches the semantics of cdk import --resource-mapping / --resource-mapping-inline. cdkd validates that every override key is a real logical ID in the template; a typo aborts the run rather than silently importing nothing. --resource-mapping and --resource-mapping-inline are mutually exclusive — pick one source.

Use selective mode when you want to adopt a few specific resources out of a larger stack — for example, you have one S3 bucket that was created manually that you want cdkd to manage, while the rest of the stack will be deployed fresh.

Selective mode is non-destructive. When state already exists for the stack, listed resources are merged into it: unlisted entries already in state are preserved (no --force needed). --force is only required when a listed override would overwrite a resource already in state — that's the one case where the merge is destructive. This is the right command for "I have a deployed stack and want to adopt one more resource into it":

# Existing state has Queue + Topic; add Bucket without affecting them.
cdkd import MyStack --resource MyBucket=my-bucket-name
# Resulting state: Queue + Topic (preserved) + Bucket (newly imported).

Mode 3: hybrid (--auto with overrides)

cdkd import MyStack \
  --resource MyBucket=my-bucket-name \
  --auto

Listed resources use the explicit physical ID you supplied; every other resource still goes through auto-resolution (Mode 1's physical-name and CloudFormation DescribeStackResources lookups). Useful when one resource cannot be auto-resolved (no explicit name, no CloudFormation counterpart) but you want cdkd to find the rest automatically.

Common flags

Flag Purpose
--dry-run Preview what would be imported. State is NOT written.
--yes Skip the confirmation prompt before writing state (and the CloudFormation retirement prompt under --migrate-from-cloudformation). Required in CI: both prompts REFUSE a non-interactive stdin (NON_INTERACTIVE_CONFIRM, exit 1) rather than hanging on it — see cli-destroy.md.
--force Confirm a destructive write to existing state — see below.
--migrate-from-cloudformation [name] After cdkd state is written, retire the source CloudFormation stack: inject DeletionPolicy: Retain + UpdateReplacePolicy: Retain on every resource via UpdateStack, then DeleteStack. AWS resources are NOT deleted. See Migrating from cdk deploy (CloudFormation) to cdkd below.
--use-cdk-bootstrap-assets Keep the CDK bootstrap asset destinations verbatim (skip the cdkd asset-storage rewrite) even when the region is opted in via cdkd bootstrap. Without it, import rewrites asset references (Lambda Code, image URIs, …) to the cdkd-owned storage — but records the pre-rewrite values in state, so the first post-import cdkd deploy repoints the live resources. See Importing a stack into a cdkd-assets region below and the asset-destinations section in docs/cli-bootstrap-gc.md.

--force is only needed when the import would lose data:

  • Auto / whole-stack mode + existing state: required. The resource map is rebuilt from the template, so any state entry not re-imported is dropped.
  • Selective mode + listed override already in state: required. The listed entry is overwritten with the new physical id.
  • Selective mode without a conflict (pure merge): not required. Unlisted state entries are preserved automatically.
  • No existing state (first-time import): not required.

Migrating from cdk deploy (CloudFormation) to cdkd

If a stack was previously deployed via cdk deploy (and is therefore managed by CloudFormation), cdkd import --migrate-from-cloudformation adopts the resources into cdkd state AND retires the source CloudFormation stack in one go:

cdkd import MyStack --migrate-from-cloudformation --yes

No --resource <id>=<physical> flags are needed — cdkd recovers each resource's physical id directly from CloudFormation via DescribeStackResources, so it works for both cdk deploy-managed and cdkd deploy-managed stacks. (This same DescribeStackResources recovery is what plain auto mode uses too — see Mode 1. aws:cdk:path could never have helped here: upstream cdk deploy keeps the path in the template's Metadata, and AWS reserves the aws: tag prefix so it is never a real AWS tag on the resource.)

The flow:

  1. DescribeStackResources — ask CloudFormation for every (LogicalResourceId, PhysicalResourceId) pair in the source stack. These are merged into the import overrides; user-supplied --resource <id>=<physical> flags take precedence over CFn's view.
  2. cdkd import runs and adopts every resource into cdkd state via each provider's import() method, using the CFn-resolved physical ids as direct lookups.
  3. cdkd writes state.
  4. DescribeStacks + GetTemplate + UpdateStack to inject DeletionPolicy: Retain and UpdateReplacePolicy: Retain on every resource — a metadata-only update.
  5. DeleteStack — every resource is now Retain, so CloudFormation walks the stack and skips every resource. The stack record disappears; the underlying AWS resources are left intact and are now solely managed by cdkd.

Steps 1–5 all run inside the same lock so a concurrent cdkd deploy cannot race the in-flight migration.

By default the CloudFormation stack name is taken from the cdkd stack name (the typical case — CDK uses the synthesized stack name as the CFn stack name). Pass an explicit value when the names differ:

cdkd import MyStack --migrate-from-cloudformation LegacyCfnStackName --yes

Nested stacks (recursive walk)

Source CloudFormation stacks that contain AWS::CloudFormation::Stack children are walked recursively:

  • Step 1 (DescribeStackResources) becomes a recursive tree walk: for every nested-stack row, cdkd calls DescribeStackResources(<child ARN>) to enumerate the child's resources, and so on to arbitrary depth. Children at every level are fetched in parallel.
  • After the root state is written, cdkd writes one v6-keyed state file per nested child at cdkd/<parent>~<childLogicalId>/<region>/state.json, with parentStack / parentLogicalId / parentRegion populated per the v6 schema. Grandchildren get cdkd/<parent>~<child>~<grand>/<region>/state.json, recursively. Per-child locks are acquired before the write and released in reverse on success or failure.
  • The root parent's state entry for each nested-stack row carries the synthesized cdkd-local ARN (arn:cdkd-local:<region>:<account>:nested-stack/<parent>/<logicalId>) — NOT the real AWS child stack ARN. This matches what NestedStackProvider.create would write at deploy time, so an import-then-deploy cycle does not surface phantom property changes.
  • Step 4 (UpdateStack with Retain injection) is also recursive: for every nested-stack row in the parent template, cdkd fetches the child's template via GetTemplate, recursively injects Retain on every non-nested-stack resource at every depth, uploads the modified child template to the cdkd state bucket, and rewrites the parent's Properties.TemplateURL to point at it. AWS CFn's parent-side DeleteStack cascades into each child — every leaf resource is Retain'd (so the AWS resource survives), and the child stack record gets deleted as a side effect.

Synth template ↔ AWS shape is validated up front: a nested-stack row in the synth template that has no matching AWS child (or vice versa) hard-errors before any state write, naming the offending logical id — matching upstream cdk import's mismatch UX.

Limitations:

  • JSON and YAML supported. The Retain-policy injection in step 4 parses the source CloudFormation template via cdkd's CFn-aware codec (src/cli/yaml-cfn.ts), which preserves every shorthand intrinsic (!Ref, !Sub, !GetAtt, !Join, ...) across the parse → inject-Retain → re-serialize round-trip. The phase-1 UPDATE submits the template in the same format as the source — a YAML-authored CFn stack stays YAML on the wire (with .yaml key suffix and application/x-yaml content type when uploaded to the cdkd state bucket).
  • 1 MB template limit. Templates up to the inline 51,200-byte TemplateBody ceiling are submitted directly. Larger templates are uploaded to the cdkd state bucket under cdkd-migrate-tmp/<stack>/<timestamp>.json and submitted via TemplateURL; the transient object is deleted in a finally immediately after UpdateStack, and its noncurrent versions are purged with it (the state bucket is versioned, so the delete alone would leave the template body readable by VersionId). That purge FAILS SOFT: it needs s3:ListBucketVersions and s3:DeleteObjectVersion on the state bucket, and without them the migration still succeeds while a warning names the two grants and the previous versions survive — see state-management.md. Templates over the 1 MB CloudFormation TemplateURL ceiling are structurally unsubmittable — cdkd fails with a clear error. cdkd state has already been written at that point, so re-runs and manual cleanup are both supported. The same ceiling applies independently to every uploaded nested-child template.
  • Not compatible with --dry-run. The post-state-write UpdateStack + DeleteStack are real side-effects and cannot be faithfully simulated. Use plain cdkd import --dry-run to preview per-resource import outcomes.
  • Partial imports leave unmanaged resources. If a resource cannot be imported (no provider, AWS not-found, etc.), DeleteStack skips it (Retain) and cdkd never wrote it into state — so the resource exists in AWS but unmanaged by both CloudFormation and cdkd. cdkd warns loudly when this happens; either re-import the missing resources first or accept the orphaning intentionally.
  • Bare cdkd import (no migration flag) does NOT recurse. Without --migrate-from-cloudformation, each AWS::CloudFormation::Stack row is treated as unsupported (the underlying NestedStackProvider has no import() method). Future work would add per-resource tag- based nested-stack adoption; for now, the migration flag is the only path.

After import

Run cdkd diff to see how the imported state lines up with the template. If the resource's actual properties differ from the template, the next cdkd deploy will UPDATE them to match. If you imported only some resources (selective mode), the remaining template resources appear as to create in the diff.

Importing a stack into a cdkd-assets region

When the target region is opted into cdkd-owned asset storage (cdkd bootstrap), the synthesized template's asset references (Lambda Code, container image URIs, the IAM grants CDK generates on the asset bucket, …) are rewritten from the CDK bootstrap locations (cdk-<qualifier>-assets-*) to the cdkd-owned ones (cdkd-assets-*).

The AWS-side resources you are adopting still hold the CDK bootstrap values, so cdkd records those pre-rewrite values in state — not the rewritten ones. cdkd import prints an info line with the count when this applies.

What that means in practice:

  • The first cdkd diff / cdkd deploy after the import shows a change for every rewritten reference, and that deploy is what repoints the live resources at cdkd asset storage.
  • That deploy is required, not cosmetic. Without it, a resource whose live value cdkd cannot read back (an AWS::IAM::Policy granting read on the asset bucket — exactly what s3deploy.BucketDeployment generates) would keep granting the CDK bootstrap bucket while the custom resource reads from the cdkd bucket, failing at runtime with AccessDenied.
  • Pass --use-cdk-bootstrap-assets (or set context.cdkd.useCdkBootstrapAssets in cdk.json) to stay on the CDK bootstrap destinations entirely — no rewrite, no post-import change.

Provider coverage

This section lists every resource type whose cdkd provider implements import(), grouped by how the import is resolved. Use it to decide whether your stack can be adopted with a bare cdkd import MyStack (all resources auto-resolve) or whether you need --resource <id>=<physical> overrides for some of them.

For resource types without auto-lookup support (ApiGateway sub-resources, niche services, anything in Cloud Control API), use the explicit --resource <id>=<physicalId> override mode — selective mode handles exactly this case. Resource types whose provider does not implement import are reported as unsupported and skipped.

Important

A cdkd physical id is not always the id CloudFormation shows you. Types whose id shape is annotated below are identified by several segments joined with a | pipe (<databaseName>|<tableName>, <restApiId>|<resourceId>|<httpMethod>, ...) — that composite is the value --resource expects and the value cdkd state show / cdkd state resources print. Quote it on a shell command line (--resource 'Id=db|table'); no escaping is needed inside a --resource-mapping JSON file. The full per-type format table lives in state-management.md.

Auto-resolved (no --resource flag needed)

These types implement import() and can resolve a physical id under auto (default) and hybrid modes without an explicit --resource override, when one of these applies:

  1. the template sets an explicit physical-name property (BucketName, RoleName, TopicName, ...) — cdkd verifies / lists by that name; or
  2. a CloudFormation stack of the same name exists — cdkd recovers every physical id from its DescribeStackResources, which is how a cdk deploy-managed stack is adopted.

Note

There is no aws:cdk:path tag lookup. AWS reserves the aws: tag prefix, so that tag never exists on a real resource and a walk keyed on it could not match — CloudFormation keeps the construct path in the template's Metadata, never as a tag. The former tag walk was removed from every provider. A CDK app that sets no explicit physical names and has no same-named CloudFormation stack must adopt those resources with --resource <logicalId>=<physicalId>.

Types with an import() that auto-resolves via the above:

  • AWS::S3::Bucket
  • AWS::Lambda::Function
  • AWS::IAM::Role
  • AWS::IAM::ManagedPolicy
  • AWS::IAM::InstanceProfile
  • AWS::IAM::User
  • AWS::CertificateManager::Certificate
  • AWS::IAM::Group
  • AWS::SNS::Topic
  • AWS::SQS::Queue
  • AWS::DynamoDB::Table
  • AWS::DynamoDB::GlobalTable
  • AWS::Logs::LogGroup
  • AWS::Events::EventBus
  • AWS::Events::Rule
  • AWS::KMS::Key
  • AWS::KMS::Alias
  • AWS::SecretsManager::Secret
  • AWS::SSM::Parameter (the parameter NAME only — an ARN or a name:version / name:label selector is REFUSED with the name to pass instead, because GetParameter accepts those forms while PutParameter / DeleteParameter reject them, so adopting one would break the next cdkd deploy and cdkd destroy)
  • AWS::EC2::VPC
  • AWS::EC2::Subnet
  • AWS::EC2::SecurityGroup
  • AWS::EC2::NatGateway
  • AWS::EC2::EIP (accepts an eipalloc-... allocation id, a public IP, or the composite <publicIp>|<allocationId>; cdkd normalizes and stores the composite)
  • AWS::RDS::DBInstance
  • AWS::RDS::DBCluster
  • AWS::RDS::DBProxy
  • AWS::RDS::DBProxyEndpoint
  • AWS::RDS::DBSubnetGroup
  • AWS::DocDB::DBInstance
  • AWS::DocDB::DBCluster
  • AWS::DocDB::DBSubnetGroup
  • AWS::Neptune::DBInstance
  • AWS::Neptune::DBCluster
  • AWS::Neptune::DBSubnetGroup
  • AWS::ECS::Cluster
  • AWS::ECS::Service (accepts the service ARN — the form cdkd stores — or the composite <clusterArn>|<serviceName>)
  • AWS::ECS::TaskDefinition
  • AWS::CloudFront::Distribution
  • AWS::Cognito::UserPool
  • AWS::ApiGatewayV2::Api
  • AWS::AppSync::GraphQLApi
  • AWS::CloudTrail::Trail
  • AWS::CloudWatch::Alarm
  • AWS::CodeBuild::Project
  • AWS::CodeCommit::Repository
  • AWS::ECR::Repository
  • AWS::ElasticLoadBalancingV2::LoadBalancer
  • AWS::ElasticLoadBalancingV2::TargetGroup
  • AWS::Route53::HostedZone (resolved from the template's Name via ListHostedZonesByName; when a public and a private zone share that name — split-horizon DNS — the template's own VPCs picks the side, and a name that is still ambiguous is REFUSED rather than guessed at — reported as a failed row naming --resource <logicalId>=<hostedZoneId>, not as not-found. The adopted row records the same Id / NameServers attributes a cdkd deploy CREATE records, so Fn::GetAtt <Zone>.NameServers — and the Fn::Join over it that CDK's zone.hostedZoneNameServers emits — resolves on an imported zone exactly as on a deployed one; a zone with no delegation set records the empty LIST. The delegation-set read is best-effort ONLY on the auto-resolved path, where it costs one extra GetHostedZone: if that call fails the zone is still adopted, with a warning, and any attributes already in state for it are preserved. With an explicit --resource override there is no extra call — the id-verification GetHostedZone supplies the attributes — so a denied GetHostedZone fails that row as it always has. Neither path heals on a plain cdkd deploy (an unchanged zone is NO_CHANGE and never calls update()); re-run the import for that row with --force, or make a template change that forces an UPDATE)
  • AWS::StepFunctions::StateMachine
  • AWS::Glue::Database
  • AWS::Glue::Table (stored and displayed as the composite <databaseName>|<tableName>; --resource also accepts CloudFormation's bare table name, paired with the template's DatabaseName)
  • AWS::Glue::Job
  • AWS::Glue::Crawler
  • AWS::Glue::Connection
  • AWS::Glue::Trigger
  • AWS::Glue::Workflow
  • AWS::Glue::SecurityConfiguration
  • AWS::Kinesis::Stream
  • AWS::Kinesis::StreamConsumer
  • AWS::KinesisFirehose::DeliveryStream
  • AWS::WAFv2::WebACL
  • AWS::EFS::FileSystem
  • AWS::EFS::AccessPoint
  • AWS::ElastiCache::CacheCluster
  • AWS::ElastiCache::SubnetGroup
  • AWS::Lambda::LayerVersion
  • AWS::ServiceDiscovery::Service
  • AWS::ServiceDiscovery::PrivateDnsNamespace
  • AWS::ServiceDiscovery::HttpNamespace
  • AWS::ServiceDiscovery::PublicDnsNamespace
  • AWS::S3Express::DirectoryBucket
  • AWS::S3Tables::TableBucket
  • AWS::S3Vectors::VectorBucket
  • AWS::DLM::LifecyclePolicy
  • AWS::FSx::FileSystem
  • AWS::Budgets::Budget (the template Budget.BudgetName resolves it without a flag)
  • AWS::EMR::Cluster (override with the cluster id j-XXXX)

Override-only — no standalone identity / list API

These resource types have no AWS-side identity that cdkd can list and match on. Use --resource <logicalId>=<physicalId> (or --resource-mapping <file> / --resource-mapping-inline '<json>') to provide the physical id explicitly.

  • AWS::IAM::Policy (inline)
  • AWS::IAM::UserToGroupAddition
  • AWS::CloudWatch::AnomalyDetector (detectors are addressed by their metric descriptor, carry no tags, and have no name; pass any stable id via --resource — cdkd re-derives the canonical descriptor-based id on the next replacement)
  • AWS::IAM::AccessKey (keys are not taggable and the template carries no property equal to the key id; pass the AKIA... id via --resource, which cdkd verifies with GetAccessKeyLastUsed. Note: the imported record has no cached SecretAccessKey — IAM returns it only from CreateAccessKey — so Fn::GetAtt [<key>, SecretAccessKey] cannot resolve for an imported key; mint a new key via replacement if the secret is needed)
  • AWS::Scheduler::Schedule (schedules are not taggable; the template Name + GroupName also resolve without a flag)
  • AWS::CloudFormation::WaitConditionHandle (no AWS-queryable resource exists behind a handle; an explicit --resource id — or CloudFormation's pre-signed-URL physical id during --migrate-from-cloudformation — is recorded verbatim, and a synthesized placeholder is used otherwise)
  • AWS::CloudFront::OriginAccessControl (OACs are not taggable and the config's Name is a display field AWS does not accept as a lookup key; pass the E... id via --resource, which cdkd verifies with GetOriginAccessControl)

Override-only — sub-resources without a standalone identity

Sub-resources of a parent (an API Gateway Method belongs to a Resource which belongs to a RestApi; a Route53 RecordSet belongs to a HostedZone) have no standalone name or list API cdkd can resolve them by. Provide the physical id via --resource.

  • AWS::ApiGateway::Authorizer
  • AWS::ApiGateway::Resource
  • AWS::ApiGateway::Deployment
  • AWS::ApiGateway::Stage
  • AWS::ApiGateway::Method (composite: --resource <logicalId>=<restApiId>|<resourceId>|<httpMethod>)
  • AWS::ApiGatewayV2::Stage
  • AWS::ApiGatewayV2::Integration
  • AWS::ApiGatewayV2::Route
  • AWS::ApiGatewayV2::Authorizer
  • AWS::AppSync::GraphQLSchema
  • AWS::AppSync::DataSource (composite: --resource <logicalId>=<apiId>|<name>)
  • AWS::AppSync::Resolver (composite: --resource <logicalId>=<apiId>|<typeName>|<fieldName>)
  • AWS::AppSync::ApiKey (composite: --resource <logicalId>=<apiId>|<apiKeyId>)
  • AWS::S3Tables::Namespace (composite: --resource <logicalId>=<tableBucketARN>|<namespaceName>; only the parent AWS::S3Tables::TableBucket auto-resolves)
  • AWS::S3Tables::Table (composite: --resource <logicalId>=<tableBucketARN>|<namespace>|<name>; only the parent AWS::S3Tables::TableBucket auto-resolves)
  • AWS::Route53::RecordSet (composite: --resource <logicalId>=<hostedZoneId>|<name>|<type>, where <name> is the record name exactly as the template spells it — CDK emits a trailing dot)
  • AWS::ElasticLoadBalancingV2::Listener
  • AWS::EFS::MountTarget
  • AWS::RDS::DBProxyTargetGroup
  • AWS::EC2::SecurityGroupIngress (pass the sgr-... rule id — CloudFormation's own identifier for the type and the id the EC2 console shows. cdkd verifies it with DescribeSecurityGroupRules, declines an EGRESS rule id, and records its own composite <groupId>|<ipProtocol>|<fromPort>|<toPort> as the physical id plus the rule id as the Id attribute. The composite itself is deliberately NOT accepted here: the same tuple can name several rules)

Override-only — sub-resources / attachments

Attachment-style resources (a SNS Subscription pinning a Topic to an endpoint, a Lambda Permission granting a principal access to a function) have no taggable identity either. Provide the physical id via --resource.

  • AWS::SNS::Subscription
  • AWS::SNS::TopicPolicy
  • AWS::SQS::QueuePolicy
  • AWS::S3::BucketPolicy
  • AWS::Lambda::Permission (pass the bare statement id — the form cdkd stores; the legacy Cloud-Control-produced composite <functionArn>|<statementId> is also read correctly)
  • AWS::Lambda::EventSourceMapping
  • AWS::Lambda::Url
  • AWS::Lambda::EventInvokeConfig (composite: --resource <logicalId>=<functionName>|<qualifier>; a bare function name is read as qualifier $LATEST)
  • AWS::CloudFormation::CustomResource
  • AWS::CloudFront::CloudFrontOriginAccessIdentity
  • AWS::BedrockAgentCore::Runtime (adopt by ARN via --resource)
  • AWS::BedrockAgentCore::Evaluator (accepts the evaluator ARN or bare id; an id is resolved to the canonical ARN via GetEvaluator)
  • AWS::Lambda::MicrovmImage (adopt by image ARN via --resource; a bare name is rejected — GetMicrovmImage requires the ARN)

Note: AWS::BedrockAgentCore::Browser / AWS::BedrockAgentCore::CodeInterpreter are adopt-only singletons pointing at the AWS-managed defaults; their import is a live auto-lookup (GetBrowser / GetCodeInterpreter) that needs no --resource override at all.

Cloud Control API fallback

Any other CC-API-supported resource type can be imported via the same --resource <logicalId>=<physicalId> override. cdkd does not run auto-lookup over Cloud Control API by default — it would issue an aws-cloudcontrol:ListResources call per type, which is too expensive for whole-stack adoption.

Unsupported

Resource types whose cdkd provider does not implement import() (or which have no provider at all) are reported as unsupported in the import summary and skipped. CDK Stages — separate top-level stacks under one app — are fine; pass the stack's display path or physical name as the positional argument.

Nested CloudFormation stacks (AWS::CloudFormation::Stack): bare cdkd import (auto / selective / hybrid mode) still treats each nested- stack row as unsupported — there is no per-resource import() on the NestedStackProvider. To adopt a parent stack with nested children, use cdkd import --migrate-from-cloudformation instead, which recursively walks the tree, writes one v6-keyed state file per child (cdkd/<parent>~<childLogicalId>/<region>/state.json), and retires the whole tree via a single parent-side DeleteStack cascade. See the --migrate-from-cloudformation section below for details.

AWS::AutoScaling::AutoScalingGroup is also currently unsupported — the SDK provider exists for create / update / delete / readCurrentState but has not yet been wired for import(). Track-able via a follow-up that adds an override-only import() keyed on the group name.

AWS::EMR::InstanceGroupConfig / AWS::EMR::InstanceFleetConfig are likewise currently unsupported for import — the SDK providers exist for create / update / delete but not import(). They are cluster sub-resources with no standalone name (they are listed via ListInstanceGroups / ListInstanceFleets under a parent ClusterId), so a future override-only import() keyed on the group / fleet id (ig-XXXX / if-XXXX) is the natural follow-up.

Adding a new entry

When adding import() support to a provider, add the resource type to the appropriate section above. Keep entries one-per-line so parallel PRs don't conflict on rebase.

cdkd import vs upstream cdk import

cdkd's import command mirrors the surface of upstream cdk import where it can, but the underlying mechanism is fundamentally different and a handful of upstream-only flags are not implemented. Use this table to predict behavior when migrating from cdk import.

Topic cdk import (upstream) cdkd import
Mechanism CloudFormation CreateChangeSet with ResourcesToImport — atomic, all-or-nothing. Per-resource SDK calls (e.g. s3:HeadBucket, lambda:GetFunction, IAM ListRoleTags). Not atomic.
Failure mode Failed import rolls the changeset back; the stack is left unchanged. Per-resource: imported / skipped-not-found / skipped-no-impl / skipped-out-of-scope / failed rows are summarized. State is written for whatever succeeded — but only after a confirmation prompt (or --yes), so a partial run is opt-in. To roll a partial import back, use cdkd state orphan <stack> (drops the state record only).
Selective mode (--resource-mapping <file>) Supported. Listed resources are imported; unlisted resources cause the changeset to fail. Supported. Listed resources are imported; unlisted resources are reported as out of scope and left out of state (next cdkd deploy will CREATE them).
Selective mode (--resource <id>=<physical> repeatable) Not supported (upstream uses interactive prompts or a mapping file). Supported as cdkd's CLI-friendly equivalent.
--resource-mapping-inline '<json>' Supported (use in non-TTY environments). Supported. Same shape as --resource-mapping <file> but supplied as a string — useful for non-TTY CI scripts that do not want a separate file. Mutually exclusive with --resource-mapping.
--record-resource-mapping <file> Supported (writes the mapping the user typed at the prompt to a file for re-use). Supported. Writes the resolved {logicalId: physicalId} map (covers explicit overrides AND auto-mode name / CloudFormation resolution) to the file before the confirmation prompt. The file is produced even if the user says "no" or under --dry-run, so the resolved data is never thrown away.
Interactive prompt for missing IDs Default in TTY — prompts for every resource not covered by a mapping file. Not supported. cdkd is non-interactive: missing logical IDs are resolved from the template's physical-name property, then from a same-named CloudFormation stack's DescribeStackResources, in auto / hybrid modes, or skipped as out of scope in selective mode. The only prompt is the final "write state?" confirmation, which --yes skips.
Typo'd logical ID Aborts with a clear error before any AWS calls. Aborts with a clear error before any AWS calls — checked against the synthesized template.
Whole-stack import with no per-resource ids Not supported. cdkd-specific. With no flags cdkd resolves each resource from the template's physical-name property, then from a same-named CloudFormation stack's DescribeStackResources. Use --migrate-from-cloudformation when you also want the source CloudFormation stack retired.
Hybrid mode (overrides + auto-resolution) Not supported. cdkd-specific. --auto together with --resource lets listed resources use the explicit physical id while everything else still goes through auto-resolution (name property + CloudFormation lookup).
Nested stacks (AWS::CloudFormation::Stack) Explicitly unsupported. Supported via cdkd import --migrate-from-cloudformation: recursively walks the tree via DescribeStackResources, writes one v6-keyed state file per child (cdkd/<parent>~<childLogicalId>/<region>/state.json), recursively injects DeletionPolicy: Retain into every leaf resource template, then retires the whole tree via a single parent-side DeleteStack cascade. Bare cdkd import (auto / selective / hybrid mode) still reports each nested-stack row as unsupported. CDK Stages (separate top-level stacks) are also fine: pass the stack's display path or physical name as the positional argument.
Bootstrap requirement Bootstrap v12+ (deploy role needs to read the encrypted staging bucket). cdkd's own state bucket; no CDK bootstrap version requirement.
Resource-type coverage Whatever CloudFormation supports for import. The set of cdkd providers that implement import() — see Provider coverage above. For any other CC-API-supported type, use --resource <id>=<physical> to drive the Cloud Control API fallback. The two lists overlap heavily but are not identical.
Confirmation prompt before writing state n/a (CloudFormation operates atomically). Yes — cdkd asks before writing the state file. Skip with --yes.
--force "Continue even if the diff includes updates or deletions" — about diff strictness. "Confirm a destructive write to existing state" — required for auto/whole-stack rebuild and for overwriting a listed entry already in state; not required for a pure selective merge. Same flag name, different meaning.
--dry-run Implied by --no-execute (creates the changeset without executing). Native: shows the import plan and exits without writing state.

Practical implications when migrating from cdk import

  • If you script around --resource-mapping <file>: behavior matches. The file format ({"LogicalId": "physical-id"}) is the same.
  • If you script around --resource-mapping-inline: behavior matches. The JSON shape is the same as --resource-mapping <file>.
  • If you script around --record-resource-mapping <file>: behavior matches. cdkd writes the resolved {logicalId: physicalId} map to the file before the confirmation prompt — and even if the user says "no" or under --dry-run — so you can capture cdkd's auto-mode resolution result and replay it via --resource-mapping in CI.
  • If your workflow relies on the interactive prompt: rewrite as --resource-mapping <file>. cdkd will not prompt.
  • If you rely on atomic rollback: cdkd cannot offer that — its per-resource model writes state only after the full pass completes (and after confirmation), so a partial run is bounded, but if a later resource fails after several earlier ones already returned successfully and you confirm the write, those earlier ones are in cdkd state. Use cdkd state orphan <stack> to back out.
  • If you import nested stacks: neither tool supports this. Convert to top-level CDK stacks first.

Last updated: