@beesolve/aws-accounts 1.2.0 → 1.3.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.
@@ -153,6 +153,22 @@ const provisionIdcPermissionSetOperationSchema = v.strictObject({
153
153
  permissionSetName: v.string(),
154
154
  targetScope: v.literal("ALL_PROVISIONED_ACCOUNTS")
155
155
  });
156
+ const permissionsBoundaryOperationValueSchema = v.union([
157
+ v.strictObject({ managedPolicyArn: v.string() }),
158
+ v.strictObject({
159
+ customerManagedPolicyName: v.string(),
160
+ customerManagedPolicyPath: v.string()
161
+ })
162
+ ]);
163
+ const putIdcPermissionSetPermissionsBoundaryOperationSchema = v.strictObject({
164
+ kind: v.literal("putIdcPermissionSetPermissionsBoundary"),
165
+ permissionSetName: v.string(),
166
+ permissionsBoundary: permissionsBoundaryOperationValueSchema
167
+ });
168
+ const deleteIdcPermissionSetPermissionsBoundaryOperationSchema = v.strictObject({
169
+ kind: v.literal("deleteIdcPermissionSetPermissionsBoundary"),
170
+ permissionSetName: v.string()
171
+ });
156
172
  const grantIdcAccountAssignmentOperationSchema = v.strictObject({
157
173
  kind: v.literal("grantIdcAccountAssignment"),
158
174
  accountName: v.string(),
@@ -197,6 +213,18 @@ const deleteAlternateContactOperationSchema = v.strictObject({
197
213
  accountName: v.string(),
198
214
  contactType: alternateContactTypeSchema
199
215
  });
216
+ const registerDelegatedAdministratorOperationSchema = v.strictObject({
217
+ kind: v.literal("registerDelegatedAdministrator"),
218
+ accountId: v.string(),
219
+ accountName: v.string(),
220
+ servicePrincipal: v.string()
221
+ });
222
+ const deregisterDelegatedAdministratorOperationSchema = v.strictObject({
223
+ kind: v.literal("deregisterDelegatedAdministrator"),
224
+ accountId: v.string(),
225
+ accountName: v.string(),
226
+ servicePrincipal: v.string()
227
+ });
200
228
  const createOrgPolicyOperationSchema = v.strictObject({
201
229
  kind: v.literal("createOrgPolicy"),
202
230
  policyName: v.string(),
@@ -204,7 +232,8 @@ const createOrgPolicyOperationSchema = v.strictObject({
204
232
  "SERVICE_CONTROL_POLICY",
205
233
  "RESOURCE_CONTROL_POLICY",
206
234
  "TAG_POLICY",
207
- "AISERVICES_OPT_OUT_POLICY"
235
+ "AISERVICES_OPT_OUT_POLICY",
236
+ "BACKUP_POLICY"
208
237
  ]),
209
238
  description: v.string(),
210
239
  content: v.string()
@@ -268,6 +297,8 @@ const operationSchema = v.variant("kind", [
268
297
  attachIdcCustomerManagedPolicyReferenceToPermissionSetOperationSchema,
269
298
  detachIdcCustomerManagedPolicyReferenceFromPermissionSetOperationSchema,
270
299
  provisionIdcPermissionSetOperationSchema,
300
+ putIdcPermissionSetPermissionsBoundaryOperationSchema,
301
+ deleteIdcPermissionSetPermissionsBoundaryOperationSchema,
271
302
  grantIdcAccountAssignmentOperationSchema,
272
303
  revokeIdcAccountAssignmentOperationSchema,
273
304
  createOrgPolicyOperationSchema,
@@ -278,13 +309,16 @@ const operationSchema = v.variant("kind", [
278
309
  deleteOrgPolicyOperationSchema,
279
310
  putAlternateContactOperationSchema,
280
311
  deleteAlternateContactOperationSchema,
281
- setIdcAccessControlAttributesOperationSchema
312
+ setIdcAccessControlAttributesOperationSchema,
313
+ registerDelegatedAdministratorOperationSchema,
314
+ deregisterDelegatedAdministratorOperationSchema
282
315
  ]);
283
316
  const unsupportedDiffKindSchema = v.picklist([
284
317
  "ambiguousOuRename",
285
318
  "reparentedOu",
286
319
  "newOuWithUnknownParent",
287
320
  "newAccountWithUnknownOu",
321
+ "existingAccountWithUnknownTargetOu",
288
322
  "removedOu"
289
323
  ]);
290
324
  const unsupportedDiffCategorySchema = v.picklist([
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,36 @@ 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
+ try {
532
+ const response = await props.ssoAdminClient.send(
533
+ new GetPermissionsBoundaryForPermissionSetCommand({
534
+ InstanceArn: props.instanceArn,
535
+ PermissionSetArn: props.permissionSetArn
536
+ })
537
+ );
538
+ const boundary = response.PermissionsBoundary;
539
+ if (boundary == null) {
540
+ return null;
541
+ }
542
+ if (boundary.ManagedPolicyArn != null) {
543
+ return { managedPolicyArn: boundary.ManagedPolicyArn };
544
+ }
545
+ const ref = boundary.CustomerManagedPolicyReference;
546
+ if (ref?.Name != null) {
547
+ return {
548
+ customerManagedPolicyName: ref.Name,
549
+ customerManagedPolicyPath: ref.Path ?? "/"
550
+ };
551
+ }
552
+ return null;
553
+ } catch (err) {
554
+ if (err != null && typeof err === "object" && "name" in err && err.name === "ResourceNotFoundException") {
555
+ return null;
556
+ }
557
+ throw err;
558
+ }
559
+ }
477
560
  async function listManagedPoliciesInPermissionSet(props) {
478
561
  const managedPolicies = [];
479
562
  let nextToken;
@@ -571,11 +654,7 @@ async function listAccountsForPermissionSet(props) {
571
654
  } while (nextToken != null);
572
655
  return accountIds;
573
656
  }
574
- const ALTERNATE_CONTACT_TYPES = [
575
- "BILLING",
576
- "OPERATIONS",
577
- "SECURITY"
578
- ];
657
+ const ALTERNATE_CONTACT_TYPES = ["BILLING", "OPERATIONS", "SECURITY"];
579
658
  async function scanAlternateContacts(props) {
580
659
  const results = await Promise.all(
581
660
  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,