@go-to-k/cdkd 0.36.0 → 0.37.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 +8 -1
- package/dist/cli.js +563 -1
- package/dist/cli.js.map +2 -2
- package/dist/go-to-k-cdkd-0.37.0.tgz +0 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.36.0.tgz +0 -0
package/README.md
CHANGED
|
@@ -208,8 +208,15 @@ alias cdkd="node $(pwd)/dist/cli.js"
|
|
|
208
208
|
|
|
209
209
|
## Quick Start
|
|
210
210
|
|
|
211
|
+
> **First-time setup**: cdkd requires a one-time `cdkd bootstrap` per AWS
|
|
212
|
+
> account before any other command will work — it creates the S3 state
|
|
213
|
+
> bucket (`cdkd-state-{accountId}`) that cdkd uses to track deployed
|
|
214
|
+
> resources. This is separate from `cdk bootstrap` (which sets up the
|
|
215
|
+
> CDK asset bucket / ECR repo and is also required — see
|
|
216
|
+
> [Prerequisites](#prerequisites)).
|
|
217
|
+
|
|
211
218
|
```bash
|
|
212
|
-
# Bootstrap (creates S3 state bucket -
|
|
219
|
+
# Bootstrap (creates S3 state bucket — one-time setup, once per AWS account)
|
|
213
220
|
cdkd bootstrap
|
|
214
221
|
|
|
215
222
|
# List stacks in the CDK app
|
package/dist/cli.js
CHANGED
|
@@ -9028,6 +9028,85 @@ var IAMRoleProvider = class {
|
|
|
9028
9028
|
throw err;
|
|
9029
9029
|
}
|
|
9030
9030
|
}
|
|
9031
|
+
/**
|
|
9032
|
+
* Read the AWS-current IAM role configuration in CFn-property shape.
|
|
9033
|
+
*
|
|
9034
|
+
* Issues `GetRole` for the top-level role configuration and
|
|
9035
|
+
* `ListRolePolicies` + `ListAttachedRolePolicies` for inline / managed
|
|
9036
|
+
* policy *names*. AWS URL-decodes `AssumeRolePolicyDocument` for us
|
|
9037
|
+
* when it surfaces — we re-parse it as JSON so the comparator can match
|
|
9038
|
+
* against state's already-parsed object.
|
|
9039
|
+
*
|
|
9040
|
+
* Coverage and shape decisions:
|
|
9041
|
+
* - `RoleName`, `Description`, `MaxSessionDuration`, `Path`,
|
|
9042
|
+
* `PermissionsBoundary` — straight from `Role.*`.
|
|
9043
|
+
* - `AssumeRolePolicyDocument` — `Role.AssumeRolePolicyDocument` is a
|
|
9044
|
+
* URL-encoded JSON string; we URL-decode + JSON-parse so cdkd state's
|
|
9045
|
+
* object form compares cleanly. (Both shapes — string and object — are
|
|
9046
|
+
* accepted by `create()`, but state typically stores the parsed object
|
|
9047
|
+
* after intrinsic resolution.)
|
|
9048
|
+
* - `ManagedPolicyArns` — array of ARN strings from
|
|
9049
|
+
* `ListAttachedRolePolicies`.
|
|
9050
|
+
* - `Policies` (inline policies with `PolicyDocument` bodies) is
|
|
9051
|
+
* intentionally omitted: surfacing names without bodies guarantees a
|
|
9052
|
+
* PolicyDocument-shaped drift on every role, and fetching every body
|
|
9053
|
+
* costs one extra `GetRolePolicy` per inline policy. Out of scope for
|
|
9054
|
+
* v1 — drift detection on inline IAM policy bodies can ship in a
|
|
9055
|
+
* follow-up.
|
|
9056
|
+
* - `Tags` is omitted for the same reason as Lambda's tags handling
|
|
9057
|
+
* (CDK auto-injects `aws:cdk:path` and the shape decision belongs in a
|
|
9058
|
+
* dedicated tags PR).
|
|
9059
|
+
*
|
|
9060
|
+
* Returns `undefined` when the role is gone (`NoSuchEntityException`).
|
|
9061
|
+
*/
|
|
9062
|
+
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
9063
|
+
let role;
|
|
9064
|
+
try {
|
|
9065
|
+
const resp = await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
|
|
9066
|
+
role = resp.Role;
|
|
9067
|
+
} catch (err) {
|
|
9068
|
+
if (err instanceof NoSuchEntityException)
|
|
9069
|
+
return void 0;
|
|
9070
|
+
throw err;
|
|
9071
|
+
}
|
|
9072
|
+
if (!role)
|
|
9073
|
+
return void 0;
|
|
9074
|
+
const result = {};
|
|
9075
|
+
if (role.RoleName !== void 0)
|
|
9076
|
+
result["RoleName"] = role.RoleName;
|
|
9077
|
+
if (role.Description !== void 0 && role.Description !== "") {
|
|
9078
|
+
result["Description"] = role.Description;
|
|
9079
|
+
}
|
|
9080
|
+
if (role.MaxSessionDuration !== void 0) {
|
|
9081
|
+
result["MaxSessionDuration"] = role.MaxSessionDuration;
|
|
9082
|
+
}
|
|
9083
|
+
if (role.Path !== void 0)
|
|
9084
|
+
result["Path"] = role.Path;
|
|
9085
|
+
if (role.PermissionsBoundary?.PermissionsBoundaryArn !== void 0) {
|
|
9086
|
+
result["PermissionsBoundary"] = role.PermissionsBoundary.PermissionsBoundaryArn;
|
|
9087
|
+
}
|
|
9088
|
+
if (role.AssumeRolePolicyDocument) {
|
|
9089
|
+
try {
|
|
9090
|
+
result["AssumeRolePolicyDocument"] = JSON.parse(
|
|
9091
|
+
decodeURIComponent(role.AssumeRolePolicyDocument)
|
|
9092
|
+
);
|
|
9093
|
+
} catch {
|
|
9094
|
+
result["AssumeRolePolicyDocument"] = role.AssumeRolePolicyDocument;
|
|
9095
|
+
}
|
|
9096
|
+
}
|
|
9097
|
+
try {
|
|
9098
|
+
const attached = await this.iamClient.send(
|
|
9099
|
+
new ListAttachedRolePoliciesCommand({ RoleName: physicalId })
|
|
9100
|
+
);
|
|
9101
|
+
const arns = (attached.AttachedPolicies ?? []).map((p) => p.PolicyArn).filter((arn) => !!arn);
|
|
9102
|
+
if (arns.length > 0)
|
|
9103
|
+
result["ManagedPolicyArns"] = arns;
|
|
9104
|
+
} catch (err) {
|
|
9105
|
+
if (!(err instanceof NoSuchEntityException))
|
|
9106
|
+
throw err;
|
|
9107
|
+
}
|
|
9108
|
+
return result;
|
|
9109
|
+
}
|
|
9031
9110
|
/**
|
|
9032
9111
|
* Adopt an existing IAM role into cdkd state.
|
|
9033
9112
|
*
|
|
@@ -10831,7 +10910,10 @@ import {
|
|
|
10831
10910
|
PutBucketInventoryConfigurationCommand,
|
|
10832
10911
|
PutBucketReplicationCommand,
|
|
10833
10912
|
PutObjectLockConfigurationCommand,
|
|
10913
|
+
GetBucketEncryptionCommand,
|
|
10834
10914
|
GetBucketTaggingCommand,
|
|
10915
|
+
GetBucketVersioningCommand,
|
|
10916
|
+
GetPublicAccessBlockCommand,
|
|
10835
10917
|
NoSuchBucket,
|
|
10836
10918
|
ListObjectVersionsCommand,
|
|
10837
10919
|
DeleteObjectsCommand
|
|
@@ -11688,6 +11770,119 @@ var S3BucketProvider = class {
|
|
|
11688
11770
|
);
|
|
11689
11771
|
}
|
|
11690
11772
|
}
|
|
11773
|
+
/**
|
|
11774
|
+
* Read the AWS-current S3 bucket configuration in CFn-property shape.
|
|
11775
|
+
*
|
|
11776
|
+
* Issues a small handful of independent S3 GET calls and stitches them
|
|
11777
|
+
* into a single CFn-shaped object. Each call can throw a "feature not
|
|
11778
|
+
* configured" error (`NoSuchBucketConfiguration`,
|
|
11779
|
+
* `ServerSideEncryptionConfigurationNotFoundError`, `NoSuchTagSet`,
|
|
11780
|
+
* `NoSuchPublicAccessBlockConfiguration`) — those are caught individually
|
|
11781
|
+
* and the corresponding key is omitted from the result, NOT treated as
|
|
11782
|
+
* the bucket being absent.
|
|
11783
|
+
*
|
|
11784
|
+
* Only the bucket-gone case (`NoSuchBucket`, HTTP 404 from `HeadBucket`)
|
|
11785
|
+
* returns `undefined`.
|
|
11786
|
+
*
|
|
11787
|
+
* Coverage: `BucketName`, `VersioningConfiguration`, `BucketEncryption`,
|
|
11788
|
+
* `PublicAccessBlockConfiguration`, `Tags`. Other configuration
|
|
11789
|
+
* properties (Lifecycle, CORS, Website, Logging, Notification,
|
|
11790
|
+
* Replication, ObjectLock, Accelerate, Metrics/Analytics/IntelligentTier/
|
|
11791
|
+
* Inventory) are out of scope for v1 — they each need their own GET +
|
|
11792
|
+
* shape mapping; CC API drift detection picks them up via `GetResource`
|
|
11793
|
+
* once a user works through the SDK provider boundary.
|
|
11794
|
+
*/
|
|
11795
|
+
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
11796
|
+
try {
|
|
11797
|
+
await this.s3Client.send(new HeadBucketCommand3({ Bucket: physicalId }));
|
|
11798
|
+
} catch (err) {
|
|
11799
|
+
const e = err;
|
|
11800
|
+
if (err instanceof NoSuchBucket || e.name === "NotFound" || e.name === "NoSuchBucket" || e.$metadata?.httpStatusCode === 404) {
|
|
11801
|
+
return void 0;
|
|
11802
|
+
}
|
|
11803
|
+
throw err;
|
|
11804
|
+
}
|
|
11805
|
+
const result = {
|
|
11806
|
+
BucketName: physicalId
|
|
11807
|
+
};
|
|
11808
|
+
{
|
|
11809
|
+
const resp = await this.s3Client.send(new GetBucketVersioningCommand({ Bucket: physicalId }));
|
|
11810
|
+
if (resp.Status) {
|
|
11811
|
+
result["VersioningConfiguration"] = { Status: resp.Status };
|
|
11812
|
+
}
|
|
11813
|
+
}
|
|
11814
|
+
try {
|
|
11815
|
+
const resp = await this.s3Client.send(new GetBucketEncryptionCommand({ Bucket: physicalId }));
|
|
11816
|
+
const rules = resp.ServerSideEncryptionConfiguration?.Rules;
|
|
11817
|
+
if (rules && rules.length > 0) {
|
|
11818
|
+
result["BucketEncryption"] = {
|
|
11819
|
+
ServerSideEncryptionConfiguration: rules.map((rule) => {
|
|
11820
|
+
const out = {};
|
|
11821
|
+
const sse = rule.ApplyServerSideEncryptionByDefault;
|
|
11822
|
+
if (sse) {
|
|
11823
|
+
const sseOut = {};
|
|
11824
|
+
if (sse.SSEAlgorithm !== void 0)
|
|
11825
|
+
sseOut["SSEAlgorithm"] = sse.SSEAlgorithm;
|
|
11826
|
+
if (sse.KMSMasterKeyID !== void 0)
|
|
11827
|
+
sseOut["KMSMasterKeyID"] = sse.KMSMasterKeyID;
|
|
11828
|
+
out["ServerSideEncryptionByDefault"] = sseOut;
|
|
11829
|
+
}
|
|
11830
|
+
if (rule.BucketKeyEnabled !== void 0)
|
|
11831
|
+
out["BucketKeyEnabled"] = rule.BucketKeyEnabled;
|
|
11832
|
+
return out;
|
|
11833
|
+
})
|
|
11834
|
+
};
|
|
11835
|
+
}
|
|
11836
|
+
} catch (err) {
|
|
11837
|
+
const e = err;
|
|
11838
|
+
if (e.name !== "ServerSideEncryptionConfigurationNotFoundError") {
|
|
11839
|
+
throw err;
|
|
11840
|
+
}
|
|
11841
|
+
}
|
|
11842
|
+
try {
|
|
11843
|
+
const resp = await this.s3Client.send(
|
|
11844
|
+
new GetPublicAccessBlockCommand({ Bucket: physicalId })
|
|
11845
|
+
);
|
|
11846
|
+
const cfg = resp.PublicAccessBlockConfiguration;
|
|
11847
|
+
if (cfg) {
|
|
11848
|
+
const out = {};
|
|
11849
|
+
if (cfg.BlockPublicAcls !== void 0)
|
|
11850
|
+
out["BlockPublicAcls"] = cfg.BlockPublicAcls;
|
|
11851
|
+
if (cfg.BlockPublicPolicy !== void 0)
|
|
11852
|
+
out["BlockPublicPolicy"] = cfg.BlockPublicPolicy;
|
|
11853
|
+
if (cfg.IgnorePublicAcls !== void 0)
|
|
11854
|
+
out["IgnorePublicAcls"] = cfg.IgnorePublicAcls;
|
|
11855
|
+
if (cfg.RestrictPublicBuckets !== void 0) {
|
|
11856
|
+
out["RestrictPublicBuckets"] = cfg.RestrictPublicBuckets;
|
|
11857
|
+
}
|
|
11858
|
+
if (Object.keys(out).length > 0) {
|
|
11859
|
+
result["PublicAccessBlockConfiguration"] = out;
|
|
11860
|
+
}
|
|
11861
|
+
}
|
|
11862
|
+
} catch (err) {
|
|
11863
|
+
const e = err;
|
|
11864
|
+
if (e.name !== "NoSuchPublicAccessBlockConfiguration") {
|
|
11865
|
+
throw err;
|
|
11866
|
+
}
|
|
11867
|
+
}
|
|
11868
|
+
try {
|
|
11869
|
+
const resp = await this.s3Client.send(new GetBucketTaggingCommand({ Bucket: physicalId }));
|
|
11870
|
+
if (resp.TagSet && resp.TagSet.length > 0) {
|
|
11871
|
+
const tags = resp.TagSet.filter((t) => t.Key && !t.Key.startsWith("aws:")).map((t) => ({
|
|
11872
|
+
Key: t.Key,
|
|
11873
|
+
Value: t.Value
|
|
11874
|
+
}));
|
|
11875
|
+
if (tags.length > 0)
|
|
11876
|
+
result["Tags"] = tags;
|
|
11877
|
+
}
|
|
11878
|
+
} catch (err) {
|
|
11879
|
+
const e = err;
|
|
11880
|
+
if (e.name !== "NoSuchTagSet") {
|
|
11881
|
+
throw err;
|
|
11882
|
+
}
|
|
11883
|
+
}
|
|
11884
|
+
return result;
|
|
11885
|
+
}
|
|
11691
11886
|
/**
|
|
11692
11887
|
* Adopt an existing S3 bucket into cdkd state.
|
|
11693
11888
|
*
|
|
@@ -12238,6 +12433,90 @@ var SQSQueueProvider = class {
|
|
|
12238
12433
|
return void 0;
|
|
12239
12434
|
}
|
|
12240
12435
|
}
|
|
12436
|
+
/**
|
|
12437
|
+
* Read the AWS-current SQS queue configuration in CFn-property shape.
|
|
12438
|
+
*
|
|
12439
|
+
* Issues `GetQueueAttributes` for every attribute that maps back to a
|
|
12440
|
+
* cdkd-managed CFn property. AWS returns ALL attribute values as strings;
|
|
12441
|
+
* we type-coerce numeric attributes back to numbers and parse
|
|
12442
|
+
* `RedrivePolicy` from JSON so the comparator matches cdkd state's
|
|
12443
|
+
* already-typed values.
|
|
12444
|
+
*
|
|
12445
|
+
* `QueueName` is derived from the URL tail (the `physicalId` is the
|
|
12446
|
+
* queue URL), not surfaced by `GetQueueAttributes`.
|
|
12447
|
+
*
|
|
12448
|
+
* `Tags` is omitted: `ListQueueTags` is a separate call and tag drift is
|
|
12449
|
+
* generally less interesting than configuration drift; the `aws:cdk:path`
|
|
12450
|
+
* shape question is also out of scope here.
|
|
12451
|
+
*
|
|
12452
|
+
* Returns `undefined` when the queue is gone (`QueueDoesNotExist`).
|
|
12453
|
+
*/
|
|
12454
|
+
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
12455
|
+
let attributes;
|
|
12456
|
+
try {
|
|
12457
|
+
const resp = await this.sqsClient.send(
|
|
12458
|
+
new GetQueueAttributesCommand({
|
|
12459
|
+
QueueUrl: physicalId,
|
|
12460
|
+
AttributeNames: ["All"]
|
|
12461
|
+
})
|
|
12462
|
+
);
|
|
12463
|
+
attributes = resp.Attributes;
|
|
12464
|
+
} catch (err) {
|
|
12465
|
+
if (err instanceof QueueDoesNotExist)
|
|
12466
|
+
return void 0;
|
|
12467
|
+
throw err;
|
|
12468
|
+
}
|
|
12469
|
+
if (!attributes)
|
|
12470
|
+
return void 0;
|
|
12471
|
+
const result = {};
|
|
12472
|
+
const tail = physicalId.substring(physicalId.lastIndexOf("/") + 1);
|
|
12473
|
+
if (tail)
|
|
12474
|
+
result["QueueName"] = tail;
|
|
12475
|
+
const numeric = [
|
|
12476
|
+
"VisibilityTimeout",
|
|
12477
|
+
"MaximumMessageSize",
|
|
12478
|
+
"MessageRetentionPeriod",
|
|
12479
|
+
"DelaySeconds",
|
|
12480
|
+
"ReceiveMessageWaitTimeSeconds",
|
|
12481
|
+
"KmsDataKeyReusePeriodSeconds"
|
|
12482
|
+
];
|
|
12483
|
+
for (const key of numeric) {
|
|
12484
|
+
const v = attributes[key];
|
|
12485
|
+
if (v !== void 0) {
|
|
12486
|
+
const n = Number(v);
|
|
12487
|
+
if (!Number.isNaN(n))
|
|
12488
|
+
result[key] = n;
|
|
12489
|
+
}
|
|
12490
|
+
}
|
|
12491
|
+
const bool = [
|
|
12492
|
+
"FifoQueue",
|
|
12493
|
+
"ContentBasedDeduplication",
|
|
12494
|
+
"SqsManagedSseEnabled"
|
|
12495
|
+
];
|
|
12496
|
+
for (const key of bool) {
|
|
12497
|
+
const v = attributes[key];
|
|
12498
|
+
if (v !== void 0)
|
|
12499
|
+
result[key] = v === "true";
|
|
12500
|
+
}
|
|
12501
|
+
const str = [
|
|
12502
|
+
"KmsMasterKeyId",
|
|
12503
|
+
"DeduplicationScope",
|
|
12504
|
+
"FifoThroughputLimit"
|
|
12505
|
+
];
|
|
12506
|
+
for (const key of str) {
|
|
12507
|
+
const v = attributes[key];
|
|
12508
|
+
if (v !== void 0)
|
|
12509
|
+
result[key] = v;
|
|
12510
|
+
}
|
|
12511
|
+
if (attributes["RedrivePolicy"]) {
|
|
12512
|
+
try {
|
|
12513
|
+
result["RedrivePolicy"] = JSON.parse(attributes["RedrivePolicy"]);
|
|
12514
|
+
} catch {
|
|
12515
|
+
result["RedrivePolicy"] = attributes["RedrivePolicy"];
|
|
12516
|
+
}
|
|
12517
|
+
}
|
|
12518
|
+
return result;
|
|
12519
|
+
}
|
|
12241
12520
|
/**
|
|
12242
12521
|
* Adopt an existing SQS queue into cdkd state.
|
|
12243
12522
|
*
|
|
@@ -12795,6 +13074,76 @@ var SNSTopicProvider = class {
|
|
|
12795
13074
|
return void 0;
|
|
12796
13075
|
}
|
|
12797
13076
|
}
|
|
13077
|
+
/**
|
|
13078
|
+
* Read the AWS-current SNS topic configuration in CFn-property shape.
|
|
13079
|
+
*
|
|
13080
|
+
* Issues `GetTopicAttributes` for the topic-level configuration. AWS
|
|
13081
|
+
* returns ALL attribute values as strings; we type-coerce booleans back
|
|
13082
|
+
* to booleans and parse `ArchivePolicy` / `DataProtectionPolicy` from
|
|
13083
|
+
* JSON strings so the comparator matches cdkd state's typed values.
|
|
13084
|
+
*
|
|
13085
|
+
* `TopicName` is derived from the ARN tail (the `physicalId` is the
|
|
13086
|
+
* topic ARN).
|
|
13087
|
+
*
|
|
13088
|
+
* `Tags` and `DeliveryStatusLogging` are intentionally omitted:
|
|
13089
|
+
* `ListTagsForResource` is a separate call, and `DeliveryStatusLogging`
|
|
13090
|
+
* fans out into per-protocol attributes (`{Protocol}SuccessFeedbackRoleArn`,
|
|
13091
|
+
* etc.) whose round-trip back to the CFn array shape needs more thought
|
|
13092
|
+
* than fits in this PR.
|
|
13093
|
+
*
|
|
13094
|
+
* `Subscription` is omitted because CDK manages it via separate
|
|
13095
|
+
* `AWS::SNS::Subscription` resources, not as a Topic property.
|
|
13096
|
+
*
|
|
13097
|
+
* Returns `undefined` when the topic is gone (`NotFoundException`).
|
|
13098
|
+
*/
|
|
13099
|
+
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
13100
|
+
let attrs;
|
|
13101
|
+
try {
|
|
13102
|
+
const resp = await this.snsClient.send(
|
|
13103
|
+
new GetTopicAttributesCommand({ TopicArn: physicalId })
|
|
13104
|
+
);
|
|
13105
|
+
attrs = resp.Attributes;
|
|
13106
|
+
} catch (err) {
|
|
13107
|
+
if (err instanceof NotFoundException)
|
|
13108
|
+
return void 0;
|
|
13109
|
+
throw err;
|
|
13110
|
+
}
|
|
13111
|
+
if (!attrs)
|
|
13112
|
+
return void 0;
|
|
13113
|
+
const result = {};
|
|
13114
|
+
const tail = physicalId.substring(physicalId.lastIndexOf(":") + 1);
|
|
13115
|
+
if (tail)
|
|
13116
|
+
result["TopicName"] = tail;
|
|
13117
|
+
const bool = ["FifoTopic", "ContentBasedDeduplication"];
|
|
13118
|
+
for (const key of bool) {
|
|
13119
|
+
const v = attrs[key];
|
|
13120
|
+
if (v !== void 0)
|
|
13121
|
+
result[key] = v === "true";
|
|
13122
|
+
}
|
|
13123
|
+
const str = [
|
|
13124
|
+
"DisplayName",
|
|
13125
|
+
"KmsMasterKeyId",
|
|
13126
|
+
"TracingConfig",
|
|
13127
|
+
"SignatureVersion",
|
|
13128
|
+
"FifoThroughputScope"
|
|
13129
|
+
];
|
|
13130
|
+
for (const key of str) {
|
|
13131
|
+
const v = attrs[key];
|
|
13132
|
+
if (v !== void 0 && v !== "")
|
|
13133
|
+
result[key] = v;
|
|
13134
|
+
}
|
|
13135
|
+
for (const key of ["ArchivePolicy", "DataProtectionPolicy"]) {
|
|
13136
|
+
const v = attrs[key];
|
|
13137
|
+
if (v) {
|
|
13138
|
+
try {
|
|
13139
|
+
result[key] = JSON.parse(v);
|
|
13140
|
+
} catch {
|
|
13141
|
+
result[key] = v;
|
|
13142
|
+
}
|
|
13143
|
+
}
|
|
13144
|
+
}
|
|
13145
|
+
return result;
|
|
13146
|
+
}
|
|
12798
13147
|
/**
|
|
12799
13148
|
* Adopt an existing SNS topic into cdkd state.
|
|
12800
13149
|
*
|
|
@@ -13880,6 +14229,93 @@ var LambdaFunctionProvider = class {
|
|
|
13880
14229
|
throw err;
|
|
13881
14230
|
}
|
|
13882
14231
|
}
|
|
14232
|
+
/**
|
|
14233
|
+
* Read the AWS-current Lambda function configuration in CFn-property shape.
|
|
14234
|
+
*
|
|
14235
|
+
* Issues a single `GetFunction` and surfaces the same property keys
|
|
14236
|
+
* `create()` accepts (`Runtime`, `Handler`, `Role`, `Timeout`, `MemorySize`,
|
|
14237
|
+
* `Description`, `Environment`, `Layers`, `Architectures`, `PackageType`,
|
|
14238
|
+
* `TracingConfig`, `EphemeralStorage`, `VpcConfig`, plus the physical
|
|
14239
|
+
* `FunctionName`). The drift comparator only descends into keys present in
|
|
14240
|
+
* cdkd state, so AWS-managed fields (timestamps, FunctionArn, RevisionId,
|
|
14241
|
+
* etc.) are filtered at compare time — we still avoid serializing them on
|
|
14242
|
+
* the wire.
|
|
14243
|
+
*
|
|
14244
|
+
* `Code` is intentionally omitted: `GetFunction` returns a pre-signed S3
|
|
14245
|
+
* URL for the deployed code, not the asset hash cdkd state holds, so they
|
|
14246
|
+
* could never match. Lambda code drift is best detected separately (the
|
|
14247
|
+
* function's `CodeSha256` does live in `GetFunction` but is not what
|
|
14248
|
+
* cdkd's `Code: { S3Bucket, S3Key }` state property carries).
|
|
14249
|
+
*
|
|
14250
|
+
* `Tags` is omitted as well: `GetFunction` returns Tags as an object map,
|
|
14251
|
+
* while CFn / cdkd state holds them as `[{Key, Value}]`. Re-shaping
|
|
14252
|
+
* accurately requires deciding how to handle the auto-injected
|
|
14253
|
+
* `aws:cdk:path` tag, which is out of scope for this PR. Tag drift is
|
|
14254
|
+
* typically less interesting than configuration drift.
|
|
14255
|
+
*
|
|
14256
|
+
* Returns `undefined` when the function is gone (`ResourceNotFoundException`).
|
|
14257
|
+
*/
|
|
14258
|
+
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
14259
|
+
try {
|
|
14260
|
+
const resp = await this.lambdaClient.send(
|
|
14261
|
+
new GetFunctionCommand({ FunctionName: physicalId })
|
|
14262
|
+
);
|
|
14263
|
+
const cfg = resp.Configuration;
|
|
14264
|
+
if (!cfg)
|
|
14265
|
+
return void 0;
|
|
14266
|
+
const result = {};
|
|
14267
|
+
if (cfg.FunctionName !== void 0)
|
|
14268
|
+
result["FunctionName"] = cfg.FunctionName;
|
|
14269
|
+
if (cfg.Runtime !== void 0)
|
|
14270
|
+
result["Runtime"] = cfg.Runtime;
|
|
14271
|
+
if (cfg.Handler !== void 0)
|
|
14272
|
+
result["Handler"] = cfg.Handler;
|
|
14273
|
+
if (cfg.Role !== void 0)
|
|
14274
|
+
result["Role"] = cfg.Role;
|
|
14275
|
+
if (cfg.Timeout !== void 0)
|
|
14276
|
+
result["Timeout"] = cfg.Timeout;
|
|
14277
|
+
if (cfg.MemorySize !== void 0)
|
|
14278
|
+
result["MemorySize"] = cfg.MemorySize;
|
|
14279
|
+
if (cfg.Description !== void 0 && cfg.Description !== "") {
|
|
14280
|
+
result["Description"] = cfg.Description;
|
|
14281
|
+
}
|
|
14282
|
+
if (cfg.Environment?.Variables) {
|
|
14283
|
+
result["Environment"] = { Variables: cfg.Environment.Variables };
|
|
14284
|
+
}
|
|
14285
|
+
if (cfg.Layers && cfg.Layers.length > 0) {
|
|
14286
|
+
result["Layers"] = cfg.Layers.map((l) => l.Arn).filter((arn) => !!arn);
|
|
14287
|
+
}
|
|
14288
|
+
if (cfg.Architectures && cfg.Architectures.length > 0) {
|
|
14289
|
+
result["Architectures"] = [...cfg.Architectures];
|
|
14290
|
+
}
|
|
14291
|
+
if (cfg.PackageType !== void 0)
|
|
14292
|
+
result["PackageType"] = cfg.PackageType;
|
|
14293
|
+
if (cfg.TracingConfig?.Mode !== void 0) {
|
|
14294
|
+
result["TracingConfig"] = { Mode: cfg.TracingConfig.Mode };
|
|
14295
|
+
}
|
|
14296
|
+
if (cfg.EphemeralStorage?.Size !== void 0) {
|
|
14297
|
+
result["EphemeralStorage"] = { Size: cfg.EphemeralStorage.Size };
|
|
14298
|
+
}
|
|
14299
|
+
if (cfg.VpcConfig) {
|
|
14300
|
+
const vpc = {};
|
|
14301
|
+
if (cfg.VpcConfig.SubnetIds)
|
|
14302
|
+
vpc["SubnetIds"] = [...cfg.VpcConfig.SubnetIds];
|
|
14303
|
+
if (cfg.VpcConfig.SecurityGroupIds) {
|
|
14304
|
+
vpc["SecurityGroupIds"] = [...cfg.VpcConfig.SecurityGroupIds];
|
|
14305
|
+
}
|
|
14306
|
+
if (cfg.VpcConfig.Ipv6AllowedForDualStack !== void 0) {
|
|
14307
|
+
vpc["Ipv6AllowedForDualStack"] = cfg.VpcConfig.Ipv6AllowedForDualStack;
|
|
14308
|
+
}
|
|
14309
|
+
if (Object.keys(vpc).length > 0)
|
|
14310
|
+
result["VpcConfig"] = vpc;
|
|
14311
|
+
}
|
|
14312
|
+
return result;
|
|
14313
|
+
} catch (err) {
|
|
14314
|
+
if (err instanceof ResourceNotFoundException)
|
|
14315
|
+
return void 0;
|
|
14316
|
+
throw err;
|
|
14317
|
+
}
|
|
14318
|
+
}
|
|
13883
14319
|
/**
|
|
13884
14320
|
* Adopt an existing Lambda function into cdkd state.
|
|
13885
14321
|
*
|
|
@@ -15055,6 +15491,90 @@ var DynamoDBTableProvider = class {
|
|
|
15055
15491
|
throw err;
|
|
15056
15492
|
}
|
|
15057
15493
|
}
|
|
15494
|
+
/**
|
|
15495
|
+
* Read the AWS-current DynamoDB table configuration in CFn-property shape.
|
|
15496
|
+
*
|
|
15497
|
+
* `DescribeTable` returns every field cdkd manages in one call. AWS uses
|
|
15498
|
+
* the same property names CFn does (KeySchema, AttributeDefinitions,
|
|
15499
|
+
* BillingModeSummary.BillingMode, ProvisionedThroughput, etc.) — the only
|
|
15500
|
+
* shape differences are wrapping:
|
|
15501
|
+
* - BillingMode lives under `BillingModeSummary.BillingMode` in the API
|
|
15502
|
+
* response, but the CFn property is a flat `BillingMode` string.
|
|
15503
|
+
* - StreamSpecification's CFn shape includes only `StreamViewType`; the
|
|
15504
|
+
* API response carries `StreamEnabled` too. We surface both since the
|
|
15505
|
+
* drift comparator only descends into keys present in state.
|
|
15506
|
+
* - GSI / LSI in the API response include `IndexStatus`, `ItemCount` and
|
|
15507
|
+
* sizing fields that cdkd never sets; the comparator filters them.
|
|
15508
|
+
*
|
|
15509
|
+
* Returns `undefined` when the table is gone (`ResourceNotFoundException`).
|
|
15510
|
+
*
|
|
15511
|
+
* Tags are intentionally omitted: `ListTagsOfResource` is a separate call
|
|
15512
|
+
* and tag drift is generally less interesting than table-config drift;
|
|
15513
|
+
* including it would also force a tag-shape decision on the
|
|
15514
|
+
* `aws:cdk:path` auto-tag, which is out of scope here.
|
|
15515
|
+
*/
|
|
15516
|
+
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
15517
|
+
try {
|
|
15518
|
+
const resp = await this.dynamoDBClient.send(
|
|
15519
|
+
new DescribeTableCommand2({ TableName: physicalId })
|
|
15520
|
+
);
|
|
15521
|
+
const table = resp.Table;
|
|
15522
|
+
if (!table)
|
|
15523
|
+
return void 0;
|
|
15524
|
+
const result = {};
|
|
15525
|
+
if (table.TableName !== void 0)
|
|
15526
|
+
result["TableName"] = table.TableName;
|
|
15527
|
+
if (table.KeySchema)
|
|
15528
|
+
result["KeySchema"] = table.KeySchema;
|
|
15529
|
+
if (table.AttributeDefinitions) {
|
|
15530
|
+
result["AttributeDefinitions"] = table.AttributeDefinitions;
|
|
15531
|
+
}
|
|
15532
|
+
if (table.BillingModeSummary?.BillingMode) {
|
|
15533
|
+
result["BillingMode"] = table.BillingModeSummary.BillingMode;
|
|
15534
|
+
}
|
|
15535
|
+
if (table.ProvisionedThroughput) {
|
|
15536
|
+
result["ProvisionedThroughput"] = {
|
|
15537
|
+
ReadCapacityUnits: table.ProvisionedThroughput.ReadCapacityUnits,
|
|
15538
|
+
WriteCapacityUnits: table.ProvisionedThroughput.WriteCapacityUnits
|
|
15539
|
+
};
|
|
15540
|
+
}
|
|
15541
|
+
if (table.StreamSpecification) {
|
|
15542
|
+
result["StreamSpecification"] = {
|
|
15543
|
+
StreamEnabled: table.StreamSpecification.StreamEnabled,
|
|
15544
|
+
StreamViewType: table.StreamSpecification.StreamViewType
|
|
15545
|
+
};
|
|
15546
|
+
}
|
|
15547
|
+
if (table.GlobalSecondaryIndexes && table.GlobalSecondaryIndexes.length > 0) {
|
|
15548
|
+
result["GlobalSecondaryIndexes"] = table.GlobalSecondaryIndexes;
|
|
15549
|
+
}
|
|
15550
|
+
if (table.LocalSecondaryIndexes && table.LocalSecondaryIndexes.length > 0) {
|
|
15551
|
+
result["LocalSecondaryIndexes"] = table.LocalSecondaryIndexes;
|
|
15552
|
+
}
|
|
15553
|
+
if (table.SSEDescription) {
|
|
15554
|
+
const sse = {};
|
|
15555
|
+
if (table.SSEDescription.Status === "ENABLED")
|
|
15556
|
+
sse["SSEEnabled"] = true;
|
|
15557
|
+
if (table.SSEDescription.KMSMasterKeyArn !== void 0) {
|
|
15558
|
+
sse["KMSMasterKeyId"] = table.SSEDescription.KMSMasterKeyArn;
|
|
15559
|
+
}
|
|
15560
|
+
if (table.SSEDescription.SSEType !== void 0)
|
|
15561
|
+
sse["SSEType"] = table.SSEDescription.SSEType;
|
|
15562
|
+
if (Object.keys(sse).length > 0)
|
|
15563
|
+
result["SSESpecification"] = sse;
|
|
15564
|
+
}
|
|
15565
|
+
if (table.DeletionProtectionEnabled !== void 0) {
|
|
15566
|
+
result["DeletionProtectionEnabled"] = table.DeletionProtectionEnabled;
|
|
15567
|
+
}
|
|
15568
|
+
if (table.TableClassSummary?.TableClass) {
|
|
15569
|
+
result["TableClass"] = table.TableClassSummary.TableClass;
|
|
15570
|
+
}
|
|
15571
|
+
return result;
|
|
15572
|
+
} catch (err) {
|
|
15573
|
+
if (err instanceof ResourceNotFoundException6)
|
|
15574
|
+
return void 0;
|
|
15575
|
+
throw err;
|
|
15576
|
+
}
|
|
15577
|
+
}
|
|
15058
15578
|
/**
|
|
15059
15579
|
* Adopt an existing DynamoDB table into cdkd state.
|
|
15060
15580
|
*
|
|
@@ -15359,6 +15879,48 @@ var LogsLogGroupProvider = class {
|
|
|
15359
15879
|
}
|
|
15360
15880
|
return this.buildArn(physicalId);
|
|
15361
15881
|
}
|
|
15882
|
+
/**
|
|
15883
|
+
* Read the AWS-current log group configuration in CFn-property shape.
|
|
15884
|
+
*
|
|
15885
|
+
* Issues `DescribeLogGroups` filtered by exact name and picks the first
|
|
15886
|
+
* (and only) match. AWS uses camelCase field names in the API response
|
|
15887
|
+
* (`logGroupName`, `kmsKeyId`, `retentionInDays`); we map them back to
|
|
15888
|
+
* the CFn-cased keys cdkd state holds (`LogGroupName`, `KmsKeyId`,
|
|
15889
|
+
* `RetentionInDays`).
|
|
15890
|
+
*
|
|
15891
|
+
* Coverage: `LogGroupName`, `KmsKeyId`, `RetentionInDays`,
|
|
15892
|
+
* `LogGroupClass`. Other handledProperties (`DataProtectionPolicy`,
|
|
15893
|
+
* `Tags`, `FieldIndexPolicies`, `ResourcePolicyDocument`,
|
|
15894
|
+
* `DeletionProtectionEnabled`, `BearerTokenAuthenticationEnabled`) need
|
|
15895
|
+
* their own per-property API call and are out of scope for v1.
|
|
15896
|
+
*
|
|
15897
|
+
* Returns `undefined` when the log group is gone.
|
|
15898
|
+
*/
|
|
15899
|
+
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
15900
|
+
try {
|
|
15901
|
+
const resp = await this.logsClient.send(
|
|
15902
|
+
new DescribeLogGroupsCommand({ logGroupNamePrefix: physicalId })
|
|
15903
|
+
);
|
|
15904
|
+
const found = resp.logGroups?.find((g) => g.logGroupName === physicalId);
|
|
15905
|
+
if (!found)
|
|
15906
|
+
return void 0;
|
|
15907
|
+
const result = {};
|
|
15908
|
+
if (found.logGroupName !== void 0)
|
|
15909
|
+
result["LogGroupName"] = found.logGroupName;
|
|
15910
|
+
if (found.kmsKeyId !== void 0)
|
|
15911
|
+
result["KmsKeyId"] = found.kmsKeyId;
|
|
15912
|
+
if (found.retentionInDays !== void 0) {
|
|
15913
|
+
result["RetentionInDays"] = found.retentionInDays;
|
|
15914
|
+
}
|
|
15915
|
+
if (found.logGroupClass !== void 0)
|
|
15916
|
+
result["LogGroupClass"] = found.logGroupClass;
|
|
15917
|
+
return result;
|
|
15918
|
+
} catch (err) {
|
|
15919
|
+
if (err instanceof ResourceNotFoundException7)
|
|
15920
|
+
return void 0;
|
|
15921
|
+
throw err;
|
|
15922
|
+
}
|
|
15923
|
+
}
|
|
15362
15924
|
/**
|
|
15363
15925
|
* Adopt an existing CloudWatch Logs log group into cdkd state.
|
|
15364
15926
|
*
|
|
@@ -36982,7 +37544,7 @@ function reorderArgs(argv) {
|
|
|
36982
37544
|
}
|
|
36983
37545
|
async function main() {
|
|
36984
37546
|
const program = new Command14();
|
|
36985
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
37547
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.37.0");
|
|
36986
37548
|
program.addCommand(createBootstrapCommand());
|
|
36987
37549
|
program.addCommand(createSynthCommand());
|
|
36988
37550
|
program.addCommand(createListCommand());
|