@go-to-k/cdkd 0.13.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/README.md +10 -1
- package/dist/cli.js +1339 -32
- package/dist/cli.js.map +4 -4
- package/dist/go-to-k-cdkd-0.15.0.tgz +0 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.13.0.tgz +0 -0
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
|
|
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";
|
|
@@ -998,15 +998,15 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
|
|
|
998
998
|
return syncResult;
|
|
999
999
|
const logger = getLogger();
|
|
1000
1000
|
logger.debug("No state bucket specified, resolving default from account...");
|
|
1001
|
-
const { GetCallerIdentityCommand:
|
|
1002
|
-
const { S3Client:
|
|
1001
|
+
const { GetCallerIdentityCommand: GetCallerIdentityCommand9 } = await import("@aws-sdk/client-sts");
|
|
1002
|
+
const { S3Client: S3Client11 } = await import("@aws-sdk/client-s3");
|
|
1003
1003
|
const { getAwsClients: getAwsClients2 } = await Promise.resolve().then(() => (init_aws_clients(), aws_clients_exports));
|
|
1004
1004
|
const awsClients = getAwsClients2();
|
|
1005
|
-
const identity = await awsClients.sts.send(new
|
|
1005
|
+
const identity = await awsClients.sts.send(new GetCallerIdentityCommand9({}));
|
|
1006
1006
|
const accountId = identity.Account;
|
|
1007
1007
|
const newName = getDefaultStateBucketName(accountId);
|
|
1008
1008
|
const legacyName = getLegacyStateBucketName(accountId, region);
|
|
1009
|
-
const probe = new
|
|
1009
|
+
const probe = new S3Client11({ region: "us-east-1" });
|
|
1010
1010
|
try {
|
|
1011
1011
|
if (await bucketExists(probe, newName)) {
|
|
1012
1012
|
logger.debug(`State bucket: ${newName}`);
|
|
@@ -1014,7 +1014,11 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
|
|
|
1014
1014
|
}
|
|
1015
1015
|
if (await bucketExists(probe, legacyName)) {
|
|
1016
1016
|
logger.warn(
|
|
1017
|
-
`Using legacy state bucket name '${legacyName}'. The default has changed to '${newName}'.
|
|
1017
|
+
`Using legacy state bucket name '${legacyName}'. The default has changed to '${newName}'. To migrate, run:
|
|
1018
|
+
|
|
1019
|
+
cdkd state migrate-bucket --region ${region}
|
|
1020
|
+
|
|
1021
|
+
(add --remove-legacy to delete the legacy bucket after a successful copy; legacy support will be dropped in a future release.)`
|
|
1018
1022
|
);
|
|
1019
1023
|
return { bucket: legacyName, source: "default-legacy" };
|
|
1020
1024
|
}
|
|
@@ -1026,9 +1030,9 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
|
|
|
1026
1030
|
}
|
|
1027
1031
|
}
|
|
1028
1032
|
async function bucketExists(client, bucketName) {
|
|
1029
|
-
const { HeadBucketCommand:
|
|
1033
|
+
const { HeadBucketCommand: HeadBucketCommand5 } = await import("@aws-sdk/client-s3");
|
|
1030
1034
|
try {
|
|
1031
|
-
await client.send(new
|
|
1035
|
+
await client.send(new HeadBucketCommand5({ Bucket: bucketName }));
|
|
1032
1036
|
return true;
|
|
1033
1037
|
} catch (error) {
|
|
1034
1038
|
const err = error;
|
|
@@ -1072,10 +1076,10 @@ async function bootstrapCommand(options) {
|
|
|
1072
1076
|
logger.info(`Using default state bucket: ${bucketName}`);
|
|
1073
1077
|
}
|
|
1074
1078
|
try {
|
|
1075
|
-
let
|
|
1079
|
+
let bucketExists3 = false;
|
|
1076
1080
|
try {
|
|
1077
1081
|
await s3Client.send(new HeadBucketCommand({ Bucket: bucketName }));
|
|
1078
|
-
|
|
1082
|
+
bucketExists3 = true;
|
|
1079
1083
|
logger.info(`Bucket ${bucketName} already exists`);
|
|
1080
1084
|
} catch (error) {
|
|
1081
1085
|
const err = error;
|
|
@@ -1085,7 +1089,7 @@ async function bootstrapCommand(options) {
|
|
|
1085
1089
|
throw normalizeAwsError(error, { bucket: bucketName, operation: "HeadBucket" });
|
|
1086
1090
|
}
|
|
1087
1091
|
}
|
|
1088
|
-
if (
|
|
1092
|
+
if (bucketExists3) {
|
|
1089
1093
|
if (!options.force) {
|
|
1090
1094
|
logger.warn(
|
|
1091
1095
|
`Bucket ${bucketName} already exists. Use --force to reconfigure (this will not delete existing state)`
|
|
@@ -3239,9 +3243,9 @@ var AssetPublisher = class {
|
|
|
3239
3243
|
const region = options.region || process.env["AWS_REGION"] || "us-east-1";
|
|
3240
3244
|
let accountId = options.accountId;
|
|
3241
3245
|
if (!accountId) {
|
|
3242
|
-
const { STSClient: STSClient7, GetCallerIdentityCommand:
|
|
3246
|
+
const { STSClient: STSClient7, GetCallerIdentityCommand: GetCallerIdentityCommand9 } = await import("@aws-sdk/client-sts");
|
|
3243
3247
|
const stsClient = new STSClient7({ region });
|
|
3244
|
-
const identity = await stsClient.send(new
|
|
3248
|
+
const identity = await stsClient.send(new GetCallerIdentityCommand9({}));
|
|
3245
3249
|
accountId = identity.Account;
|
|
3246
3250
|
stsClient.destroy();
|
|
3247
3251
|
}
|
|
@@ -7670,6 +7674,8 @@ import {
|
|
|
7670
7674
|
UntagRoleCommand,
|
|
7671
7675
|
PutRolePermissionsBoundaryCommand,
|
|
7672
7676
|
DeleteRolePermissionsBoundaryCommand,
|
|
7677
|
+
ListRolesCommand,
|
|
7678
|
+
ListRoleTagsCommand,
|
|
7673
7679
|
NoSuchEntityException
|
|
7674
7680
|
} from "@aws-sdk/client-iam";
|
|
7675
7681
|
init_aws_clients();
|
|
@@ -7778,6 +7784,32 @@ function applyDefaultNameForFallback(logicalId, resourceType, properties) {
|
|
|
7778
7784
|
};
|
|
7779
7785
|
}
|
|
7780
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
|
+
|
|
7781
7813
|
// src/provisioning/providers/iam-role-provider.ts
|
|
7782
7814
|
var IAMRoleProvider = class {
|
|
7783
7815
|
iamClient;
|
|
@@ -8284,6 +8316,58 @@ var IAMRoleProvider = class {
|
|
|
8284
8316
|
this.logger.debug(`Added/updated ${tagsToAdd.length} tags on role ${roleName}`);
|
|
8285
8317
|
}
|
|
8286
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
|
+
}
|
|
8287
8371
|
};
|
|
8288
8372
|
|
|
8289
8373
|
// src/provisioning/providers/iam-policy-provider.ts
|
|
@@ -9864,6 +9948,8 @@ var IAMUserGroupProvider = class {
|
|
|
9864
9948
|
import {
|
|
9865
9949
|
CreateBucketCommand as CreateBucketCommand2,
|
|
9866
9950
|
DeleteBucketCommand,
|
|
9951
|
+
HeadBucketCommand as HeadBucketCommand3,
|
|
9952
|
+
ListBucketsCommand,
|
|
9867
9953
|
PutBucketVersioningCommand as PutBucketVersioningCommand2,
|
|
9868
9954
|
PutBucketTaggingCommand,
|
|
9869
9955
|
PutBucketOwnershipControlsCommand,
|
|
@@ -9881,6 +9967,7 @@ import {
|
|
|
9881
9967
|
PutBucketInventoryConfigurationCommand,
|
|
9882
9968
|
PutBucketReplicationCommand,
|
|
9883
9969
|
PutObjectLockConfigurationCommand,
|
|
9970
|
+
GetBucketTaggingCommand,
|
|
9884
9971
|
NoSuchBucket,
|
|
9885
9972
|
ListObjectVersionsCommand,
|
|
9886
9973
|
DeleteObjectsCommand
|
|
@@ -10719,6 +10806,53 @@ var S3BucketProvider = class {
|
|
|
10719
10806
|
);
|
|
10720
10807
|
}
|
|
10721
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
|
+
}
|
|
10722
10856
|
/**
|
|
10723
10857
|
* Delete a bucket, emptying it first if not empty.
|
|
10724
10858
|
* Handles the race condition where objects (e.g., ALB logs) are written
|
|
@@ -10955,6 +11089,9 @@ import {
|
|
|
10955
11089
|
CreateQueueCommand,
|
|
10956
11090
|
DeleteQueueCommand,
|
|
10957
11091
|
GetQueueAttributesCommand,
|
|
11092
|
+
GetQueueUrlCommand,
|
|
11093
|
+
ListQueuesCommand,
|
|
11094
|
+
ListQueueTagsCommand,
|
|
10958
11095
|
SetQueueAttributesCommand,
|
|
10959
11096
|
QueueDoesNotExist
|
|
10960
11097
|
} from "@aws-sdk/client-sqs";
|
|
@@ -11163,6 +11300,68 @@ var SQSQueueProvider = class {
|
|
|
11163
11300
|
return `arn:aws:sqs:unknown:unknown:${queueName}`;
|
|
11164
11301
|
}
|
|
11165
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
|
+
}
|
|
11166
11365
|
};
|
|
11167
11366
|
|
|
11168
11367
|
// src/provisioning/providers/sqs-queue-policy-provider.ts
|
|
@@ -11325,6 +11524,9 @@ var SQSQueuePolicyProvider = class {
|
|
|
11325
11524
|
import {
|
|
11326
11525
|
CreateTopicCommand,
|
|
11327
11526
|
DeleteTopicCommand,
|
|
11527
|
+
GetTopicAttributesCommand,
|
|
11528
|
+
ListTopicsCommand,
|
|
11529
|
+
ListTagsForResourceCommand,
|
|
11328
11530
|
SetTopicAttributesCommand,
|
|
11329
11531
|
TagResourceCommand,
|
|
11330
11532
|
UntagResourceCommand,
|
|
@@ -11614,6 +11816,63 @@ var SNSTopicProvider = class {
|
|
|
11614
11816
|
);
|
|
11615
11817
|
}
|
|
11616
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
|
+
}
|
|
11617
11876
|
};
|
|
11618
11877
|
|
|
11619
11878
|
// src/provisioning/providers/sns-subscription-provider.ts
|
|
@@ -11913,6 +12172,8 @@ import {
|
|
|
11913
12172
|
UpdateFunctionCodeCommand,
|
|
11914
12173
|
DeleteFunctionCommand,
|
|
11915
12174
|
GetFunctionCommand,
|
|
12175
|
+
ListFunctionsCommand,
|
|
12176
|
+
ListTagsCommand,
|
|
11916
12177
|
ResourceNotFoundException
|
|
11917
12178
|
} from "@aws-sdk/client-lambda";
|
|
11918
12179
|
import {
|
|
@@ -12497,6 +12758,57 @@ var LambdaFunctionProvider = class {
|
|
|
12497
12758
|
}
|
|
12498
12759
|
return (crc ^ 4294967295) >>> 0;
|
|
12499
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
|
+
}
|
|
12500
12812
|
};
|
|
12501
12813
|
|
|
12502
12814
|
// src/provisioning/providers/lambda-permission-provider.ts
|
|
@@ -13218,6 +13530,8 @@ import {
|
|
|
13218
13530
|
CreateTableCommand,
|
|
13219
13531
|
DeleteTableCommand,
|
|
13220
13532
|
DescribeTableCommand as DescribeTableCommand2,
|
|
13533
|
+
ListTablesCommand,
|
|
13534
|
+
ListTagsOfResourceCommand,
|
|
13221
13535
|
ResourceNotFoundException as ResourceNotFoundException6
|
|
13222
13536
|
} from "@aws-sdk/client-dynamodb";
|
|
13223
13537
|
init_aws_clients();
|
|
@@ -13429,12 +13743,70 @@ var DynamoDBTableProvider = class {
|
|
|
13429
13743
|
}
|
|
13430
13744
|
throw new Error(`Table ${tableName} did not reach ACTIVE status within ${maxAttempts} seconds`);
|
|
13431
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
|
+
}
|
|
13432
13802
|
};
|
|
13433
13803
|
|
|
13434
13804
|
// src/provisioning/providers/logs-loggroup-provider.ts
|
|
13435
13805
|
import {
|
|
13436
13806
|
CreateLogGroupCommand,
|
|
13437
13807
|
DeleteLogGroupCommand,
|
|
13808
|
+
DescribeLogGroupsCommand,
|
|
13809
|
+
ListTagsForResourceCommand as ListTagsForResourceCommand2,
|
|
13438
13810
|
PutRetentionPolicyCommand,
|
|
13439
13811
|
DeleteRetentionPolicyCommand,
|
|
13440
13812
|
TagResourceCommand as TagResourceCommand2,
|
|
@@ -13658,6 +14030,61 @@ var LogsLogGroupProvider = class {
|
|
|
13658
14030
|
return `arn:aws:logs:unknown:unknown:log-group:${logGroupName}:*`;
|
|
13659
14031
|
}
|
|
13660
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
|
+
}
|
|
13661
14088
|
};
|
|
13662
14089
|
|
|
13663
14090
|
// src/provisioning/providers/cloudwatch-alarm-provider.ts
|
|
@@ -13888,6 +14315,8 @@ var CloudWatchAlarmProvider = class {
|
|
|
13888
14315
|
import {
|
|
13889
14316
|
CreateSecretCommand,
|
|
13890
14317
|
DeleteSecretCommand,
|
|
14318
|
+
DescribeSecretCommand,
|
|
14319
|
+
ListSecretsCommand,
|
|
13891
14320
|
UpdateSecretCommand,
|
|
13892
14321
|
TagResourceCommand as TagResourceCommand3,
|
|
13893
14322
|
UntagResourceCommand as UntagResourceCommand3,
|
|
@@ -14155,10 +14584,65 @@ var SecretsManagerSecretProvider = class {
|
|
|
14155
14584
|
}
|
|
14156
14585
|
return password;
|
|
14157
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
|
+
}
|
|
14158
14639
|
};
|
|
14159
14640
|
|
|
14160
14641
|
// src/provisioning/providers/ssm-parameter-provider.ts
|
|
14161
14642
|
import {
|
|
14643
|
+
DescribeParametersCommand,
|
|
14644
|
+
GetParameterCommand as GetParameterCommand3,
|
|
14645
|
+
ListTagsForResourceCommand as ListTagsForResourceCommand3,
|
|
14162
14646
|
PutParameterCommand,
|
|
14163
14647
|
DeleteParameterCommand,
|
|
14164
14648
|
AddTagsToResourceCommand,
|
|
@@ -14370,6 +14854,57 @@ var SSMParameterProvider = class {
|
|
|
14370
14854
|
);
|
|
14371
14855
|
}
|
|
14372
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
|
+
}
|
|
14373
14908
|
};
|
|
14374
14909
|
|
|
14375
14910
|
// src/provisioning/providers/eventbridge-rule-provider.ts
|
|
@@ -14668,7 +15203,9 @@ import {
|
|
|
14668
15203
|
DeleteEventBusCommand,
|
|
14669
15204
|
UpdateEventBusCommand,
|
|
14670
15205
|
DescribeEventBusCommand,
|
|
15206
|
+
ListEventBusesCommand,
|
|
14671
15207
|
ListRulesCommand,
|
|
15208
|
+
ListTagsForResourceCommand as ListTagsForResourceCommand4,
|
|
14672
15209
|
RemoveTargetsCommand as RemoveTargetsCommand2,
|
|
14673
15210
|
DeleteRuleCommand as DeleteRuleCommand2,
|
|
14674
15211
|
ListTargetsByRuleCommand as ListTargetsByRuleCommand2,
|
|
@@ -14909,6 +15446,52 @@ var EventBridgeBusProvider = class {
|
|
|
14909
15446
|
);
|
|
14910
15447
|
}
|
|
14911
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
|
+
}
|
|
14912
15495
|
};
|
|
14913
15496
|
|
|
14914
15497
|
// src/provisioning/providers/ec2-provider.ts
|
|
@@ -24215,6 +24798,10 @@ var GlueProvider = class {
|
|
|
24215
24798
|
import {
|
|
24216
24799
|
KMSClient as KMSClient2,
|
|
24217
24800
|
CreateKeyCommand,
|
|
24801
|
+
DescribeKeyCommand,
|
|
24802
|
+
ListAliasesCommand as ListAliasesCommand2,
|
|
24803
|
+
ListKeysCommand,
|
|
24804
|
+
ListResourceTagsCommand,
|
|
24218
24805
|
ScheduleKeyDeletionCommand,
|
|
24219
24806
|
CreateAliasCommand,
|
|
24220
24807
|
DeleteAliasCommand,
|
|
@@ -24597,6 +25184,81 @@ var KMSProvider = class {
|
|
|
24597
25184
|
);
|
|
24598
25185
|
}
|
|
24599
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
|
+
}
|
|
24600
25262
|
};
|
|
24601
25263
|
|
|
24602
25264
|
// src/provisioning/providers/kinesis-provider.ts
|
|
@@ -26575,7 +27237,7 @@ import {
|
|
|
26575
27237
|
CreateTableCommand as CreateTableCommand3,
|
|
26576
27238
|
DeleteTableCommand as DeleteTableCommand3,
|
|
26577
27239
|
ListNamespacesCommand,
|
|
26578
|
-
ListTablesCommand,
|
|
27240
|
+
ListTablesCommand as ListTablesCommand2,
|
|
26579
27241
|
NotFoundException as NotFoundException6
|
|
26580
27242
|
} from "@aws-sdk/client-s3tables";
|
|
26581
27243
|
var S3TablesProvider = class {
|
|
@@ -26721,7 +27383,7 @@ var S3TablesProvider = class {
|
|
|
26721
27383
|
let tableContinuationToken;
|
|
26722
27384
|
do {
|
|
26723
27385
|
const tablesResult = await this.getClient().send(
|
|
26724
|
-
new
|
|
27386
|
+
new ListTablesCommand2({
|
|
26725
27387
|
tableBucketARN,
|
|
26726
27388
|
namespace: namespaceName,
|
|
26727
27389
|
continuationToken: tableContinuationToken
|
|
@@ -28716,11 +29378,11 @@ async function deployCommand(stacks, options) {
|
|
|
28716
29378
|
addDependencies(stack.stackName);
|
|
28717
29379
|
}
|
|
28718
29380
|
}
|
|
28719
|
-
const { STSClient: STSClient7, GetCallerIdentityCommand:
|
|
29381
|
+
const { STSClient: STSClient7, GetCallerIdentityCommand: GetCallerIdentityCommand9 } = await import("@aws-sdk/client-sts");
|
|
28720
29382
|
const stsClient = new STSClient7({
|
|
28721
29383
|
region: options.region || process.env["AWS_REGION"] || "us-east-1"
|
|
28722
29384
|
});
|
|
28723
|
-
const callerIdentity = await stsClient.send(new
|
|
29385
|
+
const callerIdentity = await stsClient.send(new GetCallerIdentityCommand9({}));
|
|
28724
29386
|
const accountId = callerIdentity.Account;
|
|
28725
29387
|
stsClient.destroy();
|
|
28726
29388
|
const assetPublisher = new AssetPublisher();
|
|
@@ -29551,14 +30213,321 @@ function createForceUnlockCommand() {
|
|
|
29551
30213
|
}
|
|
29552
30214
|
|
|
29553
30215
|
// src/cli/commands/state.ts
|
|
29554
|
-
import * as
|
|
29555
|
-
import { Command as
|
|
30216
|
+
import * as readline3 from "node:readline/promises";
|
|
30217
|
+
import { Command as Command10, Option as Option5 } from "commander";
|
|
29556
30218
|
import {
|
|
29557
30219
|
GetBucketLocationCommand as GetBucketLocationCommand2,
|
|
29558
30220
|
GetObjectCommand as GetObjectCommand4,
|
|
29559
|
-
ListObjectsV2Command as
|
|
30221
|
+
ListObjectsV2Command as ListObjectsV2Command4
|
|
29560
30222
|
} from "@aws-sdk/client-s3";
|
|
29561
30223
|
init_aws_clients();
|
|
30224
|
+
|
|
30225
|
+
// src/cli/commands/state-migrate-bucket.ts
|
|
30226
|
+
import * as readline2 from "node:readline/promises";
|
|
30227
|
+
import { Command as Command9 } from "commander";
|
|
30228
|
+
import {
|
|
30229
|
+
CopyObjectCommand,
|
|
30230
|
+
CreateBucketCommand as CreateBucketCommand4,
|
|
30231
|
+
DeleteBucketCommand as DeleteBucketCommand3,
|
|
30232
|
+
DeleteObjectsCommand as DeleteObjectsCommand3,
|
|
30233
|
+
HeadBucketCommand as HeadBucketCommand4,
|
|
30234
|
+
ListObjectVersionsCommand as ListObjectVersionsCommand2,
|
|
30235
|
+
ListObjectsV2Command as ListObjectsV2Command3,
|
|
30236
|
+
PutBucketEncryptionCommand as PutBucketEncryptionCommand3,
|
|
30237
|
+
PutBucketPolicyCommand as PutBucketPolicyCommand3,
|
|
30238
|
+
PutBucketVersioningCommand as PutBucketVersioningCommand3,
|
|
30239
|
+
S3Client as S3Client10
|
|
30240
|
+
} from "@aws-sdk/client-s3";
|
|
30241
|
+
import { GetCallerIdentityCommand as GetCallerIdentityCommand8 } from "@aws-sdk/client-sts";
|
|
30242
|
+
init_aws_clients();
|
|
30243
|
+
async function stateMigrateBucketCommand(options) {
|
|
30244
|
+
const logger = getLogger();
|
|
30245
|
+
if (options.verbose)
|
|
30246
|
+
logger.setLevel("debug");
|
|
30247
|
+
const region = options.region || process.env["AWS_REGION"] || "us-east-1";
|
|
30248
|
+
const awsClients = new AwsClients({
|
|
30249
|
+
region,
|
|
30250
|
+
...options.profile && { profile: options.profile }
|
|
30251
|
+
});
|
|
30252
|
+
setAwsClients(awsClients);
|
|
30253
|
+
try {
|
|
30254
|
+
const identity = await awsClients.sts.send(new GetCallerIdentityCommand8({}));
|
|
30255
|
+
const accountId = identity.Account;
|
|
30256
|
+
if (!accountId) {
|
|
30257
|
+
throw new Error("STS GetCallerIdentity returned no Account id.");
|
|
30258
|
+
}
|
|
30259
|
+
const legacyBucket = options.legacyBucket ?? getLegacyStateBucketName(accountId, region);
|
|
30260
|
+
const newBucket = options.newBucket ?? getDefaultStateBucketName(accountId);
|
|
30261
|
+
if (legacyBucket === newBucket) {
|
|
30262
|
+
logger.warn(
|
|
30263
|
+
`Source and destination resolve to the same bucket (${legacyBucket}); nothing to do.`
|
|
30264
|
+
);
|
|
30265
|
+
return;
|
|
30266
|
+
}
|
|
30267
|
+
logger.info("Migrating state bucket:");
|
|
30268
|
+
logger.info(` source: ${legacyBucket} (resolved for --region ${region})`);
|
|
30269
|
+
logger.info(` destination: ${newBucket}`);
|
|
30270
|
+
const probeRegion = "us-east-1";
|
|
30271
|
+
const probe = new S3Client10({ region: probeRegion });
|
|
30272
|
+
let sourceExists;
|
|
30273
|
+
try {
|
|
30274
|
+
sourceExists = await bucketExists2(probe, legacyBucket);
|
|
30275
|
+
} finally {
|
|
30276
|
+
probe.destroy();
|
|
30277
|
+
}
|
|
30278
|
+
if (!sourceExists) {
|
|
30279
|
+
throw new Error(
|
|
30280
|
+
`Source bucket '${legacyBucket}' does not exist. Nothing to migrate. (Tip: run \`cdkd state info\` to confirm which bucket cdkd is reading from.)`
|
|
30281
|
+
);
|
|
30282
|
+
}
|
|
30283
|
+
const legacyRegion = await resolveBucketRegion(legacyBucket);
|
|
30284
|
+
logger.info(` source bucket actual region: ${legacyRegion}`);
|
|
30285
|
+
const legacyS3 = new S3Client10({ region: legacyRegion });
|
|
30286
|
+
try {
|
|
30287
|
+
await assertNoActiveLocks(legacyS3, legacyBucket);
|
|
30288
|
+
const sourceObjects = await listAllObjects(legacyS3, legacyBucket);
|
|
30289
|
+
logger.info(` source object count: ${sourceObjects.length}`);
|
|
30290
|
+
if (sourceObjects.length === 0) {
|
|
30291
|
+
logger.info("Source bucket is empty \u2014 no objects to copy.");
|
|
30292
|
+
}
|
|
30293
|
+
if (!options.yes) {
|
|
30294
|
+
const action = options.removeLegacy ? "and DELETE the source bucket" : "(source bucket will be kept)";
|
|
30295
|
+
const ok = await confirmPrompt(
|
|
30296
|
+
`Copy ${sourceObjects.length} object(s) from ${legacyBucket} -> ${newBucket} ${action}?`
|
|
30297
|
+
);
|
|
30298
|
+
if (!ok) {
|
|
30299
|
+
logger.info("Migration cancelled.");
|
|
30300
|
+
return;
|
|
30301
|
+
}
|
|
30302
|
+
}
|
|
30303
|
+
if (options.dryRun) {
|
|
30304
|
+
logger.info("--dry-run: no changes will be made. Stopping here.");
|
|
30305
|
+
return;
|
|
30306
|
+
}
|
|
30307
|
+
const newS3 = await ensureDestinationBucket(newBucket, legacyRegion, accountId, logger);
|
|
30308
|
+
try {
|
|
30309
|
+
let copied = 0;
|
|
30310
|
+
for (const obj of sourceObjects) {
|
|
30311
|
+
if (!obj.Key)
|
|
30312
|
+
continue;
|
|
30313
|
+
await newS3.send(
|
|
30314
|
+
new CopyObjectCommand({
|
|
30315
|
+
Bucket: newBucket,
|
|
30316
|
+
Key: obj.Key,
|
|
30317
|
+
// CopySource needs encoding for slashes inside the key path.
|
|
30318
|
+
CopySource: encodeURIComponent(`${legacyBucket}/${obj.Key}`)
|
|
30319
|
+
})
|
|
30320
|
+
);
|
|
30321
|
+
copied++;
|
|
30322
|
+
logger.debug(` copied ${obj.Key}`);
|
|
30323
|
+
}
|
|
30324
|
+
logger.info(`\u2713 Copied ${copied} object(s) to ${newBucket}`);
|
|
30325
|
+
const destObjects = await listAllObjects(newS3, newBucket);
|
|
30326
|
+
if (destObjects.length < sourceObjects.length) {
|
|
30327
|
+
throw new Error(
|
|
30328
|
+
`Migration verification failed: source has ${sourceObjects.length} object(s), destination has ${destObjects.length}. Aborting before any source-bucket cleanup.`
|
|
30329
|
+
);
|
|
30330
|
+
}
|
|
30331
|
+
logger.info("\u2713 Object count verified at destination");
|
|
30332
|
+
if (options.removeLegacy) {
|
|
30333
|
+
logger.info(`Emptying source bucket ${legacyBucket} (all versions + delete markers)...`);
|
|
30334
|
+
await emptyBucketAllVersions(legacyS3, legacyBucket);
|
|
30335
|
+
logger.info(`Deleting source bucket ${legacyBucket}...`);
|
|
30336
|
+
await legacyS3.send(new DeleteBucketCommand3({ Bucket: legacyBucket }));
|
|
30337
|
+
logger.info(`\u2713 Deleted source bucket: ${legacyBucket}`);
|
|
30338
|
+
} else {
|
|
30339
|
+
logger.info(
|
|
30340
|
+
`Source bucket ${legacyBucket} kept. Pass --remove-legacy on a future run to delete it.`
|
|
30341
|
+
);
|
|
30342
|
+
}
|
|
30343
|
+
logger.info(`\u2713 Migration complete: ${legacyBucket} -> ${newBucket}`);
|
|
30344
|
+
} finally {
|
|
30345
|
+
newS3.destroy();
|
|
30346
|
+
}
|
|
30347
|
+
} finally {
|
|
30348
|
+
legacyS3.destroy();
|
|
30349
|
+
}
|
|
30350
|
+
} finally {
|
|
30351
|
+
awsClients.destroy();
|
|
30352
|
+
}
|
|
30353
|
+
}
|
|
30354
|
+
async function bucketExists2(s3, bucketName) {
|
|
30355
|
+
try {
|
|
30356
|
+
await s3.send(new HeadBucketCommand4({ Bucket: bucketName }));
|
|
30357
|
+
return true;
|
|
30358
|
+
} catch (error) {
|
|
30359
|
+
const err = error;
|
|
30360
|
+
const status = err.$metadata?.httpStatusCode;
|
|
30361
|
+
if (err.name === "NotFound" || err.name === "NoSuchBucket" || status === 404) {
|
|
30362
|
+
return false;
|
|
30363
|
+
}
|
|
30364
|
+
if (status === 301 || status === 403)
|
|
30365
|
+
return true;
|
|
30366
|
+
throw error;
|
|
30367
|
+
}
|
|
30368
|
+
}
|
|
30369
|
+
async function listAllObjects(s3, bucket) {
|
|
30370
|
+
const all = [];
|
|
30371
|
+
let continuationToken;
|
|
30372
|
+
do {
|
|
30373
|
+
const resp = await s3.send(
|
|
30374
|
+
new ListObjectsV2Command3({
|
|
30375
|
+
Bucket: bucket,
|
|
30376
|
+
...continuationToken && { ContinuationToken: continuationToken }
|
|
30377
|
+
})
|
|
30378
|
+
);
|
|
30379
|
+
if (resp.Contents)
|
|
30380
|
+
all.push(...resp.Contents);
|
|
30381
|
+
continuationToken = resp.NextContinuationToken;
|
|
30382
|
+
} while (continuationToken);
|
|
30383
|
+
return all;
|
|
30384
|
+
}
|
|
30385
|
+
async function assertNoActiveLocks(s3, bucket) {
|
|
30386
|
+
const all = await listAllObjects(s3, bucket);
|
|
30387
|
+
const locks = all.map((o) => o.Key).filter((k) => typeof k === "string" && k.endsWith("/lock.json"));
|
|
30388
|
+
if (locks.length > 0) {
|
|
30389
|
+
const sample = locks.slice(0, 3).join(", ");
|
|
30390
|
+
const more = locks.length > 3 ? ` (+${locks.length - 3} more)` : "";
|
|
30391
|
+
throw new Error(
|
|
30392
|
+
`Refusing to migrate: ${locks.length} active lock file(s) found in '${bucket}': ${sample}${more}. Wait for in-flight cdkd operations to complete, or run 'cdkd force-unlock <stack>' if a lock is stale.`
|
|
30393
|
+
);
|
|
30394
|
+
}
|
|
30395
|
+
}
|
|
30396
|
+
async function ensureDestinationBucket(bucketName, region, accountId, logger) {
|
|
30397
|
+
const probe = new S3Client10({ region });
|
|
30398
|
+
let exists;
|
|
30399
|
+
try {
|
|
30400
|
+
exists = await bucketExists2(probe, bucketName);
|
|
30401
|
+
} finally {
|
|
30402
|
+
probe.destroy();
|
|
30403
|
+
}
|
|
30404
|
+
if (exists) {
|
|
30405
|
+
logger.info(`Destination bucket ${bucketName} already exists; reusing it.`);
|
|
30406
|
+
const actual = await resolveBucketRegion(bucketName);
|
|
30407
|
+
if (actual !== region) {
|
|
30408
|
+
logger.warn(
|
|
30409
|
+
`Destination bucket lives in ${actual}, but source is in ${region}. Cross-region copy is supported but slower; objects will be replicated to ${actual}.`
|
|
30410
|
+
);
|
|
30411
|
+
}
|
|
30412
|
+
return new S3Client10({ region: actual });
|
|
30413
|
+
}
|
|
30414
|
+
logger.info(`Creating destination bucket ${bucketName} in ${region}...`);
|
|
30415
|
+
const s3 = new S3Client10({ region });
|
|
30416
|
+
const createParams = { Bucket: bucketName };
|
|
30417
|
+
if (region !== "us-east-1") {
|
|
30418
|
+
createParams.CreateBucketConfiguration = {
|
|
30419
|
+
LocationConstraint: region
|
|
30420
|
+
};
|
|
30421
|
+
}
|
|
30422
|
+
await s3.send(new CreateBucketCommand4(createParams));
|
|
30423
|
+
logger.info(`\u2713 Created destination bucket: ${bucketName}`);
|
|
30424
|
+
await s3.send(
|
|
30425
|
+
new PutBucketVersioningCommand3({
|
|
30426
|
+
Bucket: bucketName,
|
|
30427
|
+
VersioningConfiguration: { Status: "Enabled" }
|
|
30428
|
+
})
|
|
30429
|
+
);
|
|
30430
|
+
await s3.send(
|
|
30431
|
+
new PutBucketEncryptionCommand3({
|
|
30432
|
+
Bucket: bucketName,
|
|
30433
|
+
ServerSideEncryptionConfiguration: {
|
|
30434
|
+
Rules: [
|
|
30435
|
+
{
|
|
30436
|
+
ApplyServerSideEncryptionByDefault: { SSEAlgorithm: "AES256" },
|
|
30437
|
+
BucketKeyEnabled: true
|
|
30438
|
+
}
|
|
30439
|
+
]
|
|
30440
|
+
}
|
|
30441
|
+
})
|
|
30442
|
+
);
|
|
30443
|
+
await s3.send(
|
|
30444
|
+
new PutBucketPolicyCommand3({
|
|
30445
|
+
Bucket: bucketName,
|
|
30446
|
+
Policy: JSON.stringify({
|
|
30447
|
+
Version: "2012-10-17",
|
|
30448
|
+
Statement: [
|
|
30449
|
+
{
|
|
30450
|
+
Sid: "DenyExternalAccess",
|
|
30451
|
+
Effect: "Deny",
|
|
30452
|
+
Principal: "*",
|
|
30453
|
+
Action: "s3:*",
|
|
30454
|
+
Resource: [`arn:aws:s3:::${bucketName}`, `arn:aws:s3:::${bucketName}/*`],
|
|
30455
|
+
Condition: { StringNotEquals: { "aws:PrincipalAccount": accountId } }
|
|
30456
|
+
}
|
|
30457
|
+
]
|
|
30458
|
+
})
|
|
30459
|
+
})
|
|
30460
|
+
);
|
|
30461
|
+
logger.info("\u2713 Applied versioning, encryption, and account-only access policy");
|
|
30462
|
+
return s3;
|
|
30463
|
+
}
|
|
30464
|
+
async function emptyBucketAllVersions(s3, bucket) {
|
|
30465
|
+
let keyMarker;
|
|
30466
|
+
let versionIdMarker;
|
|
30467
|
+
do {
|
|
30468
|
+
const resp = await s3.send(
|
|
30469
|
+
new ListObjectVersionsCommand2({
|
|
30470
|
+
Bucket: bucket,
|
|
30471
|
+
...keyMarker && { KeyMarker: keyMarker },
|
|
30472
|
+
...versionIdMarker && { VersionIdMarker: versionIdMarker }
|
|
30473
|
+
})
|
|
30474
|
+
);
|
|
30475
|
+
const ids = [];
|
|
30476
|
+
for (const v of resp.Versions ?? []) {
|
|
30477
|
+
if (v.Key && v.VersionId)
|
|
30478
|
+
ids.push({ Key: v.Key, VersionId: v.VersionId });
|
|
30479
|
+
}
|
|
30480
|
+
for (const dm of resp.DeleteMarkers ?? []) {
|
|
30481
|
+
if (dm.Key && dm.VersionId)
|
|
30482
|
+
ids.push({ Key: dm.Key, VersionId: dm.VersionId });
|
|
30483
|
+
}
|
|
30484
|
+
for (let i = 0; i < ids.length; i += 1e3) {
|
|
30485
|
+
const batch = ids.slice(i, i + 1e3);
|
|
30486
|
+
await s3.send(
|
|
30487
|
+
new DeleteObjectsCommand3({
|
|
30488
|
+
Bucket: bucket,
|
|
30489
|
+
Delete: {
|
|
30490
|
+
Objects: batch,
|
|
30491
|
+
Quiet: true
|
|
30492
|
+
}
|
|
30493
|
+
})
|
|
30494
|
+
);
|
|
30495
|
+
}
|
|
30496
|
+
keyMarker = resp.NextKeyMarker;
|
|
30497
|
+
versionIdMarker = resp.NextVersionIdMarker;
|
|
30498
|
+
} while (keyMarker || versionIdMarker);
|
|
30499
|
+
}
|
|
30500
|
+
async function confirmPrompt(prompt) {
|
|
30501
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
30502
|
+
try {
|
|
30503
|
+
const ans = await rl.question(`${prompt} [y/N] `);
|
|
30504
|
+
return /^y(es)?$/i.test(ans.trim());
|
|
30505
|
+
} finally {
|
|
30506
|
+
rl.close();
|
|
30507
|
+
}
|
|
30508
|
+
}
|
|
30509
|
+
function createStateMigrateBucketCommand() {
|
|
30510
|
+
const cmd = new Command9("migrate-bucket").description(
|
|
30511
|
+
"Migrate state from the legacy region-suffixed bucket (cdkd-state-{account}-{region}) to the new region-free default (cdkd-state-{account}). Source bucket is kept by default; pass --remove-legacy to delete it after a successful migration."
|
|
30512
|
+
).option(
|
|
30513
|
+
"--region <region>",
|
|
30514
|
+
"Region of the legacy bucket to migrate. Defaults to AWS_REGION or us-east-1. Run once per region for multi-region setups."
|
|
30515
|
+
).option(
|
|
30516
|
+
"--legacy-bucket <name>",
|
|
30517
|
+
"Override the legacy (source) bucket name (default: derived from STS account + --region)."
|
|
30518
|
+
).option(
|
|
30519
|
+
"--new-bucket <name>",
|
|
30520
|
+
"Override the new (destination) bucket name (default: cdkd-state-{accountId})."
|
|
30521
|
+
).option("--dry-run", "Show planned actions without making changes", false).option(
|
|
30522
|
+
"--remove-legacy",
|
|
30523
|
+
"Delete the source bucket after successful migration. Default: keep it.",
|
|
30524
|
+
false
|
|
30525
|
+
).action(withErrorHandling(stateMigrateBucketCommand));
|
|
30526
|
+
commonOptions.forEach((o) => cmd.addOption(o));
|
|
30527
|
+
return cmd;
|
|
30528
|
+
}
|
|
30529
|
+
|
|
30530
|
+
// src/cli/commands/state.ts
|
|
29562
30531
|
function formatStackRef(ref) {
|
|
29563
30532
|
return ref.region ? `${ref.stackName} (${ref.region})` : ref.stackName;
|
|
29564
30533
|
}
|
|
@@ -29696,7 +30665,7 @@ async function stateListCommand(options) {
|
|
|
29696
30665
|
}
|
|
29697
30666
|
}
|
|
29698
30667
|
function createStateListCommand() {
|
|
29699
|
-
const cmd = new
|
|
30668
|
+
const cmd = new Command10("list").alias("ls").description("List stacks registered in the cdkd state bucket").option("-l, --long", "Show resource count, last-modified time, and lock status", false).option("--json", "Output as JSON", false).action(withErrorHandling(stateListCommand));
|
|
29700
30669
|
[...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
|
|
29701
30670
|
cmd.addOption(deprecatedRegionOption);
|
|
29702
30671
|
return cmd;
|
|
@@ -29800,7 +30769,7 @@ function formatLockSummary(lockInfo) {
|
|
|
29800
30769
|
return `locked by ${lockInfo.owner}${opStr}, ${expiresStr}`;
|
|
29801
30770
|
}
|
|
29802
30771
|
function createStateResourcesCommand() {
|
|
29803
|
-
const cmd = new
|
|
30772
|
+
const cmd = new Command10("resources").description("List resources recorded in a stack's state").argument("<stack>", "Stack name (physical CloudFormation name)").option("-l, --long", "Include dependencies and attributes per resource", false).option("--json", "Output as JSON", false).addOption(stackRegionOption()).action(withErrorHandling(stateResourcesCommand));
|
|
29804
30773
|
[...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
|
|
29805
30774
|
cmd.addOption(deprecatedRegionOption);
|
|
29806
30775
|
return cmd;
|
|
@@ -29888,7 +30857,7 @@ async function stateShowCommand(stackName, options) {
|
|
|
29888
30857
|
}
|
|
29889
30858
|
}
|
|
29890
30859
|
function createStateShowCommand() {
|
|
29891
|
-
const cmd = new
|
|
30860
|
+
const cmd = new Command10("show").description("Show the full cdkd state record for a stack (metadata, outputs, resources)").argument("<stack>", "Stack name (physical CloudFormation name)").option("--json", "Output the raw state and lock as JSON", false).addOption(stackRegionOption()).action(withErrorHandling(stateShowCommand));
|
|
29892
30861
|
[...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
|
|
29893
30862
|
cmd.addOption(deprecatedRegionOption);
|
|
29894
30863
|
return cmd;
|
|
@@ -29936,7 +30905,7 @@ Use 'cdkd destroy ${stackName}' if you want to delete the actual resources.
|
|
|
29936
30905
|
|
|
29937
30906
|
`
|
|
29938
30907
|
);
|
|
29939
|
-
const rl =
|
|
30908
|
+
const rl = readline3.createInterface({
|
|
29940
30909
|
input: process.stdin,
|
|
29941
30910
|
output: process.stdout
|
|
29942
30911
|
});
|
|
@@ -29971,7 +30940,7 @@ function stackRegionOption() {
|
|
|
29971
30940
|
);
|
|
29972
30941
|
}
|
|
29973
30942
|
function createStateRmCommand() {
|
|
29974
|
-
const cmd = new
|
|
30943
|
+
const cmd = new Command10("rm").description("Remove cdkd state for one or more stacks (does NOT delete AWS resources)").argument("<stacks...>", "Stack name(s) to remove from state").option("-f, --force", "Skip confirmation and remove even if the stack is locked", false).addOption(stackRegionOption()).action(withErrorHandling(stateRmCommand));
|
|
29975
30944
|
[...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
|
|
29976
30945
|
cmd.addOption(deprecatedRegionOption);
|
|
29977
30946
|
return cmd;
|
|
@@ -30021,7 +30990,7 @@ WARNING: This destroys ${stackNames.length} stack(s) and removes their state rec
|
|
|
30021
30990
|
`);
|
|
30022
30991
|
}
|
|
30023
30992
|
process.stdout.write("\n");
|
|
30024
|
-
const rl =
|
|
30993
|
+
const rl = readline3.createInterface({
|
|
30025
30994
|
input: process.stdin,
|
|
30026
30995
|
output: process.stdout
|
|
30027
30996
|
});
|
|
@@ -30096,7 +31065,7 @@ Preparing to destroy stack: ${stackName}${ref.region ? ` (${ref.region})` : ""}`
|
|
|
30096
31065
|
}
|
|
30097
31066
|
}
|
|
30098
31067
|
function createStateDestroyCommand() {
|
|
30099
|
-
const cmd = new
|
|
31068
|
+
const cmd = new Command10("destroy").description(
|
|
30100
31069
|
"Destroy a stack's AWS resources and remove its state record without requiring the CDK app. For removing only the state record (keeping AWS resources intact), use 'cdkd state rm'."
|
|
30101
31070
|
).argument("[stacks...]", "Stack name(s) to destroy (physical CloudFormation names)").option("--all", "Destroy every stack in the state bucket", false).addOption(stackRegionOption()).addHelpText(
|
|
30102
31071
|
"after",
|
|
@@ -30149,7 +31118,7 @@ async function listStateFileKeys(awsClients, bucket, prefix) {
|
|
|
30149
31118
|
const searchPrefix = `${prefix}/`;
|
|
30150
31119
|
do {
|
|
30151
31120
|
const resp = await awsClients.s3.send(
|
|
30152
|
-
new
|
|
31121
|
+
new ListObjectsV2Command4({
|
|
30153
31122
|
Bucket: bucket,
|
|
30154
31123
|
Prefix: searchPrefix,
|
|
30155
31124
|
...continuationToken && { ContinuationToken: continuationToken }
|
|
@@ -30228,22 +31197,358 @@ async function stateInfoCommand(options) {
|
|
|
30228
31197
|
}
|
|
30229
31198
|
}
|
|
30230
31199
|
function createStateInfoCommand() {
|
|
30231
|
-
const cmd = new
|
|
31200
|
+
const cmd = new Command10("info").description(
|
|
30232
31201
|
"Show cdkd state bucket info (bucket name, region, source, schema version, stack count)"
|
|
30233
31202
|
).option("--json", "Output as JSON", false).action(withErrorHandling(stateInfoCommand));
|
|
30234
31203
|
[...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
|
|
30235
31204
|
return cmd;
|
|
30236
31205
|
}
|
|
30237
31206
|
function createStateCommand() {
|
|
30238
|
-
const cmd = new
|
|
31207
|
+
const cmd = new Command10("state").description("Manage cdkd state stored in S3");
|
|
30239
31208
|
cmd.addCommand(createStateInfoCommand());
|
|
30240
31209
|
cmd.addCommand(createStateListCommand());
|
|
30241
31210
|
cmd.addCommand(createStateResourcesCommand());
|
|
30242
31211
|
cmd.addCommand(createStateShowCommand());
|
|
30243
31212
|
cmd.addCommand(createStateRmCommand());
|
|
30244
31213
|
cmd.addCommand(createStateDestroyCommand());
|
|
31214
|
+
cmd.addCommand(createStateMigrateBucketCommand());
|
|
31215
|
+
return cmd;
|
|
31216
|
+
}
|
|
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
|
+
);
|
|
30245
31547
|
return cmd;
|
|
30246
31548
|
}
|
|
31549
|
+
function collectMultiple(value, previous) {
|
|
31550
|
+
return [...previous ?? [], value];
|
|
31551
|
+
}
|
|
30247
31552
|
|
|
30248
31553
|
// src/cli/index.ts
|
|
30249
31554
|
var SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -30254,6 +31559,7 @@ var SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
|
30254
31559
|
"deploy",
|
|
30255
31560
|
"diff",
|
|
30256
31561
|
"destroy",
|
|
31562
|
+
"import",
|
|
30257
31563
|
"publish-assets",
|
|
30258
31564
|
"force-unlock",
|
|
30259
31565
|
"state"
|
|
@@ -30269,14 +31575,15 @@ function reorderArgs(argv) {
|
|
|
30269
31575
|
return [...prefix, ...cmdAndAfter, ...beforeCmd];
|
|
30270
31576
|
}
|
|
30271
31577
|
async function main() {
|
|
30272
|
-
const program = new
|
|
30273
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("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");
|
|
30274
31580
|
program.addCommand(createBootstrapCommand());
|
|
30275
31581
|
program.addCommand(createSynthCommand());
|
|
30276
31582
|
program.addCommand(createListCommand());
|
|
30277
31583
|
program.addCommand(createDeployCommand());
|
|
30278
31584
|
program.addCommand(createDiffCommand());
|
|
30279
31585
|
program.addCommand(createDestroyCommand());
|
|
31586
|
+
program.addCommand(createImportCommand());
|
|
30280
31587
|
program.addCommand(createPublishAssetsCommand());
|
|
30281
31588
|
program.addCommand(createForceUnlockCommand());
|
|
30282
31589
|
program.addCommand(createStateCommand());
|