@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.
@@ -0,0 +1,1203 @@
1
+ import {
2
+ PutAccountNameCommand
3
+ } from "@aws-sdk/client-account";
4
+ import {
5
+ CreateOrganizationalUnitCommand,
6
+ DeleteOrganizationalUnitCommand,
7
+ ListAccountsForParentCommand,
8
+ ListOrganizationalUnitsForParentCommand,
9
+ MoveAccountCommand,
10
+ TagResourceCommand,
11
+ UntagResourceCommand,
12
+ UpdateOrganizationalUnitCommand
13
+ } from "@aws-sdk/client-organizations";
14
+ import {
15
+ CreateGroupMembershipCommand,
16
+ CreateGroupCommand,
17
+ CreateUserCommand,
18
+ DeleteGroupCommand,
19
+ DeleteGroupMembershipCommand,
20
+ DeleteUserCommand,
21
+ GetGroupMembershipIdCommand,
22
+ UpdateGroupCommand,
23
+ UpdateUserCommand
24
+ } from "@aws-sdk/client-identitystore";
25
+ import {
26
+ CreateAccountAssignmentCommand,
27
+ AttachCustomerManagedPolicyReferenceToPermissionSetCommand,
28
+ AttachManagedPolicyToPermissionSetCommand,
29
+ CreatePermissionSetCommand,
30
+ DeleteAccountAssignmentCommand,
31
+ DeleteInlinePolicyFromPermissionSetCommand,
32
+ DeletePermissionSetCommand,
33
+ DescribeAccountAssignmentCreationStatusCommand,
34
+ DescribeAccountAssignmentDeletionStatusCommand,
35
+ DescribePermissionSetProvisioningStatusCommand,
36
+ DetachCustomerManagedPolicyReferenceFromPermissionSetCommand,
37
+ DetachManagedPolicyFromPermissionSetCommand,
38
+ ProvisionPermissionSetCommand,
39
+ PutInlinePolicyToPermissionSetCommand,
40
+ UpdatePermissionSetCommand
41
+ } from "@aws-sdk/client-sso-admin";
42
+ import { createAccountAndMoveToOu } from "./accountCreation.js";
43
+ import { assertUnreachable, delay } from "./helpers.js";
44
+ import {
45
+ addGroupMembershipToWorkingState,
46
+ addAccountAssignmentToWorkingState,
47
+ createGroupMembershipKey,
48
+ moveAccountInWorkingState,
49
+ removeAccountAssignmentFromWorkingState,
50
+ removeGroupMembershipFromWorkingState,
51
+ removeIdcGroupFromWorkingState,
52
+ removeIdcPermissionSetFromWorkingState,
53
+ removeIdcUserFromWorkingState,
54
+ removeOrganizationalUnitFromWorkingState,
55
+ renameOrganizationalUnitInWorkingState,
56
+ upsertIdcGroupInWorkingState,
57
+ upsertIdcPermissionSetInWorkingState,
58
+ upsertIdcUserInWorkingState,
59
+ upsertAccountInWorkingState,
60
+ upsertOrganizationalUnitInWorkingState
61
+ } from "./state.js";
62
+ async function executeOperation(props) {
63
+ const operation = props.operation;
64
+ if (operation.kind === "moveAccount") {
65
+ props.logger.log(
66
+ `Moving "${operation.accountName}" (${operation.accountId}): ${operation.fromOuName} -> ${operation.toOuName}`
67
+ );
68
+ await props.organizationsClient.send(
69
+ new MoveAccountCommand({
70
+ AccountId: operation.accountId,
71
+ SourceParentId: operation.fromOuId,
72
+ DestinationParentId: operation.toOuId
73
+ })
74
+ );
75
+ props.logger.log(`Done: "${operation.accountName}"`);
76
+ return moveAccountInWorkingState({
77
+ workingState: props.state,
78
+ accountId: operation.accountId,
79
+ parentId: operation.toOuId
80
+ });
81
+ }
82
+ if (operation.kind === "createOu") {
83
+ props.logger.log(
84
+ `Creating OU "${operation.ouName}" under ${operation.parentOuName}...`
85
+ );
86
+ const response = await props.organizationsClient.send(
87
+ new CreateOrganizationalUnitCommand({
88
+ ParentId: operation.parentOuId,
89
+ Name: operation.ouName
90
+ })
91
+ );
92
+ const createdOu = response.OrganizationalUnit;
93
+ if (createdOu?.Id == null || createdOu.Arn == null || createdOu.Name == null) {
94
+ throw new Error(
95
+ `CreateOrganizationalUnit for "${operation.ouName}" returned incomplete OU data.`
96
+ );
97
+ }
98
+ props.logger.log(`Done: "${createdOu.Name}"`);
99
+ return upsertOrganizationalUnitInWorkingState({
100
+ workingState: props.state,
101
+ organizationalUnit: {
102
+ id: createdOu.Id,
103
+ parentId: operation.parentOuId,
104
+ arn: createdOu.Arn,
105
+ name: createdOu.Name
106
+ }
107
+ });
108
+ }
109
+ if (operation.kind === "renameOu") {
110
+ props.logger.log(
111
+ `Renaming OU "${operation.fromOuName}" -> "${operation.toOuName}"...`
112
+ );
113
+ await props.organizationsClient.send(
114
+ new UpdateOrganizationalUnitCommand({
115
+ OrganizationalUnitId: operation.ouId,
116
+ Name: operation.toOuName
117
+ })
118
+ );
119
+ props.logger.log(`Done: "${operation.toOuName}"`);
120
+ return renameOrganizationalUnitInWorkingState({
121
+ workingState: props.state,
122
+ organizationalUnitId: operation.ouId,
123
+ name: operation.toOuName
124
+ });
125
+ }
126
+ if (operation.kind === "deleteOu") {
127
+ props.logger.log(`Deleting OU "${operation.ouName}"...`);
128
+ await assertOrganizationalUnitIsEmpty({
129
+ organizationsClient: props.organizationsClient,
130
+ organizationalUnitId: operation.ouId,
131
+ organizationalUnitName: operation.ouName
132
+ });
133
+ await props.organizationsClient.send(
134
+ new DeleteOrganizationalUnitCommand({
135
+ OrganizationalUnitId: operation.ouId
136
+ })
137
+ );
138
+ props.logger.log(`Done: "${operation.ouName}"`);
139
+ return removeOrganizationalUnitFromWorkingState({
140
+ workingState: props.state,
141
+ organizationalUnitId: operation.ouId
142
+ });
143
+ }
144
+ if (operation.kind === "createAccount") {
145
+ const result = await createAccountAndMoveToOu({
146
+ organizationsClient: props.organizationsClient,
147
+ logger: props.logger,
148
+ accountName: operation.accountName,
149
+ accountEmail: operation.accountEmail,
150
+ sourceParentId: props.context.organization.rootId,
151
+ destinationParentId: operation.targetOuId,
152
+ timeoutInMs: props.runtime.createAccount.timeoutInMs,
153
+ pollIntervalInMs: props.runtime.createAccount.pollIntervalInMs
154
+ });
155
+ return upsertAccountInWorkingState({
156
+ workingState: props.state,
157
+ account: {
158
+ id: result.account.id,
159
+ arn: result.account.arn,
160
+ name: result.account.name,
161
+ email: result.account.email,
162
+ status: result.account.status,
163
+ parentId: operation.targetOuId,
164
+ tags: []
165
+ }
166
+ });
167
+ }
168
+ if (operation.kind === "updateAccountTags") {
169
+ const account = props.state.organization.accountsById[operation.accountId];
170
+ if (account == null) {
171
+ throw new Error(
172
+ `Could not resolve account "${operation.accountName}" (${operation.accountId}) in working state.`
173
+ );
174
+ }
175
+ const currentTags = new Map(
176
+ (account.tags ?? []).map((tag) => [tag.key, tag.value])
177
+ );
178
+ const desiredTags = new Map(Object.entries(operation.tags));
179
+ const tagsToApply = [...desiredTags.entries()].filter(([key, value]) => currentTags.get(key) !== value).map(([Key, Value]) => ({ Key, Value }));
180
+ const tagKeysToRemove = [...currentTags.keys()].filter(
181
+ (key) => desiredTags.has(key) === false
182
+ );
183
+ props.logger.log(
184
+ `Updating account tags "${operation.accountName}" (${operation.accountId})...`
185
+ );
186
+ if (tagsToApply.length > 0) {
187
+ await props.organizationsClient.send(
188
+ new TagResourceCommand({
189
+ ResourceId: operation.accountId,
190
+ Tags: tagsToApply
191
+ })
192
+ );
193
+ }
194
+ if (tagKeysToRemove.length > 0) {
195
+ await props.organizationsClient.send(
196
+ new UntagResourceCommand({
197
+ ResourceId: operation.accountId,
198
+ TagKeys: tagKeysToRemove
199
+ })
200
+ );
201
+ }
202
+ props.logger.log(`Done: tags updated for "${operation.accountName}"`);
203
+ return upsertAccountInWorkingState({
204
+ workingState: props.state,
205
+ account: {
206
+ ...account,
207
+ tags: Object.entries(operation.tags).map(([key, value]) => ({ key, value }))
208
+ }
209
+ });
210
+ }
211
+ if (operation.kind === "updateAccountName") {
212
+ props.logger.log(
213
+ `Renaming account (${operation.accountId}): "${operation.fromAccountName}" -> "${operation.toAccountName}"...`
214
+ );
215
+ await props.accountClient.send(
216
+ new PutAccountNameCommand({
217
+ AccountId: operation.accountId,
218
+ AccountName: operation.toAccountName
219
+ })
220
+ );
221
+ props.logger.log(
222
+ `Done: account "${operation.toAccountName}" (${operation.accountId})`
223
+ );
224
+ const account = props.state.organization.accountsById[operation.accountId];
225
+ if (account == null) {
226
+ throw new Error(
227
+ `Could not resolve account (${operation.accountId}) in working state after rename.`
228
+ );
229
+ }
230
+ return upsertAccountInWorkingState({
231
+ workingState: props.state,
232
+ account: {
233
+ ...account,
234
+ name: operation.toAccountName
235
+ }
236
+ });
237
+ }
238
+ if (operation.kind === "removeAccount") {
239
+ props.logger.log(
240
+ `Moving removed account "${operation.accountName}" (${operation.accountId}) to ${operation.toOuName}...`
241
+ );
242
+ await props.organizationsClient.send(
243
+ new MoveAccountCommand({
244
+ AccountId: operation.accountId,
245
+ SourceParentId: operation.fromOuId,
246
+ DestinationParentId: operation.toOuId
247
+ })
248
+ );
249
+ props.logger.log(`Done: "${operation.accountName}" -> ${operation.toOuName}`);
250
+ return moveAccountInWorkingState({
251
+ workingState: props.state,
252
+ accountId: operation.accountId,
253
+ parentId: operation.toOuId
254
+ });
255
+ }
256
+ if (operation.kind === "createIdcUser") {
257
+ props.logger.log(`Creating IdC user "${operation.userName}"...`);
258
+ const response = await props.identityStoreClient.send(
259
+ new CreateUserCommand({
260
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
261
+ UserName: operation.userName,
262
+ DisplayName: operation.displayName,
263
+ Name: buildIdentityStoreUserName({
264
+ userName: operation.userName,
265
+ displayName: operation.displayName
266
+ }),
267
+ Emails: operation.email.length > 0 ? [
268
+ {
269
+ Value: operation.email,
270
+ Type: "Work",
271
+ Primary: true
272
+ }
273
+ ] : void 0
274
+ })
275
+ );
276
+ if (response.UserId == null) {
277
+ throw new Error(
278
+ `CreateUser for "${operation.userName}" returned no user id.`
279
+ );
280
+ }
281
+ props.logger.log(`Done: "${operation.userName}"`);
282
+ return upsertIdcUserInWorkingState({
283
+ workingState: props.state,
284
+ user: {
285
+ userId: response.UserId,
286
+ userName: operation.userName,
287
+ displayName: operation.displayName,
288
+ email: operation.email
289
+ }
290
+ });
291
+ }
292
+ if (operation.kind === "updateIdcUser") {
293
+ const user = resolveUserByName({
294
+ state: props.state,
295
+ userName: operation.userName
296
+ });
297
+ const operations = [];
298
+ if (user.displayName !== operation.displayName) {
299
+ operations.push({
300
+ AttributePath: "displayName",
301
+ AttributeValue: operation.displayName
302
+ });
303
+ operations.push({
304
+ AttributePath: "name",
305
+ AttributeValue: buildIdentityStoreUserName({
306
+ userName: operation.userName,
307
+ displayName: operation.displayName
308
+ })
309
+ });
310
+ }
311
+ if (user.email !== operation.email && operation.email.length > 0) {
312
+ operations.push({
313
+ AttributePath: "emails",
314
+ AttributeValue: [
315
+ {
316
+ Value: operation.email,
317
+ Type: "Work",
318
+ Primary: true
319
+ }
320
+ ]
321
+ });
322
+ }
323
+ if (operations.length === 0) {
324
+ return props.state;
325
+ }
326
+ props.logger.log(`Updating IdC user "${operation.userName}"...`);
327
+ await props.identityStoreClient.send(
328
+ new UpdateUserCommand({
329
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
330
+ UserId: user.userId,
331
+ Operations: operations
332
+ })
333
+ );
334
+ props.logger.log(`Done: "${operation.userName}"`);
335
+ return upsertIdcUserInWorkingState({
336
+ workingState: props.state,
337
+ user: {
338
+ ...user,
339
+ displayName: operation.displayName,
340
+ email: operation.email.length > 0 ? operation.email : user.email
341
+ }
342
+ });
343
+ }
344
+ if (operation.kind === "deleteIdcUser") {
345
+ const user = resolveUserByName({
346
+ state: props.state,
347
+ userName: operation.userName
348
+ });
349
+ props.logger.log(`Deleting IdC user "${operation.userName}"...`);
350
+ await props.identityStoreClient.send(
351
+ new DeleteUserCommand({
352
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
353
+ UserId: user.userId
354
+ })
355
+ );
356
+ props.logger.log(`Done: "${operation.userName}"`);
357
+ return removeIdcUserFromWorkingState({
358
+ workingState: props.state,
359
+ userName: operation.userName
360
+ });
361
+ }
362
+ if (operation.kind === "createIdcGroup") {
363
+ props.logger.log(`Creating IdC group "${operation.groupDisplayName}"...`);
364
+ const response = await props.identityStoreClient.send(
365
+ new CreateGroupCommand({
366
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
367
+ DisplayName: operation.groupDisplayName,
368
+ Description: operation.description.trim().length > 0 ? operation.description : void 0
369
+ })
370
+ );
371
+ if (response.GroupId == null) {
372
+ throw new Error(
373
+ `CreateGroup for "${operation.groupDisplayName}" returned no group id.`
374
+ );
375
+ }
376
+ props.logger.log(`Done: "${operation.groupDisplayName}"`);
377
+ return upsertIdcGroupInWorkingState({
378
+ workingState: props.state,
379
+ group: {
380
+ groupId: response.GroupId,
381
+ displayName: operation.groupDisplayName,
382
+ description: operation.description
383
+ }
384
+ });
385
+ }
386
+ if (operation.kind === "updateIdcGroupDescription") {
387
+ const group = resolveGroupByDisplayName({
388
+ state: props.state,
389
+ groupDisplayName: operation.groupDisplayName
390
+ });
391
+ props.logger.log(
392
+ `Updating IdC group description for "${operation.groupDisplayName}"...`
393
+ );
394
+ await props.identityStoreClient.send(
395
+ new UpdateGroupCommand({
396
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
397
+ GroupId: group.groupId,
398
+ Operations: [
399
+ {
400
+ AttributePath: "description",
401
+ AttributeValue: operation.description
402
+ }
403
+ ]
404
+ })
405
+ );
406
+ props.logger.log(`Done: group "${operation.groupDisplayName}"`);
407
+ return upsertIdcGroupInWorkingState({
408
+ workingState: props.state,
409
+ group: {
410
+ ...group,
411
+ description: operation.description
412
+ }
413
+ });
414
+ }
415
+ if (operation.kind === "deleteIdcGroup") {
416
+ const group = resolveGroupByDisplayName({
417
+ state: props.state,
418
+ groupDisplayName: operation.groupDisplayName
419
+ });
420
+ props.logger.log(`Deleting IdC group "${operation.groupDisplayName}"...`);
421
+ await props.identityStoreClient.send(
422
+ new DeleteGroupCommand({
423
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
424
+ GroupId: group.groupId
425
+ })
426
+ );
427
+ props.logger.log(`Done: "${operation.groupDisplayName}"`);
428
+ return removeIdcGroupFromWorkingState({
429
+ workingState: props.state,
430
+ groupDisplayName: operation.groupDisplayName
431
+ });
432
+ }
433
+ if (operation.kind === "addIdcGroupMembership") {
434
+ const resolvedMembership = resolveGroupMembershipDependencies({
435
+ state: props.state,
436
+ groupDisplayName: operation.groupDisplayName,
437
+ userName: operation.userName
438
+ });
439
+ props.logger.log(
440
+ `Adding user "${operation.userName}" to IdC group "${operation.groupDisplayName}"...`
441
+ );
442
+ const response = await props.identityStoreClient.send(
443
+ new CreateGroupMembershipCommand({
444
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
445
+ GroupId: resolvedMembership.groupId,
446
+ MemberId: {
447
+ UserId: resolvedMembership.userId
448
+ }
449
+ })
450
+ );
451
+ if (response.MembershipId == null) {
452
+ throw new Error(
453
+ `CreateGroupMembership for group "${operation.groupDisplayName}" and user "${operation.userName}" returned no membership id.`
454
+ );
455
+ }
456
+ props.logger.log(
457
+ `Done: user "${operation.userName}" -> group "${operation.groupDisplayName}"`
458
+ );
459
+ return addGroupMembershipToWorkingState({
460
+ workingState: props.state,
461
+ groupMembership: {
462
+ membershipId: response.MembershipId,
463
+ groupId: resolvedMembership.groupId,
464
+ userId: resolvedMembership.userId
465
+ }
466
+ });
467
+ }
468
+ if (operation.kind === "createIdcPermissionSet") {
469
+ props.logger.log(
470
+ `Creating IdC permission set "${operation.permissionSetName}"...`
471
+ );
472
+ const response = await props.ssoAdminClient.send(
473
+ new CreatePermissionSetCommand({
474
+ InstanceArn: props.state.identityCenter.instanceArn,
475
+ Name: operation.permissionSetName,
476
+ Description: operation.description.length > 0 ? operation.description : void 0
477
+ })
478
+ );
479
+ const permissionSetArn = response.PermissionSet?.PermissionSetArn;
480
+ if (permissionSetArn == null) {
481
+ throw new Error(
482
+ `CreatePermissionSet for "${operation.permissionSetName}" returned no permission set arn.`
483
+ );
484
+ }
485
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
486
+ return upsertIdcPermissionSetInWorkingState({
487
+ workingState: props.state,
488
+ permissionSet: {
489
+ permissionSetArn,
490
+ name: operation.permissionSetName,
491
+ description: operation.description,
492
+ inlinePolicy: null,
493
+ awsManagedPolicies: [],
494
+ customerManagedPolicies: []
495
+ }
496
+ });
497
+ }
498
+ if (operation.kind === "updateIdcPermissionSetDescription") {
499
+ const permissionSet = resolvePermissionSetByName({
500
+ state: props.state,
501
+ permissionSetName: operation.permissionSetName
502
+ });
503
+ props.logger.log(
504
+ `Updating IdC permission set description for "${operation.permissionSetName}"...`
505
+ );
506
+ await props.ssoAdminClient.send(
507
+ new UpdatePermissionSetCommand({
508
+ InstanceArn: props.state.identityCenter.instanceArn,
509
+ PermissionSetArn: permissionSet.permissionSetArn,
510
+ Description: operation.description.trim().length > 0 ? operation.description : void 0
511
+ })
512
+ );
513
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
514
+ return upsertIdcPermissionSetInWorkingState({
515
+ workingState: props.state,
516
+ permissionSet: {
517
+ ...permissionSet,
518
+ description: operation.description
519
+ }
520
+ });
521
+ }
522
+ if (operation.kind === "deleteIdcPermissionSet") {
523
+ const permissionSet = resolvePermissionSetByName({
524
+ state: props.state,
525
+ permissionSetName: operation.permissionSetName
526
+ });
527
+ props.logger.log(
528
+ `Deleting IdC permission set "${operation.permissionSetName}"...`
529
+ );
530
+ await props.ssoAdminClient.send(
531
+ new DeletePermissionSetCommand({
532
+ InstanceArn: props.state.identityCenter.instanceArn,
533
+ PermissionSetArn: permissionSet.permissionSetArn
534
+ })
535
+ );
536
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
537
+ return removeIdcPermissionSetFromWorkingState({
538
+ workingState: props.state,
539
+ permissionSetName: operation.permissionSetName
540
+ });
541
+ }
542
+ if (operation.kind === "putIdcPermissionSetInlinePolicy") {
543
+ const permissionSet = resolvePermissionSetByName({
544
+ state: props.state,
545
+ permissionSetName: operation.permissionSetName
546
+ });
547
+ props.logger.log(
548
+ `Putting inline policy on IdC permission set "${operation.permissionSetName}"...`
549
+ );
550
+ await props.ssoAdminClient.send(
551
+ new PutInlinePolicyToPermissionSetCommand({
552
+ InstanceArn: props.state.identityCenter.instanceArn,
553
+ PermissionSetArn: permissionSet.permissionSetArn,
554
+ InlinePolicy: operation.inlinePolicy
555
+ })
556
+ );
557
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
558
+ return upsertPermissionSetPolicyState({
559
+ state: props.state,
560
+ permissionSetName: operation.permissionSetName,
561
+ update: (currentPermissionSet) => ({
562
+ ...currentPermissionSet,
563
+ inlinePolicy: operation.inlinePolicy
564
+ })
565
+ });
566
+ }
567
+ if (operation.kind === "deleteIdcPermissionSetInlinePolicy") {
568
+ const permissionSet = resolvePermissionSetByName({
569
+ state: props.state,
570
+ permissionSetName: operation.permissionSetName
571
+ });
572
+ props.logger.log(
573
+ `Deleting inline policy from IdC permission set "${operation.permissionSetName}"...`
574
+ );
575
+ await props.ssoAdminClient.send(
576
+ new DeleteInlinePolicyFromPermissionSetCommand({
577
+ InstanceArn: props.state.identityCenter.instanceArn,
578
+ PermissionSetArn: permissionSet.permissionSetArn
579
+ })
580
+ );
581
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
582
+ return upsertPermissionSetPolicyState({
583
+ state: props.state,
584
+ permissionSetName: operation.permissionSetName,
585
+ update: (currentPermissionSet) => ({
586
+ ...currentPermissionSet,
587
+ inlinePolicy: null
588
+ })
589
+ });
590
+ }
591
+ if (operation.kind === "attachIdcManagedPolicyToPermissionSet") {
592
+ const permissionSet = resolvePermissionSetByName({
593
+ state: props.state,
594
+ permissionSetName: operation.permissionSetName
595
+ });
596
+ props.logger.log(
597
+ `Attaching managed policy "${operation.managedPolicyArn}" to IdC permission set "${operation.permissionSetName}"...`
598
+ );
599
+ await props.ssoAdminClient.send(
600
+ new AttachManagedPolicyToPermissionSetCommand({
601
+ InstanceArn: props.state.identityCenter.instanceArn,
602
+ PermissionSetArn: permissionSet.permissionSetArn,
603
+ ManagedPolicyArn: operation.managedPolicyArn
604
+ })
605
+ );
606
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
607
+ return upsertPermissionSetPolicyState({
608
+ state: props.state,
609
+ permissionSetName: operation.permissionSetName,
610
+ update: (currentPermissionSet) => ({
611
+ ...currentPermissionSet,
612
+ awsManagedPolicies: [
613
+ ...currentPermissionSet.awsManagedPolicies,
614
+ operation.managedPolicyArn
615
+ ]
616
+ })
617
+ });
618
+ }
619
+ if (operation.kind === "detachIdcManagedPolicyFromPermissionSet") {
620
+ const permissionSet = resolvePermissionSetByName({
621
+ state: props.state,
622
+ permissionSetName: operation.permissionSetName
623
+ });
624
+ props.logger.log(
625
+ `Detaching managed policy "${operation.managedPolicyArn}" from IdC permission set "${operation.permissionSetName}"...`
626
+ );
627
+ await props.ssoAdminClient.send(
628
+ new DetachManagedPolicyFromPermissionSetCommand({
629
+ InstanceArn: props.state.identityCenter.instanceArn,
630
+ PermissionSetArn: permissionSet.permissionSetArn,
631
+ ManagedPolicyArn: operation.managedPolicyArn
632
+ })
633
+ );
634
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
635
+ return upsertPermissionSetPolicyState({
636
+ state: props.state,
637
+ permissionSetName: operation.permissionSetName,
638
+ update: (currentPermissionSet) => ({
639
+ ...currentPermissionSet,
640
+ awsManagedPolicies: currentPermissionSet.awsManagedPolicies.filter(
641
+ (managedPolicyArn) => managedPolicyArn !== operation.managedPolicyArn
642
+ )
643
+ })
644
+ });
645
+ }
646
+ if (operation.kind === "attachIdcCustomerManagedPolicyReferenceToPermissionSet") {
647
+ const permissionSet = resolvePermissionSetByName({
648
+ state: props.state,
649
+ permissionSetName: operation.permissionSetName
650
+ });
651
+ props.logger.log(
652
+ `Attaching customer-managed policy "${operation.customerManagedPolicyPath}${operation.customerManagedPolicyName}" to IdC permission set "${operation.permissionSetName}"...`
653
+ );
654
+ await props.ssoAdminClient.send(
655
+ new AttachCustomerManagedPolicyReferenceToPermissionSetCommand({
656
+ InstanceArn: props.state.identityCenter.instanceArn,
657
+ PermissionSetArn: permissionSet.permissionSetArn,
658
+ CustomerManagedPolicyReference: {
659
+ Name: operation.customerManagedPolicyName,
660
+ Path: operation.customerManagedPolicyPath
661
+ }
662
+ })
663
+ );
664
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
665
+ return upsertPermissionSetPolicyState({
666
+ state: props.state,
667
+ permissionSetName: operation.permissionSetName,
668
+ update: (currentPermissionSet) => ({
669
+ ...currentPermissionSet,
670
+ customerManagedPolicies: [
671
+ ...currentPermissionSet.customerManagedPolicies,
672
+ {
673
+ name: operation.customerManagedPolicyName,
674
+ path: operation.customerManagedPolicyPath
675
+ }
676
+ ]
677
+ })
678
+ });
679
+ }
680
+ if (operation.kind === "detachIdcCustomerManagedPolicyReferenceFromPermissionSet") {
681
+ const permissionSet = resolvePermissionSetByName({
682
+ state: props.state,
683
+ permissionSetName: operation.permissionSetName
684
+ });
685
+ props.logger.log(
686
+ `Detaching customer-managed policy "${operation.customerManagedPolicyPath}${operation.customerManagedPolicyName}" from IdC permission set "${operation.permissionSetName}"...`
687
+ );
688
+ await props.ssoAdminClient.send(
689
+ new DetachCustomerManagedPolicyReferenceFromPermissionSetCommand({
690
+ InstanceArn: props.state.identityCenter.instanceArn,
691
+ PermissionSetArn: permissionSet.permissionSetArn,
692
+ CustomerManagedPolicyReference: {
693
+ Name: operation.customerManagedPolicyName,
694
+ Path: operation.customerManagedPolicyPath
695
+ }
696
+ })
697
+ );
698
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
699
+ return upsertPermissionSetPolicyState({
700
+ state: props.state,
701
+ permissionSetName: operation.permissionSetName,
702
+ update: (currentPermissionSet) => ({
703
+ ...currentPermissionSet,
704
+ customerManagedPolicies: currentPermissionSet.customerManagedPolicies.filter(
705
+ (customerManagedPolicy) => customerManagedPolicy.name !== operation.customerManagedPolicyName || customerManagedPolicy.path !== operation.customerManagedPolicyPath
706
+ )
707
+ })
708
+ });
709
+ }
710
+ if (operation.kind === "provisionIdcPermissionSet") {
711
+ const permissionSet = resolvePermissionSetByName({
712
+ state: props.state,
713
+ permissionSetName: operation.permissionSetName
714
+ });
715
+ props.logger.log(
716
+ `Provisioning IdC permission set "${operation.permissionSetName}" to all provisioned accounts...`
717
+ );
718
+ const response = await props.ssoAdminClient.send(
719
+ new ProvisionPermissionSetCommand({
720
+ InstanceArn: props.state.identityCenter.instanceArn,
721
+ PermissionSetArn: permissionSet.permissionSetArn,
722
+ TargetType: operation.targetScope
723
+ })
724
+ );
725
+ const requestId = response.PermissionSetProvisioningStatus?.RequestId ?? void 0;
726
+ if (requestId == null) {
727
+ throw new Error(
728
+ `ProvisionPermissionSet for "${operation.permissionSetName}" returned no request id.`
729
+ );
730
+ }
731
+ await waitForPermissionSetProvisioningSuccess({
732
+ ssoAdminClient: props.ssoAdminClient,
733
+ logger: props.logger,
734
+ instanceArn: props.state.identityCenter.instanceArn,
735
+ requestId,
736
+ timeoutInMs: props.runtime.permissionSetProvisioning.timeoutInMs,
737
+ pollIntervalInMs: props.runtime.permissionSetProvisioning.pollIntervalInMs,
738
+ operationLabel: `"${operation.permissionSetName}"`
739
+ });
740
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
741
+ return props.state;
742
+ }
743
+ if (operation.kind === "removeIdcGroupMembership") {
744
+ const resolvedMembership = resolveGroupMembershipDependencies({
745
+ state: props.state,
746
+ groupDisplayName: operation.groupDisplayName,
747
+ userName: operation.userName
748
+ });
749
+ const membershipId = await resolveGroupMembershipId({
750
+ state: props.state,
751
+ identityStoreClient: props.identityStoreClient,
752
+ groupId: resolvedMembership.groupId,
753
+ userId: resolvedMembership.userId
754
+ });
755
+ props.logger.log(
756
+ `Removing user "${operation.userName}" from IdC group "${operation.groupDisplayName}"...`
757
+ );
758
+ await props.identityStoreClient.send(
759
+ new DeleteGroupMembershipCommand({
760
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
761
+ MembershipId: membershipId
762
+ })
763
+ );
764
+ props.logger.log(
765
+ `Done: user "${operation.userName}" x group "${operation.groupDisplayName}"`
766
+ );
767
+ return removeGroupMembershipFromWorkingState({
768
+ workingState: props.state,
769
+ groupMembership: {
770
+ groupId: resolvedMembership.groupId,
771
+ userId: resolvedMembership.userId
772
+ }
773
+ });
774
+ }
775
+ if (operation.kind === "grantIdcAccountAssignment") {
776
+ const resolvedAssignment = resolveAssignmentDependencies({
777
+ state: props.state,
778
+ accountName: operation.accountName,
779
+ permissionSetName: operation.permissionSetName,
780
+ principalType: operation.principalType,
781
+ principalName: operation.principalName
782
+ });
783
+ props.logger.log(
784
+ `Granting IdC assignment "${operation.permissionSetName}" to ${formatPrincipalLabel(
785
+ {
786
+ principalType: operation.principalType,
787
+ principalName: operation.principalName
788
+ }
789
+ )} on "${operation.accountName}"...`
790
+ );
791
+ const response = await props.ssoAdminClient.send(
792
+ new CreateAccountAssignmentCommand({
793
+ InstanceArn: props.state.identityCenter.instanceArn,
794
+ TargetId: resolvedAssignment.accountId,
795
+ TargetType: "AWS_ACCOUNT",
796
+ PermissionSetArn: resolvedAssignment.permissionSetArn,
797
+ PrincipalType: resolvedAssignment.principalType,
798
+ PrincipalId: resolvedAssignment.principalId
799
+ })
800
+ );
801
+ const requestId = response.AccountAssignmentCreationStatus?.RequestId;
802
+ if (requestId == null) {
803
+ throw new Error(
804
+ `CreateAccountAssignment for "${operation.permissionSetName}" on "${operation.accountName}" returned no request id.`
805
+ );
806
+ }
807
+ await waitForAccountAssignmentCreationSuccess({
808
+ ssoAdminClient: props.ssoAdminClient,
809
+ logger: props.logger,
810
+ instanceArn: props.state.identityCenter.instanceArn,
811
+ requestId,
812
+ timeoutInMs: props.runtime.accountAssignment.timeoutInMs,
813
+ pollIntervalInMs: props.runtime.accountAssignment.pollIntervalInMs,
814
+ operationLabel: `"${operation.permissionSetName}" on "${operation.accountName}"`
815
+ });
816
+ props.logger.log(
817
+ `Done: "${operation.permissionSetName}" -> "${operation.accountName}"`
818
+ );
819
+ return addAccountAssignmentToWorkingState({
820
+ workingState: props.state,
821
+ accountAssignment: {
822
+ accountId: resolvedAssignment.accountId,
823
+ permissionSetArn: resolvedAssignment.permissionSetArn,
824
+ principalId: resolvedAssignment.principalId,
825
+ principalType: resolvedAssignment.principalType
826
+ }
827
+ });
828
+ }
829
+ if (operation.kind === "revokeIdcAccountAssignment") {
830
+ const resolvedAssignment = resolveAssignmentDependencies({
831
+ state: props.state,
832
+ accountName: operation.accountName,
833
+ permissionSetName: operation.permissionSetName,
834
+ principalType: operation.principalType,
835
+ principalName: operation.principalName
836
+ });
837
+ props.logger.log(
838
+ `Revoking IdC assignment "${operation.permissionSetName}" from ${formatPrincipalLabel(
839
+ {
840
+ principalType: operation.principalType,
841
+ principalName: operation.principalName
842
+ }
843
+ )} on "${operation.accountName}"...`
844
+ );
845
+ const response = await props.ssoAdminClient.send(
846
+ new DeleteAccountAssignmentCommand({
847
+ InstanceArn: props.state.identityCenter.instanceArn,
848
+ TargetId: resolvedAssignment.accountId,
849
+ TargetType: "AWS_ACCOUNT",
850
+ PermissionSetArn: resolvedAssignment.permissionSetArn,
851
+ PrincipalType: resolvedAssignment.principalType,
852
+ PrincipalId: resolvedAssignment.principalId
853
+ })
854
+ );
855
+ const requestId = response.AccountAssignmentDeletionStatus?.RequestId;
856
+ if (requestId == null) {
857
+ throw new Error(
858
+ `DeleteAccountAssignment for "${operation.permissionSetName}" on "${operation.accountName}" returned no request id.`
859
+ );
860
+ }
861
+ await waitForAccountAssignmentDeletionSuccess({
862
+ ssoAdminClient: props.ssoAdminClient,
863
+ logger: props.logger,
864
+ instanceArn: props.state.identityCenter.instanceArn,
865
+ requestId,
866
+ timeoutInMs: props.runtime.accountAssignment.timeoutInMs,
867
+ pollIntervalInMs: props.runtime.accountAssignment.pollIntervalInMs,
868
+ operationLabel: `"${operation.permissionSetName}" on "${operation.accountName}"`
869
+ });
870
+ props.logger.log(
871
+ `Done: "${operation.permissionSetName}" x "${operation.accountName}"`
872
+ );
873
+ return removeAccountAssignmentFromWorkingState({
874
+ workingState: props.state,
875
+ accountAssignment: {
876
+ accountId: resolvedAssignment.accountId,
877
+ permissionSetArn: resolvedAssignment.permissionSetArn,
878
+ principalId: resolvedAssignment.principalId,
879
+ principalType: resolvedAssignment.principalType
880
+ }
881
+ });
882
+ }
883
+ assertUnreachable(operation, "Unsupported operation kind in apply.");
884
+ }
885
+ function resolveAssignmentDependencies(props) {
886
+ const account = props.state.organization.accountsByName[props.accountName];
887
+ if (account == null) {
888
+ throw new Error(
889
+ `Could not resolve account "${props.accountName}" in working state.`
890
+ );
891
+ }
892
+ const permissionSet = props.state.identityCenter.permissionSetsByName[props.permissionSetName];
893
+ if (permissionSet == null) {
894
+ throw new Error(
895
+ `Could not resolve permission set "${props.permissionSetName}" in working state.`
896
+ );
897
+ }
898
+ if (props.principalType === "GROUP") {
899
+ const group = props.state.identityCenter.groupsByDisplayName[props.principalName];
900
+ if (group == null) {
901
+ throw new Error(
902
+ `Could not resolve group "${props.principalName}" in working state.`
903
+ );
904
+ }
905
+ return {
906
+ accountId: account.id,
907
+ permissionSetArn: permissionSet.permissionSetArn,
908
+ principalId: group.groupId,
909
+ principalType: props.principalType
910
+ };
911
+ }
912
+ const user = props.state.identityCenter.usersByUserName[props.principalName];
913
+ if (user == null) {
914
+ throw new Error(
915
+ `Could not resolve user "${props.principalName}" in working state.`
916
+ );
917
+ }
918
+ return {
919
+ accountId: account.id,
920
+ permissionSetArn: permissionSet.permissionSetArn,
921
+ principalId: user.userId,
922
+ principalType: props.principalType
923
+ };
924
+ }
925
+ function resolveUserByName(props) {
926
+ const user = props.state.identityCenter.usersByUserName[props.userName];
927
+ if (user == null) {
928
+ throw new Error(
929
+ `Could not resolve user "${props.userName}" in working state.`
930
+ );
931
+ }
932
+ return user;
933
+ }
934
+ function resolveGroupByDisplayName(props) {
935
+ const group = props.state.identityCenter.groupsByDisplayName[props.groupDisplayName];
936
+ if (group == null) {
937
+ throw new Error(
938
+ `Could not resolve group "${props.groupDisplayName}" in working state.`
939
+ );
940
+ }
941
+ return group;
942
+ }
943
+ function resolvePermissionSetByName(props) {
944
+ const permissionSet = props.state.identityCenter.permissionSetsByName[props.permissionSetName];
945
+ if (permissionSet == null) {
946
+ throw new Error(
947
+ `Could not resolve permission set "${props.permissionSetName}" in working state.`
948
+ );
949
+ }
950
+ return permissionSet;
951
+ }
952
+ function upsertPermissionSetPolicyState(props) {
953
+ const permissionSet = resolvePermissionSetByName({
954
+ state: props.state,
955
+ permissionSetName: props.permissionSetName
956
+ });
957
+ const nextPermissionSet = props.update(permissionSet);
958
+ return upsertIdcPermissionSetInWorkingState({
959
+ workingState: props.state,
960
+ permissionSet: {
961
+ ...nextPermissionSet,
962
+ awsManagedPolicies: [...new Set(nextPermissionSet.awsManagedPolicies)].sort(
963
+ (left, right) => left.localeCompare(right)
964
+ ),
965
+ customerManagedPolicies: [
966
+ ...nextPermissionSet.customerManagedPolicies
967
+ ].sort((left, right) => {
968
+ const pathComparison = left.path.localeCompare(right.path);
969
+ if (pathComparison !== 0) {
970
+ return pathComparison;
971
+ }
972
+ return left.name.localeCompare(right.name);
973
+ })
974
+ }
975
+ });
976
+ }
977
+ function buildIdentityStoreUserName(props) {
978
+ const normalizedDisplayName = props.displayName.trim();
979
+ const fallbackName = normalizedDisplayName.length > 0 ? normalizedDisplayName : props.userName;
980
+ const [givenName, ...familyNameParts] = fallbackName.split(/\s+/).filter((part) => part.length > 0);
981
+ const familyName = familyNameParts.join(" ");
982
+ return {
983
+ Formatted: fallbackName,
984
+ GivenName: givenName ?? fallbackName,
985
+ FamilyName: familyName.length > 0 ? familyName : fallbackName
986
+ };
987
+ }
988
+ async function assertOrganizationalUnitIsEmpty(props) {
989
+ const childOrganizationalUnit = await listFirstChildOrganizationalUnit({
990
+ organizationsClient: props.organizationsClient,
991
+ parentId: props.organizationalUnitId
992
+ });
993
+ if (childOrganizationalUnit != null) {
994
+ throw new Error(
995
+ `Refusing to delete OU "${props.organizationalUnitName}": live AWS preflight failed [child-ou-present]: ${formatLivePreflightResource({
996
+ resourceType: "child OU",
997
+ name: childOrganizationalUnit.Name,
998
+ id: childOrganizationalUnit.Id
999
+ })} is still attached.`
1000
+ );
1001
+ }
1002
+ const account = await listFirstAccountForParent({
1003
+ organizationsClient: props.organizationsClient,
1004
+ parentId: props.organizationalUnitId
1005
+ });
1006
+ if (account != null) {
1007
+ throw new Error(
1008
+ `Refusing to delete OU "${props.organizationalUnitName}": live AWS preflight failed [account-present]: ${formatLivePreflightResource({
1009
+ resourceType: "account",
1010
+ name: account.Name,
1011
+ id: account.Id
1012
+ })} is still attached.`
1013
+ );
1014
+ }
1015
+ }
1016
+ function formatLivePreflightResource(props) {
1017
+ const quotedName = props.name != null ? `"${props.name}"` : void 0;
1018
+ if (quotedName != null && props.id != null) {
1019
+ return `${props.resourceType} ${quotedName} (${props.id})`;
1020
+ }
1021
+ if (quotedName != null) {
1022
+ return `${props.resourceType} ${quotedName}`;
1023
+ }
1024
+ if (props.id != null) {
1025
+ return `${props.resourceType} (${props.id})`;
1026
+ }
1027
+ return `${props.resourceType} "unknown"`;
1028
+ }
1029
+ async function listFirstChildOrganizationalUnit(props) {
1030
+ let nextToken;
1031
+ do {
1032
+ const response = await props.organizationsClient.send(
1033
+ new ListOrganizationalUnitsForParentCommand({
1034
+ ParentId: props.parentId,
1035
+ NextToken: nextToken
1036
+ })
1037
+ );
1038
+ const organizationalUnit = response.OrganizationalUnits?.[0];
1039
+ if (organizationalUnit != null) {
1040
+ return organizationalUnit;
1041
+ }
1042
+ nextToken = response.NextToken;
1043
+ } while (nextToken != null);
1044
+ return void 0;
1045
+ }
1046
+ async function listFirstAccountForParent(props) {
1047
+ let nextToken;
1048
+ do {
1049
+ const response = await props.organizationsClient.send(
1050
+ new ListAccountsForParentCommand({
1051
+ ParentId: props.parentId,
1052
+ NextToken: nextToken
1053
+ })
1054
+ );
1055
+ const account = response.Accounts?.[0];
1056
+ if (account != null) {
1057
+ return account;
1058
+ }
1059
+ nextToken = response.NextToken;
1060
+ } while (nextToken != null);
1061
+ return void 0;
1062
+ }
1063
+ async function waitForAccountAssignmentCreationSuccess(props) {
1064
+ const startedAt = Date.now();
1065
+ let lastStatus;
1066
+ while (Date.now() - startedAt < props.timeoutInMs) {
1067
+ const response = await props.ssoAdminClient.send(
1068
+ new DescribeAccountAssignmentCreationStatusCommand({
1069
+ InstanceArn: props.instanceArn,
1070
+ AccountAssignmentCreationRequestId: props.requestId
1071
+ })
1072
+ );
1073
+ const status = response.AccountAssignmentCreationStatus;
1074
+ const state = status?.Status ?? "UNKNOWN";
1075
+ if (state !== lastStatus) {
1076
+ props.logger.log(`CreateAccountAssignment status: ${state}`);
1077
+ lastStatus = state;
1078
+ }
1079
+ if (state === "SUCCEEDED") {
1080
+ return;
1081
+ }
1082
+ if (state === "FAILED") {
1083
+ throw new Error(
1084
+ `CreateAccountAssignment failed for ${props.operationLabel}: ${status?.FailureReason ?? "unknown reason"}.`
1085
+ );
1086
+ }
1087
+ await delay(props.pollIntervalInMs);
1088
+ }
1089
+ throw new Error(
1090
+ `CreateAccountAssignment timed out after ${props.timeoutInMs}ms for ${props.operationLabel}.`
1091
+ );
1092
+ }
1093
+ async function waitForAccountAssignmentDeletionSuccess(props) {
1094
+ const startedAt = Date.now();
1095
+ let lastStatus;
1096
+ while (Date.now() - startedAt < props.timeoutInMs) {
1097
+ const response = await props.ssoAdminClient.send(
1098
+ new DescribeAccountAssignmentDeletionStatusCommand({
1099
+ InstanceArn: props.instanceArn,
1100
+ AccountAssignmentDeletionRequestId: props.requestId
1101
+ })
1102
+ );
1103
+ const status = response.AccountAssignmentDeletionStatus;
1104
+ const state = status?.Status ?? "UNKNOWN";
1105
+ if (state !== lastStatus) {
1106
+ props.logger.log(`DeleteAccountAssignment status: ${state}`);
1107
+ lastStatus = state;
1108
+ }
1109
+ if (state === "SUCCEEDED") {
1110
+ return;
1111
+ }
1112
+ if (state === "FAILED") {
1113
+ throw new Error(
1114
+ `DeleteAccountAssignment failed for ${props.operationLabel}: ${status?.FailureReason ?? "unknown reason"}.`
1115
+ );
1116
+ }
1117
+ await delay(props.pollIntervalInMs);
1118
+ }
1119
+ throw new Error(
1120
+ `DeleteAccountAssignment timed out after ${props.timeoutInMs}ms for ${props.operationLabel}.`
1121
+ );
1122
+ }
1123
+ async function waitForPermissionSetProvisioningSuccess(props) {
1124
+ const startedAt = Date.now();
1125
+ let lastStatus;
1126
+ while (Date.now() - startedAt < props.timeoutInMs) {
1127
+ const response = await props.ssoAdminClient.send(
1128
+ new DescribePermissionSetProvisioningStatusCommand({
1129
+ InstanceArn: props.instanceArn,
1130
+ ProvisionPermissionSetRequestId: props.requestId
1131
+ })
1132
+ );
1133
+ const status = response.PermissionSetProvisioningStatus;
1134
+ const state = status?.Status ?? "UNKNOWN";
1135
+ if (state !== lastStatus) {
1136
+ props.logger.log(`ProvisionPermissionSet status: ${state}`);
1137
+ lastStatus = state;
1138
+ }
1139
+ if (state === "SUCCEEDED") {
1140
+ return;
1141
+ }
1142
+ if (state === "FAILED") {
1143
+ throw new Error(
1144
+ `ProvisionPermissionSet failed for ${props.operationLabel}: ${status?.FailureReason ?? "unknown reason"}.`
1145
+ );
1146
+ }
1147
+ await delay(props.pollIntervalInMs);
1148
+ }
1149
+ throw new Error(
1150
+ `ProvisionPermissionSet timed out after ${props.timeoutInMs}ms for ${props.operationLabel}.`
1151
+ );
1152
+ }
1153
+ function formatPrincipalLabel(props) {
1154
+ if (props.principalType === "GROUP") {
1155
+ return `group "${props.principalName}"`;
1156
+ }
1157
+ return `user "${props.principalName}"`;
1158
+ }
1159
+ function resolveGroupMembershipDependencies(props) {
1160
+ const group = props.state.identityCenter.groupsByDisplayName[props.groupDisplayName];
1161
+ if (group == null) {
1162
+ throw new Error(
1163
+ `Could not resolve group "${props.groupDisplayName}" in working state.`
1164
+ );
1165
+ }
1166
+ const user = props.state.identityCenter.usersByUserName[props.userName];
1167
+ if (user == null) {
1168
+ throw new Error(
1169
+ `Could not resolve user "${props.userName}" in working state.`
1170
+ );
1171
+ }
1172
+ return {
1173
+ groupId: group.groupId,
1174
+ userId: user.userId
1175
+ };
1176
+ }
1177
+ async function resolveGroupMembershipId(props) {
1178
+ const existingMembership = props.state.identityCenter.groupMembershipsByKey[createGroupMembershipKey({
1179
+ groupId: props.groupId,
1180
+ userId: props.userId
1181
+ })];
1182
+ if (existingMembership?.membershipId != null) {
1183
+ return existingMembership.membershipId;
1184
+ }
1185
+ const response = await props.identityStoreClient.send(
1186
+ new GetGroupMembershipIdCommand({
1187
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
1188
+ GroupId: props.groupId,
1189
+ MemberId: {
1190
+ UserId: props.userId
1191
+ }
1192
+ })
1193
+ );
1194
+ if (response.MembershipId == null) {
1195
+ throw new Error(
1196
+ `GetGroupMembershipId returned no membership id for group "${props.groupId}" and user "${props.userId}".`
1197
+ );
1198
+ }
1199
+ return response.MembershipId;
1200
+ }
1201
+ export {
1202
+ executeOperation
1203
+ };