@go-to-k/cdkd 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -447,7 +447,7 @@ var init_aws_clients = __esm({
447
447
  });
448
448
 
449
449
  // src/cli/index.ts
450
- import { Command as Command11 } from "commander";
450
+ import { Command as Command12 } from "commander";
451
451
 
452
452
  // src/cli/commands/bootstrap.ts
453
453
  import { Command, Option as Option2 } from "commander";
@@ -1030,9 +1030,9 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
1030
1030
  }
1031
1031
  }
1032
1032
  async function bucketExists(client, bucketName) {
1033
- const { HeadBucketCommand: HeadBucketCommand4 } = await import("@aws-sdk/client-s3");
1033
+ const { HeadBucketCommand: HeadBucketCommand5 } = await import("@aws-sdk/client-s3");
1034
1034
  try {
1035
- await client.send(new HeadBucketCommand4({ Bucket: bucketName }));
1035
+ await client.send(new HeadBucketCommand5({ Bucket: bucketName }));
1036
1036
  return true;
1037
1037
  } catch (error) {
1038
1038
  const err = error;
@@ -7674,6 +7674,8 @@ import {
7674
7674
  UntagRoleCommand,
7675
7675
  PutRolePermissionsBoundaryCommand,
7676
7676
  DeleteRolePermissionsBoundaryCommand,
7677
+ ListRolesCommand,
7678
+ ListRoleTagsCommand,
7677
7679
  NoSuchEntityException
7678
7680
  } from "@aws-sdk/client-iam";
7679
7681
  init_aws_clients();
@@ -7782,6 +7784,32 @@ function applyDefaultNameForFallback(logicalId, resourceType, properties) {
7782
7784
  };
7783
7785
  }
7784
7786
 
7787
+ // src/provisioning/import-helpers.ts
7788
+ function readNameProperty(input, propertyName) {
7789
+ const value = input.properties?.[propertyName];
7790
+ return typeof value === "string" && value.length > 0 ? value : void 0;
7791
+ }
7792
+ function resolveExplicitPhysicalId(input, nameProperty) {
7793
+ if (input.knownPhysicalId)
7794
+ return input.knownPhysicalId;
7795
+ if (nameProperty) {
7796
+ const name = readNameProperty(input, nameProperty);
7797
+ if (name)
7798
+ return name;
7799
+ }
7800
+ return void 0;
7801
+ }
7802
+ var CDK_PATH_TAG = "aws:cdk:path";
7803
+ function matchesCdkPath(tags, cdkPath) {
7804
+ if (!tags || !cdkPath)
7805
+ return false;
7806
+ for (const t of tags) {
7807
+ if (t.Key === CDK_PATH_TAG && t.Value === cdkPath)
7808
+ return true;
7809
+ }
7810
+ return false;
7811
+ }
7812
+
7785
7813
  // src/provisioning/providers/iam-role-provider.ts
7786
7814
  var IAMRoleProvider = class {
7787
7815
  iamClient;
@@ -8288,6 +8316,58 @@ var IAMRoleProvider = class {
8288
8316
  this.logger.debug(`Added/updated ${tagsToAdd.length} tags on role ${roleName}`);
8289
8317
  }
8290
8318
  }
8319
+ /**
8320
+ * Adopt an existing IAM role into cdkd state.
8321
+ *
8322
+ * Lookup order:
8323
+ * 1. `--resource` override or `Properties.RoleName` → use directly,
8324
+ * verify via `GetRole`.
8325
+ * 2. `ListRoles` + `ListRoleTags`, match `aws:cdk:path` tag.
8326
+ *
8327
+ * `ListRoles` is paginated and IAM is global (no region scoping), so this
8328
+ * walks every role in the account once. Acceptable for the cardinalities
8329
+ * we expect (typically <100 roles per account); larger accounts may want
8330
+ * to provide `--resource` overrides instead.
8331
+ */
8332
+ async import(input) {
8333
+ const explicit = resolveExplicitPhysicalId(input, "RoleName");
8334
+ if (explicit) {
8335
+ try {
8336
+ await this.iamClient.send(new GetRoleCommand({ RoleName: explicit }));
8337
+ return { physicalId: explicit, attributes: {} };
8338
+ } catch (err) {
8339
+ if (err instanceof NoSuchEntityException)
8340
+ return null;
8341
+ throw err;
8342
+ }
8343
+ }
8344
+ if (!input.cdkPath)
8345
+ return null;
8346
+ let marker;
8347
+ do {
8348
+ const list = await this.iamClient.send(
8349
+ new ListRolesCommand({ ...marker && { Marker: marker } })
8350
+ );
8351
+ for (const role of list.Roles ?? []) {
8352
+ if (!role.RoleName)
8353
+ continue;
8354
+ try {
8355
+ const tags = await this.iamClient.send(
8356
+ new ListRoleTagsCommand({ RoleName: role.RoleName })
8357
+ );
8358
+ if (matchesCdkPath(tags.Tags, input.cdkPath)) {
8359
+ return { physicalId: role.RoleName, attributes: {} };
8360
+ }
8361
+ } catch (err) {
8362
+ if (err instanceof NoSuchEntityException)
8363
+ continue;
8364
+ throw err;
8365
+ }
8366
+ }
8367
+ marker = list.IsTruncated ? list.Marker : void 0;
8368
+ } while (marker);
8369
+ return null;
8370
+ }
8291
8371
  };
8292
8372
 
8293
8373
  // src/provisioning/providers/iam-policy-provider.ts
