@beesolve/aws-accounts 1.0.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/diff.js ADDED
@@ -0,0 +1,1012 @@
1
+ import * as v from "valibot";
2
+ import {
3
+ planSchema
4
+ } from "./operations.js";
5
+ const pendingCreationId = "__pending_creation__";
6
+ const operationExecutionPriority = {
7
+ createOu: 1,
8
+ renameOu: 2,
9
+ createAccount: 3,
10
+ updateAccountTags: 4,
11
+ updateAccountName: 5,
12
+ moveAccount: 6,
13
+ removeAccount: 7,
14
+ createIdcUser: 8,
15
+ updateIdcUser: 9,
16
+ createIdcGroup: 10,
17
+ updateIdcGroupDescription: 11,
18
+ addIdcGroupMembership: 12,
19
+ createIdcPermissionSet: 13,
20
+ updateIdcPermissionSetDescription: 14,
21
+ putIdcPermissionSetInlinePolicy: 15,
22
+ deleteIdcPermissionSetInlinePolicy: 16,
23
+ attachIdcManagedPolicyToPermissionSet: 17,
24
+ detachIdcManagedPolicyFromPermissionSet: 18,
25
+ attachIdcCustomerManagedPolicyReferenceToPermissionSet: 19,
26
+ detachIdcCustomerManagedPolicyReferenceFromPermissionSet: 20,
27
+ provisionIdcPermissionSet: 21,
28
+ grantIdcAccountAssignment: 22,
29
+ removeIdcGroupMembership: 23,
30
+ revokeIdcAccountAssignment: 24,
31
+ deleteIdcUser: 25,
32
+ deleteIdcGroup: 26,
33
+ deleteIdcPermissionSet: 27,
34
+ deleteOu: 28
35
+ };
36
+ function diffStates(props) {
37
+ const operations = [];
38
+ const unsupported = [];
39
+ const currentOrganization = normalizeOrganizationState({
40
+ state: props.current,
41
+ includeDepthById: true
42
+ });
43
+ const nextOrganization = normalizeOrganizationState({
44
+ state: props.next
45
+ });
46
+ const nextAccountIds = new Set(
47
+ nextOrganization.accounts.filter((account) => account.id !== pendingCreationId).map((account) => account.id)
48
+ );
49
+ for (const nextAccount of nextOrganization.accounts) {
50
+ const currentAccount = nextAccount.id !== pendingCreationId ? currentOrganization.accountById.get(nextAccount.id) : void 0;
51
+ if (currentAccount == null) {
52
+ if (nextAccount.id === pendingCreationId) {
53
+ const targetOuName = resolveOrganizationalUnitName({
54
+ organizationalUnitNameById: nextOrganization.organizationalUnitNameById,
55
+ rootId: nextOrganization.rootId,
56
+ organizationalUnitId: nextAccount.parentId
57
+ });
58
+ if (isResolvableOrganizationalUnitId({
59
+ rootId: nextOrganization.rootId,
60
+ organizationalUnitNameById: nextOrganization.organizationalUnitNameById,
61
+ organizationalUnitId: nextAccount.parentId
62
+ }) === false) {
63
+ unsupported.push({
64
+ kind: "newAccountWithUnknownOu",
65
+ category: "unsupportedMutation",
66
+ description: `new account "${nextAccount.name}" has unresolved target OU "${targetOuName}" (${nextAccount.parentId})`
67
+ });
68
+ continue;
69
+ }
70
+ operations.push({
71
+ kind: "createAccount",
72
+ accountName: nextAccount.name,
73
+ accountEmail: nextAccount.email,
74
+ targetOuId: nextAccount.parentId,
75
+ targetOuName
76
+ });
77
+ }
78
+ continue;
79
+ }
80
+ if (nextAccount.parentId === currentAccount.parentId) {
81
+ if (currentAccount.name !== nextAccount.name) {
82
+ operations.push({
83
+ kind: "updateAccountName",
84
+ accountId: nextAccount.id,
85
+ fromAccountName: currentAccount.name,
86
+ toAccountName: nextAccount.name
87
+ });
88
+ }
89
+ const currentTags = normalizeAccountTags(currentAccount.tags);
90
+ const nextTags = normalizeAccountTags(nextAccount.tags);
91
+ if (JSON.stringify(currentTags) !== JSON.stringify(nextTags)) {
92
+ operations.push({
93
+ kind: "updateAccountTags",
94
+ accountId: nextAccount.id,
95
+ accountName: nextAccount.name,
96
+ tags: Object.fromEntries(
97
+ (nextTags ?? []).map((tag) => [tag.key, tag.value])
98
+ )
99
+ });
100
+ }
101
+ continue;
102
+ }
103
+ if (currentAccount.id === pendingCreationId || nextAccount.id === pendingCreationId || currentAccount.parentId === pendingCreationId || nextAccount.parentId === pendingCreationId) {
104
+ continue;
105
+ }
106
+ const fromOuName = resolveOrganizationalUnitName({
107
+ organizationalUnitNameById: currentOrganization.organizationalUnitNameById,
108
+ rootId: currentOrganization.rootId,
109
+ organizationalUnitId: currentAccount.parentId
110
+ });
111
+ const toOuName = resolveOrganizationalUnitName({
112
+ organizationalUnitNameById: nextOrganization.organizationalUnitNameById,
113
+ rootId: nextOrganization.rootId,
114
+ organizationalUnitId: nextAccount.parentId
115
+ });
116
+ if (currentAccount.name !== nextAccount.name) {
117
+ operations.push({
118
+ kind: "updateAccountName",
119
+ accountId: nextAccount.id,
120
+ fromAccountName: currentAccount.name,
121
+ toAccountName: nextAccount.name
122
+ });
123
+ }
124
+ operations.push({
125
+ kind: "moveAccount",
126
+ accountId: nextAccount.id,
127
+ accountName: nextAccount.name,
128
+ fromOuId: currentAccount.parentId,
129
+ fromOuName,
130
+ toOuId: nextAccount.parentId,
131
+ toOuName
132
+ });
133
+ }
134
+ const graveyardOrganizationalUnit = currentOrganization.organizationalUnitByName.get("Graveyard");
135
+ for (const currentAccount of currentOrganization.accounts) {
136
+ if (currentAccount.id !== pendingCreationId && nextAccountIds.has(currentAccount.id)) {
137
+ continue;
138
+ }
139
+ if (graveyardOrganizationalUnit == null) {
140
+ unsupported.push({
141
+ kind: "removedOu",
142
+ category: "destructive",
143
+ description: `removed account "${currentAccount.name}" cannot be reconciled because reserved OU "Graveyard" was not found in state`
144
+ });
145
+ continue;
146
+ }
147
+ if (currentAccount.parentId === graveyardOrganizationalUnit.id) {
148
+ continue;
149
+ }
150
+ const fromOuName = resolveOrganizationalUnitName({
151
+ organizationalUnitNameById: currentOrganization.organizationalUnitNameById,
152
+ rootId: currentOrganization.rootId,
153
+ organizationalUnitId: currentAccount.parentId
154
+ });
155
+ operations.push({
156
+ kind: "removeAccount",
157
+ accountId: currentAccount.id,
158
+ accountName: currentAccount.name,
159
+ fromOuId: currentAccount.parentId,
160
+ fromOuName,
161
+ toOuId: graveyardOrganizationalUnit.id,
162
+ toOuName: graveyardOrganizationalUnit.name
163
+ });
164
+ }
165
+ for (const nextOrganizationalUnit of nextOrganization.organizationalUnits) {
166
+ const currentOrganizationalUnit = currentOrganization.organizationalUnitByName.get(
167
+ nextOrganizationalUnit.name
168
+ );
169
+ if (currentOrganizationalUnit == null) {
170
+ continue;
171
+ }
172
+ if (currentOrganizationalUnit.parentId === nextOrganizationalUnit.parentId) {
173
+ continue;
174
+ }
175
+ const fromParentOuName = resolveOrganizationalUnitName({
176
+ organizationalUnitNameById: currentOrganization.organizationalUnitNameById,
177
+ rootId: currentOrganization.rootId,
178
+ organizationalUnitId: currentOrganizationalUnit.parentId
179
+ });
180
+ const toParentOuName = resolveOrganizationalUnitName({
181
+ organizationalUnitNameById: nextOrganization.organizationalUnitNameById,
182
+ rootId: nextOrganization.rootId,
183
+ organizationalUnitId: nextOrganizationalUnit.parentId
184
+ });
185
+ unsupported.push({
186
+ kind: "reparentedOu",
187
+ category: "unsupportedMutation",
188
+ description: `OU "${nextOrganizationalUnit.name}" changed parent from "${fromParentOuName}" to "${toParentOuName}"`
189
+ });
190
+ }
191
+ const addedOrganizationalUnits = [];
192
+ const removedOrganizationalUnits = [];
193
+ for (const nextOrganizationalUnit of nextOrganization.organizationalUnits) {
194
+ if (currentOrganization.organizationalUnitByName.has(nextOrganizationalUnit.name)) {
195
+ continue;
196
+ }
197
+ addedOrganizationalUnits.push(nextOrganizationalUnit);
198
+ }
199
+ for (const currentOrganizationalUnit of currentOrganization.organizationalUnits) {
200
+ if (nextOrganization.organizationalUnitByName.has(currentOrganizationalUnit.name)) {
201
+ continue;
202
+ }
203
+ removedOrganizationalUnits.push(currentOrganizationalUnit);
204
+ }
205
+ const addedByParentId = groupOrganizationalUnitsByParentId({
206
+ organizationalUnits: addedOrganizationalUnits
207
+ });
208
+ const removedByParentId = groupOrganizationalUnitsByParentId({
209
+ organizationalUnits: removedOrganizationalUnits
210
+ });
211
+ const plannedMoveAccountDeparturesByOuId = countMoveAccountDeparturesByOuId({
212
+ operations
213
+ });
214
+ const consumedAddedOrganizationalUnitNames = /* @__PURE__ */ new Set();
215
+ const consumedRemovedOrganizationalUnitNames = /* @__PURE__ */ new Set();
216
+ const parentIds = /* @__PURE__ */ new Set([
217
+ ...addedByParentId.keys(),
218
+ ...removedByParentId.keys()
219
+ ]);
220
+ for (const parentId of parentIds) {
221
+ const parentAdded = (addedByParentId.get(parentId) ?? []).filter(
222
+ (organizationalUnit) => consumedAddedOrganizationalUnitNames.has(organizationalUnit.name) === false
223
+ );
224
+ const parentRemoved = (removedByParentId.get(parentId) ?? []).filter(
225
+ (organizationalUnit) => consumedRemovedOrganizationalUnitNames.has(organizationalUnit.name) === false
226
+ );
227
+ if (parentAdded.length === 1 && parentRemoved.length === 1) {
228
+ const added = parentAdded[0];
229
+ const removed = parentRemoved[0];
230
+ consumedAddedOrganizationalUnitNames.add(added.name);
231
+ consumedRemovedOrganizationalUnitNames.add(removed.name);
232
+ const parentOuName = resolveOrganizationalUnitName({
233
+ organizationalUnitNameById: nextOrganization.organizationalUnitNameById,
234
+ rootId: nextOrganization.rootId,
235
+ organizationalUnitId: parentId
236
+ });
237
+ operations.push({
238
+ kind: "renameOu",
239
+ ouId: removed.id,
240
+ fromOuName: removed.name,
241
+ toOuName: added.name,
242
+ parentOuId: parentId,
243
+ parentOuName
244
+ });
245
+ continue;
246
+ }
247
+ if (parentAdded.length > 0 && parentRemoved.length > 0) {
248
+ const parentOuName = resolveOrganizationalUnitName({
249
+ organizationalUnitNameById: nextOrganization.organizationalUnitNameById,
250
+ rootId: nextOrganization.rootId,
251
+ organizationalUnitId: parentId
252
+ });
253
+ unsupported.push({
254
+ kind: "ambiguousOuRename",
255
+ category: "unsupportedMutation",
256
+ description: `ambiguous OU rename under "${parentOuName}" (added: ${parentAdded.map((organizationalUnit) => organizationalUnit.name).sort((left, right) => left.localeCompare(right)).join(", ")}; removed: ${parentRemoved.map((organizationalUnit) => organizationalUnit.name).sort((left, right) => left.localeCompare(right)).join(", ")})`
257
+ });
258
+ for (const organizationalUnit of parentAdded) {
259
+ consumedAddedOrganizationalUnitNames.add(organizationalUnit.name);
260
+ }
261
+ for (const organizationalUnit of parentRemoved) {
262
+ consumedRemovedOrganizationalUnitNames.add(organizationalUnit.name);
263
+ }
264
+ continue;
265
+ }
266
+ }
267
+ for (const addedOrganizationalUnit of addedOrganizationalUnits) {
268
+ if (consumedAddedOrganizationalUnitNames.has(addedOrganizationalUnit.name)) {
269
+ continue;
270
+ }
271
+ const parentOuName = resolveOrganizationalUnitName({
272
+ organizationalUnitNameById: nextOrganization.organizationalUnitNameById,
273
+ rootId: nextOrganization.rootId,
274
+ organizationalUnitId: addedOrganizationalUnit.parentId
275
+ });
276
+ if (isResolvableOrganizationalUnitId({
277
+ rootId: nextOrganization.rootId,
278
+ organizationalUnitNameById: nextOrganization.organizationalUnitNameById,
279
+ organizationalUnitId: addedOrganizationalUnit.parentId
280
+ }) === false) {
281
+ unsupported.push({
282
+ kind: "newOuWithUnknownParent",
283
+ category: "unsupportedMutation",
284
+ description: `new OU "${addedOrganizationalUnit.name}" has unresolved parent "${parentOuName}" (${addedOrganizationalUnit.parentId})`
285
+ });
286
+ continue;
287
+ }
288
+ operations.push({
289
+ kind: "createOu",
290
+ ouName: addedOrganizationalUnit.name,
291
+ parentOuId: addedOrganizationalUnit.parentId,
292
+ parentOuName
293
+ });
294
+ }
295
+ const pendingRemovedOrganizationalUnits = removedOrganizationalUnits.filter(
296
+ (organizationalUnit) => consumedRemovedOrganizationalUnitNames.has(organizationalUnit.name) === false
297
+ );
298
+ const pendingRemovedOrganizationalUnitIds = new Set(
299
+ pendingRemovedOrganizationalUnits.map(
300
+ (organizationalUnit) => organizationalUnit.id
301
+ )
302
+ );
303
+ const deleteEligibilityByOuId = createDeleteEligibilityByOuId({
304
+ removedOrganizationalUnits: pendingRemovedOrganizationalUnits,
305
+ removedOrganizationalUnitIds: pendingRemovedOrganizationalUnitIds,
306
+ currentOrganizationalUnitsByParentId: currentOrganization.organizationalUnitsByParentId,
307
+ currentAccountsByParentId: currentOrganization.accountsByParentId,
308
+ plannedMoveAccountDeparturesByOuId
309
+ });
310
+ for (const removedOrganizationalUnit of pendingRemovedOrganizationalUnits) {
311
+ if (deleteEligibilityByOuId.get(removedOrganizationalUnit.id) === true) {
312
+ const parentOuName = resolveOrganizationalUnitName({
313
+ organizationalUnitNameById: currentOrganization.organizationalUnitNameById,
314
+ rootId: currentOrganization.rootId,
315
+ organizationalUnitId: removedOrganizationalUnit.parentId
316
+ });
317
+ operations.push({
318
+ kind: "deleteOu",
319
+ ouId: removedOrganizationalUnit.id,
320
+ ouName: removedOrganizationalUnit.name,
321
+ parentOuId: removedOrganizationalUnit.parentId,
322
+ parentOuName
323
+ });
324
+ continue;
325
+ }
326
+ unsupported.push({
327
+ kind: "removedOu",
328
+ category: "destructive",
329
+ description: `removed OU "${removedOrganizationalUnit.name}"`
330
+ });
331
+ }
332
+ const currentIdcView = normalizeIdentityCenterState({
333
+ state: props.current
334
+ });
335
+ const nextIdcView = normalizeIdentityCenterState({
336
+ state: props.next
337
+ });
338
+ for (const nextUser of props.next.identityCenter.users) {
339
+ if (currentIdcView.usersByUserName.has(nextUser.userName)) {
340
+ continue;
341
+ }
342
+ operations.push({
343
+ kind: "createIdcUser",
344
+ userName: nextUser.userName,
345
+ displayName: nextUser.displayName,
346
+ email: nextUser.email
347
+ });
348
+ }
349
+ for (const nextUser of props.next.identityCenter.users) {
350
+ const currentUser = currentIdcView.usersByUserName.get(nextUser.userName);
351
+ if (currentUser == null) {
352
+ continue;
353
+ }
354
+ const emailWouldChange = currentUser.email !== nextUser.email && nextUser.email.length > 0;
355
+ if (currentUser.displayName === nextUser.displayName && emailWouldChange === false) {
356
+ continue;
357
+ }
358
+ operations.push({
359
+ kind: "updateIdcUser",
360
+ userName: nextUser.userName,
361
+ displayName: nextUser.displayName,
362
+ email: nextUser.email
363
+ });
364
+ }
365
+ for (const nextGroup of props.next.identityCenter.groups) {
366
+ if (currentIdcView.groupsByDisplayName.has(nextGroup.displayName)) {
367
+ continue;
368
+ }
369
+ operations.push({
370
+ kind: "createIdcGroup",
371
+ groupDisplayName: nextGroup.displayName,
372
+ description: nextGroup.description ?? ""
373
+ });
374
+ }
375
+ for (const nextGroup of props.next.identityCenter.groups) {
376
+ const currentGroup = currentIdcView.groupsByDisplayName.get(
377
+ nextGroup.displayName
378
+ );
379
+ if (currentGroup == null) {
380
+ continue;
381
+ }
382
+ if ((currentGroup.description ?? "") === (nextGroup.description ?? "")) {
383
+ continue;
384
+ }
385
+ operations.push({
386
+ kind: "updateIdcGroupDescription",
387
+ groupDisplayName: nextGroup.displayName,
388
+ description: nextGroup.description ?? ""
389
+ });
390
+ }
391
+ const removedUserNames = new Set(
392
+ props.current.identityCenter.users.filter(
393
+ (user) => nextIdcView.usersByUserName.has(user.userName) === false
394
+ ).map((user) => user.userName)
395
+ );
396
+ const removedGroupDisplayNames = new Set(
397
+ props.current.identityCenter.groups.filter(
398
+ (group) => nextIdcView.groupsByDisplayName.has(group.displayName) === false
399
+ ).map((group) => group.displayName)
400
+ );
401
+ const removedPermissionSetNames = new Set(
402
+ props.current.identityCenter.permissionSets.filter(
403
+ (permissionSet) => nextIdcView.permissionSetsByName.has(permissionSet.name) === false
404
+ ).map((permissionSet) => permissionSet.name)
405
+ );
406
+ const permissionSetNamesWithDesiredAssignments = new Set(
407
+ [...nextIdcView.assignmentsByKey.values()].map(
408
+ (assignment) => assignment.permissionSetName
409
+ )
410
+ );
411
+ for (const nextMembership of nextIdcView.membershipsByKey.values()) {
412
+ const membershipKey = createNormalizedIdcMembershipKey({
413
+ membership: nextMembership
414
+ });
415
+ if (currentIdcView.membershipsByKey.has(membershipKey)) {
416
+ continue;
417
+ }
418
+ operations.push({
419
+ kind: "addIdcGroupMembership",
420
+ groupDisplayName: nextMembership.groupDisplayName,
421
+ userName: nextMembership.userName
422
+ });
423
+ }
424
+ for (const currentMembership of currentIdcView.membershipsByKey.values()) {
425
+ const membershipKey = createNormalizedIdcMembershipKey({
426
+ membership: currentMembership
427
+ });
428
+ if (nextIdcView.membershipsByKey.has(membershipKey) && removedUserNames.has(currentMembership.userName) === false && removedGroupDisplayNames.has(currentMembership.groupDisplayName) === false) {
429
+ continue;
430
+ }
431
+ operations.push({
432
+ kind: "removeIdcGroupMembership",
433
+ groupDisplayName: currentMembership.groupDisplayName,
434
+ userName: currentMembership.userName
435
+ });
436
+ }
437
+ for (const nextPermissionSet of props.next.identityCenter.permissionSets) {
438
+ const currentPermissionSet = currentIdcView.permissionSetsByName.get(nextPermissionSet.name);
439
+ if (currentPermissionSet == null) {
440
+ operations.push({
441
+ kind: "createIdcPermissionSet",
442
+ permissionSetName: nextPermissionSet.name,
443
+ description: nextPermissionSet.description
444
+ });
445
+ }
446
+ const permissionSetMutationStartIndex = operations.length;
447
+ if (currentPermissionSet != null) {
448
+ if (currentPermissionSet.description !== nextPermissionSet.description) {
449
+ operations.push({
450
+ kind: "updateIdcPermissionSetDescription",
451
+ permissionSetName: nextPermissionSet.name,
452
+ description: nextPermissionSet.description
453
+ });
454
+ }
455
+ }
456
+ const currentInlinePolicy = normalizeInlinePolicyString(
457
+ currentPermissionSet?.inlinePolicy ?? null
458
+ );
459
+ const nextInlinePolicy = normalizeInlinePolicyString(
460
+ nextPermissionSet.inlinePolicy
461
+ );
462
+ if (nextInlinePolicy != null && nextInlinePolicy !== currentInlinePolicy) {
463
+ operations.push({
464
+ kind: "putIdcPermissionSetInlinePolicy",
465
+ permissionSetName: nextPermissionSet.name,
466
+ inlinePolicy: nextInlinePolicy
467
+ });
468
+ }
469
+ if (nextInlinePolicy == null && currentInlinePolicy != null) {
470
+ operations.push({
471
+ kind: "deleteIdcPermissionSetInlinePolicy",
472
+ permissionSetName: nextPermissionSet.name
473
+ });
474
+ }
475
+ const currentAwsManagedPolicies = new Set(
476
+ currentPermissionSet?.awsManagedPolicies ?? []
477
+ );
478
+ const nextAwsManagedPolicies = new Set(nextPermissionSet.awsManagedPolicies);
479
+ for (const managedPolicyArn of nextAwsManagedPolicies) {
480
+ if (currentAwsManagedPolicies.has(managedPolicyArn)) {
481
+ continue;
482
+ }
483
+ operations.push({
484
+ kind: "attachIdcManagedPolicyToPermissionSet",
485
+ permissionSetName: nextPermissionSet.name,
486
+ managedPolicyArn
487
+ });
488
+ }
489
+ for (const managedPolicyArn of currentAwsManagedPolicies) {
490
+ if (nextAwsManagedPolicies.has(managedPolicyArn)) {
491
+ continue;
492
+ }
493
+ operations.push({
494
+ kind: "detachIdcManagedPolicyFromPermissionSet",
495
+ permissionSetName: nextPermissionSet.name,
496
+ managedPolicyArn
497
+ });
498
+ }
499
+ const currentCustomerManagedPolicies = new Map(
500
+ (currentPermissionSet?.customerManagedPolicies ?? []).map((policy) => [
501
+ createCustomerManagedPolicyReferenceKey(policy),
502
+ policy
503
+ ])
504
+ );
505
+ const nextCustomerManagedPolicies = new Map(
506
+ nextPermissionSet.customerManagedPolicies.map((policy) => [
507
+ createCustomerManagedPolicyReferenceKey(policy),
508
+ policy
509
+ ])
510
+ );
511
+ for (const [policyKey, customerManagedPolicy] of nextCustomerManagedPolicies) {
512
+ if (currentCustomerManagedPolicies.has(policyKey)) {
513
+ continue;
514
+ }
515
+ operations.push({
516
+ kind: "attachIdcCustomerManagedPolicyReferenceToPermissionSet",
517
+ permissionSetName: nextPermissionSet.name,
518
+ customerManagedPolicyName: customerManagedPolicy.name,
519
+ customerManagedPolicyPath: customerManagedPolicy.path
520
+ });
521
+ }
522
+ for (const [policyKey, customerManagedPolicy] of currentCustomerManagedPolicies) {
523
+ if (nextCustomerManagedPolicies.has(policyKey)) {
524
+ continue;
525
+ }
526
+ operations.push({
527
+ kind: "detachIdcCustomerManagedPolicyReferenceFromPermissionSet",
528
+ permissionSetName: nextPermissionSet.name,
529
+ customerManagedPolicyName: customerManagedPolicy.name,
530
+ customerManagedPolicyPath: customerManagedPolicy.path
531
+ });
532
+ }
533
+ if (currentPermissionSet != null && operations.length > permissionSetMutationStartIndex && permissionSetNamesWithDesiredAssignments.has(nextPermissionSet.name)) {
534
+ operations.push({
535
+ kind: "provisionIdcPermissionSet",
536
+ permissionSetName: nextPermissionSet.name,
537
+ targetScope: "ALL_PROVISIONED_ACCOUNTS"
538
+ });
539
+ }
540
+ }
541
+ for (const nextAssignment of nextIdcView.assignmentsByKey.values()) {
542
+ const assignmentKey = createNormalizedIdcAssignmentKey({
543
+ assignment: nextAssignment
544
+ });
545
+ if (currentIdcView.assignmentsByKey.has(assignmentKey)) {
546
+ continue;
547
+ }
548
+ operations.push({
549
+ kind: "grantIdcAccountAssignment",
550
+ accountName: nextAssignment.accountName,
551
+ permissionSetName: nextAssignment.permissionSetName,
552
+ principalType: nextAssignment.principalType,
553
+ principalName: nextAssignment.principalName
554
+ });
555
+ }
556
+ for (const currentAssignment of currentIdcView.assignmentsByKey.values()) {
557
+ const assignmentKey = createNormalizedIdcAssignmentKey({
558
+ assignment: currentAssignment
559
+ });
560
+ if (nextIdcView.assignmentsByKey.has(assignmentKey) && removedPermissionSetNames.has(currentAssignment.permissionSetName) === false && (currentAssignment.principalType === "USER" ? removedUserNames.has(currentAssignment.principalName) === false : removedGroupDisplayNames.has(currentAssignment.principalName) === false)) {
561
+ continue;
562
+ }
563
+ operations.push({
564
+ kind: "revokeIdcAccountAssignment",
565
+ accountName: currentAssignment.accountName,
566
+ permissionSetName: currentAssignment.permissionSetName,
567
+ principalType: currentAssignment.principalType,
568
+ principalName: currentAssignment.principalName
569
+ });
570
+ }
571
+ for (const removedUserName of removedUserNames) {
572
+ operations.push({
573
+ kind: "deleteIdcUser",
574
+ userName: removedUserName
575
+ });
576
+ }
577
+ for (const removedGroupDisplayName of removedGroupDisplayNames) {
578
+ operations.push({
579
+ kind: "deleteIdcGroup",
580
+ groupDisplayName: removedGroupDisplayName
581
+ });
582
+ }
583
+ for (const removedPermissionSetName of removedPermissionSetNames) {
584
+ operations.push({
585
+ kind: "deleteIdcPermissionSet",
586
+ permissionSetName: removedPermissionSetName
587
+ });
588
+ }
589
+ operations.sort((left, right) => {
590
+ const priorityComparison = getOperationExecutionPriority(left) - getOperationExecutionPriority(right);
591
+ if (priorityComparison !== 0) {
592
+ return priorityComparison;
593
+ }
594
+ if (left.kind === "deleteOu" && right.kind === "deleteOu") {
595
+ const depthComparison = (currentOrganization.organizationalUnitDepthById.get(right.ouId) ?? 0) - (currentOrganization.organizationalUnitDepthById.get(left.ouId) ?? 0);
596
+ if (depthComparison !== 0) {
597
+ return depthComparison;
598
+ }
599
+ }
600
+ return getOperationSortKey(left).localeCompare(getOperationSortKey(right));
601
+ });
602
+ unsupported.sort((left, right) => {
603
+ const kindComparison = left.kind.localeCompare(right.kind);
604
+ if (kindComparison !== 0) {
605
+ return kindComparison;
606
+ }
607
+ return left.description.localeCompare(right.description);
608
+ });
609
+ return v.parse(planSchema, {
610
+ operations,
611
+ unsupported
612
+ });
613
+ }
614
+ function groupOrganizationalUnitsByParentId(props) {
615
+ const grouped = /* @__PURE__ */ new Map();
616
+ for (const organizationalUnit of props.organizationalUnits) {
617
+ const existing = grouped.get(organizationalUnit.parentId) ?? [];
618
+ existing.push(organizationalUnit);
619
+ grouped.set(organizationalUnit.parentId, existing);
620
+ }
621
+ return grouped;
622
+ }
623
+ function countChildrenByParentId(props) {
624
+ const counts = /* @__PURE__ */ new Map();
625
+ for (const value of props.values) {
626
+ counts.set(value.parentId, (counts.get(value.parentId) ?? 0) + 1);
627
+ }
628
+ return counts;
629
+ }
630
+ function countMoveAccountDeparturesByOuId(props) {
631
+ const counts = /* @__PURE__ */ new Map();
632
+ for (const operation of props.operations) {
633
+ if (operation.kind !== "moveAccount") {
634
+ continue;
635
+ }
636
+ counts.set(operation.fromOuId, (counts.get(operation.fromOuId) ?? 0) + 1);
637
+ }
638
+ return counts;
639
+ }
640
+ function normalizeOrganizationState(props) {
641
+ const organizationalUnitByName = new Map(
642
+ props.state.organization.organizationalUnits.map((organizationalUnit) => [
643
+ organizationalUnit.name,
644
+ organizationalUnit
645
+ ])
646
+ );
647
+ const organizationalUnitById = new Map(
648
+ props.state.organization.organizationalUnits.map((organizationalUnit) => [
649
+ organizationalUnit.id,
650
+ organizationalUnit
651
+ ])
652
+ );
653
+ const accountByName = new Map(
654
+ props.state.organization.accounts.map((account) => [account.name, account])
655
+ );
656
+ const accountById = /* @__PURE__ */ new Map();
657
+ for (const account of props.state.organization.accounts) {
658
+ if (account.id !== pendingCreationId) {
659
+ accountById.set(account.id, account);
660
+ }
661
+ }
662
+ const organizationalUnitNameById = new Map(
663
+ props.state.organization.organizationalUnits.map((organizationalUnit) => [
664
+ organizationalUnit.id,
665
+ organizationalUnit.name
666
+ ])
667
+ );
668
+ const organizationalUnitsByParentId = groupOrganizationalUnitsByParentId({
669
+ organizationalUnits: props.state.organization.organizationalUnits
670
+ });
671
+ const accountsByParentId = countChildrenByParentId({
672
+ values: props.state.organization.accounts
673
+ });
674
+ return {
675
+ rootId: props.state.organization.rootId,
676
+ organizationalUnits: props.state.organization.organizationalUnits,
677
+ accounts: props.state.organization.accounts,
678
+ organizationalUnitByName,
679
+ accountByName,
680
+ accountById,
681
+ organizationalUnitNameById,
682
+ organizationalUnitsByParentId,
683
+ accountsByParentId,
684
+ organizationalUnitDepthById: props.includeDepthById ? createOrganizationalUnitDepthById({
685
+ rootId: props.state.organization.rootId,
686
+ organizationalUnitById
687
+ }) : /* @__PURE__ */ new Map()
688
+ };
689
+ }
690
+ function createDeleteEligibilityByOuId(props) {
691
+ const eligibilityByOuId = /* @__PURE__ */ new Map();
692
+ function canDeleteOrganizationalUnit(organizationalUnitId) {
693
+ const cachedEligibility = eligibilityByOuId.get(organizationalUnitId);
694
+ if (cachedEligibility != null) {
695
+ return cachedEligibility;
696
+ }
697
+ const currentChildren = props.currentOrganizationalUnitsByParentId.get(organizationalUnitId) ?? [];
698
+ for (const childOrganizationalUnit of currentChildren) {
699
+ if (props.removedOrganizationalUnitIds.has(childOrganizationalUnit.id) === false) {
700
+ eligibilityByOuId.set(organizationalUnitId, false);
701
+ return false;
702
+ }
703
+ if (canDeleteOrganizationalUnit(childOrganizationalUnit.id) === false) {
704
+ eligibilityByOuId.set(organizationalUnitId, false);
705
+ return false;
706
+ }
707
+ }
708
+ const projectedRemainingAccounts = (props.currentAccountsByParentId.get(organizationalUnitId) ?? 0) - (props.plannedMoveAccountDeparturesByOuId.get(organizationalUnitId) ?? 0);
709
+ const canDelete = projectedRemainingAccounts === 0;
710
+ eligibilityByOuId.set(organizationalUnitId, canDelete);
711
+ return canDelete;
712
+ }
713
+ for (const removedOrganizationalUnit of props.removedOrganizationalUnits) {
714
+ canDeleteOrganizationalUnit(removedOrganizationalUnit.id);
715
+ }
716
+ return eligibilityByOuId;
717
+ }
718
+ function createOrganizationalUnitDepthById(props) {
719
+ const depthById = /* @__PURE__ */ new Map();
720
+ function getDepth(organizationalUnitId) {
721
+ const cachedDepth = depthById.get(organizationalUnitId);
722
+ if (cachedDepth != null) {
723
+ return cachedDepth;
724
+ }
725
+ const organizationalUnit = props.organizationalUnitById.get(organizationalUnitId);
726
+ if (organizationalUnit == null) {
727
+ return 0;
728
+ }
729
+ if (organizationalUnit.parentId === props.rootId) {
730
+ depthById.set(organizationalUnitId, 1);
731
+ return 1;
732
+ }
733
+ const depth = getDepth(organizationalUnit.parentId) + 1;
734
+ depthById.set(organizationalUnitId, depth);
735
+ return depth;
736
+ }
737
+ for (const organizationalUnitId of props.organizationalUnitById.keys()) {
738
+ getDepth(organizationalUnitId);
739
+ }
740
+ return depthById;
741
+ }
742
+ function getOperationExecutionPriority(operation) {
743
+ return operationExecutionPriority[operation.kind];
744
+ }
745
+ function getOperationSortKey(operation) {
746
+ if (operation.kind === "moveAccount") {
747
+ return `${operation.kind}|${operation.accountName}|${operation.accountId}`;
748
+ }
749
+ if (operation.kind === "createOu") {
750
+ return `${operation.kind}|${operation.ouName}|${operation.parentOuName}`;
751
+ }
752
+ if (operation.kind === "renameOu") {
753
+ return `${operation.kind}|${operation.fromOuName}|${operation.toOuName}`;
754
+ }
755
+ if (operation.kind === "createAccount") {
756
+ return `${operation.kind}|${operation.accountName}|${operation.targetOuName}`;
757
+ }
758
+ if (operation.kind === "updateAccountTags") {
759
+ return `${operation.kind}|${operation.accountName}|${operation.accountId}`;
760
+ }
761
+ if (operation.kind === "updateAccountName") {
762
+ return `${operation.kind}|${operation.accountId}|${operation.toAccountName}`;
763
+ }
764
+ if (operation.kind === "removeAccount") {
765
+ return `${operation.kind}|${operation.accountName}|${operation.accountId}`;
766
+ }
767
+ if (operation.kind === "deleteOu") {
768
+ return `${operation.kind}|${operation.ouName}|${operation.parentOuName}`;
769
+ }
770
+ if (operation.kind === "createIdcUser") {
771
+ return `${operation.kind}|${operation.userName}`;
772
+ }
773
+ if (operation.kind === "updateIdcUser") {
774
+ return `${operation.kind}|${operation.userName}`;
775
+ }
776
+ if (operation.kind === "deleteIdcUser") {
777
+ return `${operation.kind}|${operation.userName}`;
778
+ }
779
+ if (operation.kind === "createIdcGroup") {
780
+ return `${operation.kind}|${operation.groupDisplayName}`;
781
+ }
782
+ if (operation.kind === "updateIdcGroupDescription") {
783
+ return `${operation.kind}|${operation.groupDisplayName}`;
784
+ }
785
+ if (operation.kind === "deleteIdcGroup") {
786
+ return `${operation.kind}|${operation.groupDisplayName}`;
787
+ }
788
+ if (operation.kind === "addIdcGroupMembership" || operation.kind === "removeIdcGroupMembership") {
789
+ return `${operation.kind}|${operation.groupDisplayName}|${operation.userName}`;
790
+ }
791
+ if (operation.kind === "createIdcPermissionSet") {
792
+ return `${operation.kind}|${operation.permissionSetName}`;
793
+ }
794
+ if (operation.kind === "deleteIdcPermissionSet") {
795
+ return `${operation.kind}|${operation.permissionSetName}`;
796
+ }
797
+ if (operation.kind === "putIdcPermissionSetInlinePolicy" || operation.kind === "deleteIdcPermissionSetInlinePolicy" || operation.kind === "updateIdcPermissionSetDescription" || operation.kind === "provisionIdcPermissionSet") {
798
+ return `${operation.kind}|${operation.permissionSetName}`;
799
+ }
800
+ if (operation.kind === "attachIdcManagedPolicyToPermissionSet" || operation.kind === "detachIdcManagedPolicyFromPermissionSet") {
801
+ return [
802
+ operation.kind,
803
+ operation.permissionSetName,
804
+ operation.managedPolicyArn
805
+ ].join("|");
806
+ }
807
+ if (operation.kind === "attachIdcCustomerManagedPolicyReferenceToPermissionSet" || operation.kind === "detachIdcCustomerManagedPolicyReferenceFromPermissionSet") {
808
+ return [
809
+ operation.kind,
810
+ operation.permissionSetName,
811
+ operation.customerManagedPolicyPath,
812
+ operation.customerManagedPolicyName
813
+ ].join("|");
814
+ }
815
+ if (operation.kind === "grantIdcAccountAssignment" || operation.kind === "revokeIdcAccountAssignment") {
816
+ return [
817
+ operation.kind,
818
+ operation.accountName,
819
+ operation.permissionSetName,
820
+ operation.principalType,
821
+ operation.principalName
822
+ ].join("|");
823
+ }
824
+ return "zzzz";
825
+ }
826
+ function normalizeAccountTags(tags) {
827
+ if (tags == null || tags.length === 0) {
828
+ return [];
829
+ }
830
+ return [...tags].sort(
831
+ (left, right) => [left.key, left.value].join("|").localeCompare([right.key, right.value].join("|"))
832
+ );
833
+ }
834
+ function normalizeIdentityCenterState(props) {
835
+ const usersByUserName = new Map(
836
+ props.state.identityCenter.users.map((user) => [user.userName, user])
837
+ );
838
+ const groupsByDisplayName = new Map(
839
+ props.state.identityCenter.groups.map((group) => [
840
+ group.displayName,
841
+ group
842
+ ])
843
+ );
844
+ const groupDisplayNameById = new Map(
845
+ props.state.identityCenter.groups.map((group) => [
846
+ group.groupId,
847
+ group.displayName
848
+ ])
849
+ );
850
+ const userNameById = new Map(
851
+ props.state.identityCenter.users.map((user) => [
852
+ user.userId,
853
+ user.userName
854
+ ])
855
+ );
856
+ const membershipsByKey = /* @__PURE__ */ new Map();
857
+ for (const groupMembership of props.state.identityCenter.groupMemberships) {
858
+ const groupDisplayName = groupDisplayNameById.get(groupMembership.groupId);
859
+ if (groupDisplayName == null) {
860
+ throw new Error(
861
+ `Could not resolve group display name for IdC membership groupId "${groupMembership.groupId}".`
862
+ );
863
+ }
864
+ const userName = userNameById.get(groupMembership.userId);
865
+ if (userName == null) {
866
+ throw new Error(
867
+ `Could not resolve user name for IdC membership userId "${groupMembership.userId}".`
868
+ );
869
+ }
870
+ const normalizedMembership = {
871
+ groupDisplayName,
872
+ userName
873
+ };
874
+ membershipsByKey.set(
875
+ createNormalizedIdcMembershipKey({
876
+ membership: normalizedMembership
877
+ }),
878
+ normalizedMembership
879
+ );
880
+ }
881
+ const permissionSetsByName = new Map(
882
+ props.state.identityCenter.permissionSets.map((permissionSet) => [
883
+ permissionSet.name,
884
+ permissionSet
885
+ ])
886
+ );
887
+ const accountNameById = new Map(
888
+ props.state.organization.accounts.map((account) => [
889
+ account.id,
890
+ account.name
891
+ ])
892
+ );
893
+ const permissionSetNameByArn = new Map(
894
+ props.state.identityCenter.permissionSets.map((permissionSet) => [
895
+ permissionSet.permissionSetArn,
896
+ permissionSet.name
897
+ ])
898
+ );
899
+ const assignmentsByKey = /* @__PURE__ */ new Map();
900
+ for (const accountAssignment of props.state.identityCenter.accountAssignments) {
901
+ const accountName = accountNameById.get(accountAssignment.accountId);
902
+ if (accountName == null) {
903
+ throw new Error(
904
+ `Could not resolve account name for IdC assignment accountId "${accountAssignment.accountId}".`
905
+ );
906
+ }
907
+ const permissionSetName = permissionSetNameByArn.get(
908
+ accountAssignment.permissionSetArn
909
+ );
910
+ if (permissionSetName == null) {
911
+ throw new Error(
912
+ `Could not resolve permission set name for IdC assignment permissionSetArn "${accountAssignment.permissionSetArn}".`
913
+ );
914
+ }
915
+ const principalName = resolveAssignmentPrincipalName({
916
+ principalId: accountAssignment.principalId,
917
+ principalType: accountAssignment.principalType,
918
+ groupDisplayNameById,
919
+ userNameById
920
+ });
921
+ const normalizedAssignment = {
922
+ accountName,
923
+ permissionSetName,
924
+ principalType: accountAssignment.principalType,
925
+ principalName
926
+ };
927
+ assignmentsByKey.set(
928
+ createNormalizedIdcAssignmentKey({
929
+ assignment: normalizedAssignment
930
+ }),
931
+ normalizedAssignment
932
+ );
933
+ }
934
+ return {
935
+ usersByUserName,
936
+ groupsByDisplayName,
937
+ membershipsByKey,
938
+ permissionSetsByName,
939
+ assignmentsByKey
940
+ };
941
+ }
942
+ function createNormalizedIdcMembershipKey(props) {
943
+ return [props.membership.groupDisplayName, props.membership.userName].join("|");
944
+ }
945
+ function resolveAssignmentPrincipalName(props) {
946
+ if (props.principalType === "GROUP") {
947
+ const groupDisplayName = props.groupDisplayNameById.get(props.principalId);
948
+ if (groupDisplayName == null) {
949
+ throw new Error(
950
+ `Could not resolve group display name for IdC assignment principalId "${props.principalId}".`
951
+ );
952
+ }
953
+ return groupDisplayName;
954
+ }
955
+ const userName = props.userNameById.get(props.principalId);
956
+ if (userName == null) {
957
+ throw new Error(
958
+ `Could not resolve user name for IdC assignment principalId "${props.principalId}".`
959
+ );
960
+ }
961
+ return userName;
962
+ }
963
+ function createNormalizedIdcAssignmentKey(props) {
964
+ return [
965
+ props.assignment.accountName,
966
+ props.assignment.permissionSetName,
967
+ props.assignment.principalType,
968
+ props.assignment.principalName
969
+ ].join("|");
970
+ }
971
+ function createCustomerManagedPolicyReferenceKey(props) {
972
+ return [props.path, props.name].join("|");
973
+ }
974
+ function normalizeInlinePolicyString(value) {
975
+ if (value == null) {
976
+ return null;
977
+ }
978
+ try {
979
+ return JSON.stringify(sortJsonValue(JSON.parse(value)));
980
+ } catch {
981
+ return value;
982
+ }
983
+ }
984
+ function sortJsonValue(value) {
985
+ if (Array.isArray(value)) {
986
+ return value.map((entry) => sortJsonValue(entry));
987
+ }
988
+ if (value != null && typeof value === "object") {
989
+ return Object.fromEntries(
990
+ Object.entries(value).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([key, nestedValue]) => [key, sortJsonValue(nestedValue)])
991
+ );
992
+ }
993
+ return value;
994
+ }
995
+ function resolveOrganizationalUnitName(props) {
996
+ if (props.organizationalUnitId === props.rootId) {
997
+ return "root";
998
+ }
999
+ return props.organizationalUnitNameById.get(props.organizationalUnitId) ?? "unknown";
1000
+ }
1001
+ function isResolvableOrganizationalUnitId(props) {
1002
+ if (props.organizationalUnitId === pendingCreationId) {
1003
+ return false;
1004
+ }
1005
+ if (props.organizationalUnitId === props.rootId) {
1006
+ return true;
1007
+ }
1008
+ return props.organizationalUnitNameById.has(props.organizationalUnitId);
1009
+ }
1010
+ export {
1011
+ diffStates
1012
+ };