@go-to-k/cdkd 0.42.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +576 -39
- package/dist/cli.js.map +4 -4
- package/dist/go-to-k-cdkd-0.44.0.tgz +0 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.42.0.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -7601,7 +7601,7 @@ Error: ${err.message || "Unknown error"}`,
|
|
|
7601
7601
|
* resource type that goes through CC API — the majority of cdkd's surface.
|
|
7602
7602
|
* SDK Providers add their own `readCurrentState` incrementally (PR D).
|
|
7603
7603
|
*/
|
|
7604
|
-
async readCurrentState(physicalId, _logicalId, resourceType) {
|
|
7604
|
+
async readCurrentState(physicalId, _logicalId, resourceType, _properties) {
|
|
7605
7605
|
try {
|
|
7606
7606
|
const response = await this.cloudControlClient.send(
|
|
7607
7607
|
new GetResourceCommand2({
|
|
@@ -9169,6 +9169,9 @@ import {
|
|
|
9169
9169
|
DeleteGroupPolicyCommand,
|
|
9170
9170
|
PutUserPolicyCommand,
|
|
9171
9171
|
DeleteUserPolicyCommand,
|
|
9172
|
+
GetRolePolicyCommand,
|
|
9173
|
+
GetGroupPolicyCommand,
|
|
9174
|
+
GetUserPolicyCommand,
|
|
9172
9175
|
NoSuchEntityException as NoSuchEntityException2
|
|
9173
9176
|
} from "@aws-sdk/client-iam";
|
|
9174
9177
|
init_aws_clients();
|
|
@@ -9517,6 +9520,93 @@ var IAMPolicyProvider = class {
|
|
|
9517
9520
|
);
|
|
9518
9521
|
}
|
|
9519
9522
|
}
|
|
9523
|
+
/**
|
|
9524
|
+
* Read the AWS-current IAM inline policy in CFn-property shape.
|
|
9525
|
+
*
|
|
9526
|
+
* `AWS::IAM::Policy` is an inline policy attached to one or more roles /
|
|
9527
|
+
* groups / users via `PutRolePolicy` / `PutGroupPolicy` / `PutUserPolicy`.
|
|
9528
|
+
* Each attachment is a separate API call, but the same `PolicyDocument`
|
|
9529
|
+
* is replicated across every target.
|
|
9530
|
+
*
|
|
9531
|
+
* Strategy: pick the FIRST target from `properties.Roles` / `Groups` /
|
|
9532
|
+
* `Users` (in that order), call `Get*Policy(target, policyName)`, and
|
|
9533
|
+
* surface the URL-decoded + JSON-parsed `PolicyDocument`. Roles / Groups /
|
|
9534
|
+
* Users are echoed back from state since AWS doesn't return them. This is
|
|
9535
|
+
* defensible because:
|
|
9536
|
+
* - Drift on `PolicyDocument`: caught — the document is the same on
|
|
9537
|
+
* every target, so reading any one of them surfaces the divergence.
|
|
9538
|
+
* - Drift on the target list (a role removed / added out-of-band): NOT
|
|
9539
|
+
* caught. There's no API to enumerate every role / group / user that
|
|
9540
|
+
* has an inline policy of a given name; cdkd would need to walk the
|
|
9541
|
+
* entire account. Out of scope for v1.
|
|
9542
|
+
*
|
|
9543
|
+
* Returns `undefined` when the resolved target has no inline policy of
|
|
9544
|
+
* that name (`NoSuchEntityException`) — signals "drift unknown" rather
|
|
9545
|
+
* than firing a false positive.
|
|
9546
|
+
*/
|
|
9547
|
+
async readCurrentState(physicalId, _logicalId, _resourceType, properties) {
|
|
9548
|
+
if (!properties)
|
|
9549
|
+
return void 0;
|
|
9550
|
+
const policyDocument = properties["PolicyDocument"];
|
|
9551
|
+
if (!policyDocument)
|
|
9552
|
+
return void 0;
|
|
9553
|
+
const policyName = physicalId.includes(":") ? physicalId.split(":")[0] : physicalId;
|
|
9554
|
+
const roles = properties["Roles"];
|
|
9555
|
+
const groups = properties["Groups"];
|
|
9556
|
+
const users = properties["Users"];
|
|
9557
|
+
let liveDocument;
|
|
9558
|
+
try {
|
|
9559
|
+
if (roles && roles.length > 0) {
|
|
9560
|
+
const resp = await this.iamClient.send(
|
|
9561
|
+
new GetRolePolicyCommand({ RoleName: roles[0], PolicyName: policyName })
|
|
9562
|
+
);
|
|
9563
|
+
liveDocument = this.decodePolicyDocument(resp.PolicyDocument);
|
|
9564
|
+
} else if (groups && groups.length > 0) {
|
|
9565
|
+
const resp = await this.iamClient.send(
|
|
9566
|
+
new GetGroupPolicyCommand({ GroupName: groups[0], PolicyName: policyName })
|
|
9567
|
+
);
|
|
9568
|
+
liveDocument = this.decodePolicyDocument(resp.PolicyDocument);
|
|
9569
|
+
} else if (users && users.length > 0) {
|
|
9570
|
+
const resp = await this.iamClient.send(
|
|
9571
|
+
new GetUserPolicyCommand({ UserName: users[0], PolicyName: policyName })
|
|
9572
|
+
);
|
|
9573
|
+
liveDocument = this.decodePolicyDocument(resp.PolicyDocument);
|
|
9574
|
+
} else {
|
|
9575
|
+
return void 0;
|
|
9576
|
+
}
|
|
9577
|
+
} catch (err) {
|
|
9578
|
+
if (err instanceof NoSuchEntityException2)
|
|
9579
|
+
return void 0;
|
|
9580
|
+
throw err;
|
|
9581
|
+
}
|
|
9582
|
+
if (liveDocument === void 0)
|
|
9583
|
+
return void 0;
|
|
9584
|
+
const result = {
|
|
9585
|
+
PolicyName: policyName,
|
|
9586
|
+
PolicyDocument: liveDocument
|
|
9587
|
+
};
|
|
9588
|
+
if (roles)
|
|
9589
|
+
result["Roles"] = roles;
|
|
9590
|
+
if (groups)
|
|
9591
|
+
result["Groups"] = groups;
|
|
9592
|
+
if (users)
|
|
9593
|
+
result["Users"] = users;
|
|
9594
|
+
return result;
|
|
9595
|
+
}
|
|
9596
|
+
/**
|
|
9597
|
+
* IAM Get*Policy returns the policy document as a URL-encoded JSON string
|
|
9598
|
+
* (per RFC 3986). Decode and parse it back into the object shape cdkd
|
|
9599
|
+
* state holds, so the drift comparator sees apples-to-apples.
|
|
9600
|
+
*/
|
|
9601
|
+
decodePolicyDocument(raw) {
|
|
9602
|
+
if (!raw)
|
|
9603
|
+
return void 0;
|
|
9604
|
+
try {
|
|
9605
|
+
return JSON.parse(decodeURIComponent(raw));
|
|
9606
|
+
} catch {
|
|
9607
|
+
return raw;
|
|
9608
|
+
}
|
|
9609
|
+
}
|
|
9520
9610
|
/**
|
|
9521
9611
|
* Adopt an existing IAM inline policy into cdkd state.
|
|
9522
9612
|
*
|
|
@@ -14710,6 +14800,7 @@ var LambdaFunctionProvider = class {
|
|
|
14710
14800
|
// src/provisioning/providers/lambda-permission-provider.ts
|
|
14711
14801
|
import {
|
|
14712
14802
|
AddPermissionCommand,
|
|
14803
|
+
GetPolicyCommand,
|
|
14713
14804
|
RemovePermissionCommand,
|
|
14714
14805
|
ResourceNotFoundException as ResourceNotFoundException2
|
|
14715
14806
|
} from "@aws-sdk/client-lambda";
|
|
@@ -14900,6 +14991,87 @@ var LambdaPermissionProvider = class {
|
|
|
14900
14991
|
);
|
|
14901
14992
|
}
|
|
14902
14993
|
}
|
|
14994
|
+
/**
|
|
14995
|
+
* Read the AWS-current Lambda permission in CFn-property shape.
|
|
14996
|
+
*
|
|
14997
|
+
* `AWS::Lambda::Permission` has no per-statement Get API — `GetPolicy` on
|
|
14998
|
+
* the parent function returns the entire resource-based policy as a JSON
|
|
14999
|
+
* string, and we have to scan its `Statement` array for the one with our
|
|
15000
|
+
* `Sid` (cdkd's physicalId).
|
|
15001
|
+
*
|
|
15002
|
+
* Returns `undefined` when:
|
|
15003
|
+
* - `properties.FunctionName` is missing (sub-resource needs the parent).
|
|
15004
|
+
* - The function has no policy at all (`ResourceNotFoundException`) or
|
|
15005
|
+
* the matching `Sid` isn't present.
|
|
15006
|
+
*
|
|
15007
|
+
* The reverse-mapping from policy statement back to CFn shape:
|
|
15008
|
+
* - `Action` → `Sid`'s `Action` (string or first element if array).
|
|
15009
|
+
* - `Principal` → `Service` / `AWS` / `*` (CFn flat string form).
|
|
15010
|
+
* - `Condition.ArnLike.AWS:SourceArn` → `SourceArn`.
|
|
15011
|
+
* - `Condition.StringEquals.AWS:SourceAccount` → `SourceAccount`.
|
|
15012
|
+
* - `Condition.StringEquals.aws:PrincipalOrgID` → `PrincipalOrgID`.
|
|
15013
|
+
* - `Condition.ArnLike.AWS:SourceAccount` is left alone — drift on the
|
|
15014
|
+
* condition operator key would be confusing here.
|
|
15015
|
+
*/
|
|
15016
|
+
async readCurrentState(physicalId, _logicalId, _resourceType, properties) {
|
|
15017
|
+
const functionName = properties?.["FunctionName"];
|
|
15018
|
+
if (!functionName)
|
|
15019
|
+
return void 0;
|
|
15020
|
+
const statementId = physicalId.includes("|") ? physicalId.split("|").pop() : physicalId;
|
|
15021
|
+
let policyDoc;
|
|
15022
|
+
try {
|
|
15023
|
+
const resp = await this.lambdaClient.send(
|
|
15024
|
+
new GetPolicyCommand({ FunctionName: functionName })
|
|
15025
|
+
);
|
|
15026
|
+
policyDoc = resp.Policy;
|
|
15027
|
+
} catch (err) {
|
|
15028
|
+
if (err instanceof ResourceNotFoundException2)
|
|
15029
|
+
return void 0;
|
|
15030
|
+
throw err;
|
|
15031
|
+
}
|
|
15032
|
+
if (!policyDoc)
|
|
15033
|
+
return void 0;
|
|
15034
|
+
let parsed;
|
|
15035
|
+
try {
|
|
15036
|
+
parsed = JSON.parse(policyDoc);
|
|
15037
|
+
} catch {
|
|
15038
|
+
return void 0;
|
|
15039
|
+
}
|
|
15040
|
+
const statement = parsed.Statement?.find((s) => s.Sid === statementId);
|
|
15041
|
+
if (!statement)
|
|
15042
|
+
return void 0;
|
|
15043
|
+
const result = { FunctionName: functionName };
|
|
15044
|
+
if (statement.Action !== void 0) {
|
|
15045
|
+
result["Action"] = Array.isArray(statement.Action) ? statement.Action[0] : statement.Action;
|
|
15046
|
+
}
|
|
15047
|
+
if (statement.Principal !== void 0) {
|
|
15048
|
+
const p = statement.Principal;
|
|
15049
|
+
if (typeof p === "string") {
|
|
15050
|
+
result["Principal"] = p;
|
|
15051
|
+
} else if (typeof p === "object") {
|
|
15052
|
+
const value = p["Service"] ?? p["AWS"] ?? p["Federated"] ?? void 0;
|
|
15053
|
+
if (value !== void 0) {
|
|
15054
|
+
result["Principal"] = Array.isArray(value) ? value[0] : value;
|
|
15055
|
+
}
|
|
15056
|
+
}
|
|
15057
|
+
}
|
|
15058
|
+
const condition = statement.Condition;
|
|
15059
|
+
if (condition) {
|
|
15060
|
+
const sourceArn = condition["ArnLike"]?.["AWS:SourceArn"] ?? condition["StringEquals"]?.["AWS:SourceArn"];
|
|
15061
|
+
if (sourceArn !== void 0) {
|
|
15062
|
+
result["SourceArn"] = Array.isArray(sourceArn) ? sourceArn[0] : sourceArn;
|
|
15063
|
+
}
|
|
15064
|
+
const sourceAccount = condition["StringEquals"]?.["AWS:SourceAccount"];
|
|
15065
|
+
if (sourceAccount !== void 0) {
|
|
15066
|
+
result["SourceAccount"] = Array.isArray(sourceAccount) ? sourceAccount[0] : sourceAccount;
|
|
15067
|
+
}
|
|
15068
|
+
const orgId = condition["StringEquals"]?.["aws:PrincipalOrgID"];
|
|
15069
|
+
if (orgId !== void 0) {
|
|
15070
|
+
result["PrincipalOrgID"] = Array.isArray(orgId) ? orgId[0] : orgId;
|
|
15071
|
+
}
|
|
15072
|
+
}
|
|
15073
|
+
return result;
|
|
15074
|
+
}
|
|
14903
15075
|
/**
|
|
14904
15076
|
* Adopt an existing Lambda permission into cdkd state.
|
|
14905
15077
|
*
|
|
@@ -20815,11 +20987,14 @@ import {
|
|
|
20815
20987
|
GetAccountCommand,
|
|
20816
20988
|
CreateResourceCommand as CreateResourceCommand2,
|
|
20817
20989
|
DeleteResourceCommand as DeleteResourceCommand2,
|
|
20990
|
+
GetResourceCommand as GetResourceCommand3,
|
|
20818
20991
|
CreateDeploymentCommand,
|
|
20819
20992
|
DeleteDeploymentCommand,
|
|
20993
|
+
GetDeploymentCommand,
|
|
20820
20994
|
CreateStageCommand,
|
|
20821
20995
|
UpdateStageCommand,
|
|
20822
20996
|
DeleteStageCommand,
|
|
20997
|
+
GetStageCommand,
|
|
20823
20998
|
PutMethodCommand,
|
|
20824
20999
|
DeleteMethodCommand,
|
|
20825
21000
|
GetMethodCommand,
|
|
@@ -20827,6 +21002,7 @@ import {
|
|
|
20827
21002
|
PutMethodResponseCommand,
|
|
20828
21003
|
CreateAuthorizerCommand,
|
|
20829
21004
|
DeleteAuthorizerCommand,
|
|
21005
|
+
GetAuthorizerCommand,
|
|
20830
21006
|
NotFoundException as NotFoundException3
|
|
20831
21007
|
} from "@aws-sdk/client-api-gateway";
|
|
20832
21008
|
init_aws_clients();
|
|
@@ -21822,26 +21998,126 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
|
|
|
21822
21998
|
* - `AWS::ApiGateway::Method` → `GetMethod`. PhysicalId is the composite
|
|
21823
21999
|
* `restApiId|resourceId|httpMethod`, so we have everything needed
|
|
21824
22000
|
* without `Properties`.
|
|
21825
|
-
*
|
|
21826
|
-
* **Out of scope** (returns `undefined`, falls back to "drift unknown"):
|
|
21827
22001
|
* - `AWS::ApiGateway::Authorizer` / `Resource` / `Deployment` / `Stage`:
|
|
21828
|
-
* each
|
|
21829
|
-
*
|
|
21830
|
-
* physicalId, which for these types is just the sub-resource id).
|
|
21831
|
-
* CC API drift detection picks up `AWS::ApiGateway::RestApi` itself
|
|
21832
|
-
* once the user works through the SDK provider boundary; per-sub
|
|
21833
|
-
* drift detection here would need a contract change.
|
|
22002
|
+
* each uses `properties.RestApiId` (passed through PR G's signature
|
|
22003
|
+
* extension) to issue the appropriate `Get*` call.
|
|
21834
22004
|
*/
|
|
21835
|
-
async readCurrentState(physicalId, _logicalId, resourceType) {
|
|
22005
|
+
async readCurrentState(physicalId, _logicalId, resourceType, properties) {
|
|
21836
22006
|
switch (resourceType) {
|
|
21837
22007
|
case "AWS::ApiGateway::Account":
|
|
21838
22008
|
return this.readCurrentStateAccount();
|
|
21839
22009
|
case "AWS::ApiGateway::Method":
|
|
21840
22010
|
return this.readCurrentStateMethod(physicalId);
|
|
22011
|
+
case "AWS::ApiGateway::Authorizer":
|
|
22012
|
+
return this.readCurrentStateAuthorizer(physicalId, properties);
|
|
22013
|
+
case "AWS::ApiGateway::Resource":
|
|
22014
|
+
return this.readCurrentStateResource(physicalId, properties);
|
|
22015
|
+
case "AWS::ApiGateway::Deployment":
|
|
22016
|
+
return this.readCurrentStateDeployment(physicalId, properties);
|
|
22017
|
+
case "AWS::ApiGateway::Stage":
|
|
22018
|
+
return this.readCurrentStateStage(physicalId, properties);
|
|
21841
22019
|
default:
|
|
21842
22020
|
return void 0;
|
|
21843
22021
|
}
|
|
21844
22022
|
}
|
|
22023
|
+
async readCurrentStateAuthorizer(physicalId, properties) {
|
|
22024
|
+
const restApiId = properties?.["RestApiId"];
|
|
22025
|
+
if (!restApiId)
|
|
22026
|
+
return void 0;
|
|
22027
|
+
try {
|
|
22028
|
+
const resp = await this.apiGatewayClient.send(
|
|
22029
|
+
new GetAuthorizerCommand({ restApiId, authorizerId: physicalId })
|
|
22030
|
+
);
|
|
22031
|
+
const result = { RestApiId: restApiId };
|
|
22032
|
+
if (resp.name !== void 0)
|
|
22033
|
+
result["Name"] = resp.name;
|
|
22034
|
+
if (resp.type !== void 0)
|
|
22035
|
+
result["Type"] = resp.type;
|
|
22036
|
+
if (resp.providerARNs !== void 0 && resp.providerARNs.length > 0) {
|
|
22037
|
+
result["ProviderARNs"] = [...resp.providerARNs];
|
|
22038
|
+
}
|
|
22039
|
+
if (resp.authorizerUri !== void 0)
|
|
22040
|
+
result["AuthorizerUri"] = resp.authorizerUri;
|
|
22041
|
+
if (resp.authorizerCredentials !== void 0) {
|
|
22042
|
+
result["AuthorizerCredentials"] = resp.authorizerCredentials;
|
|
22043
|
+
}
|
|
22044
|
+
if (resp.identitySource !== void 0)
|
|
22045
|
+
result["IdentitySource"] = resp.identitySource;
|
|
22046
|
+
if (resp.identityValidationExpression !== void 0) {
|
|
22047
|
+
result["IdentityValidationExpression"] = resp.identityValidationExpression;
|
|
22048
|
+
}
|
|
22049
|
+
if (resp.authorizerResultTtlInSeconds !== void 0) {
|
|
22050
|
+
result["AuthorizerResultTtlInSeconds"] = resp.authorizerResultTtlInSeconds;
|
|
22051
|
+
}
|
|
22052
|
+
return result;
|
|
22053
|
+
} catch (err) {
|
|
22054
|
+
if (err instanceof NotFoundException3)
|
|
22055
|
+
return void 0;
|
|
22056
|
+
throw err;
|
|
22057
|
+
}
|
|
22058
|
+
}
|
|
22059
|
+
async readCurrentStateResource(physicalId, properties) {
|
|
22060
|
+
const restApiId = properties?.["RestApiId"];
|
|
22061
|
+
if (!restApiId)
|
|
22062
|
+
return void 0;
|
|
22063
|
+
try {
|
|
22064
|
+
const resp = await this.apiGatewayClient.send(
|
|
22065
|
+
new GetResourceCommand3({ restApiId, resourceId: physicalId })
|
|
22066
|
+
);
|
|
22067
|
+
const result = { RestApiId: restApiId };
|
|
22068
|
+
if (resp.parentId !== void 0)
|
|
22069
|
+
result["ParentId"] = resp.parentId;
|
|
22070
|
+
if (resp.pathPart !== void 0)
|
|
22071
|
+
result["PathPart"] = resp.pathPart;
|
|
22072
|
+
return result;
|
|
22073
|
+
} catch (err) {
|
|
22074
|
+
if (err instanceof NotFoundException3)
|
|
22075
|
+
return void 0;
|
|
22076
|
+
throw err;
|
|
22077
|
+
}
|
|
22078
|
+
}
|
|
22079
|
+
async readCurrentStateDeployment(physicalId, properties) {
|
|
22080
|
+
const restApiId = properties?.["RestApiId"];
|
|
22081
|
+
if (!restApiId)
|
|
22082
|
+
return void 0;
|
|
22083
|
+
try {
|
|
22084
|
+
const resp = await this.apiGatewayClient.send(
|
|
22085
|
+
new GetDeploymentCommand({ restApiId, deploymentId: physicalId })
|
|
22086
|
+
);
|
|
22087
|
+
const result = { RestApiId: restApiId };
|
|
22088
|
+
if (resp.description !== void 0 && resp.description !== "") {
|
|
22089
|
+
result["Description"] = resp.description;
|
|
22090
|
+
}
|
|
22091
|
+
return result;
|
|
22092
|
+
} catch (err) {
|
|
22093
|
+
if (err instanceof NotFoundException3)
|
|
22094
|
+
return void 0;
|
|
22095
|
+
throw err;
|
|
22096
|
+
}
|
|
22097
|
+
}
|
|
22098
|
+
async readCurrentStateStage(physicalId, properties) {
|
|
22099
|
+
const restApiId = properties?.["RestApiId"];
|
|
22100
|
+
if (!restApiId)
|
|
22101
|
+
return void 0;
|
|
22102
|
+
try {
|
|
22103
|
+
const resp = await this.apiGatewayClient.send(
|
|
22104
|
+
new GetStageCommand({ restApiId, stageName: physicalId })
|
|
22105
|
+
);
|
|
22106
|
+
const result = { RestApiId: restApiId };
|
|
22107
|
+
if (resp.stageName !== void 0)
|
|
22108
|
+
result["StageName"] = resp.stageName;
|
|
22109
|
+
if (resp.deploymentId !== void 0)
|
|
22110
|
+
result["DeploymentId"] = resp.deploymentId;
|
|
22111
|
+
if (resp.description !== void 0 && resp.description !== "") {
|
|
22112
|
+
result["Description"] = resp.description;
|
|
22113
|
+
}
|
|
22114
|
+
return result;
|
|
22115
|
+
} catch (err) {
|
|
22116
|
+
if (err instanceof NotFoundException3)
|
|
22117
|
+
return void 0;
|
|
22118
|
+
throw err;
|
|
22119
|
+
}
|
|
22120
|
+
}
|
|
21845
22121
|
async readCurrentStateAccount() {
|
|
21846
22122
|
try {
|
|
21847
22123
|
const resp = await this.apiGatewayClient.send(new GetAccountCommand({}));
|
|
@@ -21923,12 +22199,16 @@ import {
|
|
|
21923
22199
|
DeleteApiCommand,
|
|
21924
22200
|
CreateStageCommand as CreateStageCommand2,
|
|
21925
22201
|
DeleteStageCommand as DeleteStageCommand2,
|
|
22202
|
+
GetStageCommand as GetStageCommand2,
|
|
21926
22203
|
CreateIntegrationCommand,
|
|
21927
22204
|
DeleteIntegrationCommand,
|
|
22205
|
+
GetIntegrationCommand,
|
|
21928
22206
|
CreateRouteCommand as CreateRouteCommand2,
|
|
21929
22207
|
DeleteRouteCommand as DeleteRouteCommand2,
|
|
22208
|
+
GetRouteCommand,
|
|
21930
22209
|
CreateAuthorizerCommand as CreateAuthorizerCommand2,
|
|
21931
22210
|
DeleteAuthorizerCommand as DeleteAuthorizerCommand2,
|
|
22211
|
+
GetAuthorizerCommand as GetAuthorizerCommand2,
|
|
21932
22212
|
GetApiCommand,
|
|
21933
22213
|
GetApisCommand,
|
|
21934
22214
|
NotFoundException as NotFoundException4
|
|
@@ -22472,18 +22752,27 @@ var ApiGatewayV2Provider = class {
|
|
|
22472
22752
|
* **Coverage**:
|
|
22473
22753
|
* - `AWS::ApiGatewayV2::Api` → `GetApi`. PhysicalId is the apiId,
|
|
22474
22754
|
* self-sufficient.
|
|
22475
|
-
*
|
|
22476
|
-
* **Out of scope** (returns `undefined`, falls back to "drift unknown"):
|
|
22477
22755
|
* - `AWS::ApiGatewayV2::Stage` / `Integration` / `Route` / `Authorizer`:
|
|
22478
|
-
* each
|
|
22479
|
-
*
|
|
22480
|
-
* physicalId, which for these types is just the sub-resource id).
|
|
22481
|
-
* Per-sub drift detection here would need a contract change.
|
|
22756
|
+
* each uses `properties.ApiId` (passed through PR G's signature
|
|
22757
|
+
* extension) to issue the appropriate `Get*` call.
|
|
22482
22758
|
*/
|
|
22483
|
-
async readCurrentState(physicalId, _logicalId, resourceType) {
|
|
22484
|
-
|
|
22485
|
-
|
|
22759
|
+
async readCurrentState(physicalId, _logicalId, resourceType, properties) {
|
|
22760
|
+
switch (resourceType) {
|
|
22761
|
+
case "AWS::ApiGatewayV2::Api":
|
|
22762
|
+
return this.readApi(physicalId);
|
|
22763
|
+
case "AWS::ApiGatewayV2::Stage":
|
|
22764
|
+
return this.readStage(physicalId, properties);
|
|
22765
|
+
case "AWS::ApiGatewayV2::Integration":
|
|
22766
|
+
return this.readIntegration(physicalId, properties);
|
|
22767
|
+
case "AWS::ApiGatewayV2::Route":
|
|
22768
|
+
return this.readRoute(physicalId, properties);
|
|
22769
|
+
case "AWS::ApiGatewayV2::Authorizer":
|
|
22770
|
+
return this.readAuthorizer(physicalId, properties);
|
|
22771
|
+
default:
|
|
22772
|
+
return void 0;
|
|
22486
22773
|
}
|
|
22774
|
+
}
|
|
22775
|
+
async readApi(physicalId) {
|
|
22487
22776
|
try {
|
|
22488
22777
|
const resp = await this.getClient().send(new GetApiCommand({ ApiId: physicalId }));
|
|
22489
22778
|
const result = {};
|
|
@@ -22503,6 +22792,108 @@ var ApiGatewayV2Provider = class {
|
|
|
22503
22792
|
throw err;
|
|
22504
22793
|
}
|
|
22505
22794
|
}
|
|
22795
|
+
async readStage(physicalId, properties) {
|
|
22796
|
+
const apiId = properties?.["ApiId"];
|
|
22797
|
+
if (!apiId)
|
|
22798
|
+
return void 0;
|
|
22799
|
+
try {
|
|
22800
|
+
const resp = await this.getClient().send(
|
|
22801
|
+
new GetStageCommand2({ ApiId: apiId, StageName: physicalId })
|
|
22802
|
+
);
|
|
22803
|
+
const result = { ApiId: apiId };
|
|
22804
|
+
if (resp.StageName !== void 0)
|
|
22805
|
+
result["StageName"] = resp.StageName;
|
|
22806
|
+
if (resp.AutoDeploy !== void 0)
|
|
22807
|
+
result["AutoDeploy"] = resp.AutoDeploy;
|
|
22808
|
+
if (resp.Description !== void 0 && resp.Description !== "") {
|
|
22809
|
+
result["Description"] = resp.Description;
|
|
22810
|
+
}
|
|
22811
|
+
return result;
|
|
22812
|
+
} catch (err) {
|
|
22813
|
+
if (err instanceof NotFoundException4)
|
|
22814
|
+
return void 0;
|
|
22815
|
+
throw err;
|
|
22816
|
+
}
|
|
22817
|
+
}
|
|
22818
|
+
async readIntegration(physicalId, properties) {
|
|
22819
|
+
const apiId = properties?.["ApiId"];
|
|
22820
|
+
if (!apiId)
|
|
22821
|
+
return void 0;
|
|
22822
|
+
try {
|
|
22823
|
+
const resp = await this.getClient().send(
|
|
22824
|
+
new GetIntegrationCommand({ ApiId: apiId, IntegrationId: physicalId })
|
|
22825
|
+
);
|
|
22826
|
+
const result = { ApiId: apiId };
|
|
22827
|
+
if (resp.IntegrationType !== void 0)
|
|
22828
|
+
result["IntegrationType"] = resp.IntegrationType;
|
|
22829
|
+
if (resp.IntegrationUri !== void 0)
|
|
22830
|
+
result["IntegrationUri"] = resp.IntegrationUri;
|
|
22831
|
+
if (resp.IntegrationMethod !== void 0)
|
|
22832
|
+
result["IntegrationMethod"] = resp.IntegrationMethod;
|
|
22833
|
+
if (resp.PayloadFormatVersion !== void 0) {
|
|
22834
|
+
result["PayloadFormatVersion"] = resp.PayloadFormatVersion;
|
|
22835
|
+
}
|
|
22836
|
+
return result;
|
|
22837
|
+
} catch (err) {
|
|
22838
|
+
if (err instanceof NotFoundException4)
|
|
22839
|
+
return void 0;
|
|
22840
|
+
throw err;
|
|
22841
|
+
}
|
|
22842
|
+
}
|
|
22843
|
+
async readRoute(physicalId, properties) {
|
|
22844
|
+
const apiId = properties?.["ApiId"];
|
|
22845
|
+
if (!apiId)
|
|
22846
|
+
return void 0;
|
|
22847
|
+
try {
|
|
22848
|
+
const resp = await this.getClient().send(
|
|
22849
|
+
new GetRouteCommand({ ApiId: apiId, RouteId: physicalId })
|
|
22850
|
+
);
|
|
22851
|
+
const result = { ApiId: apiId };
|
|
22852
|
+
if (resp.RouteKey !== void 0)
|
|
22853
|
+
result["RouteKey"] = resp.RouteKey;
|
|
22854
|
+
if (resp.Target !== void 0)
|
|
22855
|
+
result["Target"] = resp.Target;
|
|
22856
|
+
if (resp.AuthorizationType !== void 0)
|
|
22857
|
+
result["AuthorizationType"] = resp.AuthorizationType;
|
|
22858
|
+
if (resp.AuthorizerId !== void 0)
|
|
22859
|
+
result["AuthorizerId"] = resp.AuthorizerId;
|
|
22860
|
+
return result;
|
|
22861
|
+
} catch (err) {
|
|
22862
|
+
if (err instanceof NotFoundException4)
|
|
22863
|
+
return void 0;
|
|
22864
|
+
throw err;
|
|
22865
|
+
}
|
|
22866
|
+
}
|
|
22867
|
+
async readAuthorizer(physicalId, properties) {
|
|
22868
|
+
const apiId = properties?.["ApiId"];
|
|
22869
|
+
if (!apiId)
|
|
22870
|
+
return void 0;
|
|
22871
|
+
try {
|
|
22872
|
+
const resp = await this.getClient().send(
|
|
22873
|
+
new GetAuthorizerCommand2({ ApiId: apiId, AuthorizerId: physicalId })
|
|
22874
|
+
);
|
|
22875
|
+
const result = { ApiId: apiId };
|
|
22876
|
+
if (resp.AuthorizerType !== void 0)
|
|
22877
|
+
result["AuthorizerType"] = resp.AuthorizerType;
|
|
22878
|
+
if (resp.Name !== void 0)
|
|
22879
|
+
result["Name"] = resp.Name;
|
|
22880
|
+
if (resp.IdentitySource !== void 0 && resp.IdentitySource.length > 0) {
|
|
22881
|
+
result["IdentitySource"] = [...resp.IdentitySource];
|
|
22882
|
+
}
|
|
22883
|
+
if (resp.JwtConfiguration)
|
|
22884
|
+
result["JwtConfiguration"] = resp.JwtConfiguration;
|
|
22885
|
+
if (resp.AuthorizerUri !== void 0)
|
|
22886
|
+
result["AuthorizerUri"] = resp.AuthorizerUri;
|
|
22887
|
+
if (resp.AuthorizerPayloadFormatVersion !== void 0) {
|
|
22888
|
+
result["AuthorizerPayloadFormatVersion"] = resp.AuthorizerPayloadFormatVersion;
|
|
22889
|
+
}
|
|
22890
|
+
return result;
|
|
22891
|
+
} catch (err) {
|
|
22892
|
+
if (err instanceof NotFoundException4)
|
|
22893
|
+
return void 0;
|
|
22894
|
+
throw err;
|
|
22895
|
+
}
|
|
22896
|
+
}
|
|
22506
22897
|
// ─── Import ───────────────────────────────────────────────────────
|
|
22507
22898
|
/**
|
|
22508
22899
|
* Adopt an existing API Gateway V2 resource into cdkd state.
|
|
@@ -30120,7 +30511,7 @@ var AppSyncProvider = class {
|
|
|
30120
30511
|
*
|
|
30121
30512
|
* Returns `undefined` when the parent resource is gone (`NotFoundException`).
|
|
30122
30513
|
*/
|
|
30123
|
-
async readCurrentState(physicalId, _logicalId, resourceType) {
|
|
30514
|
+
async readCurrentState(physicalId, _logicalId, resourceType, _properties) {
|
|
30124
30515
|
switch (resourceType) {
|
|
30125
30516
|
case "AWS::AppSync::GraphQLApi":
|
|
30126
30517
|
return this.readGraphQLApi(physicalId);
|
|
@@ -31849,10 +32240,15 @@ var KinesisStreamProvider = class {
|
|
|
31849
32240
|
* mode reports an empty list).
|
|
31850
32241
|
*
|
|
31851
32242
|
* Returns `undefined` when the stream is gone (`ResourceNotFoundException`).
|
|
31852
|
-
*
|
|
31853
|
-
* `AWS::Kinesis::StreamConsumer`
|
|
32243
|
+
*
|
|
32244
|
+
* `AWS::Kinesis::StreamConsumer` is intentionally not handled here: this
|
|
32245
|
+
* provider only registers `AWS::Kinesis::Stream`, so consumer resources
|
|
32246
|
+
* route to the CC API fallback for drift detection (CC API's `GetResource`
|
|
32247
|
+
* surfaces every Kinesis consumer attribute the user can configure). A
|
|
32248
|
+
* dedicated SDK impl would require building out create/update/delete first;
|
|
32249
|
+
* out of scope for PR G.
|
|
31854
32250
|
*/
|
|
31855
|
-
async readCurrentState(physicalId, _logicalId, resourceType) {
|
|
32251
|
+
async readCurrentState(physicalId, _logicalId, resourceType, _properties) {
|
|
31856
32252
|
if (resourceType !== "AWS::Kinesis::Stream")
|
|
31857
32253
|
return void 0;
|
|
31858
32254
|
let stream;
|
|
@@ -37654,6 +38050,128 @@ function isPlainObject(value) {
|
|
|
37654
38050
|
return typeof value === "object" && value !== null;
|
|
37655
38051
|
}
|
|
37656
38052
|
|
|
38053
|
+
// src/analyzer/drift-cc-api-deny-list.ts
|
|
38054
|
+
var CC_API_FALLBACK_DENY_LIST = {
|
|
38055
|
+
// AWS::IAM::ManagedPolicy: PolicyDocument round-trips through CC API
|
|
38056
|
+
// URL-encoded; cdkd state stores it as a parsed JSON object. Without
|
|
38057
|
+
// a per-type decoder, every comparison sees a string-vs-object
|
|
38058
|
+
// mismatch and fires drift on every run. The IAM Role provider's
|
|
38059
|
+
// first-class readCurrentState handles its inline AssumeRolePolicy
|
|
38060
|
+
// the same way (URL-decode + JSON-parse); the same fix pattern is
|
|
38061
|
+
// needed for ManagedPolicy when an SDK provider is added.
|
|
38062
|
+
"AWS::IAM::ManagedPolicy": "PolicyDocument is URL-encoded JSON in CC API responses, but cdkd state stores it as a parsed object \u2014 needs per-type decode",
|
|
38063
|
+
// AWS::ApiGateway::RestApi: the `Body` property (OpenAPI spec object
|
|
38064
|
+
// when supplied via `Body` rather than `BodyS3Location`) is write-only
|
|
38065
|
+
// — `GetRestApi` does NOT return it, but cdkd state preserves the
|
|
38066
|
+
// object the user passed in. CC API GetResource inherits this — the
|
|
38067
|
+
// returned shape omits `Body`, so every drift run flags it as
|
|
38068
|
+
// missing-on-AWS. Comparison silently dropping it would also be
|
|
38069
|
+
// wrong; the right move is a dedicated SDK provider that knows to
|
|
38070
|
+
// skip `Body` entirely.
|
|
38071
|
+
"AWS::ApiGateway::RestApi": "Body / BodyS3Location are write-only inputs not returned by CC API GetResource; cdkd state preserves them",
|
|
38072
|
+
// AWS::CloudFormation::Stack: nested stacks aren't supported by cdkd's
|
|
38073
|
+
// provider registry at all; the deploy / destroy paths reject them.
|
|
38074
|
+
// Listing here is defense-in-depth — if a user manually crafts state
|
|
38075
|
+
// with one, drift via CC API would compare CFn-template-input
|
|
38076
|
+
// properties against CC API's stack-output shape (CC API's
|
|
38077
|
+
// `AWS::CloudFormation::Stack` reports outputs / status, not the
|
|
38078
|
+
// template parameters cdkd would have stored).
|
|
38079
|
+
"AWS::CloudFormation::Stack": "CC API returns runtime stack state (outputs/status), not the template parameters cdkd state stores",
|
|
38080
|
+
// AWS::EC2::LaunchTemplate: `LaunchTemplateData` ships with deeply
|
|
38081
|
+
// structured sub-objects that CC API normalizes into a versioned shape
|
|
38082
|
+
// — every UpdateLaunchTemplate (and even GetLaunchTemplate) bumps the
|
|
38083
|
+
// returned default version, and CC API attaches a synthetic
|
|
38084
|
+
// `LatestVersionNumber` / `DefaultVersionNumber` next to the template
|
|
38085
|
+
// data that drift would surface as drift on the parent. Until an SDK
|
|
38086
|
+
// provider strips those, deny.
|
|
38087
|
+
"AWS::EC2::LaunchTemplate": "CC API returns version-bumped LaunchTemplateData with synthetic LatestVersionNumber that diverges from the CFn input shape"
|
|
38088
|
+
};
|
|
38089
|
+
|
|
38090
|
+
// src/analyzer/cc-api-strip.ts
|
|
38091
|
+
var ALWAYS_STRIPPED_FIELDS = /* @__PURE__ */ new Set([
|
|
38092
|
+
// Timestamps — AWS-managed, change on every modification. Names are
|
|
38093
|
+
// unambiguous: no CFn template ever exposes a "CreationDate" /
|
|
38094
|
+
// "LastModifiedTime" as a settable input.
|
|
38095
|
+
"CreationDate",
|
|
38096
|
+
"CreationTime",
|
|
38097
|
+
"CreatedTime",
|
|
38098
|
+
"CreatedDate",
|
|
38099
|
+
"CreatedAt",
|
|
38100
|
+
"LastModifiedDate",
|
|
38101
|
+
"LastModifiedTime",
|
|
38102
|
+
"LastModified",
|
|
38103
|
+
"LastUpdatedTime",
|
|
38104
|
+
"LastUpdatedDate",
|
|
38105
|
+
"UpdatedAt",
|
|
38106
|
+
// Owner / account / principal info — derived from the calling
|
|
38107
|
+
// principal, never user-set in a CFn template. `CreatedBy` /
|
|
38108
|
+
// `OwnerArn` are unique enough that no settable CFn property
|
|
38109
|
+
// collides with them.
|
|
38110
|
+
"OwnerId",
|
|
38111
|
+
"OwnerAccountId",
|
|
38112
|
+
"CreatedBy",
|
|
38113
|
+
"OwnerArn",
|
|
38114
|
+
// Lambda-specific generated identifiers. `RevisionId` rotates on
|
|
38115
|
+
// every operation; `LastUpdateStatus*` mirror runtime state. None
|
|
38116
|
+
// are settable in a CFn template.
|
|
38117
|
+
"RevisionId",
|
|
38118
|
+
"LastUpdateStatus",
|
|
38119
|
+
"LastUpdateStatusReason",
|
|
38120
|
+
"LastUpdateStatusReasonCode",
|
|
38121
|
+
// CloudFormation/Cloud Control passthrough metadata — never
|
|
38122
|
+
// appears as a settable input in a CFn template body.
|
|
38123
|
+
"StackId",
|
|
38124
|
+
"PhysicalResourceId",
|
|
38125
|
+
"LogicalResourceId"
|
|
38126
|
+
// Notes on intentional EXCLUSIONS:
|
|
38127
|
+
//
|
|
38128
|
+
// `State` / `Status` / `StateReason` / `StatusReason` — these
|
|
38129
|
+
// names ARE used by some CFn types as settable nested properties
|
|
38130
|
+
// (e.g. `AWS::ECS::CapacityProvider.AutoScalingGroupProvider.ManagedScaling.Status`,
|
|
38131
|
+
// `AWS::S3::Bucket.VersioningConfiguration.Status`). Stripping them
|
|
38132
|
+
// globally would cause false-positive drift on a clean stack.
|
|
38133
|
+
// The comparator already ignores AWS-only top-level `Status` values
|
|
38134
|
+
// because state doesn't carry them; only the nested-name-collision
|
|
38135
|
+
// cases would have leaked through, and excluding them here protects
|
|
38136
|
+
// those.
|
|
38137
|
+
//
|
|
38138
|
+
// `Arn` — many CFn types accept `Arn` as a settable property and
|
|
38139
|
+
// cdkd state may record it at create time. Drift on `Arn` is
|
|
38140
|
+
// genuine drift the user wants to see.
|
|
38141
|
+
//
|
|
38142
|
+
// `VersionId` / `GenerationId` / `ETag` — narrow utility (only S3
|
|
38143
|
+
// / KMS / ImageBuilder use these in their Get* responses), and at
|
|
38144
|
+
// least `VersionId` IS a settable input on `AWS::S3::Bucket`'s
|
|
38145
|
+
// versioning config. Per-provider readCurrentState handles them.
|
|
38146
|
+
//
|
|
38147
|
+
// `AccountId` / `StackName` — also collide with settable inputs
|
|
38148
|
+
// on a few CFn types (`AWS::CloudWatch::CrossAccountSharingRule.AccountId`,
|
|
38149
|
+
// `AWS::CloudFormation::HookDefaultVersion.StackName`).
|
|
38150
|
+
//
|
|
38151
|
+
// `StartTime` / `EndTime` — used as settable inputs in scheduling
|
|
38152
|
+
// shapes (e.g. `AWS::AutoScaling::ScheduledAction.StartTime`).
|
|
38153
|
+
]);
|
|
38154
|
+
function stripCcApiAwsManagedFields(resourceType, awsProps) {
|
|
38155
|
+
return stripWalk(awsProps);
|
|
38156
|
+
}
|
|
38157
|
+
function stripWalk(value) {
|
|
38158
|
+
if (value === null || value === void 0)
|
|
38159
|
+
return value;
|
|
38160
|
+
if (Array.isArray(value)) {
|
|
38161
|
+
return value.map(stripWalk);
|
|
38162
|
+
}
|
|
38163
|
+
if (typeof value === "object") {
|
|
38164
|
+
const out = {};
|
|
38165
|
+
for (const [key, child] of Object.entries(value)) {
|
|
38166
|
+
if (ALWAYS_STRIPPED_FIELDS.has(key))
|
|
38167
|
+
continue;
|
|
38168
|
+
out[key] = stripWalk(child);
|
|
38169
|
+
}
|
|
38170
|
+
return out;
|
|
38171
|
+
}
|
|
38172
|
+
return value;
|
|
38173
|
+
}
|
|
38174
|
+
|
|
37657
38175
|
// src/cli/commands/drift.ts
|
|
37658
38176
|
var DriftDetectedError = class _DriftDetectedError extends CdkdError {
|
|
37659
38177
|
silent = true;
|
|
@@ -37696,6 +38214,7 @@ async function driftCommand(stacks, options) {
|
|
|
37696
38214
|
const providerRegistry = new ProviderRegistry();
|
|
37697
38215
|
registerAllProviders(providerRegistry);
|
|
37698
38216
|
providerRegistry.setCustomResourceResponseBucket(bucket);
|
|
38217
|
+
const ccApiFallback = new CloudControlProvider();
|
|
37699
38218
|
const stateRefs = await stateBackend.listStacks();
|
|
37700
38219
|
const targetRefs = resolveTargetRefs(stacks, stateRefs, options);
|
|
37701
38220
|
const reports = [];
|
|
@@ -37709,7 +38228,8 @@ async function driftCommand(stacks, options) {
|
|
|
37709
38228
|
ref.stackName,
|
|
37710
38229
|
ref.region,
|
|
37711
38230
|
stateBackend,
|
|
37712
|
-
providerRegistry
|
|
38231
|
+
providerRegistry,
|
|
38232
|
+
ccApiFallback
|
|
37713
38233
|
);
|
|
37714
38234
|
reports.push(report);
|
|
37715
38235
|
}
|
|
@@ -37780,7 +38300,7 @@ function resolveTargetRefs(stacks, stateRefs, options) {
|
|
|
37780
38300
|
}
|
|
37781
38301
|
return out;
|
|
37782
38302
|
}
|
|
37783
|
-
async function runDriftForStack(stackName, region, stateBackend, providerRegistry) {
|
|
38303
|
+
async function runDriftForStack(stackName, region, stateBackend, providerRegistry, ccApiFallback) {
|
|
37784
38304
|
const result = await stateBackend.getState(stackName, region);
|
|
37785
38305
|
if (!result) {
|
|
37786
38306
|
throw new Error(
|
|
@@ -37806,22 +38326,39 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
|
|
|
37806
38326
|
});
|
|
37807
38327
|
continue;
|
|
37808
38328
|
}
|
|
37809
|
-
let
|
|
37810
|
-
if (
|
|
37811
|
-
|
|
37812
|
-
|
|
37813
|
-
|
|
38329
|
+
let aws;
|
|
38330
|
+
if (provider.readCurrentState) {
|
|
38331
|
+
aws = await provider.readCurrentState(
|
|
38332
|
+
resource.physicalId,
|
|
38333
|
+
logicalId,
|
|
38334
|
+
resource.resourceType,
|
|
38335
|
+
resource.properties ?? {}
|
|
38336
|
+
);
|
|
38337
|
+
} else {
|
|
38338
|
+
if (CC_API_FALLBACK_DENY_LIST[resource.resourceType]) {
|
|
38339
|
+
outcomes.push({
|
|
38340
|
+
kind: "unsupported",
|
|
38341
|
+
logicalId,
|
|
38342
|
+
resourceType: resource.resourceType
|
|
38343
|
+
});
|
|
38344
|
+
continue;
|
|
37814
38345
|
}
|
|
37815
|
-
|
|
37816
|
-
|
|
37817
|
-
outcomes.push({
|
|
37818
|
-
kind: "unsupported",
|
|
38346
|
+
const ccApiAws = await ccApiFallback.readCurrentState(
|
|
38347
|
+
resource.physicalId,
|
|
37819
38348
|
logicalId,
|
|
37820
|
-
|
|
37821
|
-
|
|
37822
|
-
|
|
38349
|
+
resource.resourceType,
|
|
38350
|
+
resource.properties ?? {}
|
|
38351
|
+
);
|
|
38352
|
+
if (ccApiAws === void 0) {
|
|
38353
|
+
outcomes.push({
|
|
38354
|
+
kind: "unsupported",
|
|
38355
|
+
logicalId,
|
|
38356
|
+
resourceType: resource.resourceType
|
|
38357
|
+
});
|
|
38358
|
+
continue;
|
|
38359
|
+
}
|
|
38360
|
+
aws = stripCcApiAwsManagedFields(resource.resourceType, ccApiAws);
|
|
37823
38361
|
}
|
|
37824
|
-
const aws = await readCurrentState(resource.physicalId, logicalId, resource.resourceType);
|
|
37825
38362
|
if (aws === void 0) {
|
|
37826
38363
|
outcomes.push({
|
|
37827
38364
|
kind: "unsupported",
|
|
@@ -41359,7 +41896,7 @@ function reorderArgs(argv) {
|
|
|
41359
41896
|
}
|
|
41360
41897
|
async function main() {
|
|
41361
41898
|
const program = new Command14();
|
|
41362
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
41899
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.44.0");
|
|
41363
41900
|
program.addCommand(createBootstrapCommand());
|
|
41364
41901
|
program.addCommand(createSynthCommand());
|
|
41365
41902
|
program.addCommand(createListCommand());
|