@@ -9868,6 +9948,8 @@ var IAMUserGroupProvider = class {
9868
9948
  import {
9869
9949
  CreateBucketCommand as CreateBucketCommand2,
9870
9950
  DeleteBucketCommand,
9951
+ HeadBucketCommand as HeadBucketCommand3,
9952
+ ListBucketsCommand,
9871
9953
  PutBucketVersioningCommand as PutBucketVersioningCommand2,
9872
9954
  PutBucketTaggingCommand,
9873
9955
  PutBucketOwnershipControlsCommand,
@@ -9885,6 +9967,7 @@ import {
9885
9967
  PutBucketInventoryConfigurationCommand,
9886
9968
  PutBucketReplicationCommand,
9887
9969
  PutObjectLockConfigurationCommand,
9970
+ GetBucketTaggingCommand,
9888
9971
  NoSuchBucket,
9889
9972
  ListObjectVersionsCommand,
9890
9973
  DeleteObjectsCommand
@@ -10723,6 +10806,53 @@ var S3BucketProvider = class {
10723
10806
  );
10724
10807
  }
10725
10808
  }
10809
+ /**
10810
+ * Adopt an existing S3 bucket into cdkd state.
10811
+ *
10812
+ * Lookup order:
10813
+ * 1. `--resource <id>=<name>` override or `Properties.BucketName` → use directly,
10814
+ * verify with `HeadBucket`.
10815
+ * 2. `ListBuckets` + `GetBucketTagging`, match `aws:cdk:path` against the
10816
+ * CDK construct path.
10817
+ *
10818
+ * Returns `null` when nothing matches — caller treats this as
10819
+ * "not deployed yet" rather than a failure.
10820
+ */
10821
+ async import(input) {
10822
+ const explicit = resolveExplicitPhysicalId(input, "BucketName");
10823
+ if (explicit) {
10824
+ try {
10825
+ await this.s3Client.send(new HeadBucketCommand3({ Bucket: explicit }));
10826
+ return { physicalId: explicit, attributes: {} };
10827
+ } catch (err) {
10828
+ const e = err;
10829
+ if (e.name === "NotFound" || e.name === "NoSuchBucket") {
10830
+ return null;
10831
+ }
10832
+ throw err;
10833
+ }
10834
+ }
10835
+ if (!input.cdkPath)
10836
+ return null;
10837
+ const list = await this.s3Client.send(new ListBucketsCommand({}));
10838
+ for (const b of list.Buckets ?? []) {
10839
+ if (!b.Name)
10840
+ continue;
10841
+ try {
10842
+ const tagging = await this.s3Client.send(new GetBucketTaggingCommand({ Bucket: b.Name }));
10843
+ if (matchesCdkPath(tagging.TagSet, input.cdkPath)) {
10844
+ return { physicalId: b.Name, attributes: {} };
10845
+ }
10846
+ } catch (err) {
10847
+ const e = err;
10848
+ if (e.name === "NoSuchTagSet" || e.name === "AccessDenied" || e.$metadata?.httpStatusCode === 301) {
10849
+ continue;
10850
+ }
10851
+ throw err;
10852
+ }
10853
+ }
10854
+ return null;
10855
+ }
10726
10856
  /**
10727
10857
  * Delete a bucket, emptying it first if not empty.
10728
10858
  * Handles the race condition where objects (e.g., ALB logs) are written
@@ -10959,6 +11089,9 @@ import {
10959
11089
  CreateQueueCommand,
10960
11090
  DeleteQueueCommand,
10961
11091
  GetQueueAttributesCommand,
11092
+ GetQueueUrlCommand,
11093
+ ListQueuesCommand,
11094
+ ListQueueTagsCommand,
10962
11095
  SetQueueAttributesCommand,
10963
11096
  QueueDoesNotExist
10964
11097
  } from "@aws-sdk/client-sqs";
@@ -11167,6 +11300,68 @@ var SQSQueueProvider = class {
11167
11300
  return `arn:aws:sqs:unknown:unknown:${queueName}`;
11168
11301
  }
11169
11302
  }
11303
+ /**
11304
+ * Adopt an existing SQS queue into cdkd state.
11305
+ *
11306
+ * SQS physical IDs are queue URLs (`https://sqs.us-east-1.amazonaws.com/<account>/<name>`).
11307
+ *
11308
+ * Lookup order:
11309
+ * 1. `--resource` override (URL) → verify via `GetQueueAttributes`.
11310
+ * 2. `Properties.QueueName` → `GetQueueUrl` for direct lookup.
11311
+ * 3. `aws:cdk:path` tag match via `ListQueues` + `ListQueueTags`.
11312
+ */
11313
+ async import(input) {
11314
+ if (input.knownPhysicalId) {
11315
+ try {
11316
+ await this.sqsClient.send(
11317
+ new GetQueueAttributesCommand({
11318
+ QueueUrl: input.knownPhysicalId,
11319
+ AttributeNames: ["QueueArn"]
11320
+ })
11321
+ );
11322
+ return { physicalId: input.knownPhysicalId, attributes: {} };
11323
+ } catch (err) {
11324
+ if (err instanceof QueueDoesNotExist)
11325
+ return null;
11326
+ throw err;
11327
+ }
11328
+ }
11329
+ const explicitName = resolveExplicitPhysicalId(input, "QueueName");
11330
+ if (explicitName && !input.knownPhysicalId) {
11331
+ try {
11332
+ const resp = await this.sqsClient.send(new GetQueueUrlCommand({ QueueName: explicitName }));
11333
+ if (resp.QueueUrl)
11334
+ return { physicalId: resp.QueueUrl, attributes: {} };
11335
+ return null;
11336
+ } catch (err) {
11337
+ if (err instanceof QueueDoesNotExist)
11338
+ return null;
11339
+ throw err;
11340
+ }
11341
+ }
11342
+ if (!input.cdkPath)
11343
+ return null;
11344
+ let nextToken;
11345
+ do {
11346
+ const list = await this.sqsClient.send(
11347
+ new ListQueuesCommand({ ...nextToken && { NextToken: nextToken } })
11348
+ );
11349
+ for (const url of list.QueueUrls ?? []) {
11350
+ try {
11351
+ const tagsResp = await this.sqsClient.send(new ListQueueTagsCommand({ QueueUrl: url }));
11352
+ if (tagsResp.Tags?.[CDK_PATH_TAG] === input.cdkPath) {
11353
+ return { physicalId: url, attributes: {} };
11354
+ }
11355
+ } catch (err) {
11356
+ if (err instanceof QueueDoesNotExist)
11357
+ continue;
11358
+ throw err;
11359
+ }
11360
+ }
11361
+ nextToken = list.NextToken;
11362
+ } while (nextToken);
11363
+ return null;
11364
+ }
11170
11365
  };
11171
11366
 
11172
11367
  // src/provisioning/providers/sqs-queue-policy-provider.ts
@@ -11329,6 +11524,9 @@ var SQSQueuePolicyProvider = class {
11329
11524
  import {
11330
11525
  CreateTopicCommand,
11331
11526
  DeleteTopicCommand,
11527
+ GetTopicAttributesCommand,
11528
+ ListTopicsCommand,
11529
+ ListTagsForResourceCommand,
11332
11530
  SetTopicAttributesCommand,
11333
11531
  TagResourceCommand,
11334
11532
  UntagResourceCommand,
@@ -11618,6 +11816,63 @@ var SNSTopicProvider = class {
11618
11816
  );
11619
11817
  }
11620
11818
  }
