@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,456 @@
1
+ import {
2
+ ListGroupMembershipsCommand,
3
+ ListGroupsCommand,
4
+ ListUsersCommand
5
+ } from "@aws-sdk/client-identitystore";
6
+ import {
7
+ ListAccountsCommand,
8
+ ListOrganizationalUnitsForParentCommand,
9
+ ListParentsCommand,
10
+ ListRootsCommand,
11
+ ListTagsForResourceCommand
12
+ } from "@aws-sdk/client-organizations";
13
+ import {
14
+ DescribePermissionSetCommand,
15
+ GetInlinePolicyForPermissionSetCommand,
16
+ ListAccountAssignmentsCommand,
17
+ ListAccountsForProvisionedPermissionSetCommand,
18
+ ListCustomerManagedPolicyReferencesInPermissionSetCommand,
19
+ ListInstancesCommand,
20
+ ListManagedPoliciesInPermissionSetCommand,
21
+ ListPermissionSetsCommand
22
+ } from "@aws-sdk/client-sso-admin";
23
+ import {
24
+ createAccessRoleName
25
+ } from "./state.js";
26
+ async function scanOrganization(props) {
27
+ const roots = await props.organizationsClient.send(new ListRootsCommand({}));
28
+ const root = roots.Roots?.[0];
29
+ if (root?.Id == null) {
30
+ throw new Error("No organization root found.");
31
+ }
32
+ const organizationalUnits = await collectOrganizationalUnits({
33
+ organizationsClient: props.organizationsClient,
34
+ parentId: root.Id
35
+ });
36
+ const accounts = [];
37
+ let nextToken;
38
+ do {
39
+ const response = await props.organizationsClient.send(
40
+ new ListAccountsCommand({ NextToken: nextToken })
41
+ );
42
+ for (const account of response.Accounts ?? []) {
43
+ if (account.Id == null || account.Arn == null || account.Name == null || account.Email == null || account.Status == null) {
44
+ continue;
45
+ }
46
+ const parents = await props.organizationsClient.send(
47
+ new ListParentsCommand({ ChildId: account.Id })
48
+ );
49
+ const parentId = parents.Parents?.[0]?.Id ?? root.Id;
50
+ const tagsResponse = await props.organizationsClient.send(
51
+ new ListTagsForResourceCommand({
52
+ ResourceId: account.Id
53
+ })
54
+ );
55
+ accounts.push({
56
+ id: account.Id,
57
+ arn: account.Arn,
58
+ name: account.Name,
59
+ email: account.Email,
60
+ status: account.Status,
61
+ parentId,
62
+ tags: (tagsResponse.Tags ?? []).flatMap((tag) => {
63
+ if (tag.Key == null) {
64
+ return [];
65
+ }
66
+ return [
67
+ {
68
+ key: tag.Key,
69
+ value: tag.Value ?? ""
70
+ }
71
+ ];
72
+ })
73
+ });
74
+ }
75
+ nextToken = response.NextToken;
76
+ } while (nextToken != null);
77
+ return {
78
+ rootId: root.Id,
79
+ organizationalUnits,
80
+ accounts
81
+ };
82
+ }
83
+ async function collectOrganizationalUnits(props) {
84
+ const children = [];
85
+ let nextToken;
86
+ do {
87
+ const response = await props.organizationsClient.send(
88
+ new ListOrganizationalUnitsForParentCommand({
89
+ ParentId: props.parentId,
90
+ NextToken: nextToken
91
+ })
92
+ );
93
+ for (const ou of response.OrganizationalUnits ?? []) {
94
+ if (ou.Id == null || ou.Arn == null || ou.Name == null) {
95
+ continue;
96
+ }
97
+ children.push({
98
+ id: ou.Id,
99
+ parentId: props.parentId,
100
+ arn: ou.Arn,
101
+ name: ou.Name
102
+ });
103
+ const descendants = await collectOrganizationalUnits({
104
+ organizationsClient: props.organizationsClient,
105
+ parentId: ou.Id
106
+ });
107
+ children.push(...descendants);
108
+ }
109
+ nextToken = response.NextToken;
110
+ } while (nextToken != null);
111
+ return children;
112
+ }
113
+ async function scanIdentityCenter(props) {
114
+ const instancesResponse = await props.ssoAdminClient.send(
115
+ new ListInstancesCommand({})
116
+ );
117
+ const instances = instancesResponse.Instances ?? [];
118
+ if (instances.length === 0) {
119
+ throw new Error("No IAM Identity Center instance found.");
120
+ }
121
+ const instance = selectIdentityCenterInstance({
122
+ instances,
123
+ requestedInstanceArn: props.requestedInstanceArn
124
+ });
125
+ const [users, groups, permissionSets] = await Promise.all([
126
+ listIdentityStoreUsers({
127
+ identityStoreClient: props.identityStoreClient,
128
+ identityStoreId: instance.identityStoreId
129
+ }),
130
+ listIdentityStoreGroups({
131
+ identityStoreClient: props.identityStoreClient,
132
+ identityStoreId: instance.identityStoreId
133
+ }),
134
+ listPermissionSets({
135
+ ssoAdminClient: props.ssoAdminClient,
136
+ instanceArn: instance.instanceArn
137
+ })
138
+ ]);
139
+ const groupMemberships = await listGroupMemberships({
140
+ identityStoreClient: props.identityStoreClient,
141
+ identityStoreId: instance.identityStoreId,
142
+ groups
143
+ });
144
+ const accountAssignments = await listAccountAssignments({
145
+ ssoAdminClient: props.ssoAdminClient,
146
+ instanceArn: instance.instanceArn,
147
+ permissionSets
148
+ });
149
+ const accessRoles = accountAssignments.map((assignment) => ({
150
+ ...assignment,
151
+ roleName: createAccessRoleName(assignment)
152
+ }));
153
+ return {
154
+ instanceArn: instance.instanceArn,
155
+ identityStoreId: instance.identityStoreId,
156
+ users,
157
+ groups,
158
+ groupMemberships,
159
+ permissionSets,
160
+ accountAssignments,
161
+ accessRoles
162
+ };
163
+ }
164
+ function selectIdentityCenterInstance(props) {
165
+ if (props.requestedInstanceArn != null) {
166
+ const selected2 = props.instances.find(
167
+ (instance) => instance.InstanceArn === props.requestedInstanceArn
168
+ );
169
+ if (selected2?.InstanceArn == null || selected2.IdentityStoreId == null) {
170
+ throw new Error(
171
+ `Identity Center instance not found for --instance-arn: ${props.requestedInstanceArn}`
172
+ );
173
+ }
174
+ return {
175
+ instanceArn: selected2.InstanceArn,
176
+ identityStoreId: selected2.IdentityStoreId
177
+ };
178
+ }
179
+ if (props.instances.length > 1) {
180
+ const knownArns = props.instances.map((instance) => instance.InstanceArn).filter((value) => value != null).join(", ");
181
+ throw new Error(
182
+ `Multiple IAM Identity Center instances found. Re-run scan with --instance-arn. Available: ${knownArns}`
183
+ );
184
+ }
185
+ const selected = props.instances[0];
186
+ if (selected?.InstanceArn == null || selected.IdentityStoreId == null) {
187
+ throw new Error("IAM Identity Center instance is missing required fields.");
188
+ }
189
+ return {
190
+ instanceArn: selected.InstanceArn,
191
+ identityStoreId: selected.IdentityStoreId
192
+ };
193
+ }
194
+ async function listIdentityStoreUsers(props) {
195
+ const users = [];
196
+ let nextToken;
197
+ do {
198
+ const response = await props.identityStoreClient.send(
199
+ new ListUsersCommand({
200
+ IdentityStoreId: props.identityStoreId,
201
+ NextToken: nextToken
202
+ })
203
+ );
204
+ for (const user of response.Users ?? []) {
205
+ if (user.UserId == null || user.UserName == null) {
206
+ continue;
207
+ }
208
+ users.push({
209
+ userId: user.UserId,
210
+ userName: user.UserName,
211
+ displayName: user.DisplayName ?? "",
212
+ email: resolveIdentityStoreUserEmail({
213
+ emails: user.Emails ?? []
214
+ })
215
+ });
216
+ }
217
+ nextToken = response.NextToken;
218
+ } while (nextToken != null);
219
+ return users;
220
+ }
221
+ async function listIdentityStoreGroups(props) {
222
+ const groups = [];
223
+ let nextToken;
224
+ do {
225
+ const response = await props.identityStoreClient.send(
226
+ new ListGroupsCommand({
227
+ IdentityStoreId: props.identityStoreId,
228
+ NextToken: nextToken
229
+ })
230
+ );
231
+ for (const group of response.Groups ?? []) {
232
+ if (group.GroupId == null || group.DisplayName == null) {
233
+ continue;
234
+ }
235
+ groups.push({
236
+ groupId: group.GroupId,
237
+ displayName: group.DisplayName,
238
+ description: group.Description ?? ""
239
+ });
240
+ }
241
+ nextToken = response.NextToken;
242
+ } while (nextToken != null);
243
+ return groups;
244
+ }
245
+ async function listGroupMemberships(props) {
246
+ const groupMemberships = [];
247
+ for (const group of props.groups) {
248
+ let nextToken;
249
+ do {
250
+ const response = await props.identityStoreClient.send(
251
+ new ListGroupMembershipsCommand({
252
+ IdentityStoreId: props.identityStoreId,
253
+ GroupId: group.groupId,
254
+ NextToken: nextToken
255
+ })
256
+ );
257
+ for (const groupMembership of response.GroupMemberships ?? []) {
258
+ const userId = groupMembership.MemberId?.UserId;
259
+ if (groupMembership.MembershipId == null || groupMembership.GroupId == null || userId == null) {
260
+ continue;
261
+ }
262
+ groupMemberships.push({
263
+ membershipId: groupMembership.MembershipId,
264
+ groupId: groupMembership.GroupId,
265
+ userId
266
+ });
267
+ }
268
+ nextToken = response.NextToken;
269
+ } while (nextToken != null);
270
+ }
271
+ return groupMemberships;
272
+ }
273
+ function resolveIdentityStoreUserEmail(props) {
274
+ const primaryEmail = props.emails.find(
275
+ (email) => email.Primary === true && email.Value != null && email.Value.length > 0
276
+ );
277
+ if (primaryEmail?.Value != null) {
278
+ return primaryEmail.Value;
279
+ }
280
+ return props.emails.find((email) => email.Value != null && email.Value.length > 0)?.Value ?? "";
281
+ }
282
+ async function listPermissionSets(props) {
283
+ const permissionSetArns = [];
284
+ let nextToken;
285
+ do {
286
+ const response = await props.ssoAdminClient.send(
287
+ new ListPermissionSetsCommand({
288
+ InstanceArn: props.instanceArn,
289
+ NextToken: nextToken
290
+ })
291
+ );
292
+ permissionSetArns.push(...response.PermissionSets ?? []);
293
+ nextToken = response.NextToken;
294
+ } while (nextToken != null);
295
+ const describeResponses = await Promise.all(
296
+ permissionSetArns.map(
297
+ (permissionSetArn) => props.ssoAdminClient.send(
298
+ new DescribePermissionSetCommand({
299
+ InstanceArn: props.instanceArn,
300
+ PermissionSetArn: permissionSetArn
301
+ })
302
+ )
303
+ )
304
+ );
305
+ const permissionSets = await Promise.all(
306
+ describeResponses.map(async (response) => {
307
+ const permissionSet = response.PermissionSet;
308
+ if (permissionSet?.PermissionSetArn == null || permissionSet.Name == null) {
309
+ return void 0;
310
+ }
311
+ const [
312
+ inlinePolicy,
313
+ awsManagedPolicies,
314
+ customerManagedPolicies
315
+ ] = await Promise.all([
316
+ getInlinePolicyForPermissionSet({
317
+ ssoAdminClient: props.ssoAdminClient,
318
+ instanceArn: props.instanceArn,
319
+ permissionSetArn: permissionSet.PermissionSetArn
320
+ }),
321
+ listManagedPoliciesInPermissionSet({
322
+ ssoAdminClient: props.ssoAdminClient,
323
+ instanceArn: props.instanceArn,
324
+ permissionSetArn: permissionSet.PermissionSetArn
325
+ }),
326
+ listCustomerManagedPoliciesInPermissionSet({
327
+ ssoAdminClient: props.ssoAdminClient,
328
+ instanceArn: props.instanceArn,
329
+ permissionSetArn: permissionSet.PermissionSetArn
330
+ })
331
+ ]);
332
+ return {
333
+ permissionSetArn: permissionSet.PermissionSetArn,
334
+ name: permissionSet.Name,
335
+ description: permissionSet.Description ?? "",
336
+ inlinePolicy,
337
+ awsManagedPolicies,
338
+ customerManagedPolicies
339
+ };
340
+ })
341
+ );
342
+ return permissionSets.filter(
343
+ (permissionSet) => permissionSet != null
344
+ );
345
+ }
346
+ async function getInlinePolicyForPermissionSet(props) {
347
+ const response = await props.ssoAdminClient.send(
348
+ new GetInlinePolicyForPermissionSetCommand({
349
+ InstanceArn: props.instanceArn,
350
+ PermissionSetArn: props.permissionSetArn
351
+ })
352
+ );
353
+ const inlinePolicy = response.InlinePolicy?.trim();
354
+ return inlinePolicy != null && inlinePolicy.length > 0 ? inlinePolicy : null;
355
+ }
356
+ async function listManagedPoliciesInPermissionSet(props) {
357
+ const managedPolicies = [];
358
+ let nextToken;
359
+ do {
360
+ const response = await props.ssoAdminClient.send(
361
+ new ListManagedPoliciesInPermissionSetCommand({
362
+ InstanceArn: props.instanceArn,
363
+ PermissionSetArn: props.permissionSetArn,
364
+ NextToken: nextToken
365
+ })
366
+ );
367
+ for (const attachedManagedPolicy of response.AttachedManagedPolicies ?? []) {
368
+ if (attachedManagedPolicy.Arn == null) {
369
+ continue;
370
+ }
371
+ managedPolicies.push(attachedManagedPolicy.Arn);
372
+ }
373
+ nextToken = response.NextToken;
374
+ } while (nextToken != null);
375
+ return managedPolicies;
376
+ }
377
+ async function listCustomerManagedPoliciesInPermissionSet(props) {
378
+ const customerManagedPolicies = [];
379
+ let nextToken;
380
+ do {
381
+ const response = await props.ssoAdminClient.send(
382
+ new ListCustomerManagedPolicyReferencesInPermissionSetCommand({
383
+ InstanceArn: props.instanceArn,
384
+ PermissionSetArn: props.permissionSetArn,
385
+ NextToken: nextToken
386
+ })
387
+ );
388
+ for (const customerManagedPolicyReference of response.CustomerManagedPolicyReferences ?? []) {
389
+ if (customerManagedPolicyReference.Name == null || customerManagedPolicyReference.Path == null) {
390
+ continue;
391
+ }
392
+ customerManagedPolicies.push({
393
+ name: customerManagedPolicyReference.Name,
394
+ path: customerManagedPolicyReference.Path
395
+ });
396
+ }
397
+ nextToken = response.NextToken;
398
+ } while (nextToken != null);
399
+ return customerManagedPolicies;
400
+ }
401
+ async function listAccountAssignments(props) {
402
+ const assignments = [];
403
+ for (const permissionSet of props.permissionSets) {
404
+ const accountIds = await listAccountsForPermissionSet({
405
+ ssoAdminClient: props.ssoAdminClient,
406
+ instanceArn: props.instanceArn,
407
+ permissionSetArn: permissionSet.permissionSetArn
408
+ });
409
+ for (const accountId of accountIds) {
410
+ let nextToken;
411
+ do {
412
+ const response = await props.ssoAdminClient.send(
413
+ new ListAccountAssignmentsCommand({
414
+ InstanceArn: props.instanceArn,
415
+ AccountId: accountId,
416
+ PermissionSetArn: permissionSet.permissionSetArn,
417
+ NextToken: nextToken
418
+ })
419
+ );
420
+ for (const assignment of response.AccountAssignments ?? []) {
421
+ if (assignment.AccountId == null || assignment.PermissionSetArn == null || assignment.PrincipalId == null || assignment.PrincipalType == null) {
422
+ continue;
423
+ }
424
+ assignments.push({
425
+ accountId: assignment.AccountId,
426
+ permissionSetArn: assignment.PermissionSetArn,
427
+ principalId: assignment.PrincipalId,
428
+ principalType: assignment.PrincipalType
429
+ });
430
+ }
431
+ nextToken = response.NextToken;
432
+ } while (nextToken != null);
433
+ }
434
+ }
435
+ return assignments;
436
+ }
437
+ async function listAccountsForPermissionSet(props) {
438
+ const accountIds = [];
439
+ let nextToken;
440
+ do {
441
+ const response = await props.ssoAdminClient.send(
442
+ new ListAccountsForProvisionedPermissionSetCommand({
443
+ InstanceArn: props.instanceArn,
444
+ PermissionSetArn: props.permissionSetArn,
445
+ NextToken: nextToken
446
+ })
447
+ );
448
+ accountIds.push(...response.AccountIds ?? []);
449
+ nextToken = response.NextToken;
450
+ } while (nextToken != null);
451
+ return accountIds;
452
+ }
453
+ export {
454
+ scanIdentityCenter,
455
+ scanOrganization
456
+ };