@beesolve/aws-accounts 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/scanLogic.js CHANGED
@@ -7,6 +7,8 @@ import {
7
7
  DescribeOrganizationCommand,
8
8
  DescribePolicyCommand,
9
9
  ListAccountsCommand,
10
+ ListDelegatedAdministratorsCommand,
11
+ ListDelegatedServicesForAccountCommand,
10
12
  ListOrganizationalUnitsForParentCommand,
11
13
  ListParentsCommand,
12
14
  ListPoliciesCommand,
@@ -17,6 +19,7 @@ import {
17
19
  import {
18
20
  DescribePermissionSetCommand,
19
21
  GetInlinePolicyForPermissionSetCommand,
22
+ GetPermissionsBoundaryForPermissionSetCommand,
20
23
  ListAccountAssignmentsCommand,
21
24
  ListAccountsForProvisionedPermissionSetCommand,
22
25
  ListCustomerManagedPolicyReferencesInPermissionSetCommand,
@@ -92,22 +95,65 @@ async function scanOrganization(props) {
92
95
  }
93
96
  nextToken = response.NextToken;
94
97
  } while (nextToken != null);
95
- const { policies, policyAttachments } = await scanOrganizationPolicies({
96
- organizationsClient: props.organizationsClient
97
- });
98
+ const [{ policies, policyAttachments }, delegatedAdministrators] = await Promise.all([
99
+ scanOrganizationPolicies({
100
+ organizationsClient: props.organizationsClient
101
+ }),
102
+ scanDelegatedAdministrators({
103
+ organizationsClient: props.organizationsClient
104
+ })
105
+ ]);
98
106
  return {
99
107
  rootId: root.Id,
100
108
  organizationalUnits,
101
109
  accounts,
102
110
  policies,
103
- policyAttachments
111
+ policyAttachments,
112
+ delegatedAdministrators: delegatedAdministrators.length > 0 ? delegatedAdministrators : void 0
104
113
  };
105
114
  }
115
+ async function scanDelegatedAdministrators(props) {
116
+ const accountIds = new Array();
117
+ let nextToken;
118
+ do {
119
+ const response = await props.organizationsClient.send(
120
+ new ListDelegatedAdministratorsCommand({ NextToken: nextToken })
121
+ );
122
+ for (const admin of response.DelegatedAdministrators ?? []) {
123
+ if (admin.Id == null) {
124
+ continue;
125
+ }
126
+ accountIds.push(admin.Id);
127
+ }
128
+ nextToken = response.NextToken;
129
+ } while (nextToken != null);
130
+ const results = [];
131
+ for (const accountId of accountIds) {
132
+ let servicesNextToken;
133
+ do {
134
+ const response = await props.organizationsClient.send(
135
+ new ListDelegatedServicesForAccountCommand({
136
+ AccountId: accountId,
137
+ NextToken: servicesNextToken
138
+ })
139
+ );
140
+ for (const service of response.DelegatedServices ?? []) {
141
+ if (service.ServicePrincipal == null) {
142
+ continue;
143
+ }
144
+ results.push({ accountId, servicePrincipal: service.ServicePrincipal });
145
+ }
146
+ servicesNextToken = response.NextToken;
147
+ } while (servicesNextToken != null);
148
+ }
149
+ return results;
150
+ }
106
151
  const ORG_POLICY_TYPES = [
107
152
  "SERVICE_CONTROL_POLICY",
108
153
  "RESOURCE_CONTROL_POLICY",
109
154
  "TAG_POLICY",
110
- "AISERVICES_OPT_OUT_POLICY"
155
+ "AISERVICES_OPT_OUT_POLICY",
156
+ "BACKUP_POLICY"
111
157
  ];
112
158
  async function scanOrganizationPolicies(props) {
113
159
  const policies = [];
@@ -431,7 +477,8 @@ async function listPermissionSets(props) {
431
477
  const [
432
478
  inlinePolicy,
433
479
  awsManagedPolicies,
434
- customerManagedPolicies
480
+ customerManagedPolicies,
481
+ permissionsBoundary
435
482
  ] = await Promise.all([
436
483
  getInlinePolicyForPermissionSet({
437
484
  ssoAdminClient: props.ssoAdminClient,
@@ -447,6 +494,11 @@ async function listPermissionSets(props) {
447
494
  ssoAdminClient: props.ssoAdminClient,
448
495
  instanceArn: props.instanceArn,
449
496
  permissionSetArn: permissionSet.PermissionSetArn
497
+ }),
498
+ getPermissionsBoundaryForPermissionSet({
499
+ ssoAdminClient: props.ssoAdminClient,
500
+ instanceArn: props.instanceArn,
501
+ permissionSetArn: permissionSet.PermissionSetArn
450
502
  })
451
503
  ]);
452
504
  return {
@@ -456,7 +508,8 @@ async function listPermissionSets(props) {
456
508
  sessionDuration: permissionSet.SessionDuration ?? null,
457
509
  inlinePolicy,
458
510
  awsManagedPolicies,
459
- customerManagedPolicies
511
+ customerManagedPolicies,
512
+ permissionsBoundary
460
513
  };
461
514
  })
462
515
  );
@@ -474,6 +527,29 @@ async function getInlinePolicyForPermissionSet(props) {
474
527
  const inlinePolicy = response.InlinePolicy?.trim();
475
528
  return inlinePolicy != null && inlinePolicy.length > 0 ? inlinePolicy : null;
476
529
  }
530
+ async function getPermissionsBoundaryForPermissionSet(props) {
531
+ const response = await props.ssoAdminClient.send(
532
+ new GetPermissionsBoundaryForPermissionSetCommand({
533
+ InstanceArn: props.instanceArn,
534
+ PermissionSetArn: props.permissionSetArn
535
+ })
536
+ );
537
+ const boundary = response.PermissionsBoundary;
538
+ if (boundary == null) {
539
+ return null;
540
+ }
541
+ if (boundary.ManagedPolicyArn != null) {
542
+ return { managedPolicyArn: boundary.ManagedPolicyArn };
543
+ }
544
+ const ref = boundary.CustomerManagedPolicyReference;
545
+ if (ref?.Name != null) {
546
+ return {
547
+ customerManagedPolicyName: ref.Name,
548
+ customerManagedPolicyPath: ref.Path ?? "/"
549
+ };
550
+ }
551
+ return null;
552
+ }
477
553
  async function listManagedPoliciesInPermissionSet(props) {
478
554
  const managedPolicies = [];
479
555
  let nextToken;
@@ -571,11 +647,7 @@ async function listAccountsForPermissionSet(props) {
571
647
  } while (nextToken != null);
572
648
  return accountIds;
573
649
  }
574
- const ALTERNATE_CONTACT_TYPES = [
575
- "BILLING",
576
- "OPERATIONS",
577
- "SECURITY"
578
- ];
650
+ const ALTERNATE_CONTACT_TYPES = ["BILLING", "OPERATIONS", "SECURITY"];
579
651
  async function scanAlternateContacts(props) {
580
652
  const results = await Promise.all(
581
653
  ALTERNATE_CONTACT_TYPES.map(async (contactType) => {
package/dist/state.js CHANGED
@@ -13,7 +13,8 @@ const orgPolicyTypeSchema = v.picklist([
13
13
  "SERVICE_CONTROL_POLICY",
14
14
  "RESOURCE_CONTROL_POLICY",
15
15
  "TAG_POLICY",
16
- "AISERVICES_OPT_OUT_POLICY"
16
+ "AISERVICES_OPT_OUT_POLICY",
17
+ "BACKUP_POLICY"
17
18
  ]);
18
19
  const orgPolicySchema = v.strictObject({
19
20
  id: nonEmptyString,
@@ -69,6 +70,13 @@ const customerManagedPolicyReferenceSchema = v.strictObject({
69
70
  name: nonEmptyString,
70
71
  path: nonEmptyString
71
72
  });
73
+ const permissionsBoundarySchema = v.union([
74
+ v.strictObject({ managedPolicyArn: nonEmptyString }),
75
+ v.strictObject({
76
+ customerManagedPolicyName: nonEmptyString,
77
+ customerManagedPolicyPath: nonEmptyString
78
+ })
79
+ ]);
72
80
  const permissionSetSchema = v.strictObject({
73
81
  permissionSetArn: nonEmptyString,
74
82
  name: nonEmptyString,
@@ -76,7 +84,8 @@ const permissionSetSchema = v.strictObject({
76
84
  sessionDuration: v.nullable(v.string()),
77
85
  inlinePolicy: v.nullable(nonEmptyString),
78
86
  awsManagedPolicies: v.array(nonEmptyString),
79
- customerManagedPolicies: v.array(customerManagedPolicyReferenceSchema)
87
+ customerManagedPolicies: v.array(customerManagedPolicyReferenceSchema),
88
+ permissionsBoundary: v.nullable(permissionsBoundarySchema)
80
89
  });
81
90
  const accountAssignmentSchema = v.strictObject({
82
91
  accountId: nonEmptyString,
@@ -95,6 +104,10 @@ const accessControlAttributeSchema = v.strictObject({
95
104
  key: nonEmptyString,
96
105
  source: v.array(nonEmptyString)
97
106
  });
107
+ const delegatedAdministratorSchema = v.strictObject({
108
+ accountId: nonEmptyString,
109
+ servicePrincipal: nonEmptyString
110
+ });
98
111
  const stateSchema = v.strictObject({
99
112
  version: nonEmptyString,
100
113
  generatedAt: nonEmptyString,
@@ -103,7 +116,8 @@ const stateSchema = v.strictObject({
103
116
  organizationalUnits: v.array(organizationalUnitSchema),
104
117
  accounts: v.array(accountSchema),
105
118
  policies: v.optional(v.array(orgPolicySchema)),
106
- policyAttachments: v.optional(v.array(orgPolicyAttachmentSchema))
119
+ policyAttachments: v.optional(v.array(orgPolicyAttachmentSchema)),
120
+ delegatedAdministrators: v.optional(v.array(delegatedAdministratorSchema))
107
121
  }),
108
122
  identityCenter: v.strictObject({
109
123
  instanceArn: nonEmptyString,
@@ -123,6 +137,7 @@ function validateState(value) {
123
137
  function createWorkingState(props) {
124
138
  const policies = props.state.organization.policies ?? [];
125
139
  const policyAttachments = props.state.organization.policyAttachments ?? [];
140
+ const delegatedAdministrators = props.state.organization.delegatedAdministrators ?? [];
126
141
  return {
127
142
  version: props.state.version,
128
143
  generatedAt: props.state.generatedAt,
@@ -143,6 +158,11 @@ function createWorkingState(props) {
143
158
  policyAttachmentsByKey: toRecordByProperty(
144
159
  policyAttachments,
145
160
  createOrgPolicyAttachmentKey
161
+ ),
162
+ delegatedAdministrators: structuredClone(delegatedAdministrators),
163
+ delegatedAdministratorsByKey: toRecordByProperty(
164
+ delegatedAdministrators,
165
+ createDelegatedAdministratorKey
146
166
  )
147
167
  },
148
168
  identityCenter: createWorkingIdentityCenterState({
@@ -163,7 +183,12 @@ function materializeWorkingState(props) {
163
183
  policies: Object.values(props.workingState.organization.policiesById),
164
184
  policyAttachments: structuredClone(
165
185
  props.workingState.organization.policyAttachments
166
- )
186
+ ),
187
+ ...props.workingState.organization.delegatedAdministrators.length > 0 ? {
188
+ delegatedAdministrators: structuredClone(
189
+ props.workingState.organization.delegatedAdministrators
190
+ )
191
+ } : {}
167
192
  },
168
193
  identityCenter: {
169
194
  instanceArn: props.workingState.identityCenter.instanceArn,
@@ -396,7 +421,7 @@ function removeIdcGroupFromWorkingState(props) {
396
421
  }
397
422
  function upsertIdcPermissionSetInWorkingState(props) {
398
423
  const currentPermissionSet = props.workingState.identityCenter.permissionSetsByName[props.permissionSet.name];
399
- if (currentPermissionSet != null && currentPermissionSet.permissionSetArn === props.permissionSet.permissionSetArn && currentPermissionSet.name === props.permissionSet.name && currentPermissionSet.description === props.permissionSet.description && currentPermissionSet.sessionDuration === props.permissionSet.sessionDuration && currentPermissionSet.inlinePolicy === props.permissionSet.inlinePolicy && JSON.stringify(currentPermissionSet.awsManagedPolicies) === JSON.stringify(props.permissionSet.awsManagedPolicies) && JSON.stringify(currentPermissionSet.customerManagedPolicies) === JSON.stringify(props.permissionSet.customerManagedPolicies)) {
424
+ if (currentPermissionSet != null && currentPermissionSet.permissionSetArn === props.permissionSet.permissionSetArn && currentPermissionSet.name === props.permissionSet.name && currentPermissionSet.description === props.permissionSet.description && currentPermissionSet.sessionDuration === props.permissionSet.sessionDuration && currentPermissionSet.inlinePolicy === props.permissionSet.inlinePolicy && JSON.stringify(currentPermissionSet.awsManagedPolicies) === JSON.stringify(props.permissionSet.awsManagedPolicies) && JSON.stringify(currentPermissionSet.customerManagedPolicies) === JSON.stringify(props.permissionSet.customerManagedPolicies) && JSON.stringify(currentPermissionSet.permissionsBoundary) === JSON.stringify(props.permissionSet.permissionsBoundary)) {
400
425
  return props.workingState;
401
426
  }
402
427
  const remainingPermissionSets = props.workingState.identityCenter.permissionSets.filter(
@@ -633,6 +658,59 @@ function removeOrgPolicyAttachmentFromWorkingState(props) {
633
658
  }
634
659
  };
635
660
  }
661
+ function createDelegatedAdministratorKey(props) {
662
+ return [props.accountId, props.servicePrincipal].join("|");
663
+ }
664
+ function upsertDelegatedAdministratorInWorkingState(props) {
665
+ const key = createDelegatedAdministratorKey({
666
+ accountId: props.delegatedAdministrator.accountId,
667
+ servicePrincipal: props.delegatedAdministrator.servicePrincipal
668
+ });
669
+ if (props.workingState.organization.delegatedAdministratorsByKey[key] != null) {
670
+ return props.workingState;
671
+ }
672
+ const nextDelegatedAdministrators = [
673
+ ...props.workingState.organization.delegatedAdministrators,
674
+ props.delegatedAdministrator
675
+ ];
676
+ return {
677
+ ...props.workingState,
678
+ organization: {
679
+ ...props.workingState.organization,
680
+ delegatedAdministrators: nextDelegatedAdministrators,
681
+ delegatedAdministratorsByKey: toRecordByProperty(
682
+ nextDelegatedAdministrators,
683
+ createDelegatedAdministratorKey
684
+ )
685
+ }
686
+ };
687
+ }
688
+ function removeDelegatedAdministratorFromWorkingState(props) {
689
+ const key = createDelegatedAdministratorKey({
690
+ accountId: props.accountId,
691
+ servicePrincipal: props.servicePrincipal
692
+ });
693
+ if (props.workingState.organization.delegatedAdministratorsByKey[key] == null) {
694
+ return props.workingState;
695
+ }
696
+ const nextDelegatedAdministrators = props.workingState.organization.delegatedAdministrators.filter(
697
+ (da) => createDelegatedAdministratorKey({
698
+ accountId: da.accountId,
699
+ servicePrincipal: da.servicePrincipal
700
+ }) !== key
701
+ );
702
+ return {
703
+ ...props.workingState,
704
+ organization: {
705
+ ...props.workingState.organization,
706
+ delegatedAdministrators: nextDelegatedAdministrators,
707
+ delegatedAdministratorsByKey: toRecordByProperty(
708
+ nextDelegatedAdministrators,
709
+ createDelegatedAdministratorKey
710
+ )
711
+ }
712
+ };
713
+ }
636
714
  async function readStateFile(path) {
637
715
  const content = await readFile(path, "utf8");
638
716
  const parsed = JSON.parse(content);
@@ -756,6 +834,7 @@ export {
756
834
  moveAccountInWorkingState,
757
835
  readStateFile,
758
836
  removeAccountAssignmentFromWorkingState,
837
+ removeDelegatedAdministratorFromWorkingState,
759
838
  removeGroupMembershipFromWorkingState,
760
839
  removeIdcGroupFromWorkingState,
761
840
  removeIdcPermissionSetFromWorkingState,
@@ -766,6 +845,7 @@ export {
766
845
  renameOrganizationalUnitInWorkingState,
767
846
  stateSchema,
768
847
  upsertAccountInWorkingState,
848
+ upsertDelegatedAdministratorInWorkingState,
769
849
  upsertIdcGroupInWorkingState,
770
850
  upsertIdcPermissionSetInWorkingState,
771
851
  upsertIdcUserInWorkingState,