11819
+ /**
11820
+ * Adopt an existing SNS topic into cdkd state.
11821
+ *
11822
+ * SNS physical IDs are full ARNs (`arn:aws:sns:...:TopicName`). The
11823
+ * `--resource` override is expected to receive an ARN; bare topic names
11824
+ * trigger a `ListTopics` walk that resolves to the ARN.
11825
+ *
11826
+ * Lookup order:
11827
+ * 1. `--resource` override → trust as ARN, verify via `GetTopicAttributes`.
11828
+ * 2. `Properties.TopicName` → `ListTopics` to find matching ARN.
11829
+ * 3. `aws:cdk:path` tag match via `ListTopics` + `ListTagsForResource`.
11830
+ */
11831
+ async import(input) {
11832
+ if (input.knownPhysicalId) {
11833
+ try {
11834
+ await this.snsClient.send(
11835
+ new GetTopicAttributesCommand({ TopicArn: input.knownPhysicalId })
11836
+ );
11837
+ return { physicalId: input.knownPhysicalId, attributes: {} };
11838
+ } catch (err) {
11839
+ if (err instanceof NotFoundException)
11840
+ return null;
11841
+ throw err;
11842
+ }
11843
+ }
11844
+ const desiredName = typeof input.properties?.["TopicName"] === "string" ? input.properties["TopicName"] : void 0;
11845
+ let nextToken;
11846
+ do {
11847
+ const list = await this.snsClient.send(
11848
+ new ListTopicsCommand({ ...nextToken && { NextToken: nextToken } })
11849
+ );
11850
+ for (const t of list.Topics ?? []) {
11851
+ if (!t.TopicArn)
11852
+ continue;
11853
+ const arnTail = t.TopicArn.substring(t.TopicArn.lastIndexOf(":") + 1);
11854
+ if (desiredName && arnTail === desiredName) {
11855
+ return { physicalId: t.TopicArn, attributes: {} };
11856
+ }
11857
+ if (input.cdkPath) {
11858
+ try {
11859
+ const tagsResp = await this.snsClient.send(
11860
+ new ListTagsForResourceCommand({ ResourceArn: t.TopicArn })
11861
+ );
11862
+ if (matchesCdkPath(tagsResp.Tags, input.cdkPath)) {
11863
+ return { physicalId: t.TopicArn, attributes: {} };
11864
+ }
11865
+ } catch (err) {
11866
+ if (err instanceof NotFoundException)
11867
+ continue;
11868
+ throw err;
11869
+ }
11870
+ }
11871
+ }
11872
+ nextToken = list.NextToken;
11873
+ } while (nextToken);
11874
+ return null;
11875
+ }
11621
11876
  };
11622
11877
 
11623
11878
  // src/provisioning/providers/sns-subscription-provider.ts
@@ -11917,6 +12172,8 @@ import {
11917
12172
  UpdateFunctionCodeCommand,
11918
12173
  DeleteFunctionCommand,
11919
12174
  GetFunctionCommand,
12175
+ ListFunctionsCommand,
12176
+ ListTagsCommand,
11920
12177
  ResourceNotFoundException
11921
12178
  } from "@aws-sdk/client-lambda";
11922
12179
  import {
@@ -12501,6 +12758,57 @@ var LambdaFunctionProvider = class {
12501
12758
  }
12502
12759
  return (crc ^ 4294967295) >>> 0;
12503
12760
  }
12761
+ /**
12762
+ * Adopt an existing Lambda function into cdkd state.
12763
+ *
12764
+ * Lookup order:
12765
+ * 1. `--resource` override or `Properties.FunctionName` → use directly,
12766
+ * verify via `GetFunction`.
12767
+ * 2. `ListFunctions` + `ListTags`, match `aws:cdk:path` tag.
12768
+ *
12769
+ * Lambda's `ListTags` returns a `Tags` map keyed by tag name (unlike
12770
+ * EC2/S3 which return an array of `{Key, Value}`), so we read it directly
12771
+ * instead of going through the shared `matchesCdkPath` helper.
12772
+ */
12773
+ async import(input) {
12774
+ const explicit = resolveExplicitPhysicalId(input, "FunctionName");
12775
+ if (explicit) {
12776
+ try {
12777
+ await this.lambdaClient.send(new GetFunctionCommand({ FunctionName: explicit }));
12778
+ return { physicalId: explicit, attributes: {} };
12779
+ } catch (err) {
12780
+ if (err instanceof ResourceNotFoundException)
12781
+ return null;
12782
+ throw err;
12783
+ }
12784
+ }
12785
+ if (!input.cdkPath)
12786
+ return null;
12787
+ let marker;
12788
+ do {
12789
+ const list = await this.lambdaClient.send(
12790
+ new ListFunctionsCommand({ ...marker && { Marker: marker } })
12791
+ );
12792
+ for (const fn of list.Functions ?? []) {
12793
+ if (!fn.FunctionArn || !fn.FunctionName)
12794
+ continue;
12795
+ try {
12796
+ const tagsResp = await this.lambdaClient.send(
12797
+ new ListTagsCommand({ Resource: fn.FunctionArn })
12798
+ );
12799
+ if (tagsResp.Tags?.[CDK_PATH_TAG] === input.cdkPath) {
12800
+ return { physicalId: fn.FunctionName, attributes: {} };
12801
+ }
12802
+ } catch (err) {
12803
+ if (err instanceof ResourceNotFoundException)
12804
+ continue;
12805
+ throw err;
12806
+ }
12807
+ }
12808
+ marker = list.NextMarker;
12809
+ } while (marker);
12810
+ return null;
12811
+ }
12504
12812
  };
12505
12813
 
12506
12814
  // src/provisioning/providers/lambda-permission-provider.ts
@@ -13222,6 +13530,8 @@ import {
13222
13530
  CreateTableCommand,
13223
13531
  DeleteTableCommand,
13224
13532
  DescribeTableCommand as DescribeTableCommand2,
13533
+ ListTablesCommand,
13534
+ ListTagsOfResourceCommand,
13225
13535
  ResourceNotFoundException as ResourceNotFoundException6
13226
13536
  } from "@aws-sdk/client-dynamodb";
13227
13537
  init_aws_clients();
@@ -13433,12 +13743,70 @@ var DynamoDBTableProvider = class {
13433
13743
  }
13434
13744
  throw new Error(`Table ${tableName} did not reach ACTIVE status within ${maxAttempts} seconds`);
13435
13745
  }
13746
+ /**
13747
+ * Adopt an existing DynamoDB table into cdkd state.
13748
+ *
13749
+ * Lookup order:
13750
+ * 1. `--resource` override or `Properties.TableName` → verify via `DescribeTable`.
13751
+ * 2. `ListTables` + `ListTagsOfResource`, match `aws:cdk:path` tag.
13752
+ *
13753
+ * Tags require the table ARN, which `DescribeTable` provides; the loop
13754
+ * therefore costs one `DescribeTable` per table just to read the ARN.
13755
+ * Acceptable for typical DynamoDB cardinalities.
13756
+ */
13757
+ async import(input) {
13758
+ const explicit = resolveExplicitPhysicalId(input, "TableName");
13759
+ if (explicit) {
13760
+ try {
13761
+ await this.dynamoDBClient.send(new DescribeTableCommand2({ TableName: explicit }));
13762
+ return { physicalId: explicit, attributes: {} };
13763
+ } catch (err) {
13764
+ if (err instanceof ResourceNotFoundException6)
13765
+ return null;
13766
+ throw err;
13767
+ }
13768
+ }
13769
+ if (!input.cdkPath)
13770
+ return null;
13771
+ let exclusiveStartTableName;
13772
+ do {
13773
+ const list = await this.dynamoDBClient.send(
13774
+ new ListTablesCommand({
13775
+ ...exclusiveStartTableName && { ExclusiveStartTableName: exclusiveStartTableName }
13776
+ })
13777
+ );
13778
+ for (const name of list.TableNames ?? []) {
13779
+ try {
13780
+ const desc = await this.dynamoDBClient.send(
13781
+ new DescribeTableCommand2({ TableName: name })
13782
+ );
13783
+ const arn = desc.Table?.TableArn;
13784
+ if (!arn)
13785
+ continue;
13786
+ const tagsResp = await this.dynamoDBClient.send(
13787
+ new ListTagsOfResourceCommand({ ResourceArn: arn })
13788
+ );
13789
+ if (matchesCdkPath(tagsResp.Tags, input.cdkPath)) {
13790
+ return { physicalId: name, attributes: {} };
13791
+ }
13792
+ } catch (err) {
13793
+ if (err instanceof ResourceNotFoundException6)
13794
+ continue;
13795
+ throw err;
13796
+ }
13797
+ }
13798
+ exclusiveStartTableName = list.LastEvaluatedTableName;
13799
+ } while (exclusiveStartTableName);
13800
+ return null;
13801
+ }
13436
13802
  };
