@cdkd/cli 0.0.1 → 0.323.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.
Files changed (35) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +179 -2
  3. package/dist/asg-provider-4rNFSmz9.js +1277 -0
  4. package/dist/asg-provider-4rNFSmz9.js.map +1 -0
  5. package/dist/aws-client-defaults-BLXFsfMa.js +275 -0
  6. package/dist/aws-client-defaults-BLXFsfMa.js.map +1 -0
  7. package/dist/aws-clients-DWpHZk2l.js +440 -0
  8. package/dist/aws-clients-DWpHZk2l.js.map +1 -0
  9. package/dist/aws-failure-text-DiHodi76.js +1158 -0
  10. package/dist/aws-failure-text-DiHodi76.js.map +1 -0
  11. package/dist/cli.d.ts +1 -0
  12. package/dist/cli.js +135 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/created-resource-sink-CmrPwGge.js +2343 -0
  15. package/dist/created-resource-sink-CmrPwGge.js.map +1 -0
  16. package/dist/db-instance-modify-retry-DCLA_p9I.js +43 -0
  17. package/dist/db-instance-modify-retry-DCLA_p9I.js.map +1 -0
  18. package/dist/deploy-engine-dYZ0v6Kz.js +55863 -0
  19. package/dist/deploy-engine-dYZ0v6Kz.js.map +1 -0
  20. package/dist/iam-role-provider-Bp4osNrj.js +698 -0
  21. package/dist/iam-role-provider-Bp4osNrj.js.map +1 -0
  22. package/dist/index.d.ts +1629 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +7 -0
  25. package/dist/neptune-provider-BqtKFZaE.js +1470 -0
  26. package/dist/neptune-provider-BqtKFZaE.js.map +1 -0
  27. package/dist/program-BYT8z2ca.js +31547 -0
  28. package/dist/program-BYT8z2ca.js.map +1 -0
  29. package/dist/provider-classes-KPYbVTtY.js +55302 -0
  30. package/dist/provider-classes-KPYbVTtY.js.map +1 -0
  31. package/dist/rds-provider-C8enyD9d.js +1749 -0
  32. package/dist/rds-provider-C8enyD9d.js.map +1 -0
  33. package/dist/version-kAoaNHQI.js +11 -0
  34. package/dist/version-kAoaNHQI.js.map +1 -0
  35. package/package.json +139 -3
