@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/LICENSE +21 -0
- package/README.md +189 -0
- package/dist/accountCreation.js +135 -0
- package/dist/applyLogic.js +1203 -0
- package/dist/awsClientConfig.js +26 -0
- package/dist/awsConfig.js +1365 -0
- package/dist/cli.js +201 -0
- package/dist/commands/graveyard.js +46 -0
- package/dist/commands/regenerate.js +17 -0
- package/dist/commands/remote.js +925 -0
- package/dist/diff.js +1012 -0
- package/dist/error.js +66 -0
- package/dist/helpers.js +21 -0
- package/dist/lambda/handler.js +375 -0
- package/dist/lambdaClient.js +220 -0
- package/dist/logger.js +26 -0
- package/dist/operations.js +218 -0
- package/dist/remoteStateCache.js +38 -0
- package/dist/reservedOuDeletion.js +46 -0
- package/dist/scanLogic.js +456 -0
- package/dist/state.js +618 -0
- package/dist/tags.js +14 -0
- package/dist-lambda/handler.mjs +3558 -0
- package/dist-lambda/lambda.zip +0 -0
- package/package.json +59 -0
|
@@ -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
|
+
};
|