13437
13803
 
13438
13804
  // src/provisioning/providers/logs-loggroup-provider.ts
13439
13805
  import {
13440
13806
  CreateLogGroupCommand,
13441
13807
  DeleteLogGroupCommand,
13808
+ DescribeLogGroupsCommand,
13809
+ ListTagsForResourceCommand as ListTagsForResourceCommand2,
13442
13810
  PutRetentionPolicyCommand,
13443
13811
  DeleteRetentionPolicyCommand,
13444
13812
  TagResourceCommand as TagResourceCommand2,
@@ -13662,6 +14030,61 @@ var LogsLogGroupProvider = class {
13662
14030
  return `arn:aws:logs:unknown:unknown:log-group:${logGroupName}:*`;
13663
14031
  }
13664
14032
  }
14033
+ /**
14034
+ * Adopt an existing CloudWatch Logs log group into cdkd state.
14035
+ *
14036
+ * Lookup order:
14037
+ * 1. `--resource` override or `Properties.LogGroupName` → verify via
14038
+ * `DescribeLogGroups` (filtered by name prefix).
14039
+ * 2. `aws:cdk:path` tag match via `DescribeLogGroups` + `ListTagsForResource`.
14040
+ *
14041
+ * `ListTagsForResource` for log groups uses the log-group ARN. The
14042
+ * `DescribeLogGroups` response includes the ARN, so no extra round-trip
14043
+ * is needed beyond the per-group tag lookup.
14044
+ */
14045
+ async import(input) {
14046
+ const explicit = resolveExplicitPhysicalId(input, "LogGroupName");
14047
+ if (explicit) {
14048
+ try {
14049
+ const resp = await this.logsClient.send(
14050
+ new DescribeLogGroupsCommand({ logGroupNamePrefix: explicit })
14051
+ );
14052
+ const found = resp.logGroups?.find((g) => g.logGroupName === explicit);
14053
+ return found ? { physicalId: explicit, attributes: {} } : null;
14054
+ } catch (err) {
14055
+ if (err instanceof ResourceNotFoundException7)
14056
+ return null;
14057
+ throw err;
14058
+ }
14059
+ }
14060
+ if (!input.cdkPath)
14061
+ return null;
14062
+ let nextToken;
14063
+ do {
14064
+ const list = await this.logsClient.send(
14065
+ new DescribeLogGroupsCommand({ ...nextToken && { nextToken } })
14066
+ );
14067
+ for (const g of list.logGroups ?? []) {
14068
+ if (!g.logGroupName || !g.arn)
14069
+ continue;
14070
+ const arnForTags = g.arn.replace(/:\*$/, "");
14071
+ try {
14072
+ const tagsResp = await this.logsClient.send(
14073
+ new ListTagsForResourceCommand2({ resourceArn: arnForTags })
14074
+ );
14075
+ if (tagsResp.tags?.["aws:cdk:path"] === input.cdkPath) {
14076
+ return { physicalId: g.logGroupName, attributes: {} };
14077
+ }
14078
+ } catch (err) {
14079
+ if (err instanceof ResourceNotFoundException7)
14080
+ continue;
14081
+ throw err;
14082
+ }
14083
+ }
14084
+ nextToken = list.nextToken;
14085
+ } while (nextToken);
14086
+ return null;
14087
+ }
13665
14088
  };
13666
14089
 
13667
14090
  // src/provisioning/providers/cloudwatch-alarm-provider.ts
@@ -13892,6 +14315,8 @@ var CloudWatchAlarmProvider = class {
13892
14315
  import {
13893
14316
  CreateSecretCommand,
13894
14317
  DeleteSecretCommand,
14318
+ DescribeSecretCommand,
14319
+ ListSecretsCommand,
13895
14320
  UpdateSecretCommand,
13896
14321
  TagResourceCommand as TagResourceCommand3,
13897
14322
  UntagResourceCommand as UntagResourceCommand3,
@@ -14159,10 +14584,65 @@ var SecretsManagerSecretProvider = class {
14159
14584
  }
14160
14585
  return password;
14161
14586
  }
14587
+ /**
14588
+ * Adopt an existing Secrets Manager secret into cdkd state.
14589
+ *
14590
+ * Secrets Manager physical IDs are full secret ARNs. The CDK template's
14591
+ * `Properties.Name` (secret name) is enough to fetch the ARN via
14592
+ * `DescribeSecret`.
14593
+ *
14594
+ * Lookup order:
14595
+ * 1. `--resource` override (ARN) → verify via `DescribeSecret`.
14596
+ * 2. `Properties.Name` → `DescribeSecret` (accepts name).
14597
+ * 3. `aws:cdk:path` tag match via `ListSecrets` (which already returns Tags).
14598
+ */
14599
+ async import(input) {
14600
+ if (input.knownPhysicalId) {
14601
+ try {
14602
+ const resp = await this.smClient.send(
14603
+ new DescribeSecretCommand({ SecretId: input.knownPhysicalId })
14604
+ );
14605
+ return resp.ARN ? { physicalId: resp.ARN, attributes: {} } : null;
14606
+ } catch (err) {
14607
+ if (err instanceof ResourceNotFoundException8)
14608
+ return null;
14609
+ throw err;
14610
+ }
14611
+ }
14612
+ const name = typeof input.properties?.["Name"] === "string" ? input.properties["Name"] : void 0;
14613
+ if (name) {
14614
+ try {
14615
+ const resp = await this.smClient.send(new DescribeSecretCommand({ SecretId: name }));
14616
+ return resp.ARN ? { physicalId: resp.ARN, attributes: {} } : null;
14617
+ } catch (err) {
14618
+ if (err instanceof ResourceNotFoundException8)
14619
+ return null;
14620
+ throw err;
14621
+ }
14622
+ }
14623
+ if (!input.cdkPath)
14624
+ return null;
14625
+ let nextToken;
14626
+ do {
14627
+ const list = await this.smClient.send(
14628
+ new ListSecretsCommand({ ...nextToken && { NextToken: nextToken } })
14629
+ );
14630
+ for (const s of list.SecretList ?? []) {
14631
+ if (s.ARN && matchesCdkPath(s.Tags, input.cdkPath)) {
14632
+ return { physicalId: s.ARN, attributes: {} };
14633
+ }
14634
+ }
14635
+ nextToken = list.NextToken;
14636
+ } while (nextToken);
14637
+ return null;
14638
+ }
14162
14639
  };
