@go-to-k/cdkd 0.33.0 → 0.34.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 CHANGED
@@ -175,7 +175,7 @@ the full per-type table.
175
175
 
176
176
  - **Node.js** >= 20.0.0
177
177
  - **AWS CDK Bootstrap**: You must run `cdk bootstrap` before using cdkd. cdkd uses CDK's bootstrap bucket (`cdk-hnb659fds-assets-*`) for asset uploads (Lambda code, Docker images). Custom bootstrap qualifiers are supported — CDK embeds the correct bucket/repo names in the asset manifest during synthesis.
178
- - **AWS Credentials**: Configured via environment variables, `~/.aws/credentials`, or `--profile` option
178
+ - **AWS Credentials**: Configured via environment variables, `~/.aws/credentials`, `--profile`, or `--role-arn` option. **The credentials must have admin-equivalent permissions for the resources being deployed.** Unlike `cdk deploy`, cdkd does NOT route through CloudFormation, so there is no cfn-exec-role to delegate to — every IAM / EC2 / Lambda / etc. API call is issued from cdkd directly. CDK CLI's `cdk-hnb659fds-deploy-role-*` only carries CFn + asset-publish permissions and is therefore NOT sufficient for cdkd. See `--role-arn` in [docs/cli-reference.md](docs/cli-reference.md) for assuming a role with the right permissions.
179
179
 
180
180
  ## Installation
181
181
 