@@ -0,0 +1,698 @@
1
+ import { r as getAwsClients } from "./aws-clients-DWpHZk2l.js";
2
+ import { $ as safeMsg, N as recordedGeneratedNameKept, T as generateResourceNameWithFallback, a as safeStringify, n as describeAwsFailure, s as getLogger } from "./aws-failure-text-DiHodi76.js";
3
+ import { Ar as isThrottlingError, B as normalizeAwsTagsToCfn, Er as isTransientServerError, F as assertRegionMatch, Fn as ResourceUpdateNotSupportedError, Ft as maskerOrIdentity, Gt as markAuxiliaryFailure, It as withDerivedNameMasks, Kt as markCreatedBeforeFailure, M as RESOURCE_NOT_FOUND, Mr as markRedactedCause, Nn as ProvisioningError, Nr as wrapMaskedAwsError, Nt as isSecretDerivedValue, P as markNotGone, Ut as isAuxiliaryFailure, W as resolveExplicitPhysicalId, a as reportedCreatedResource, b as withRemovalDefaults, dt as pasteableAwsCommand, fn as ambientClientDefaults, hr as relayedNameCollision, i as reportCreatedResource, j as withoutServerErrorRetries, jr as markNonRetryable, jt as createMaskedLogSinks, o as retractCreatedResource } from "./created-resource-sink-CmrPwGge.js";
4
+ import { Qi as definedAttributes, Wi as markDeleteStarted, Yi as refuseMalformedDesiredTags, Zi as tagPlanWarning, qi as planTagDiff } from "./deploy-engine-dYZ0v6Kz.js";
5
+ import { AttachRolePolicyCommand, CreateRoleCommand, DeleteRoleCommand, DeleteRolePermissionsBoundaryCommand, DeleteRolePolicyCommand, DetachRolePolicyCommand, GetRoleCommand, GetRolePolicyCommand, IAMClient, ListAttachedRolePoliciesCommand, ListInstanceProfilesForRoleCommand, ListRolePoliciesCommand, ListRoleTagsCommand, NoSuchEntityException, PutRolePermissionsBoundaryCommand, PutRolePolicyCommand, RemoveRoleFromInstanceProfileCommand, TagRoleCommand, UntagRoleCommand, UpdateAssumeRolePolicyCommand, UpdateRoleCommand } from "@aws-sdk/client-iam";
6
+
7
+ // src/provisioning/providers/iam-create-client.ts
8
+ var IamCreateClientCache = class {
9
+ client;
10
+ shared;
11
+ constructor(shared) {
12
+ this.shared = shared;
13
+ }
14
+ get() {
15
+ const shared = this.shared();
16
+ if (!(shared instanceof IAMClient)) return Promise.resolve(shared);
17
+ this.client ??= shared.config.region().then((region) => withoutServerErrorRetries(new IAMClient({
18
+ ...ambientClientDefaults(),
19
+ region,
20
+ maxAttempts: 8
21
+ })), (error) => {
22
+ this.client = void 0;
23
+ throw error;
24
+ });
25
+ return this.client;
26
+ }
27
+ };
28
+
29
+ // src/provisioning/providers/iam-replay-adopt.ts
30
+ function isEntityAlreadyExists(error) {
31
+ return error?.name === "EntityAlreadyExistsException";
32
+ }
33
+ async function iamEntityLeftByThisCreate(query) {
34
+ const { logicalId, resourceType, noun, displayName, createError, log } = query;
35
+ if (!isEntityAlreadyExists(createError)) return void 0;
36
+ const report = reportedCreatedResource(logicalId, resourceType);
37
+ if (report?.createdResourceIdentity === void 0 || !query.namesThisEntity(report.physicalId)) return;
38
+ const { value: v } = log;
39
+ let entity;
40
+ try {
41
+ entity = await query.read(report.physicalId);
42
+ } catch (probeError) {
43
+ if (isThrottlingError(probeError) || isTransientServerError(probeError)) throw probeError;
44
+ log.debug(`IAM ${noun} ${logicalId}: could not read ${v(displayName)} to identify it: ${v(describeAwsFailure(probeError).detail)}`);
45
+ return;
46
+ }
47
+ if (query.identityOf(entity) !== report.createdResourceIdentity) {
48
+ log.warn(`IAM ${noun} ${logicalId}: ${v(displayName)} is not the ${noun} an earlier attempt of this create made, so it is left untouched. Do not delete it unless you know it is yours, whatever the failure below suggests.`);
49
+ return;
50
+ }
51
+ log.warn(`IAM ${noun} ${logicalId}: finishing ${v(displayName)}, the ${noun} an earlier attempt of this create made and could not delete. No manual deletion is needed if this create succeeds.`);
52
+ return entity;
53
+ }
54
+
55
+ // src/provisioning/providers/iam-role-provider.ts
56
+ function isRoleName(id) {
57
+ return /^[\w+=,.@-]{1,64}$/.test(id);
58
+ }
59
+ function roleIdentityToken(roleId) {
60
+ return typeof roleId === "string" && roleId !== "" ? roleId : void 0;
61
+ }
62
+ var IAMRoleProvider = class {
63
+ iamClient;
64
+ createClient = new IamCreateClientCache(() => this.iamClient);
65
+ logger = getLogger().child("IAMRoleProvider");
66
+ wrapMaskedError(mask, error, build) {
67
+ const failure = describeAwsFailure(error);
68
+ if (!(error instanceof Error)) return build(mask(failure.detail));
69
+ if (!failure.redacted) return wrapMaskedAwsError(mask, error, build);
70
+ this.logger.debug(mask(build(mask(failure.detail)).message));
71
+ const relay = isAuxiliaryFailure(error) ? "" : relayedNameCollision(failure.detail);
72
+ return markRedactedCause(build(`${mask(failure.summary)}${relay}`));
73
+ }
74
+ handledProperties = /* @__PURE__ */ new Map([["AWS::IAM::Role", /* @__PURE__ */ new Set([
75
+ "RoleName",
76
+ "AssumeRolePolicyDocument",
77
+ "Description",
78
+ "MaxSessionDuration",
79
+ "Path",
80
+ "PermissionsBoundary",
81
+ "ManagedPolicyArns",
82
+ "Policies",
83
+ "Tags"
84
+ ])]]);
85
+ removalDefaults = /* @__PURE__ */ new Map([["AWS::IAM::Role", /* @__PURE__ */ new Map([["Description", ""]])]]);
86
+ removalHandledInUpdate = /* @__PURE__ */ new Map([["AWS::IAM::Role", /* @__PURE__ */ new Set([
87
+ "RoleName",
88
+ "Path",
89
+ "AssumeRolePolicyDocument",
90
+ "PermissionsBoundary",
91
+ "ManagedPolicyArns",
92
+ "Policies",
93
+ "Tags",
94
+ "MaxSessionDuration"
95
+ ])]]);
96
+ constructor() {
97
+ const awsClients = getAwsClients();
98
+ this.iamClient = awsClients.iam;
99
+ }
100
+ async create(logicalId, resourceType, properties, context) {
101
+ const tags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
102
+ const roleName = generateResourceNameWithFallback(properties["RoleName"], logicalId, { maxLength: 64 });
103
+ const log = withDerivedNameMasks(this.logger, createMaskedLogSinks(this.logger, context?.maskSecrets), [[properties["RoleName"], roleName]]);
104
+ const { value: v } = log;
105
+ log.debug(`Creating IAM role ${logicalId}`);
106
+ const assumeRolePolicyDocument = properties["AssumeRolePolicyDocument"];
107
+ if (!assumeRolePolicyDocument) throw new ProvisioningError(`AssumeRolePolicyDocument is required for IAM role ${logicalId}`, resourceType, logicalId);
108
+ let leftBehind = false;
109
+ let createdRoleIdentity;
110
+ try {
111
+ const createParams = {
112
+ RoleName: roleName,
113
+ AssumeRolePolicyDocument: typeof assumeRolePolicyDocument === "string" ? assumeRolePolicyDocument : JSON.stringify(assumeRolePolicyDocument)
114
+ };
115
+ if (properties["Description"]) createParams.Description = properties["Description"];
116
+ if (properties["MaxSessionDuration"]) createParams.MaxSessionDuration = properties["MaxSessionDuration"];
117
+ if (properties["Path"]) createParams.Path = properties["Path"];
118
+ if (properties["PermissionsBoundary"]) createParams.PermissionsBoundary = properties["PermissionsBoundary"];
119
+ if (tags.length > 0) createParams.Tags = tags;
120
+ const createClient = await this.createClient.get();
121
+ let role;
122
+ let adopted = false;
123
+ try {
124
+ role = (await createClient.send(new CreateRoleCommand(createParams))).Role;
125
+ } catch (createError) {
126
+ role = await iamEntityLeftByThisCreate({
127
+ logicalId,
128
+ resourceType,
129
+ noun: "role",
130
+ displayName: roleName,
131
+ createError,
132
+ namesThisEntity: (reported) => reported === roleName,
133
+ read: async () => (await this.iamClient.send(new GetRoleCommand({ RoleName: roleName }))).Role,
134
+ identityOf: (live) => roleIdentityToken(live?.RoleId),
135
+ log
136
+ });
137
+ if (role === void 0) throw createError;
138
+ adopted = true;
139
+ }
140
+ leftBehind = true;
141
+ createdRoleIdentity = roleIdentityToken(role?.RoleId);
142
+ if (!adopted) {
143
+ reportCreatedResource(logicalId, resourceType, roleName, createdRoleIdentity);
144
+ log.debug(`Created IAM role: ${v(roleName)}`);
145
+ }
146
+ try {
147
+ const managedPolicyArns = properties["ManagedPolicyArns"];
148
+ if (managedPolicyArns && Array.isArray(managedPolicyArns)) for (const policyArn of managedPolicyArns) {
149
+ await this.iamClient.send(new AttachRolePolicyCommand({
150
+ RoleName: roleName,
151
+ PolicyArn: policyArn
152
+ }));
153
+ log.debug(`Attached managed policy ${v(policyArn)} to role ${v(roleName)}`);
154
+ }
155
+ const policies = properties["Policies"];
156
+ if (policies && Array.isArray(policies)) for (const policy of policies) {
157
+ const policyDoc = typeof policy.PolicyDocument === "string" ? policy.PolicyDocument : JSON.stringify(policy.PolicyDocument);
158
+ await this.iamClient.send(new PutRolePolicyCommand({
159
+ RoleName: roleName,
160
+ PolicyName: policy.PolicyName,
161
+ PolicyDocument: policyDoc
162
+ }));
163
+ log.debug(`Added inline policy ${v(policy.PolicyName)} to role ${v(roleName)}`);
164
+ }
165
+ } catch (innerError) {
166
+ if (!adopted) try {
167
+ await this.detachAllManagedPolicies(roleName, log);
168
+ await this.deleteAllInlinePolicies(roleName, log);
169
+ await this.iamClient.send(new DeleteRoleCommand({ RoleName: roleName }));
170
+ retractCreatedResource(logicalId, resourceType, roleName);
171
+ leftBehind = false;
172
+ log.debug(`Cleaned up partially-created IAM role ${logicalId} (${v(roleName)}) after wiring failure`);
173
+ } catch (cleanupError) {
174
+ const aws = pasteableAwsCommand(log.mask);
175
+ const cleanupFailure = describeAwsFailure(cleanupError);
176
+ const cleanupHead = `Failed to clean up partially-created IAM role ${logicalId} (${v(roleName)}): `;
177
+ if (cleanupFailure.redacted) log.debug(`${cleanupHead}${v(cleanupFailure.detail)}`);
178
+ log.warn(`${cleanupHead}${cleanupFailure.redacted ? `${v(cleanupFailure.summary)} ` : `${v(cleanupFailure.detail)}. `}Manual deletion may be required before the next deploy: detach managed policies (${aws`aws iam list-attached-role-policies --role-name ${roleName}`.render()} then ${aws`aws iam detach-role-policy --role-name ${roleName} --policy-arn '<arn>'`.render()}), delete inline policies (${aws`aws iam list-role-policies --role-name ${roleName}`.render()} then ${aws`aws iam delete-role-policy --role-name ${roleName} --policy-name '<name>'`.render()}), then ${aws`aws iam delete-role --role-name ${roleName}`.render()}`);
179
+ }
180
+ throw markAuxiliaryFailure(innerError, logicalId);
181
+ }
182
+ log.debug(`Successfully created IAM role ${logicalId}: ${v(roleName)}`);
183
+ return {
184
+ physicalId: roleName,
185
+ attributes: {
186
+ Arn: role?.Arn,
187
+ RoleId: role?.RoleId
188
+ }
189
+ };
190
+ } catch (error) {
191
+ const cause = error instanceof Error ? error : void 0;
192
+ throw this.wrapMaskedError(log.mask, error, (text) => {
193
+ const built = new ProvisioningError(`Failed to create IAM role ${logicalId}: ${text}`, resourceType, logicalId, roleName, cause);
194
+ return leftBehind ? markCreatedBeforeFailure(built, logicalId, resourceType, roleName, createdRoleIdentity) : built;
195
+ });
196
+ }
197
+ }
198
+ async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
199
+ const templateProperties = { ...properties };
200
+ for (const key of context?.removedProperties ?? []) if (this.removalDefaults.get(resourceType)?.has(key) === true) delete templateProperties[key];
201
+ properties = withRemovalDefaults(this.removalDefaults, resourceType, properties, previousProperties, context);
202
+ refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
203
+ const derivedRoleName = generateResourceNameWithFallback(properties["RoleName"], logicalId, { maxLength: 64 });
204
+ const log = withDerivedNameMasks(this.logger, createMaskedLogSinks(this.logger, context?.maskSecrets), [
205
+ [properties["RoleName"], derivedRoleName],
206
+ [previousProperties["RoleName"], physicalId],
207
+ ...context?.desiredFromAwsReadback === true ? [[properties["RoleName"], physicalId]] : []
208
+ ]);
209
+ const { value: v } = log;
210
+ log.debug(`Updating IAM role ${logicalId}: ${v(physicalId)}`);
211
+ const revertKeepsName = context?.desiredFromAwsReadback === true && derivedRoleName !== physicalId;
212
+ if (revertKeepsName) log.warn(`IAM role ${logicalId}: RoleName is not reverted — the role ${v(physicalId)} keeps its name (the reverted properties derive ${v(derivedRoleName)}), since a drift revert never replaces a role. 'cdkd drift' keeps reporting the name until 'cdkd drift --accept' records the live one; only a deploy renames the role.`);
213
+ const generatedNameKept = recordedGeneratedNameKept({
214
+ recorded: physicalId,
215
+ derived: derivedRoleName,
216
+ declaredName: properties["RoleName"],
217
+ previousDeclaredName: previousProperties["RoleName"],
218
+ logicalId,
219
+ options: { maxLength: 64 }
220
+ });
221
+ if (generatedNameKept) log.debug(`IAM role ${logicalId}: keeping its generated name ${v(physicalId)} (the template names no role)`);
222
+ const newRoleName = revertKeepsName || generatedNameKept ? physicalId : derivedRoleName;
223
+ const newPath = properties["Path"] || "/";
224
+ const oldPath = previousProperties["Path"] || "/";
225
+ const pathBehindSecret = newRoleName === physicalId && newPath !== oldPath && isSecretDerivedValue(previousProperties["Path"], maskerOrIdentity(void 0));
226
+ let livePath;
227
+ if (pathBehindSecret) try {
228
+ livePath = (await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }))).Role?.Path;
229
+ } catch (error) {
230
+ const cause = error instanceof Error ? error : void 0;
231
+ throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to read the path of IAM role ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
232
+ }
233
+ if (pathBehindSecret) {
234
+ if (livePath === void 0 || livePath === "") throw markNonRetryable(new ProvisioningError(`IAM role ${logicalId}: its Path comes from a secret reference, and IAM returned no path for ${v(physicalId)} to confirm it against. Nothing was changed.`, resourceType, logicalId, physicalId));
235
+ if (livePath !== newPath) log.warn(`IAM role ${logicalId}: the secret its Path comes from now resolves to a different path than the role ${v(physicalId)} has. A role cannot move in place, so it keeps its path; the new value takes effect when the template changes the reference, which replaces the role.`);
236
+ }
237
+ const needsReplacement = newRoleName !== physicalId || newPath !== oldPath && !pathBehindSecret;
238
+ if (needsReplacement && context?.desiredFromAwsReadback === true) throw markNonRetryable(new ProvisioningError(`IAM role ${logicalId}: Path cannot be reverted to ${v(newPath)} — a role's Path cannot change in place, and a drift revert never replaces the role ${v(physicalId)}. Nothing was changed; deploy the stack to replace it.`, resourceType, logicalId, physicalId));
239
+ if (needsReplacement && newRoleName === physicalId) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, log.mask(`Path changed from ${v(oldPath)} to ${v(newPath)}, and an IAM role cannot move to another path; a replacement under the same name ${v(physicalId)} must delete the role first — re-deploy with --replace, or give the role a new name`));
240
+ if (needsReplacement) {
241
+ log.debug(`RoleName changed, replacing role: ${v(physicalId)} (RoleName: from ${v(physicalId)} to ${v(newRoleName)})`);
242
+ properties = templateProperties;
243
+ const createResult = await this.create(logicalId, resourceType, properties, { maskSecrets: log.mask });
244
+ let orphanReason;
245
+ try {
246
+ const deleteResult = await this.delete(logicalId, physicalId, resourceType, void 0, { maskSecrets: log.mask });
247
+ if (deleteResult?.outcome === "skipped") {
248
+ orphanReason = log.mask(`old role ${v(physicalId)} was not deleted: ${deleteResult.reason}`);
249
+ log.warn(`Skipped deleting old role ${v(physicalId)} during replacement: ${deleteResult.reason}. The old role may be orphaned and require manual cleanup.`);
250
+ }
251
+ } catch (error) {
252
+ orphanReason = log.mask(`old role ${v(physicalId)} could not be deleted: ${safeStringify(error)}`);
253
+ log.warn(`Failed to delete old role ${v(physicalId)} during replacement: ${v(safeStringify(error))}. The old role may be orphaned and require manual cleanup.`);
254
+ }
255
+ const base = {
256
+ physicalId: createResult.physicalId,
257
+ wasReplaced: true,
258
+ ...createResult.attributes ? { attributes: createResult.attributes } : {}
259
+ };
260
+ return orphanReason !== void 0 ? {
261
+ ...base,
262
+ outcome: "partial",
263
+ reason: orphanReason
264
+ } : base;
265
+ }
266
+ try {
267
+ const updateParams = { RoleName: physicalId };
268
+ const descriptionInput = properties["Description"];
269
+ if (descriptionInput !== void 0) updateParams.Description = descriptionInput;
270
+ const maxSessionDurationInput = properties["MaxSessionDuration"];
271
+ if (maxSessionDurationInput !== void 0) updateParams.MaxSessionDuration = maxSessionDurationInput;
272
+ await this.iamClient.send(new UpdateRoleCommand(updateParams));
273
+ const newAssumePolicy = properties["AssumeRolePolicyDocument"];
274
+ const oldAssumePolicy = previousProperties["AssumeRolePolicyDocument"];
275
+ if (newAssumePolicy) {
276
+ const newPolicyStr = typeof newAssumePolicy === "string" ? newAssumePolicy : JSON.stringify(newAssumePolicy);
277
+ if (newPolicyStr !== (oldAssumePolicy ? typeof oldAssumePolicy === "string" ? oldAssumePolicy : JSON.stringify(oldAssumePolicy) : "")) {
278
+ await this.iamClient.send(new UpdateAssumeRolePolicyCommand({
279
+ RoleName: physicalId,
280
+ PolicyDocument: newPolicyStr
281
+ }));
282
+ log.debug(`Updated assume role policy for ${v(physicalId)}`);
283
+ }
284
+ }
285
+ const newBoundary = properties["PermissionsBoundary"];
286
+ const oldBoundary = previousProperties["PermissionsBoundary"];
287
+ if (newBoundary !== oldBoundary) {
288
+ if (newBoundary) {
289
+ await this.iamClient.send(new PutRolePermissionsBoundaryCommand({
290
+ RoleName: physicalId,
291
+ PermissionsBoundary: newBoundary
292
+ }));
293
+ log.debug(`Set permissions boundary for ${v(physicalId)}: ${v(newBoundary)}`);
294
+ } else if (oldBoundary) {
295
+ await this.iamClient.send(new DeleteRolePermissionsBoundaryCommand({ RoleName: physicalId }));
296
+ log.debug(`Removed permissions boundary from ${v(physicalId)}`);
297
+ }
298
+ }
299
+ await this.updateManagedPolicies(physicalId, properties["ManagedPolicyArns"], previousProperties["ManagedPolicyArns"], log);
300
+ await this.updateInlinePolicies(physicalId, properties["Policies"], previousProperties["Policies"], log, context?.inlinePolicyClaimed);
301
+ await this.updateTags(physicalId, resourceType, logicalId, properties["Tags"], previousProperties["Tags"], log);
302
+ log.debug(`Successfully updated IAM role ${logicalId}`);
303
+ const getRoleResponse = await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
304
+ return {
305
+ physicalId,
306
+ wasReplaced: false,
307
+ attributes: {
308
+ Arn: getRoleResponse.Role?.Arn,
309
+ RoleId: getRoleResponse.Role?.RoleId
310
+ }
311
+ };
312
+ } catch (error) {
313
+ const cause = error instanceof Error ? error : void 0;
314
+ throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to update IAM role ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
315
+ }
316
+ }
317
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
318
+ const log = createMaskedLogSinks(this.logger, context?.maskSecrets);
319
+ const { value: v } = log;
320
+ log.debug(`Deleting IAM role ${logicalId}: ${v(physicalId)}`);
321
+ const progress = { stripped: false };
322
+ try {
323
+ try {
324
+ await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
325
+ } catch (error) {
326
+ if (error instanceof NoSuchEntityException) {
327
+ const clientRegion = await this.iamClient.config.region();
328
+ assertRegionMatch(clientRegion, context?.expectedRegion, resourceType, logicalId, physicalId);
329
+ if (context?.failedCreateOrphan === true) this.logger.info(safeMsg` IAM role ${physicalId} (${logicalId}), which a failed deploy created, is already gone; nothing to delete`);
330
+ else log.debug(`Role ${v(physicalId)} does not exist, skipping deletion`);
331
+ return;
332
+ }
333
+ throw error;
334
+ }
335
+ await this.detachAllManagedPolicies(physicalId, log, progress);
336
+ await this.deleteAllInlinePolicies(physicalId, log, progress);
337
+ await this.removeFromAllInstanceProfiles(physicalId, log, progress);
338
+ await this.iamClient.send(new DeleteRoleCommand({ RoleName: physicalId }));
339
+ log.debug(`Successfully deleted IAM role ${logicalId}`);
340
+ } catch (error) {
341
+ const cause = error instanceof Error ? error : void 0;
342
+ const failure = this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(log.mask(`Failed to delete IAM role ${logicalId}: ${text}`), resourceType, logicalId, physicalId, cause));
343
+ const marked = error instanceof Error && error.name.includes("NotFound") ? markNotGone(failure) : failure;
344
+ throw progress.stripped ? markDeleteStarted(marked) : marked;
345
+ }
346
+ }
347
+ async detachAllManagedPolicies(roleName, log, progress) {
348
+ const { value: v } = log;
349
+ log.debug(`Detaching all managed policies from role ${v(roleName)}`);
350
+ try {
351
+ const policies = (await this.iamClient.send(new ListAttachedRolePoliciesCommand({ RoleName: roleName }))).AttachedPolicies || [];
352
+ if (policies.length === 0) {
353
+ log.debug(`No managed policies attached to role ${v(roleName)}`);
354
+ return;
355
+ }
356
+ for (const policy of policies) if (policy.PolicyArn) try {
357
+ await this.iamClient.send(new DetachRolePolicyCommand({
358
+ RoleName: roleName,
359
+ PolicyArn: policy.PolicyArn
360
+ }));
361
+ if (progress) progress.stripped = true;
362
+ log.debug(`Detached managed policy ${v(policy.PolicyArn)} from role ${v(roleName)}`);
363
+ } catch (error) {
364
+ if (error instanceof NoSuchEntityException) log.debug(`Managed policy ${v(policy.PolicyArn)} already detached from role ${v(roleName)}`);
365
+ else throw error;
366
+ }
367
+ log.debug(`Detached ${policies.length} managed policies from role ${v(roleName)}`);
368
+ } catch (error) {
369
+ if (error instanceof NoSuchEntityException) {
370
+ log.debug(`Role ${v(roleName)} not found when detaching managed policies`);
371
+ return;
372
+ }
373
+ throw error;
374
+ }
375
+ }
376
+ async deleteAllInlinePolicies(roleName, log, progress) {
377
+ const { value: v } = log;
378
+ log.debug(`Deleting all inline policies from role ${v(roleName)}`);
379
+ try {
380
+ const policyNames = (await this.iamClient.send(new ListRolePoliciesCommand({ RoleName: roleName }))).PolicyNames || [];
381
+ if (policyNames.length === 0) {
382
+ log.debug(`No inline policies on role ${v(roleName)}`);
383
+ return;
384
+ }
385
+ for (const policyName of policyNames) try {
386
+ await this.iamClient.send(new DeleteRolePolicyCommand({
387
+ RoleName: roleName,
388
+ PolicyName: policyName
389
+ }));
390
+ if (progress) progress.stripped = true;
391
+ log.debug(`Deleted inline policy ${v(policyName)} from role ${v(roleName)}`);
392
+ } catch (error) {
393
+ if (error instanceof NoSuchEntityException) log.debug(`Inline policy ${v(policyName)} already deleted from role ${v(roleName)}`);
394
+ else throw error;
395
+ }
396
+ log.debug(`Deleted ${policyNames.length} inline policies from role ${v(roleName)}`);
397
+ } catch (error) {
398
+ if (error instanceof NoSuchEntityException) {
399
+ log.debug(`Role ${v(roleName)} not found when deleting inline policies`);
400
+ return;
401
+ }
402
+ throw error;
403
+ }
404
+ }
405
+ async removeFromAllInstanceProfiles(roleName, log, progress) {
406
+ const { value: v } = log;
407
+ log.debug(`Removing role ${v(roleName)} from all instance profiles`);
408
+ try {
409
+ const profiles = (await this.iamClient.send(new ListInstanceProfilesForRoleCommand({ RoleName: roleName }))).InstanceProfiles || [];
410
+ if (profiles.length === 0) {
411
+ log.debug(`No instance profiles associated with role ${v(roleName)}`);
412
+ return;
413
+ }
414
+ for (const profile of profiles) if (profile.InstanceProfileName) try {
415
+ await this.iamClient.send(new RemoveRoleFromInstanceProfileCommand({
416
+ RoleName: roleName,
417
+ InstanceProfileName: profile.InstanceProfileName
418
+ }));
419
+ if (progress) progress.stripped = true;
420
+ log.debug(`Removed role ${v(roleName)} from instance profile ${v(profile.InstanceProfileName)}`);
421
+ } catch (error) {
422
+ if (error instanceof NoSuchEntityException) log.debug(`Role ${v(roleName)} already removed from instance profile ${v(profile.InstanceProfileName)}`);
423
+ else throw error;
424
+ }
425
+ log.debug(`Removed role ${v(roleName)} from ${profiles.length} instance profiles`);
426
+ } catch (error) {
427
+ if (error instanceof NoSuchEntityException) {
428
+ log.debug(`Role ${v(roleName)} not found when removing from instance profiles`);
429
+ return;
430
+ }
431
+ throw error;
432
+ }
433
+ }
434
+ async updateManagedPolicies(roleName, newPolicies, oldPolicies, log) {
435
+ const { value: v } = log;
436
+ const newSet = new Set(newPolicies || []);
437
+ const oldSet = new Set(oldPolicies || []);
438
+ for (const policyArn of newSet) if (!oldSet.has(policyArn)) {
439
+ await this.iamClient.send(new AttachRolePolicyCommand({
440
+ RoleName: roleName,
441
+ PolicyArn: policyArn
442
+ }));
443
+ log.debug(`Attached managed policy ${v(policyArn)}`);
444
+ }
445
+ for (const policyArn of oldSet) if (!newSet.has(policyArn)) {
446
+ await this.iamClient.send(new DetachRolePolicyCommand({
447
+ RoleName: roleName,
448
+ PolicyArn: policyArn
449
+ }));
450
+ log.debug(`Detached managed policy ${v(policyArn)}`);
451
+ }
452
+ }
453
+ async updateInlinePolicies(roleName, newPolicies, oldPolicies, log, claimed) {
454
+ const { value: v } = log;
455
+ const newMap = new Map((newPolicies || []).map((p) => [p.PolicyName, p.PolicyDocument]));
456
+ const oldMap = new Map((oldPolicies || []).map((p) => [p.PolicyName, p.PolicyDocument]));
457
+ for (const [policyName, policyDoc] of newMap) {
458
+ const policyDocument = typeof policyDoc === "string" ? policyDoc : JSON.stringify(policyDoc);
459
+ await this.iamClient.send(new PutRolePolicyCommand({
460
+ RoleName: roleName,
461
+ PolicyName: policyName,
462
+ PolicyDocument: policyDocument
463
+ }));
464
+ log.debug(`Updated inline policy ${v(policyName)}`);
465
+ }
466
+ for (const policyName of oldMap.keys()) if (!newMap.has(policyName)) {
467
+ if (claimed?.("role", roleName, policyName) === true) {
468
+ log.debug(`Kept inline policy ${v(policyName)} on role ${v(roleName)}: another resource of this deploy or rollback wrote it`);
469
+ continue;
470
+ }
471
+ await this.iamClient.send(new DeleteRolePolicyCommand({
472
+ RoleName: roleName,
473
+ PolicyName: policyName
474
+ }));
475
+ log.debug(`Deleted inline policy ${v(policyName)}`);
476
+ }
477
+ }
478
+ async updateTags(roleName, resourceType, logicalId, newTags, oldTags, log) {
479
+ const { value: v } = log;
480
+ const plan = planTagDiff(oldTags, newTags);
481
+ const tagWarning = tagPlanWarning(plan, resourceType, logicalId);
482
+ if (tagWarning !== void 0) log.warn(tagWarning);
483
+ const tagsToRemove = plan.remove;
484
+ const tagsToAdd = [...plan.set].map(([Key, Value]) => ({
485
+ Key,
486
+ Value
487
+ }));
488
+ if (tagsToRemove.length > 0) {
489
+ await this.iamClient.send(new UntagRoleCommand({
490
+ RoleName: roleName,
491
+ TagKeys: tagsToRemove
492
+ }));
493
+ log.debug(`Removed ${tagsToRemove.length} tags from role ${v(roleName)}`);
494
+ }
495
+ if (tagsToAdd.length > 0) {
496
+ await this.iamClient.send(new TagRoleCommand({
497
+ RoleName: roleName,
498
+ Tags: tagsToAdd
499
+ }));
500
+ log.debug(`Added/updated ${tagsToAdd.length} tags on role ${v(roleName)}`);
501
+ }
502
+ }
503
+ async getAttribute(physicalId, _resourceType, attributeName) {
504
+ try {
505
+ const resp = await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
506
+ switch (attributeName) {
507
+ case "Arn": return resp.Role?.Arn;
508
+ case "RoleId": return resp.Role?.RoleId;
509
+ default: return;
510
+ }
511
+ } catch (err) {
512
+ if (err instanceof NoSuchEntityException) return void 0;
513
+ throw err;
514
+ }
515
+ }
516
+ async isSameResource(journaledPhysicalId, record, resourceType, context) {
517
+ if (resourceType !== "AWS::IAM::Role") return "unknown";
518
+ if (!isRoleName(journaledPhysicalId) || !isRoleName(record.physicalId)) return "unknown";
519
+ if (journaledPhysicalId === record.physicalId) return "same";
520
+ if (journaledPhysicalId.toLowerCase() === record.physicalId.toLowerCase()) return "unknown";
521
+ if (await this.iamClient.config.region() !== context.expectedRegion) return "unknown";
522
+ const recordRoleId = await this.readRoleIdIfExists(record.physicalId);
523
+ if (recordRoleId === void 0) return "unknown";
524
+ const journaledRoleId = await this.readRoleIdIfExists(journaledPhysicalId);
525
+ if (journaledRoleId === void 0) return "different";
526
+ return journaledRoleId === recordRoleId ? "same" : "different";
527
+ }
528
+ async resourceIdentity(physicalId, resourceType, context) {
529
+ if (resourceType !== "AWS::IAM::Role" || !isRoleName(physicalId)) return void 0;
530
+ if (await this.iamClient.config.region() !== context.expectedRegion) return void 0;
531
+ const roleId = await this.readRoleIdIfExists(physicalId);
532
+ return roleId === void 0 ? RESOURCE_NOT_FOUND : roleId;
533
+ }
534
+ async readRoleIdIfExists(roleName) {
535
+ let response;
536
+ try {
537
+ response = await this.iamClient.send(new GetRoleCommand({ RoleName: roleName }));
538
+ } catch (error) {
539
+ if (error instanceof NoSuchEntityException) return void 0;
540
+ throw error;
541
+ }
542
+ if (response.Role?.RoleName !== roleName) throw new Error("GetRole answered for another role than the one asked for");
543
+ const roleId = roleIdentityToken(response.Role.RoleId);
544
+ if (roleId === void 0) throw new Error("GetRole returned no RoleId");
545
+ return roleId;
546
+ }
547
+ async readCurrentState(physicalId, _logicalId, _resourceType, properties, context) {
548
+ let role;
549
+ try {
550
+ role = (await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }))).Role;
551
+ } catch (err) {
552
+ if (err instanceof NoSuchEntityException) return RESOURCE_NOT_FOUND;
553
+ throw err;
554
+ }
555
+ if (!role) return void 0;
556
+ const result = {};
557
+ if (role.RoleName !== void 0) result["RoleName"] = role.RoleName;
558
+ result["Description"] = role.Description ?? "";
559
+ if (role.MaxSessionDuration !== void 0) result["MaxSessionDuration"] = role.MaxSessionDuration;
560
+ if (role.Path !== void 0) result["Path"] = role.Path;
561
+ result["PermissionsBoundary"] = role.PermissionsBoundary?.PermissionsBoundaryArn ?? "";
562
+ if (role.AssumeRolePolicyDocument) try {
563
+ result["AssumeRolePolicyDocument"] = JSON.parse(decodeURIComponent(role.AssumeRolePolicyDocument));
564
+ } catch {
565
+ result["AssumeRolePolicyDocument"] = role.AssumeRolePolicyDocument;
566
+ }
567
+ try {
568
+ result["ManagedPolicyArns"] = ((await this.iamClient.send(new ListAttachedRolePoliciesCommand({ RoleName: physicalId }))).AttachedPolicies ?? []).map((p) => p.PolicyArn).filter((arn) => !!arn);
569
+ } catch (err) {
570
+ if (!(err instanceof NoSuchEntityException)) throw err;
571
+ }
572
+ try {
573
+ const policyNames = [];
574
+ let policyMarker;
575
+ while (true) {
576
+ const listResp = await this.iamClient.send(new ListRolePoliciesCommand({
577
+ RoleName: physicalId,
578
+ ...policyMarker ? { Marker: policyMarker } : {}
579
+ }));
580
+ for (const name of listResp.PolicyNames ?? []) policyNames.push(name);
581
+ if (!listResp.IsTruncated) break;
582
+ policyMarker = listResp.Marker;
583
+ }
584
+ const managedByOtherResource = collectInlinePolicyNamesManagedBySiblings(physicalId, context, "Roles");
585
+ const filteredNames = policyNames.filter((n) => !managedByOtherResource.has(n));
586
+ const bodies = /* @__PURE__ */ new Map();
587
+ await Promise.all(filteredNames.map(async (name) => {
588
+ const resp = await this.iamClient.send(new GetRolePolicyCommand({
589
+ RoleName: physicalId,
590
+ PolicyName: name
591
+ }));
592
+ if (!resp.PolicyDocument) return;
593
+ let parsed;
594
+ try {
595
+ parsed = JSON.parse(decodeURIComponent(resp.PolicyDocument));
596
+ } catch {
597
+ parsed = resp.PolicyDocument;
598
+ }
599
+ bodies.set(name, parsed);
600
+ }));
601
+ const statePolicies = properties?.["Policies"] ?? [];
602
+ const remaining = new Set(bodies.keys());
603
+ const inline = [];
604
+ for (const sp of statePolicies) {
605
+ const name = sp?.PolicyName;
606
+ if (typeof name !== "string") continue;
607
+ if (bodies.has(name)) {
608
+ inline.push({
609
+ PolicyName: name,
610
+ PolicyDocument: bodies.get(name)
611
+ });
612
+ remaining.delete(name);
613
+ }
614
+ }
615
+ for (const name of [...remaining].sort()) inline.push({
616
+ PolicyName: name,
617
+ PolicyDocument: bodies.get(name)
618
+ });
619
+ result["Policies"] = inline;
620
+ } catch (err) {
621
+ if (!(err instanceof NoSuchEntityException)) throw err;
622
+ }
623
+ try {
624
+ const collected = [];
625
+ let marker;
626
+ while (true) {
627
+ const tagsResp = await this.iamClient.send(new ListRoleTagsCommand({
628
+ RoleName: physicalId,
629
+ ...marker ? { Marker: marker } : {}
630
+ }));
631
+ if (tagsResp.Tags) for (const t of tagsResp.Tags) collected.push({
632
+ Key: t.Key,
633
+ Value: t.Value
634
+ });
635
+ if (!tagsResp.IsTruncated) break;
636
+ marker = tagsResp.Marker;
637
+ }
638
+ result["Tags"] = normalizeAwsTagsToCfn(collected);
639
+ } catch (err) {
640
+ if (!(err instanceof NoSuchEntityException)) throw err;
641
+ }
642
+ return result;
643
+ }
644
+ async import(input) {
645
+ const explicit = resolveExplicitPhysicalId(input, "RoleName");
646
+ if (explicit) try {
647
+ const resp = await this.iamClient.send(new GetRoleCommand({ RoleName: explicit }));
648
+ return {
649
+ physicalId: explicit,
650
+ attributes: definedAttributes({
651
+ Arn: resp.Role?.Arn,
652
+ RoleId: resp.Role?.RoleId
653
+ })
654
+ };
655
+ } catch (err) {
656
+ if (err instanceof NoSuchEntityException) return null;
657
+ throw err;
658
+ }
659
+ return null;
660
+ }
661
+ };
662
+ const STANDALONE_POLICY_SIBLINGS = {
663
+ Roles: {
664
+ type: "AWS::IAM::RolePolicy",
665
+ field: "RoleName"
666
+ },
667
+ Users: {
668
+ type: "AWS::IAM::UserPolicy",
669
+ field: "UserName"
670
+ },
671
+ Groups: {
672
+ type: "AWS::IAM::GroupPolicy",
673
+ field: "GroupName"
674
+ }
675
+ };
676
+ function collectInlinePolicyNamesManagedBySiblings(targetPhysicalId, context, attachmentField) {
677
+ const result = /* @__PURE__ */ new Set();
678
+ const siblings = context?.siblings;
679
+ if (!siblings) return result;
680
+ const standalone = STANDALONE_POLICY_SIBLINGS[attachmentField];
681
+ for (const sibling of Object.values(siblings)) {
682
+ if (sibling.resourceType === standalone.type) {
683
+ const name = sibling.properties["PolicyName"];
684
+ if (sibling.properties[standalone.field] === targetPhysicalId && typeof name === "string") result.add(name);
685
+ continue;
686
+ }
687
+ if (sibling.resourceType !== "AWS::IAM::Policy") continue;
688
+ const attachments = sibling.properties[attachmentField];
689
+ if (!Array.isArray(attachments)) continue;
690
+ if (!attachments.some((a) => a === targetPhysicalId)) continue;
691
+ const name = sibling.properties["PolicyName"];
692
+ if (typeof name === "string") result.add(name);
693
+ }
694
+ return result;
695
+ }
696
+
697
+ export { IamCreateClientCache as a, isEntityAlreadyExists as i, collectInlinePolicyNamesManagedBySiblings as n, iamEntityLeftByThisCreate as r, IAMRoleProvider as t };
698
+ //# sourceMappingURL=iam-role-provider-Bp4osNrj.js.map