14163
14640
 
14164
14641
  // src/provisioning/providers/ssm-parameter-provider.ts
14165
14642
  import {
14643
+ DescribeParametersCommand,
14644
+ GetParameterCommand as GetParameterCommand3,
14645
+ ListTagsForResourceCommand as ListTagsForResourceCommand3,
14166
14646
  PutParameterCommand,
14167
14647
  DeleteParameterCommand,
14168
14648
  AddTagsToResourceCommand,
@@ -14374,6 +14854,57 @@ var SSMParameterProvider = class {
14374
14854
  );
14375
14855
  }
14376
14856
  }
14857
+ /**
14858
+ * Adopt an existing SSM parameter into cdkd state.
14859
+ *
14860
+ * SSM physical IDs ARE the parameter names (`/foo/bar`). The CDK template
14861
+ * usually carries `Properties.Name` explicitly, so the explicit-name path
14862
+ * covers most cases. The tag-based fallback is rarely needed.
14863
+ *
14864
+ * Lookup order:
14865
+ * 1. `--resource` override or `Properties.Name` → verify via `GetParameter`.
14866
+ * 2. `aws:cdk:path` tag match via `DescribeParameters` + `ListTagsForResource`
14867
+ * (`ResourceType: 'Parameter'`, `ResourceId: <name>`).
14868
+ */
14869
+ async import(input) {
14870
+ const explicit = resolveExplicitPhysicalId(input, "Name");
14871
+ if (explicit) {
14872
+ try {
14873
+ await this.ssmClient.send(new GetParameterCommand3({ Name: explicit }));
14874
+ return { physicalId: explicit, attributes: {} };
14875
+ } catch (err) {
14876
+ if (err instanceof ParameterNotFound)
14877
+ return null;
14878
+ throw err;
14879
+ }
14880
+ }
14881
+ if (!input.cdkPath)
14882
+ return null;
14883
+ let nextToken;
14884
+ do {
14885
+ const list = await this.ssmClient.send(
14886
+ new DescribeParametersCommand({ ...nextToken && { NextToken: nextToken } })
14887
+ );
14888
+ for (const p of list.Parameters ?? []) {
14889
+ if (!p.Name)
14890
+ continue;
14891
+ try {
14892
+ const tagsResp = await this.ssmClient.send(
14893
+ new ListTagsForResourceCommand3({ ResourceType: "Parameter", ResourceId: p.Name })
14894
+ );
14895
+ if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
14896
+ return { physicalId: p.Name, attributes: {} };
14897
+ }
14898
+ } catch (err) {
14899
+ if (err instanceof ParameterNotFound)
14900
+ continue;
14901
+ throw err;
14902
+ }
14903
+ }
14904
+ nextToken = list.NextToken;
14905
+ } while (nextToken);
14906
+ return null;
14907
+ }
14377
14908
  };
14378
14909
 
14379
14910
  // src/provisioning/providers/eventbridge-rule-provider.ts
@@ -14672,7 +15203,9 @@ import {
14672
15203
  DeleteEventBusCommand,
14673
15204
  UpdateEventBusCommand,
14674
15205
  DescribeEventBusCommand,
15206
+ ListEventBusesCommand,
14675
15207
  ListRulesCommand,
15208
+ ListTagsForResourceCommand as ListTagsForResourceCommand4,
14676
15209
  RemoveTargetsCommand as RemoveTargetsCommand2,
14677
15210
  DeleteRuleCommand as DeleteRuleCommand2,
14678
15211
  ListTargetsByRuleCommand as ListTargetsByRuleCommand2,
@@ -14913,6 +15446,52 @@ var EventBridgeBusProvider = class {
14913
15446
  );
14914
15447
  }
14915
15448
  }
15449
+ /**
15450
+ * Adopt an existing EventBridge event bus into cdkd state.
15451
+ *
15452
+ * Lookup order:
15453
+ * 1. `--resource` override or `Properties.Name` → verify via `DescribeEventBus`.
15454
+ * 2. `aws:cdk:path` tag match via `ListEventBuses` + `ListTagsForResource`.
15455
+ */
15456
+ async import(input) {
15457
+ const explicit = resolveExplicitPhysicalId(input, "Name");
15458
+ if (explicit) {
15459
+ try {
15460
+ await this.eventBridgeClient.send(new DescribeEventBusCommand({ Name: explicit }));
15461
+ return { physicalId: explicit, attributes: {} };
15462
+ } catch (err) {
15463
+ if (err instanceof ResourceNotFoundException10)
15464
+ return null;
15465
+ throw err;
15466
+ }
15467
+ }
15468
+ if (!input.cdkPath)
15469
+ return null;
15470
+ let nextToken;
15471
+ do {
15472
+ const list = await this.eventBridgeClient.send(
15473
+ new ListEventBusesCommand({ ...nextToken && { NextToken: nextToken } })
15474
+ );
15475
+ for (const bus of list.EventBuses ?? []) {
15476
+ if (!bus.Name || !bus.Arn)
15477
+ continue;
15478
+ try {
15479
+ const tagsResp = await this.eventBridgeClient.send(
15480
+ new ListTagsForResourceCommand4({ ResourceARN: bus.Arn })
15481
+ );
15482
+ if (matchesCdkPath(tagsResp.Tags, input.cdkPath)) {
15483
+ return { physicalId: bus.Name, attributes: {} };
15484
+ }
15485
+ } catch (err) {
15486
+ if (err instanceof ResourceNotFoundException10)
15487
+ continue;
15488
+ throw err;
15489
+ }
15490
+ }
15491
+ nextToken = list.NextToken;
15492
+ } while (nextToken);
15493
+ return null;
15494
+ }
14916
15495
  };
14917
15496
 
14918
15497
  // src/provisioning/providers/ec2-provider.ts
@@ -24219,6 +24798,10 @@ var GlueProvider = class {
24219
24798
  import {
24220
24799
  KMSClient as KMSClient2,
24221
24800
  CreateKeyCommand,
24801
+ DescribeKeyCommand,
24802
+ ListAliasesCommand as ListAliasesCommand2,
24803
+ ListKeysCommand,
24804
+ ListResourceTagsCommand,
24222
24805
  ScheduleKeyDeletionCommand,
24223
24806
  CreateAliasCommand,
24224
24807
  DeleteAliasCommand,
@@ -24601,6 +25184,81 @@ var KMSProvider = class {
24601
25184
  );
24602
25185
  }
24603
25186
  }