package/dist/cli.js CHANGED
@@ -477,6 +477,10 @@ function parseContextOptions(contextArgs) {
477
477
  var commonOptions = [
478
478
  new Option("--verbose", "Enable verbose logging").default(false),
479
479
  new Option("--profile <profile>", "AWS profile"),
480
+ new Option(
481
+ "--role-arn <arn>",
482
+ "IAM role ARN to assume for AWS API calls (env: CDKD_ROLE_ARN); the role MUST have admin-equivalent permissions because cdkd issues raw service API calls and does not route through CloudFormation, so CDK CLI deploy-roles will NOT work"
483
+ ),
480
484
  new Option(
481
485
  "-y, --yes",
482
486
  "Automatically answer interactive prompts with the recommended response (e.g. confirm destroy)"
@@ -1256,6 +1260,41 @@ function normalizeAwsError(err, context = {}) {
1256
1260
  // src/cli/commands/bootstrap.ts
1257
1261
  init_aws_clients();
1258
1262
 
1263
+ // src/utils/role-arn.ts
1264
+ import { STSClient as STSClient2, AssumeRoleCommand } from "@aws-sdk/client-sts";
1265
+ async function applyRoleArnIfSet(opts) {
1266
+ const roleArn = opts.roleArn || process.env["CDKD_ROLE_ARN"];
1267
+ if (!roleArn)
1268
+ return;
1269
+ const logger = getLogger().child("role-arn");
1270
+ logger.debug(`Assuming role ${roleArn}...`);
1271
+ const sts = new STSClient2({ ...opts.region && { region: opts.region } });
1272
+ try {
1273
+ const response = await sts.send(
1274
+ new AssumeRoleCommand({
1275
+ RoleArn: roleArn,
1276
+ RoleSessionName: `cdkd-${Date.now()}`,
1277
+ DurationSeconds: 3600
1278
+ })
1279
+ );
1280
+ if (!response.Credentials) {
1281
+ throw new Error(`AssumeRole returned no credentials for role ${roleArn}`);
1282
+ }
1283
+ const { AccessKeyId, SecretAccessKey, SessionToken, Expiration } = response.Credentials;
1284
+ if (!AccessKeyId || !SecretAccessKey || !SessionToken) {
1285
+ throw new Error(`AssumeRole response missing credentials fields for role ${roleArn}`);
1286
+ }
1287
+ process.env["AWS_ACCESS_KEY_ID"] = AccessKeyId;
1288
+ process.env["AWS_SECRET_ACCESS_KEY"] = SecretAccessKey;
1289
+ process.env["AWS_SESSION_TOKEN"] = SessionToken;
1290
+ logger.info(
1291
+ `Assumed role ${roleArn} (session expires ${Expiration?.toISOString() ?? "unknown"})`
1292
+ );
1293
+ } finally {
1294
+ sts.destroy();
1295
+ }
1296
+ }
1297
+
1259
1298
  // src/cli/config-loader.ts
1260
1299
  import { readFileSync, existsSync } from "node:fs";
1261
1300
  import { resolve, join } from "node:path";
@@ -1409,6 +1448,7 @@ async function bootstrapCommand(options) {
1409
1448
  }
1410
1449
  logger.info("Starting cdkd bootstrap...");
1411
1450
  logger.debug("Options:", options);
1451
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
1412
1452
  const awsClients = new AwsClients({
1413
1453
  ...options.region && { region: options.region },
1414
1454
  ...options.profile && { profile: options.profile }
@@ -1553,7 +1593,7 @@ import { join as join4 } from "path";
1553
1593
  // src/synthesis/synthesizer.ts
1554
1594
  import { existsSync as existsSync3, mkdirSync, statSync } from "node:fs";
1555
1595
  import { resolve as resolve3 } from "node:path";
1556
- import { GetCallerIdentityCommand as GetCallerIdentityCommand2, STSClient as STSClient2 } from "@aws-sdk/client-sts";
1596
+ import { GetCallerIdentityCommand as GetCallerIdentityCommand2, STSClient as STSClient3 } from "@aws-sdk/client-sts";
1557
1597
 
1558
1598
  // src/synthesis/app-executor.ts
1559
1599
  import { spawn } from "node:child_process";
@@ -2744,7 +2784,7 @@ var Synthesizer = class {
2744
2784
  const region = options.region || process.env["AWS_REGION"] || process.env["AWS_DEFAULT_REGION"];
2745
2785
  let accountId;
2746
2786
  try {
2747
- const stsClient = new STSClient2({ ...region && { region } });
2787
+ const stsClient = new STSClient3({ ...region && { region } });
2748
2788
  const identity = await stsClient.send(new GetCallerIdentityCommand2({}));
2749
2789
  accountId = identity.Account;
2750
2790
  stsClient.destroy();
@@ -2876,6 +2916,7 @@ async function synthCommand(options) {
2876
2916
  logger.setLevel("debug");
2877
2917
  }
2878
2918
  warnIfDeprecatedRegion(options);
2919
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
2879
2920
  const app = resolveApp(options.app);
2880
2921
  if (!app) {
2881
2922
  throw new Error(
@@ -3007,6 +3048,7 @@ async function listCommand(patterns, options) {
3007
3048
  logger.setLevel("debug");
3008
3049
  }
3009
3050
  warnIfDeprecatedRegion(options);
3051
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
3010
3052
  const app = resolveApp(options.app);
3011
3053
  if (!app) {
3012
3054
  throw new Error(
@@ -3597,8 +3639,8 @@ var AssetPublisher = class {
3597
3639
  const region = options.region || process.env["AWS_REGION"] || "us-east-1";
3598
3640
  let accountId = options.accountId;
3599
3641
  if (!accountId) {
3600
- const { STSClient: STSClient9, GetCallerIdentityCommand: GetCallerIdentityCommand11 } = await import("@aws-sdk/client-sts");
3601
- const stsClient = new STSClient9({ region });
3642
+ const { STSClient: STSClient10, GetCallerIdentityCommand: GetCallerIdentityCommand11 } = await import("@aws-sdk/client-sts");
3643
+ const stsClient = new STSClient10({ region });
3602
3644
  const identity = await stsClient.send(new GetCallerIdentityCommand11({}));
3603
3645
  accountId = identity.Account;
3604
3646
  stsClient.destroy();
@@ -25556,7 +25598,7 @@ import {
25556
25598
  ModifyCacheClusterCommand,
25557
25599
  ListTagsForResourceCommand as ListTagsForResourceCommand14
25558
25600
  } from "@aws-sdk/client-elasticache";
25559
- import { STSClient as STSClient5, GetCallerIdentityCommand as GetCallerIdentityCommand6 } from "@aws-sdk/client-sts";
25601
+ import { STSClient as STSClient6, GetCallerIdentityCommand as GetCallerIdentityCommand6 } from "@aws-sdk/client-sts";
25560
25602
  var ElastiCacheProvider = class {
25561
25603
  client;
25562
25604
  stsClient;
@@ -26079,7 +26121,7 @@ var ElastiCacheProvider = class {
26079
26121
  if (this.cachedAccountId)
26080
26122
  return this.cachedAccountId;
26081
26123
  if (!this.stsClient) {
26082
- this.stsClient = new STSClient5(this.providerRegion ? { region: this.providerRegion } : {});
26124
+ this.stsClient = new STSClient6(this.providerRegion ? { region: this.providerRegion } : {});
26083
26125
  }
26084
26126
  const identity = await this.stsClient.send(new GetCallerIdentityCommand6({}));
26085
26127
  if (!identity.Account) {
@@ -26106,7 +26148,7 @@ import {
26106
26148
  NamespaceNotFound,
26107
26149
  ServiceNotFound
26108
26150
  } from "@aws-sdk/client-servicediscovery";
26109
- import { STSClient as STSClient6, GetCallerIdentityCommand as GetCallerIdentityCommand7 } from "@aws-sdk/client-sts";
26151
+ import { STSClient as STSClient7, GetCallerIdentityCommand as GetCallerIdentityCommand7 } from "@aws-sdk/client-sts";
26110
26152
  var ServiceDiscoveryProvider = class {
26111
26153
  client;
26112
26154
  stsClient;
@@ -26138,7 +26180,7 @@ var ServiceDiscoveryProvider = class {
26138
26180
  }
26139
26181
  getStsClient() {
26140
26182
  if (!this.stsClient) {
26141
- this.stsClient = new STSClient6(this.providerRegion ? { region: this.providerRegion } : {});
26183
+ this.stsClient = new STSClient7(this.providerRegion ? { region: this.providerRegion } : {});
26142
26184
  }
26143
26185
  return this.stsClient;
26144
26186
  }
@@ -27121,7 +27163,7 @@ import {
27121
27163
  GetTagsCommand,
27122
27164
  EntityNotFoundException
27123
27165
  } from "@aws-sdk/client-glue";
27124
- import { STSClient as STSClient7, GetCallerIdentityCommand as GetCallerIdentityCommand8 } from "@aws-sdk/client-sts";
27166
+ import { STSClient as STSClient8, GetCallerIdentityCommand as GetCallerIdentityCommand8 } from "@aws-sdk/client-sts";
27125
27167
  var GlueProvider = class {
27126
27168
  client;
27127
27169
  stsClient;
@@ -27648,7 +27690,7 @@ var GlueProvider = class {
27648
27690
  if (this.cachedAccountId)
27649
27691
  return this.cachedAccountId;
27650
27692
  if (!this.stsClient) {
27651
- this.stsClient = new STSClient7(this.providerRegion ? { region: this.providerRegion } : {});
27693
+ this.stsClient = new STSClient8(this.providerRegion ? { region: this.providerRegion } : {});
27652
27694
  }
27653
27695
  const identity = await this.stsClient.send(new GetCallerIdentityCommand8({}));
27654
27696
  if (!identity.Account) {
@@ -32919,6 +32961,7 @@ async function deployCommand(stacks, options) {
32919
32961
  ...options.resourceWarnAfter && { resourceWarnAfter: options.resourceWarnAfter },
32920
32962
  ...options.resourceTimeout && { resourceTimeout: options.resourceTimeout }
32921
32963
  });
32964
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
32922
32965
  if (!options.wait) {
32923
32966
  process.env["CDKD_NO_WAIT"] = "true";
32924
32967
  }
@@ -33020,8 +33063,8 @@ async function deployCommand(stacks, options) {
33020
33063
  addDependencies(stack.stackName);
33021
33064
  }
33022
33065
  }
33023
- const { STSClient: STSClient9, GetCallerIdentityCommand: GetCallerIdentityCommand11 } = await import("@aws-sdk/client-sts");
33024
- const stsClient = new STSClient9({
33066
+ const { STSClient: STSClient10, GetCallerIdentityCommand: GetCallerIdentityCommand11 } = await import("@aws-sdk/client-sts");
33067
+ const stsClient = new STSClient10({
33025
33068
  region: options.region || process.env["AWS_REGION"] || "us-east-1"
33026
33069
  });
33027
33070
  const callerIdentity = await stsClient.send(new GetCallerIdentityCommand11({}));
@@ -33265,6 +33308,7 @@ async function diffCommand(stacks, options) {
33265
33308
  logger.setLevel("debug");
33266
33309
  }
33267
33310
  warnIfDeprecatedRegion(options);
33311
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
33268
33312
  const app = resolveApp(options.app);
33269
33313
  if (!app) {
33270
33314
  throw new Error(
@@ -33677,6 +33721,7 @@ async function destroyCommand(stackArgs, options) {
33677
33721
  ...options.resourceWarnAfter && { resourceWarnAfter: options.resourceWarnAfter },
33678
33722
  ...options.resourceTimeout && { resourceTimeout: options.resourceTimeout }
33679
33723
  });
33724
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
33680
33725
  const region = options.region || process.env["AWS_REGION"] || "us-east-1";
33681
33726
  const stateBucket = await resolveStateBucketWithDefault(options.stateBucket, region);
33682
33727
  logger.info("Starting stack destruction...");
@@ -34278,6 +34323,7 @@ async function orphanCommand(pathArgs, options) {
34278
34323
  if (options.verbose)
34279
34324
  logger.setLevel("debug");
34280
34325
  warnIfDeprecatedRegion(options);
34326
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
34281
34327
  if (pathArgs.length === 0) {
34282
34328
  throw new Error(
34283
34329
  "'cdkd orphan' requires at least one construct path, e.g. 'cdkd orphan MyStack/MyTable'.\n To remove a stack's state record (the previous behavior), use:\n cdkd state orphan MyStack"
@@ -34567,6 +34613,7 @@ async function publishAssetsCommand(options) {
34567
34613
  logger.setLevel("debug");
34568
34614
  }
34569
34615
  warnIfDeprecatedRegion(options);
34616
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
34570
34617
  logger.info("Publishing assets...");
34571
34618
  logger.debug("Asset manifest path:", options.path);
34572
34619
  const publisher = new AssetPublisher();
@@ -34601,6 +34648,7 @@ async function forceUnlockCommand(stackArgs, options) {
34601
34648
  logger.setLevel("debug");
34602
34649
  }
34603
34650
  warnIfDeprecatedRegion(options);
34651
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
34604
34652
  const stackPatterns = stackArgs.length > 0 ? stackArgs : options.stack ? [options.stack] : [];
34605
34653
  if (stackPatterns.length === 0) {
34606
34654
  throw new Error("Stack name is required. Usage: cdkd force-unlock <stack-name>");
@@ -34696,6 +34744,7 @@ async function stateMigrateCommand(options) {
34696
34744
  const logger = getLogger();
34697
34745
  if (options.verbose)
34698
34746
  logger.setLevel("debug");
34747
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
34699
34748
  const region = options.region || process.env["AWS_REGION"] || "us-east-1";
34700
34749
  const awsClients = new AwsClients({
34701
34750
  region,
@@ -35009,6 +35058,7 @@ function resolveSingleRegion(stackName, refs, requestedRegion) {
35009
35058
  }
35010
35059
  async function setupStateBackend(options) {
35011
35060
  warnIfDeprecatedRegion(options);
35061
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
35012
35062
  const awsClients = new AwsClients({
35013
35063
  ...options.region && { region: options.region },
35014
35064
  ...options.profile && { profile: options.profile }
@@ -35624,6 +35674,7 @@ async function stateInfoCommand(options) {
35624
35674
  const logger = getLogger();
35625
35675
  if (options.verbose)
35626
35676
  logger.setLevel("debug");
35677
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
35627
35678
  const awsClients = new AwsClients({
35628
35679
  ...options.region && { region: options.region },
35629
35680
  ...options.profile && { profile: options.profile }
@@ -35916,6 +35967,7 @@ async function importCommand(stackArg, options) {
35916
35967
  logger.setLevel("debug");
35917
35968
  process.env["CDKD_NO_LIVE"] = "1";
35918
35969
  }
35970
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
35919
35971
  const region = options.region || process.env["AWS_REGION"] || "us-east-1";
35920
35972
  const stateBucket = await resolveStateBucketWithDefault(options.stateBucket, region);
35921
35973
  if (options.region) {
@@ -36436,7 +36488,7 @@ function reorderArgs(argv) {
36436
36488
  }
36437
36489
  async function main() {
36438
36490
  const program = new Command13();
36439
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.33.0");
36491
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.34.0");
36440
36492
  program.addCommand(createBootstrapCommand());
36441
36493
  program.addCommand(createSynthCommand());
36442
36494
  program.addCommand(createListCommand());