@go-to-k/cdkd 0.51.0 → 0.51.2

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.
Binary file
package/dist/index.js CHANGED
@@ -7636,6 +7636,7 @@ import {
7636
7636
  UpdateAssumeRolePolicyCommand,
7637
7637
  DeleteRoleCommand,
7638
7638
  GetRoleCommand,
7639
+ GetRolePolicyCommand,
7639
7640
  PutRolePolicyCommand,
7640
7641
  DeleteRolePolicyCommand,
7641
7642
  ListRolePoliciesCommand,
@@ -8248,8 +8249,14 @@ var IAMRoleProvider = class {
8248
8249
  * against state's already-parsed object.
8249
8250
  *
8250
8251
  * Coverage and shape decisions:
8251
- * - `RoleName`, `Description`, `MaxSessionDuration`, `Path`,
8252
- * `PermissionsBoundary` — straight from `Role.*`.
8252
+ * - `RoleName`, `Description`, `MaxSessionDuration`, `Path` — straight
8253
+ * from `Role.*`.
8254
+ * - `PermissionsBoundary` — emitted as `'' ` placeholder when AWS has
8255
+ * none, so a console-side ADD on a role that was deployed without a
8256
+ * boundary surfaces as drift. (The drift comparator's top-level walk
8257
+ * is state-keys-only; without the always-emit placeholder a fresh
8258
+ * `PermissionsBoundary` on the AWS side would never enter
8259
+ * `observedProperties` and the comparator would silently ignore it.)
8253
8260
  * - `AssumeRolePolicyDocument` — `Role.AssumeRolePolicyDocument` is a
8254
8261
  * URL-encoded JSON string; we URL-decode + JSON-parse so cdkd state's
8255
8262
  * object form compares cleanly. (Both shapes — string and object — are
@@ -8257,20 +8264,23 @@ var IAMRoleProvider = class {
8257
8264
  * after intrinsic resolution.)
8258
8265
  * - `ManagedPolicyArns` — array of ARN strings from
8259
8266
  * `ListAttachedRolePolicies`.
8260
- * - `Policies` (inline policies with `PolicyDocument` bodies) is
8261
- * intentionally omitted: surfacing names without bodies guarantees a
8262
- * PolicyDocument-shaped drift on every role, and fetching every body
8263
- * costs one extra `GetRolePolicy` per inline policy. Out of scope for
8264
- * v1 drift detection on inline IAM policy bodies can ship in a
8265
- * follow-up.
8267
+ * - `Policies` inline policies surfaced as `[{PolicyName, PolicyDocument}]`.
8268
+ * `ListRolePolicies` for names + `GetRolePolicy` per name for the
8269
+ * body (URL-decoded + JSON-parsed). Ordering is reconciled against
8270
+ * state's `Policies` array (when supplied via the `properties`
8271
+ * parameter) so a state-vs-AWS positional compare doesn't fire false
8272
+ * drift purely from `ListRolePolicies` returning lexicographic order;
8273
+ * AWS-only policies (added via console) are appended at the end so
8274
+ * they still surface as drift via length / content mismatch.
8266
8275
  * - `Tags` is surfaced via `ListRoleTags` (paginated). CDK's `aws:*`
8267
8276
  * auto-tags are filtered out by `normalizeAwsTagsToCfn` so they don't
8268
- * fire false-positive drift; the result key is omitted entirely when
8269
- * AWS reports no user tags (matches `create()`'s behavior).
8277
+ * fire false-positive drift; always emitted (even when empty) so a
8278
+ * console-side tag ADD on an originally-untagged role surfaces as
8279
+ * drift on the v3 observedProperties baseline.
8270
8280
  *
8271
8281
  * Returns `undefined` when the role is gone (`NoSuchEntityException`).
8272
8282
  */
8273
- async readCurrentState(physicalId, _logicalId, _resourceType) {
8283
+ async readCurrentState(physicalId, _logicalId, _resourceType, properties) {
8274
8284
  let role;
8275
8285
  try {
8276
8286
  const resp = await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
@@ -8291,9 +8301,7 @@ var IAMRoleProvider = class {
8291
8301
  }
8292
8302
  if (role.Path !== void 0)
8293
8303
  result["Path"] = role.Path;
8294
- if (role.PermissionsBoundary?.PermissionsBoundaryArn !== void 0) {
8295
- result["PermissionsBoundary"] = role.PermissionsBoundary.PermissionsBoundaryArn;
8296
- }
8304
+ result["PermissionsBoundary"] = role.PermissionsBoundary?.PermissionsBoundaryArn ?? "";
8297
8305
  if (role.AssumeRolePolicyDocument) {
8298
8306
  try {
8299
8307
  result["AssumeRolePolicyDocument"] = JSON.parse(
@@ -8313,6 +8321,59 @@ var IAMRoleProvider = class {
8313
8321
  if (!(err instanceof NoSuchEntityException))
8314
8322
  throw err;
8315
8323
  }
8324
+ try {
8325
+ const policyNames = [];
8326
+ let policyMarker;
8327
+ while (true) {
8328
+ const listResp = await this.iamClient.send(
8329
+ new ListRolePoliciesCommand({
8330
+ RoleName: physicalId,
8331
+ ...policyMarker ? { Marker: policyMarker } : {}
8332
+ })
8333
+ );
8334
+ for (const name of listResp.PolicyNames ?? [])
8335
+ policyNames.push(name);
8336
+ if (!listResp.IsTruncated)
8337
+ break;
8338
+ policyMarker = listResp.Marker;
8339
+ }
8340
+ const bodies = /* @__PURE__ */ new Map();
8341
+ await Promise.all(
8342
+ policyNames.map(async (name) => {
8343
+ const resp = await this.iamClient.send(
8344
+ new GetRolePolicyCommand({ RoleName: physicalId, PolicyName: name })
8345
+ );
8346
+ if (!resp.PolicyDocument)
8347
+ return;
8348
+ let parsed;
8349
+ try {
8350
+ parsed = JSON.parse(decodeURIComponent(resp.PolicyDocument));
8351
+ } catch {
8352
+ parsed = resp.PolicyDocument;
8353
+ }
8354
+ bodies.set(name, parsed);
8355
+ })
8356
+ );
8357
+ const statePolicies = properties?.["Policies"] ?? [];
8358
+ const remaining = new Set(bodies.keys());
8359
+ const inline = [];
8360
+ for (const sp of statePolicies) {
8361
+ const name = sp?.PolicyName;
8362
+ if (typeof name !== "string")
8363
+ continue;
8364
+ if (bodies.has(name)) {
8365
+ inline.push({ PolicyName: name, PolicyDocument: bodies.get(name) });
8366
+ remaining.delete(name);
8367
+ }
8368
+ }
8369
+ for (const name of [...remaining].sort()) {
8370
+ inline.push({ PolicyName: name, PolicyDocument: bodies.get(name) });
8371
+ }
8372
+ result["Policies"] = inline;
8373
+ } catch (err) {
8374
+ if (!(err instanceof NoSuchEntityException))
8375
+ throw err;
8376
+ }
8316
8377
  try {
8317
8378
  const collected = [];
8318
8379
  let marker;
@@ -8340,18 +8401,6 @@ var IAMRoleProvider = class {
8340
8401
  }
8341
8402
  return result;
8342
8403
  }
8343
- /**
8344
- * `Policies` (inline policy bodies) are intentionally omitted from
8345
- * `readCurrentState`: surfacing the names without bodies would
8346
- * guarantee a `PolicyDocument`-shaped drift on every role, and
8347
- * fetching every body costs one extra `GetRolePolicy` per inline
8348
- * policy. Tell the drift comparator to skip the whole subtree until a
8349
- * dedicated PR adds proper inline-policy drift via per-name
8350
- * `GetRolePolicy`.
8351
- */
8352
- getDriftUnknownPaths() {
8353
- return ["Policies"];
8354
- }
8355
8404
  /**
8356
8405
  * Adopt an existing IAM role into cdkd state.
8357
8406
  *