25187
+ /**
25188
+ * Adopt an existing KMS key or alias into cdkd state.
25189
+ *
25190
+ * KMS keys have no `Properties.KeyName` field — physical IDs are
25191
+ * AWS-generated UUIDs. So:
25192
+ * - For `AWS::KMS::Key`: `--resource MyKey=<keyId>` is the only explicit
25193
+ * path; auto-lookup walks `ListKeys` + `ListResourceTags` matching
25194
+ * `aws:cdk:path`.
25195
+ * - For `AWS::KMS::Alias`: `Properties.AliasName` is explicit and reliable.
25196
+ */
25197
+ async import(input) {
25198
+ if (input.resourceType === "AWS::KMS::Alias") {
25199
+ const aliasName = input.knownPhysicalId ?? (typeof input.properties?.["AliasName"] === "string" ? input.properties["AliasName"] : void 0);
25200
+ if (!aliasName)
25201
+ return null;
25202
+ try {
25203
+ let marker2;
25204
+ do {
25205
+ const list = await this.getClient().send(
25206
+ new ListAliasesCommand2({ ...marker2 && { Marker: marker2 } })
25207
+ );
25208
+ const found = list.Aliases?.find(
25209
+ (a) => a.AliasName === aliasName
25210
+ );
25211
+ if (found)
25212
+ return { physicalId: aliasName, attributes: {} };
25213
+ marker2 = list.NextMarker;
25214
+ } while (marker2);
25215
+ return null;
25216
+ } catch (err) {
25217
+ if (err instanceof NotFoundException5)
25218
+ return null;
25219
+ throw err;
25220
+ }
25221
+ }
25222
+ if (input.knownPhysicalId) {
25223
+ try {
25224
+ await this.getClient().send(new DescribeKeyCommand({ KeyId: input.knownPhysicalId }));
25225
+ return { physicalId: input.knownPhysicalId, attributes: {} };
25226
+ } catch (err) {
25227
+ if (err instanceof NotFoundException5)
25228
+ return null;
25229
+ throw err;
25230
+ }
25231
+ }
25232
+ if (!input.cdkPath)
25233
+ return null;
25234
+ let marker;
25235
+ do {
25236
+ const list = await this.getClient().send(
25237
+ new ListKeysCommand({ ...marker && { Marker: marker } })
25238
+ );
25239
+ for (const key of list.Keys ?? []) {
25240
+ if (!key.KeyId)
25241
+ continue;
25242
+ try {
25243
+ const tagsResp = await this.getClient().send(
25244
+ new ListResourceTagsCommand({ KeyId: key.KeyId })
25245
+ );
25246
+ for (const tag of tagsResp.Tags ?? []) {
25247
+ if (tag.TagKey === CDK_PATH_TAG && tag.TagValue === input.cdkPath) {
25248
+ return { physicalId: key.KeyId, attributes: {} };
25249
+ }
25250
+ }
25251
+ } catch (err) {
25252
+ const name = err.name;
25253
+ if (name === "AccessDeniedException" || err instanceof NotFoundException5)
25254
+ continue;
25255
+ throw err;
25256
+ }
25257
+ }
25258
+ marker = list.NextMarker;
25259
+ } while (marker);
25260
+ return null;
25261
+ }
24604
25262
  };
24605
25263
 
24606
25264
  // src/provisioning/providers/kinesis-provider.ts
@@ -26579,7 +27237,7 @@ import {
26579
27237
  CreateTableCommand as CreateTableCommand3,
26580
27238
  DeleteTableCommand as DeleteTableCommand3,
26581
27239
  ListNamespacesCommand,
26582
- ListTablesCommand,
27240
+ ListTablesCommand as ListTablesCommand2,
26583
27241
  NotFoundException as NotFoundException6
26584
27242
  } from "@aws-sdk/client-s3tables";
26585
27243
  var S3TablesProvider = class {
@@ -26725,7 +27383,7 @@ var S3TablesProvider = class {
26725
27383
  let tableContinuationToken;
26726
27384
  do {
26727
27385
  const tablesResult = await this.getClient().send(
26728
- new ListTablesCommand({
27386
+ new ListTablesCommand2({
26729
27387
  tableBucketARN,
26730
27388
  namespace: namespaceName,
26731
27389
  continuationToken: tableContinuationToken
@@ -29572,7 +30230,7 @@ import {
29572
30230
  CreateBucketCommand as CreateBucketCommand4,
29573
30231
  DeleteBucketCommand as DeleteBucketCommand3,
29574
30232
  DeleteObjectsCommand as DeleteObjectsCommand3,
29575
- HeadBucketCommand as HeadBucketCommand3,
30233
+ HeadBucketCommand as HeadBucketCommand4,
29576
30234
  ListObjectVersionsCommand as ListObjectVersionsCommand2,
29577
30235
  ListObjectsV2Command as ListObjectsV2Command3,
29578
30236
  PutBucketEncryptionCommand as PutBucketEncryptionCommand3,
@@ -29695,7 +30353,7 @@ async function stateMigrateBucketCommand(options) {
29695
30353
  }
29696
30354
  async function bucketExists2(s3, bucketName) {
29697
30355
  try {
29698
- await s3.send(new HeadBucketCommand3({ Bucket: bucketName }));
30356
+ await s3.send(new HeadBucketCommand4({ Bucket: bucketName }));
29699
30357
  return true;
29700
30358
  } catch (error) {
29701
30359
  const err = error;
@@ -30557,6 +31215,341 @@ function createStateCommand() {
30557
31215
  return cmd;
30558
31216
  }
30559
31217
 
31218
+ // src/cli/commands/import.ts
31219
+ import { readFileSync as readFileSync5 } from "node:fs";
31220
+ import * as readline4 from "node:readline/promises";
31221
+ import { Command as Command11 } from "commander";
31222
+ init_aws_clients();
31223
+ async function importCommand(stackArg, options) {
31224
+ const logger = getLogger();
31225
+ if (options.verbose) {
31226
+ logger.setLevel("debug");
31227
+ process.env["CDKD_NO_LIVE"] = "1";
31228
+ }
31229
+ const region = options.region || process.env["AWS_REGION"] || "us-east-1";
31230
+ const stateBucket = await resolveStateBucketWithDefault(options.stateBucket, region);
31231
+ if (options.region) {
31232
+ process.env["AWS_REGION"] = options.region;
31233
+ process.env["AWS_DEFAULT_REGION"] = options.region;
31234
+ }
31235
+ const awsClients = new AwsClients({
31236
+ ...options.region && { region: options.region },
31237
+ ...options.profile && { profile: options.profile }
31238
+ });
31239
+ setAwsClients(awsClients);
31240
+ try {
31241
+ const stateConfig = { bucket: stateBucket, prefix: options.statePrefix };
31242
+ const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
31243
+ ...options.region && { region: options.region },
31244
+ ...options.profile && { profile: options.profile }
31245
+ });
31246
+ await stateBackend.verifyBucketExists();
31247
+ const lockManager = new LockManager(awsClients.s3, stateConfig);
31248
+ const providerRegistry = new ProviderRegistry();
31249
+ registerAllProviders(providerRegistry);
31250
+ const appCmd = options.app || resolveApp();
31251
+ if (!appCmd) {
31252
+ throw new Error(
31253
+ "`cdkd state import` requires a CDK app: pass --app or set it in cdk.json. The template is read to find logical IDs, resource types, and dependencies."
31254
+ );
31255
+ }
31256
+ logger.info("Synthesizing CDK app to read template...");
31257
+ const synthesizer = new Synthesizer();
31258
+ const context = parseContextOptions(options.context);
31259
+ const result = await synthesizer.synthesize({
31260
+ app: appCmd,
31261
+ output: options.output || "cdk.out",
31262
+ ...Object.keys(context).length > 0 && { context }
31263
+ });
31264
+ let stackInfo;
31265
+ if (stackArg) {
31266
+ stackInfo = result.stacks.find((s) => s.stackName === stackArg || s.displayName === stackArg);
31267
+ if (!stackInfo) {
31268
+ throw new Error(
31269
+ `Stack '${stackArg}' not found in synthesized app. Available: ${result.stacks.map((s) => s.stackName).join(", ")}`
31270
+ );
31271
+ }
31272
+ } else if (result.stacks.length === 1) {
31273
+ stackInfo = result.stacks[0];
31274
+ } else {
31275
+ throw new Error(
31276
+ `Multiple stacks found: ${result.stacks.map((s) => s.stackName).join(", ")}. Specify the stack name as a positional argument.`
31277
+ );
31278
+ }
31279
+ const targetRegion = stackInfo.region || region;
31280
+ logger.info(`Target stack: ${stackInfo.stackName} (${targetRegion})`);
31281
+ const existing = await stateBackend.stateExists(stackInfo.stackName, targetRegion);
31282
+ if (existing && !options.force) {
31283
+ throw new Error(
31284
+ `State already exists for stack '${stackInfo.stackName}' (${targetRegion}). Pass --force to overwrite. (cdkd state import rebuilds the resource map from AWS, so the existing state \u2014 including any drift you've manually edited \u2014 will be lost.)`
31285
+ );
31286
+ }
31287
+ const overrides = parseResourceOverrides(options.resource, options.resourceMapping);
31288
+ if (overrides.size > 0) {
31289
+ logger.debug(`User-supplied physical IDs: ${[...overrides.keys()].join(", ")}`);
31290
+ }
31291
+ const template = stackInfo.template;
31292
+ const templateParser = new TemplateParser();
31293
+ const resources = collectImportableResources(template);
31294
+ logger.info(`Found ${resources.length} resource(s) in template`);
31295
+ const owner = `${process.env["USER"] || "unknown"}@${process.env["HOSTNAME"] || "host"}:${process.pid}`;
31296
+ await lockManager.acquireLock(stackInfo.stackName, targetRegion, owner, "import");
31297
+ try {
31298
+ const rows = [];
31299
+ for (const { logicalId, resource } of resources) {
31300
+ const outcome = await importOne({
31301
+ logicalId,
31302
+ resource,
31303
+ stackName: stackInfo.stackName,
31304
+ region: targetRegion,
31305
+ providerRegistry,
31306
+ override: overrides.get(logicalId)
31307
+ });
31308
+ rows.push(outcome);
31309
+ }
31310
+ printSummary(rows);
31311
+ if (options.dryRun) {
31312
+ logger.info("--dry-run: state will NOT be written. Re-run without --dry-run to apply.");
31313
+ return;
31314
+ }
31315
+ const importedRows = rows.filter((r) => r.outcome === "imported");
31316
+ if (importedRows.length === 0) {
31317
+ logger.warn("No resources were successfully imported. State will not be written.");
31318
+ return;
31319
+ }
31320
+ if (!options.yes) {
31321
+ const ok = await confirmPrompt2(
31322
+ `Write state for ${stackInfo.stackName} (${targetRegion}) with ${importedRows.length} resource(s)?`
31323
+ );
31324
+ if (!ok) {
31325
+ logger.info("Import cancelled.");
31326
+ return;
31327
+ }
31328
+ }
31329
+ const stackState = buildStackState(
31330
+ stackInfo.stackName,
31331
+ targetRegion,
31332
+ rows,
31333
+ templateParser,
31334
+ template
31335
+ );
31336
+ await stateBackend.saveState(stackInfo.stackName, targetRegion, stackState);
31337
+ logger.info(`\u2713 State written: ${stackInfo.stackName} (${targetRegion})`);
31338
+ logger.info(
31339
+ ` ${importedRows.length} resource(s) imported. Run 'cdkd diff' to see how the imported state lines up with the template.`
31340
+ );
31341
+ } finally {
31342
+ await lockManager.releaseLock(stackInfo.stackName, targetRegion).catch((err) => {
31343
+ logger.warn(`Failed to release lock: ${err instanceof Error ? err.message : String(err)}`);
31344
+ });
31345
+ }
31346
+ } finally {
31347
+ awsClients.destroy();
31348
+ }
31349
+ }
31350
+ async function importOne(task) {
31351
+ const logger = getLogger();
31352
+ const { logicalId, resource, stackName, region, providerRegistry, override } = task;
31353
+ if (!providerRegistry.hasProvider(resource.Type)) {
31354
+ return {
31355
+ logicalId,
31356
+ resourceType: resource.Type,
31357
+ outcome: "skipped-no-impl",
31358
+ reason: "no provider registered"
31359
+ };
31360
+ }
31361
+ const provider = providerRegistry.getProvider(resource.Type);
31362
+ if (!provider.import) {
31363
+ return {
31364
+ logicalId,
31365
+ resourceType: resource.Type,
31366
+ outcome: "skipped-no-impl",
31367
+ reason: `provider does not implement import (yet)`
31368
+ };
31369
+ }
31370
+ const cdkPath = readCdkPath(resource);
31371
+ const input = {
31372
+ logicalId,
31373
+ resourceType: resource.Type,
31374
+ cdkPath,
31375
+ stackName,
31376
+ region,
31377
+ properties: resource.Properties ?? {},
31378
+ ...override !== void 0 && { knownPhysicalId: override }
31379
+ };
31380
+ try {
31381
+ const result = await provider.import(input);
31382
+ if (!result) {
31383
+ return {
31384
+ logicalId,
31385
+ resourceType: resource.Type,
31386
+ outcome: "skipped-not-found",
31387
+ reason: "no matching AWS resource"
31388
+ };
31389
+ }
31390
+ return {
31391
+ logicalId,
31392
+ resourceType: resource.Type,
31393
+ outcome: "imported",
31394
+ physicalId: result.physicalId
31395
+ };
31396
+ } catch (error) {
31397
+ const msg = error instanceof Error ? error.message : String(error);
31398
+ logger.error(`Failed to import ${logicalId} (${resource.Type}): ${msg}`);
31399
+ return {
31400
+ logicalId,
31401
+ resourceType: resource.Type,
31402
+ outcome: "failed",
31403
+ reason: msg
31404
+ };
31405
+ }
31406
+ }
31407
+ function parseResourceOverrides(flags, mappingFile) {
31408
+ const map = /* @__PURE__ */ new Map();
31409
+ if (mappingFile) {
31410
+ let parsed;
31411
+ try {
31412
+ parsed = JSON.parse(readFileSync5(mappingFile, "utf-8"));
31413
+ } catch (err) {
31414
+ throw new Error(
31415
+ `Failed to read --resource-mapping file '${mappingFile}': ` + (err instanceof Error ? err.message : String(err))
31416
+ );
31417
+ }
31418
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
31419
+ throw new Error(
31420
+ `--resource-mapping file '${mappingFile}' must be a JSON object {logicalId: physicalId}`
31421
+ );
31422
+ }
31423
+ for (const [key, value] of Object.entries(parsed)) {
31424
+ if (typeof value !== "string") {
31425
+ throw new Error(
31426
+ `--resource-mapping: value for '${key}' must be a string, got ${typeof value}`
31427
+ );
31428
+ }
31429
+ map.set(key, value);
31430
+ }
31431
+ }
31432
+ for (const entry of flags ?? []) {
31433
+ const eq = entry.indexOf("=");
31434
+ if (eq <= 0 || eq === entry.length - 1) {
31435
+ throw new Error(`--resource expects 'logicalId=physicalId', got '${entry}'`);
31436
+ }
31437
+ map.set(entry.slice(0, eq), entry.slice(eq + 1));
31438
+ }
31439
+ return map;
31440
+ }
31441
+ function readCdkPath(resource) {
31442
+ const meta = resource.Metadata;
31443
+ if (!meta)
31444
+ return "";
31445
+ const v = meta["aws:cdk:path"];
31446
+ return typeof v === "string" ? v : "";
31447
+ }
31448
+ function collectImportableResources(template) {
31449
+ const out = [];
31450
+ for (const [logicalId, resource] of Object.entries(template.Resources)) {
31451
+ if (resource.Type === "AWS::CDK::Metadata")
31452
+ continue;
31453
+ out.push({ logicalId, resource });
31454
+ }
31455
+ return out;
31456
+ }
31457
+ function buildStackState(stackName, region, rows, templateParser, template) {
31458
+ const resources = {};
31459
+ for (const row of rows) {
31460
+ if (row.outcome !== "imported" || !row.physicalId)
31461
+ continue;
31462
+ const tmplResource = template.Resources[row.logicalId];
31463
+ if (!tmplResource)
31464
+ continue;
31465
+ const deps = templateParser.extractDependencies(tmplResource);
31466
+ resources[row.logicalId] = {
31467
+ physicalId: row.physicalId,
31468
+ resourceType: row.resourceType,
31469
+ properties: tmplResource.Properties ?? {},
31470
+ attributes: {},
31471
+ dependencies: [...deps]
31472
+ };
31473
+ }
31474
+ return {
31475
+ version: 2,
31476
+ stackName,
31477
+ region,
31478
+ resources,
31479
+ outputs: {},
31480
+ lastModified: Date.now()
31481
+ };
31482
+ }
31483
+ function printSummary(rows) {
31484
+ const logger = getLogger();
31485
+ const counts = {
31486
+ imported: 0,
31487
+ "skipped-no-impl": 0,
31488
+ "skipped-not-found": 0,
31489
+ failed: 0
31490
+ };
31491
+ logger.info("");
31492
+ logger.info("Import plan:");
31493
+ for (const r of rows) {
31494
+ counts[r.outcome]++;
31495
+ const tag = formatOutcome(r.outcome);
31496
+ const detail = r.outcome === "imported" ? ` (${r.physicalId})` : r.reason ? ` \u2014 ${r.reason}` : "";
31497
+ logger.info(` ${tag} ${r.logicalId} (${r.resourceType})${detail}`);
31498
+ }
31499
+ logger.info("");
31500
+ logger.info(
31501
+ `Summary: ${counts.imported} imported, ${counts["skipped-not-found"]} not found, ${counts["skipped-no-impl"]} unsupported, ${counts.failed} failed`
31502
+ );
31503
+ }
31504
+ function formatOutcome(outcome) {
31505
+ switch (outcome) {
31506
+ case "imported":
31507
+ return "\u2713";
31508
+ case "skipped-not-found":
31509
+ return "\xB7";
31510
+ case "skipped-no-impl":
31511
+ return "?";
31512
+ case "failed":
31513
+ return "\u2717";
31514
+ }
31515
+ }
31516
+ async function confirmPrompt2(prompt) {
31517
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
31518
+ try {
31519
+ const ans = await rl.question(`${prompt} [y/N] `);
31520
+ return /^y(es)?$/i.test(ans.trim());
31521
+ } finally {
31522
+ rl.close();
31523
+ }
31524
+ }
31525
+ function createImportCommand() {
31526
+ const cmd = new Command11("import").description(
31527
+ "Adopt already-deployed AWS resources into cdkd state. Reads the CDK app to find logical IDs, resource types, and dependencies; uses the aws:cdk:path tag (or explicit --resource overrides) to find each resource in AWS."
31528
+ ).argument(
31529
+ "[stack]",
31530
+ "Stack to import. Optional when the synthesized app contains exactly one stack."
31531
+ ).option(
31532
+ "--resource <id=physical>",
31533
+ "Explicit physical-id override for one logical ID. Repeatable. Bypasses tag-based auto-lookup for that resource only.",
31534
+ collectMultiple,
31535
+ []
31536
+ ).option(
31537
+ "--resource-mapping <file>",
31538
+ "Path to a JSON file of {logicalId: physicalId} overrides (CDK CLI `cdk import --resource-mapping` compatible)."
31539
+ ).option("--dry-run", "Show planned imports without writing state", false).option(
31540
+ "--force",
31541
+ "Overwrite an existing state record. Without this, an existing state file aborts the import.",
31542
+ false
31543
+ ).action(withErrorHandling(importCommand));
31544
+ [...commonOptions, ...appOptions, ...stateOptions, ...contextOptions].forEach(
31545
+ (o) => cmd.addOption(o)
31546
+ );
31547
+ return cmd;
31548
+ }
31549
+ function collectMultiple(value, previous) {
31550
+ return [...previous ?? [], value];
31551
+ }
31552
+
30560
31553
  // src/cli/index.ts
30561
31554
  var SUBCOMMANDS = /* @__PURE__ */ new Set([
30562
31555
  "bootstrap",
@@ -30566,6 +31559,7 @@ var SUBCOMMANDS = /* @__PURE__ */ new Set([
30566
31559
  "deploy",
30567
31560
  "diff",
30568
31561
  "destroy",
31562
+ "import",
30569
31563
  "publish-assets",
30570
31564
  "force-unlock",
30571
31565
  "state"
@@ -30581,14 +31575,15 @@ function reorderArgs(argv) {
30581
31575
  return [...prefix, ...cmdAndAfter, ...beforeCmd];
30582
31576
  }
30583
31577
  async function main() {
30584
- const program = new Command11();
30585
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.14.0");
31578
+ const program = new Command12();
31579
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.15.0");
30586
31580
  program.addCommand(createBootstrapCommand());
30587
31581
  program.addCommand(createSynthCommand());
30588
31582
  program.addCommand(createListCommand());
30589
31583
  program.addCommand(createDeployCommand());
30590
31584
  program.addCommand(createDiffCommand());
30591
31585
  program.addCommand(createDestroyCommand());
31586
+ program.addCommand(createImportCommand());
30592
31587
  program.addCommand(createPublishAssetsCommand());
30593
31588
  program.addCommand(createForceUnlockCommand());
30594
31589
  program.addCommand(createStateCommand());