@beesolve/aws-accounts 1.0.7 → 1.2.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.
@@ -9,7 +9,7 @@ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
9
9
  import { OrganizationsClient as OrganizationsClient3 } from "@aws-sdk/client-organizations";
10
10
  import { SSOAdminClient as SSOAdminClient3 } from "@aws-sdk/client-sso-admin";
11
11
  import { IdentitystoreClient as IdentitystoreClient3 } from "@aws-sdk/client-identitystore";
12
- import { AccountClient as AccountClient2 } from "@aws-sdk/client-account";
12
+ import { AccountClient as AccountClient3 } from "@aws-sdk/client-account";
13
13
 
14
14
  // node_modules/valibot/dist/index.mjs
15
15
  var store$4;
@@ -739,13 +739,19 @@ var removeIdcGroupMembershipOperationSchema = strictObject({
739
739
  var createIdcPermissionSetOperationSchema = strictObject({
740
740
  kind: literal("createIdcPermissionSet"),
741
741
  permissionSetName: string(),
742
- description: string()
742
+ description: string(),
743
+ sessionDuration: nullable(string())
743
744
  });
744
745
  var updateIdcPermissionSetDescriptionOperationSchema = strictObject({
745
746
  kind: literal("updateIdcPermissionSetDescription"),
746
747
  permissionSetName: string(),
747
748
  description: string()
748
749
  });
750
+ var updateIdcPermissionSetSessionDurationOperationSchema = strictObject({
751
+ kind: literal("updateIdcPermissionSetSessionDuration"),
752
+ permissionSetName: string(),
753
+ sessionDuration: nullable(string())
754
+ });
749
755
  var deleteIdcPermissionSetOperationSchema = strictObject({
750
756
  kind: literal("deleteIdcPermissionSet"),
751
757
  permissionSetName: string()
@@ -800,6 +806,79 @@ var revokeIdcAccountAssignmentOperationSchema = strictObject({
800
806
  principalType: picklist(["GROUP", "USER"]),
801
807
  principalName: string()
802
808
  });
809
+ var setIdcAccessControlAttributesOperationSchema = strictObject({
810
+ kind: literal("setIdcAccessControlAttributes"),
811
+ attributes: array(
812
+ strictObject({
813
+ key: string(),
814
+ source: array(string())
815
+ })
816
+ )
817
+ });
818
+ var alternateContactTypeSchema = picklist([
819
+ "BILLING",
820
+ "OPERATIONS",
821
+ "SECURITY"
822
+ ]);
823
+ var putAlternateContactOperationSchema = strictObject({
824
+ kind: literal("putAlternateContact"),
825
+ accountId: string(),
826
+ accountName: string(),
827
+ contactType: alternateContactTypeSchema,
828
+ name: string(),
829
+ email: string(),
830
+ phone: string(),
831
+ title: optional(string())
832
+ });
833
+ var deleteAlternateContactOperationSchema = strictObject({
834
+ kind: literal("deleteAlternateContact"),
835
+ accountId: string(),
836
+ accountName: string(),
837
+ contactType: alternateContactTypeSchema
838
+ });
839
+ var createOrgPolicyOperationSchema = strictObject({
840
+ kind: literal("createOrgPolicy"),
841
+ policyName: string(),
842
+ policyType: picklist([
843
+ "SERVICE_CONTROL_POLICY",
844
+ "RESOURCE_CONTROL_POLICY",
845
+ "TAG_POLICY",
846
+ "AISERVICES_OPT_OUT_POLICY"
847
+ ]),
848
+ description: string(),
849
+ content: string()
850
+ });
851
+ var updateOrgPolicyContentOperationSchema = strictObject({
852
+ kind: literal("updateOrgPolicyContent"),
853
+ policyId: string(),
854
+ policyName: string(),
855
+ content: string()
856
+ });
857
+ var updateOrgPolicyDescriptionOperationSchema = strictObject({
858
+ kind: literal("updateOrgPolicyDescription"),
859
+ policyId: string(),
860
+ policyName: string(),
861
+ description: string()
862
+ });
863
+ var attachOrgPolicyOperationSchema = strictObject({
864
+ kind: literal("attachOrgPolicy"),
865
+ policyId: string(),
866
+ policyName: string(),
867
+ targetId: string(),
868
+ targetName: string()
869
+ });
870
+ var detachOrgPolicyOperationSchema = strictObject({
871
+ kind: literal("detachOrgPolicy"),
872
+ policyId: string(),
873
+ policyName: string(),
874
+ targetId: string(),
875
+ targetName: string()
876
+ });
877
+ var deleteOrgPolicyOperationSchema = strictObject({
878
+ kind: literal("deleteOrgPolicy"),
879
+ policyId: string(),
880
+ policyName: string()
881
+ });
803
882
  var operationSchema = variant("kind", [
804
883
  moveAccountOperationSchema,
805
884
  createOuOperationSchema,
@@ -819,6 +898,7 @@ var operationSchema = variant("kind", [
819
898
  removeIdcGroupMembershipOperationSchema,
820
899
  createIdcPermissionSetOperationSchema,
821
900
  updateIdcPermissionSetDescriptionOperationSchema,
901
+ updateIdcPermissionSetSessionDurationOperationSchema,
822
902
  deleteIdcPermissionSetOperationSchema,
823
903
  putIdcPermissionSetInlinePolicyOperationSchema,
824
904
  deleteIdcPermissionSetInlinePolicyOperationSchema,
@@ -828,7 +908,16 @@ var operationSchema = variant("kind", [
828
908
  detachIdcCustomerManagedPolicyReferenceFromPermissionSetOperationSchema,
829
909
  provisionIdcPermissionSetOperationSchema,
830
910
  grantIdcAccountAssignmentOperationSchema,
831
- revokeIdcAccountAssignmentOperationSchema
911
+ revokeIdcAccountAssignmentOperationSchema,
912
+ createOrgPolicyOperationSchema,
913
+ updateOrgPolicyContentOperationSchema,
914
+ updateOrgPolicyDescriptionOperationSchema,
915
+ attachOrgPolicyOperationSchema,
916
+ detachOrgPolicyOperationSchema,
917
+ deleteOrgPolicyOperationSchema,
918
+ putAlternateContactOperationSchema,
919
+ deleteAlternateContactOperationSchema,
920
+ setIdcAccessControlAttributesOperationSchema
832
921
  ]);
833
922
  var unsupportedDiffKindSchema = picklist([
834
923
  "ambiguousOuRename",
@@ -878,10 +967,36 @@ var organizationalUnitSchema = strictObject({
878
967
  arn: nonEmptyString,
879
968
  name: nonEmptyString
880
969
  });
970
+ var orgPolicyTypeSchema = picklist([
971
+ "SERVICE_CONTROL_POLICY",
972
+ "RESOURCE_CONTROL_POLICY",
973
+ "TAG_POLICY",
974
+ "AISERVICES_OPT_OUT_POLICY"
975
+ ]);
976
+ var orgPolicySchema = strictObject({
977
+ id: nonEmptyString,
978
+ arn: nonEmptyString,
979
+ name: nonEmptyString,
980
+ description: string(),
981
+ type: orgPolicyTypeSchema,
982
+ content: nonEmptyString
983
+ });
984
+ var orgPolicyAttachmentSchema = strictObject({
985
+ policyId: nonEmptyString,
986
+ targetId: nonEmptyString,
987
+ targetType: picklist(["ROOT", "ORGANIZATIONAL_UNIT", "ACCOUNT"])
988
+ });
881
989
  var accountTagSchema = strictObject({
882
990
  key: nonEmptyString,
883
991
  value: string()
884
992
  });
993
+ var alternateContactSchema = strictObject({
994
+ contactType: picklist(["BILLING", "OPERATIONS", "SECURITY"]),
995
+ name: string(),
996
+ email: string(),
997
+ phone: string(),
998
+ title: optional(string())
999
+ });
885
1000
  var accountSchema = strictObject({
886
1001
  id: nonEmptyString,
887
1002
  arn: nonEmptyString,
@@ -889,7 +1004,8 @@ var accountSchema = strictObject({
889
1004
  email: nonEmptyString,
890
1005
  status: nonEmptyString,
891
1006
  parentId: nonEmptyString,
892
- tags: array(accountTagSchema)
1007
+ tags: array(accountTagSchema),
1008
+ alternateContacts: optional(array(alternateContactSchema))
893
1009
  });
894
1010
  var userSchema = strictObject({
895
1011
  userId: nonEmptyString,
@@ -915,6 +1031,7 @@ var permissionSetSchema = strictObject({
915
1031
  permissionSetArn: nonEmptyString,
916
1032
  name: nonEmptyString,
917
1033
  description: string(),
1034
+ sessionDuration: nullable(string()),
918
1035
  inlinePolicy: nullable(nonEmptyString),
919
1036
  awsManagedPolicies: array(nonEmptyString),
920
1037
  customerManagedPolicies: array(customerManagedPolicyReferenceSchema)
@@ -932,13 +1049,19 @@ var accessRoleSchema = strictObject({
932
1049
  principalType: principalTypeSchema,
933
1050
  roleName: nonEmptyString
934
1051
  });
1052
+ var accessControlAttributeSchema = strictObject({
1053
+ key: nonEmptyString,
1054
+ source: array(nonEmptyString)
1055
+ });
935
1056
  var stateSchema = strictObject({
936
1057
  version: nonEmptyString,
937
1058
  generatedAt: nonEmptyString,
938
1059
  organization: strictObject({
939
1060
  rootId: nonEmptyString,
940
1061
  organizationalUnits: array(organizationalUnitSchema),
941
- accounts: array(accountSchema)
1062
+ accounts: array(accountSchema),
1063
+ policies: optional(array(orgPolicySchema)),
1064
+ policyAttachments: optional(array(orgPolicyAttachmentSchema))
942
1065
  }),
943
1066
  identityCenter: strictObject({
944
1067
  instanceArn: nonEmptyString,
@@ -948,10 +1071,13 @@ var stateSchema = strictObject({
948
1071
  groupMemberships: array(groupMembershipSchema),
949
1072
  permissionSets: array(permissionSetSchema),
950
1073
  accountAssignments: array(accountAssignmentSchema),
951
- accessRoles: array(accessRoleSchema)
1074
+ accessRoles: array(accessRoleSchema),
1075
+ accessControlAttributes: array(accessControlAttributeSchema)
952
1076
  })
953
1077
  });
954
1078
  function createWorkingState(props) {
1079
+ const policies = props.state.organization.policies ?? [];
1080
+ const policyAttachments = props.state.organization.policyAttachments ?? [];
955
1081
  return {
956
1082
  version: props.state.version,
957
1083
  generatedAt: props.state.generatedAt,
@@ -965,6 +1091,13 @@ function createWorkingState(props) {
965
1091
  accountsByName: toRecordByProperty(
966
1092
  props.state.organization.accounts,
967
1093
  "name"
1094
+ ),
1095
+ policiesById: toRecordByProperty(policies, "id"),
1096
+ policiesByName: toRecordByProperty(policies, "name"),
1097
+ policyAttachments: structuredClone(policyAttachments),
1098
+ policyAttachmentsByKey: toRecordByProperty(
1099
+ policyAttachments,
1100
+ createOrgPolicyAttachmentKey
968
1101
  )
969
1102
  },
970
1103
  identityCenter: createWorkingIdentityCenterState({
@@ -981,7 +1114,11 @@ function materializeWorkingState(props) {
981
1114
  organizationalUnits: Object.values(
982
1115
  props.workingState.organization.organizationalUnitsById
983
1116
  ),
984
- accounts: Object.values(props.workingState.organization.accountsById)
1117
+ accounts: Object.values(props.workingState.organization.accountsById),
1118
+ policies: Object.values(props.workingState.organization.policiesById),
1119
+ policyAttachments: structuredClone(
1120
+ props.workingState.organization.policyAttachments
1121
+ )
985
1122
  },
986
1123
  identityCenter: {
987
1124
  instanceArn: props.workingState.identityCenter.instanceArn,
@@ -999,6 +1136,9 @@ function materializeWorkingState(props) {
999
1136
  ),
1000
1137
  accessRoles: structuredClone(
1001
1138
  props.workingState.identityCenter.accessRoles
1139
+ ),
1140
+ accessControlAttributes: structuredClone(
1141
+ props.workingState.identityCenter.accessControlAttributes
1002
1142
  )
1003
1143
  }
1004
1144
  };
@@ -1211,7 +1351,7 @@ function removeIdcGroupFromWorkingState(props) {
1211
1351
  }
1212
1352
  function upsertIdcPermissionSetInWorkingState(props) {
1213
1353
  const currentPermissionSet = props.workingState.identityCenter.permissionSetsByName[props.permissionSet.name];
1214
- if (currentPermissionSet != null && currentPermissionSet.permissionSetArn === props.permissionSet.permissionSetArn && currentPermissionSet.name === props.permissionSet.name && currentPermissionSet.description === props.permissionSet.description && currentPermissionSet.inlinePolicy === props.permissionSet.inlinePolicy && JSON.stringify(currentPermissionSet.awsManagedPolicies) === JSON.stringify(props.permissionSet.awsManagedPolicies) && JSON.stringify(currentPermissionSet.customerManagedPolicies) === JSON.stringify(props.permissionSet.customerManagedPolicies)) {
1354
+ 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)) {
1215
1355
  return props.workingState;
1216
1356
  }
1217
1357
  const remainingPermissionSets = props.workingState.identityCenter.permissionSets.filter(
@@ -1353,6 +1493,101 @@ function removeAccountAssignmentFromWorkingState(props) {
1353
1493
  })
1354
1494
  };
1355
1495
  }
1496
+ function createOrgPolicyAttachmentKey(props) {
1497
+ return [props.policyId, props.targetId].join("|");
1498
+ }
1499
+ function upsertOrgPolicyInWorkingState(props) {
1500
+ const currentPolicy = props.workingState.organization.policiesById[props.policy.id];
1501
+ if (currentPolicy != null && currentPolicy.id === props.policy.id && currentPolicy.arn === props.policy.arn && currentPolicy.name === props.policy.name && currentPolicy.description === props.policy.description && currentPolicy.type === props.policy.type && currentPolicy.content === props.policy.content) {
1502
+ return props.workingState;
1503
+ }
1504
+ const remainingPolicies = Object.values(
1505
+ props.workingState.organization.policiesById
1506
+ ).filter((p) => p.id !== props.policy.id);
1507
+ const nextPolicies = [...remainingPolicies, props.policy];
1508
+ return {
1509
+ ...props.workingState,
1510
+ organization: {
1511
+ ...props.workingState.organization,
1512
+ policiesById: toRecordByProperty(nextPolicies, "id"),
1513
+ policiesByName: toRecordByProperty(nextPolicies, "name")
1514
+ }
1515
+ };
1516
+ }
1517
+ function removeOrgPolicyFromWorkingState(props) {
1518
+ if (props.workingState.organization.policiesById[props.policyId] == null) {
1519
+ return props.workingState;
1520
+ }
1521
+ const nextPolicies = Object.values(
1522
+ props.workingState.organization.policiesById
1523
+ ).filter((p) => p.id !== props.policyId);
1524
+ const nextAttachments = props.workingState.organization.policyAttachments.filter(
1525
+ (a) => a.policyId !== props.policyId
1526
+ );
1527
+ return {
1528
+ ...props.workingState,
1529
+ organization: {
1530
+ ...props.workingState.organization,
1531
+ policiesById: toRecordByProperty(nextPolicies, "id"),
1532
+ policiesByName: toRecordByProperty(nextPolicies, "name"),
1533
+ policyAttachments: nextAttachments,
1534
+ policyAttachmentsByKey: toRecordByProperty(
1535
+ nextAttachments,
1536
+ createOrgPolicyAttachmentKey
1537
+ )
1538
+ }
1539
+ };
1540
+ }
1541
+ function addOrgPolicyAttachmentToWorkingState(props) {
1542
+ const key = createOrgPolicyAttachmentKey({
1543
+ policyId: props.attachment.policyId,
1544
+ targetId: props.attachment.targetId
1545
+ });
1546
+ if (props.workingState.organization.policyAttachmentsByKey[key] != null) {
1547
+ return props.workingState;
1548
+ }
1549
+ const nextAttachments = [
1550
+ ...props.workingState.organization.policyAttachments,
1551
+ props.attachment
1552
+ ];
1553
+ return {
1554
+ ...props.workingState,
1555
+ organization: {
1556
+ ...props.workingState.organization,
1557
+ policyAttachments: nextAttachments,
1558
+ policyAttachmentsByKey: toRecordByProperty(
1559
+ nextAttachments,
1560
+ createOrgPolicyAttachmentKey
1561
+ )
1562
+ }
1563
+ };
1564
+ }
1565
+ function removeOrgPolicyAttachmentFromWorkingState(props) {
1566
+ const key = createOrgPolicyAttachmentKey({
1567
+ policyId: props.policyId,
1568
+ targetId: props.targetId
1569
+ });
1570
+ if (props.workingState.organization.policyAttachmentsByKey[key] == null) {
1571
+ return props.workingState;
1572
+ }
1573
+ const nextAttachments = props.workingState.organization.policyAttachments.filter(
1574
+ (a) => createOrgPolicyAttachmentKey({
1575
+ policyId: a.policyId,
1576
+ targetId: a.targetId
1577
+ }) !== key
1578
+ );
1579
+ return {
1580
+ ...props.workingState,
1581
+ organization: {
1582
+ ...props.workingState.organization,
1583
+ policyAttachments: nextAttachments,
1584
+ policyAttachmentsByKey: toRecordByProperty(
1585
+ nextAttachments,
1586
+ createOrgPolicyAttachmentKey
1587
+ )
1588
+ }
1589
+ };
1590
+ }
1356
1591
  function createAccessRoleName(assignment) {
1357
1592
  return `AWSReservedSSO_${assignment.permissionSetArn.split("/").at(-1) ?? "PermissionSet"}_${assignment.accountId}`;
1358
1593
  }
@@ -1387,7 +1622,10 @@ function createWorkingIdentityCenterState(props) {
1387
1622
  ),
1388
1623
  accessRoles: createAccessRoles({
1389
1624
  accountAssignments
1390
- })
1625
+ }),
1626
+ accessControlAttributes: structuredClone(
1627
+ props.identityCenter.accessControlAttributes ?? []
1628
+ )
1391
1629
  };
1392
1630
  }
1393
1631
  function materializeWorkingIdentityCenterState(props) {
@@ -1401,7 +1639,10 @@ function materializeWorkingIdentityCenterState(props) {
1401
1639
  accountAssignments: structuredClone(
1402
1640
  props.identityCenter.accountAssignments
1403
1641
  ),
1404
- accessRoles: structuredClone(props.identityCenter.accessRoles)
1642
+ accessRoles: structuredClone(props.identityCenter.accessRoles),
1643
+ accessControlAttributes: structuredClone(
1644
+ props.identityCenter.accessControlAttributes
1645
+ )
1405
1646
  };
1406
1647
  }
1407
1648
  function createAccessRoles(props) {
@@ -1440,11 +1681,15 @@ import {
1440
1681
  ListUsersCommand
1441
1682
  } from "@aws-sdk/client-identitystore";
1442
1683
  import {
1684
+ DescribeOrganizationCommand,
1685
+ DescribePolicyCommand,
1443
1686
  ListAccountsCommand,
1444
1687
  ListOrganizationalUnitsForParentCommand,
1445
1688
  ListParentsCommand,
1689
+ ListPoliciesCommand,
1446
1690
  ListRootsCommand,
1447
- ListTagsForResourceCommand
1691
+ ListTagsForResourceCommand,
1692
+ ListTargetsForPolicyCommand
1448
1693
  } from "@aws-sdk/client-organizations";
1449
1694
  import {
1450
1695
  DescribePermissionSetCommand,
@@ -1452,16 +1697,24 @@ import {
1452
1697
  ListAccountAssignmentsCommand,
1453
1698
  ListAccountsForProvisionedPermissionSetCommand,
1454
1699
  ListCustomerManagedPolicyReferencesInPermissionSetCommand,
1700
+ DescribeInstanceAccessControlAttributeConfigurationCommand,
1455
1701
  ListInstancesCommand,
1456
1702
  ListManagedPoliciesInPermissionSetCommand,
1457
1703
  ListPermissionSetsCommand
1458
1704
  } from "@aws-sdk/client-sso-admin";
1705
+ import {
1706
+ GetAlternateContactCommand
1707
+ } from "@aws-sdk/client-account";
1459
1708
  async function scanOrganization(props) {
1460
- const roots = await props.organizationsClient.send(new ListRootsCommand({}));
1461
- const root = roots.Roots?.[0];
1709
+ const [rootsResponse, orgResponse] = await Promise.all([
1710
+ props.organizationsClient.send(new ListRootsCommand({})),
1711
+ props.organizationsClient.send(new DescribeOrganizationCommand({}))
1712
+ ]);
1713
+ const root = rootsResponse.Roots?.[0];
1462
1714
  if (root?.Id == null) {
1463
1715
  throw new Error("No organization root found.");
1464
1716
  }
1717
+ const managementAccountId = orgResponse.Organization?.MasterAccountId;
1465
1718
  const organizationalUnits = await collectOrganizationalUnits({
1466
1719
  organizationsClient: props.organizationsClient,
1467
1720
  parentId: root.Id
@@ -1485,6 +1738,11 @@ async function scanOrganization(props) {
1485
1738
  ResourceId: account.Id
1486
1739
  })
1487
1740
  );
1741
+ const alternateContacts = await scanAlternateContacts({
1742
+ accountClient: props.accountClient,
1743
+ accountId: account.Id,
1744
+ isManagementAccount: account.Id === managementAccountId
1745
+ });
1488
1746
  accounts.push({
1489
1747
  id: account.Id,
1490
1748
  arn: account.Arn,
@@ -1502,17 +1760,95 @@ async function scanOrganization(props) {
1502
1760
  value: tag.Value ?? ""
1503
1761
  }
1504
1762
  ];
1505
- })
1763
+ }),
1764
+ alternateContacts: alternateContacts.length > 0 ? alternateContacts : void 0
1506
1765
  });
1507
1766
  }
1508
1767
  nextToken = response.NextToken;
1509
1768
  } while (nextToken != null);
1769
+ const { policies, policyAttachments } = await scanOrganizationPolicies({
1770
+ organizationsClient: props.organizationsClient
1771
+ });
1510
1772
  return {
1511
1773
  rootId: root.Id,
1512
1774
  organizationalUnits,
1513
- accounts
1775
+ accounts,
1776
+ policies,
1777
+ policyAttachments
1514
1778
  };
1515
1779
  }
1780
+ var ORG_POLICY_TYPES = [
1781
+ "SERVICE_CONTROL_POLICY",
1782
+ "RESOURCE_CONTROL_POLICY",
1783
+ "TAG_POLICY",
1784
+ "AISERVICES_OPT_OUT_POLICY"
1785
+ ];
1786
+ async function scanOrganizationPolicies(props) {
1787
+ const policies = [];
1788
+ const policyAttachments = [];
1789
+ for (const policyType of ORG_POLICY_TYPES) {
1790
+ let nextToken;
1791
+ const policyIds = [];
1792
+ do {
1793
+ const response = await props.organizationsClient.send(
1794
+ new ListPoliciesCommand({ Filter: policyType, NextToken: nextToken })
1795
+ );
1796
+ for (const summary of response.Policies ?? []) {
1797
+ if (summary.Id == null || summary.AwsManaged === true) {
1798
+ continue;
1799
+ }
1800
+ policyIds.push(summary.Id);
1801
+ }
1802
+ nextToken = response.NextToken;
1803
+ } while (nextToken != null);
1804
+ for (const policyId of policyIds) {
1805
+ const describeResponse = await props.organizationsClient.send(
1806
+ new DescribePolicyCommand({ PolicyId: policyId })
1807
+ );
1808
+ const policy = describeResponse.Policy;
1809
+ if (policy?.PolicySummary?.Id == null || policy.PolicySummary.Arn == null || policy.PolicySummary.Name == null) {
1810
+ continue;
1811
+ }
1812
+ const content = policy.Content;
1813
+ if (content == null || content.length === 0) {
1814
+ continue;
1815
+ }
1816
+ policies.push({
1817
+ id: policy.PolicySummary.Id,
1818
+ arn: policy.PolicySummary.Arn,
1819
+ name: policy.PolicySummary.Name,
1820
+ description: policy.PolicySummary.Description ?? "",
1821
+ type: policyType,
1822
+ content
1823
+ });
1824
+ let targetsNextToken;
1825
+ do {
1826
+ const targetsResponse = await props.organizationsClient.send(
1827
+ new ListTargetsForPolicyCommand({
1828
+ PolicyId: policyId,
1829
+ NextToken: targetsNextToken
1830
+ })
1831
+ );
1832
+ for (const target of targetsResponse.Targets ?? []) {
1833
+ if (target.TargetId == null || target.Type == null) {
1834
+ continue;
1835
+ }
1836
+ const targetType = target.Type;
1837
+ if (targetType !== "ROOT" && targetType !== "ORGANIZATIONAL_UNIT" && targetType !== "ACCOUNT") {
1838
+ continue;
1839
+ }
1840
+ policyAttachments.push({
1841
+ policyId,
1842
+ targetId: target.TargetId,
1843
+ targetType
1844
+ });
1845
+ }
1846
+ targetsNextToken = targetsResponse.NextToken;
1847
+ } while (targetsNextToken != null);
1848
+ }
1849
+ }
1850
+ return { policies, policyAttachments };
1851
+ }
1516
1852
  async function collectOrganizationalUnits(props) {
1517
1853
  const children = [];
1518
1854
  let nextToken;
@@ -1555,7 +1891,7 @@ async function scanIdentityCenter(props) {
1555
1891
  instances,
1556
1892
  requestedInstanceArn: props.requestedInstanceArn
1557
1893
  });
1558
- const [users, groups, permissionSets] = await Promise.all([
1894
+ const [users, groups, permissionSets, accessControlAttributes] = await Promise.all([
1559
1895
  listIdentityStoreUsers({
1560
1896
  identityStoreClient: props.identityStoreClient,
1561
1897
  identityStoreId: instance.identityStoreId
@@ -1567,6 +1903,10 @@ async function scanIdentityCenter(props) {
1567
1903
  listPermissionSets({
1568
1904
  ssoAdminClient: props.ssoAdminClient,
1569
1905
  instanceArn: instance.instanceArn
1906
+ }),
1907
+ scanAccessControlAttributes({
1908
+ ssoAdminClient: props.ssoAdminClient,
1909
+ instanceArn: instance.instanceArn
1570
1910
  })
1571
1911
  ]);
1572
1912
  const groupMemberships = await listGroupMemberships({
@@ -1591,9 +1931,30 @@ async function scanIdentityCenter(props) {
1591
1931
  groupMemberships,
1592
1932
  permissionSets,
1593
1933
  accountAssignments,
1594
- accessRoles
1934
+ accessRoles,
1935
+ accessControlAttributes
1595
1936
  };
1596
1937
  }
1938
+ async function scanAccessControlAttributes(props) {
1939
+ let response;
1940
+ try {
1941
+ response = await props.ssoAdminClient.send(
1942
+ new DescribeInstanceAccessControlAttributeConfigurationCommand({
1943
+ InstanceArn: props.instanceArn
1944
+ })
1945
+ );
1946
+ } catch (err) {
1947
+ if (err != null && typeof err === "object" && "name" in err && err.name === "ResourceNotFoundException") {
1948
+ return [];
1949
+ }
1950
+ throw err;
1951
+ }
1952
+ const attributes = response.InstanceAccessControlAttributeConfiguration?.AccessControlAttributes ?? [];
1953
+ return attributes.filter((attr) => attr.Key != null).map((attr) => ({
1954
+ key: attr.Key,
1955
+ source: attr.Value?.Source ?? []
1956
+ }));
1957
+ }
1597
1958
  function selectIdentityCenterInstance(props) {
1598
1959
  if (props.requestedInstanceArn != null) {
1599
1960
  const selected2 = props.instances.find(
@@ -1766,6 +2127,7 @@ async function listPermissionSets(props) {
1766
2127
  permissionSetArn: permissionSet.PermissionSetArn,
1767
2128
  name: permissionSet.Name,
1768
2129
  description: permissionSet.Description ?? "",
2130
+ sessionDuration: permissionSet.SessionDuration ?? null,
1769
2131
  inlinePolicy,
1770
2132
  awsManagedPolicies,
1771
2133
  customerManagedPolicies
@@ -1883,20 +2245,63 @@ async function listAccountsForPermissionSet(props) {
1883
2245
  } while (nextToken != null);
1884
2246
  return accountIds;
1885
2247
  }
2248
+ var ALTERNATE_CONTACT_TYPES = [
2249
+ "BILLING",
2250
+ "OPERATIONS",
2251
+ "SECURITY"
2252
+ ];
2253
+ async function scanAlternateContacts(props) {
2254
+ const results = await Promise.all(
2255
+ ALTERNATE_CONTACT_TYPES.map(async (contactType) => {
2256
+ try {
2257
+ const response = await props.accountClient.send(
2258
+ new GetAlternateContactCommand({
2259
+ AccountId: props.isManagementAccount ? void 0 : props.accountId,
2260
+ AlternateContactType: contactType
2261
+ })
2262
+ );
2263
+ const c = response.AlternateContact;
2264
+ if (c == null || c.EmailAddress == null || c.Name == null) {
2265
+ return null;
2266
+ }
2267
+ return {
2268
+ contactType,
2269
+ name: c.Name,
2270
+ email: c.EmailAddress,
2271
+ phone: c.PhoneNumber ?? "",
2272
+ ...c.Title != null ? { title: c.Title } : {}
2273
+ };
2274
+ } catch (error) {
2275
+ if (error != null && typeof error === "object" && "name" in error && error.name === "ResourceNotFoundException") {
2276
+ return null;
2277
+ }
2278
+ throw error;
2279
+ }
2280
+ })
2281
+ );
2282
+ return results.filter((c) => c != null);
2283
+ }
1886
2284
 
1887
2285
  // src/applyLogic.ts
1888
2286
  import {
1889
- PutAccountNameCommand
2287
+ DeleteAlternateContactCommand,
2288
+ PutAccountNameCommand,
2289
+ PutAlternateContactCommand
1890
2290
  } from "@aws-sdk/client-account";
1891
2291
  import {
2292
+ AttachPolicyCommand,
1892
2293
  CreateOrganizationalUnitCommand,
2294
+ CreatePolicyCommand,
1893
2295
  DeleteOrganizationalUnitCommand,
2296
+ DeletePolicyCommand,
2297
+ DetachPolicyCommand,
1894
2298
  ListAccountsForParentCommand,
1895
2299
  ListOrganizationalUnitsForParentCommand as ListOrganizationalUnitsForParentCommand2,
1896
2300
  MoveAccountCommand as MoveAccountCommand2,
1897
2301
  TagResourceCommand,
1898
2302
  UntagResourceCommand,
1899
- UpdateOrganizationalUnitCommand
2303
+ UpdateOrganizationalUnitCommand,
2304
+ UpdatePolicyCommand
1900
2305
  } from "@aws-sdk/client-organizations";
1901
2306
  import {
1902
2307
  CreateGroupMembershipCommand,
@@ -1924,6 +2329,7 @@ import {
1924
2329
  DetachManagedPolicyFromPermissionSetCommand,
1925
2330
  ProvisionPermissionSetCommand,
1926
2331
  PutInlinePolicyToPermissionSetCommand,
2332
+ UpdateInstanceAccessControlAttributeConfigurationCommand,
1927
2333
  UpdatePermissionSetCommand
1928
2334
  } from "@aws-sdk/client-sso-admin";
1929
2335
 
@@ -2206,7 +2612,10 @@ async function executeOperation(props) {
2206
2612
  workingState: props.state,
2207
2613
  account: {
2208
2614
  ...account,
2209
- tags: Object.entries(operation.tags).map(([key, value]) => ({ key, value }))
2615
+ tags: Object.entries(operation.tags).map(([key, value]) => ({
2616
+ key,
2617
+ value
2618
+ }))
2210
2619
  }
2211
2620
  });
2212
2621
  }
@@ -2248,7 +2657,9 @@ async function executeOperation(props) {
2248
2657
  DestinationParentId: operation.toOuId
2249
2658
  })
2250
2659
  );
2251
- props.logger.log(`Done: "${operation.accountName}" -> ${operation.toOuName}`);
2660
+ props.logger.log(
2661
+ `Done: "${operation.accountName}" -> ${operation.toOuName}`
2662
+ );
2252
2663
  return moveAccountInWorkingState({
2253
2664
  workingState: props.state,
2254
2665
  accountId: operation.accountId,
@@ -2475,7 +2886,8 @@ async function executeOperation(props) {
2475
2886
  new CreatePermissionSetCommand({
2476
2887
  InstanceArn: props.state.identityCenter.instanceArn,
2477
2888
  Name: operation.permissionSetName,
2478
- Description: operation.description.length > 0 ? operation.description : void 0
2889
+ Description: operation.description.length > 0 ? operation.description : void 0,
2890
+ SessionDuration: operation.sessionDuration ?? void 0
2479
2891
  })
2480
2892
  );
2481
2893
  const permissionSetArn = response.PermissionSet?.PermissionSetArn;
@@ -2491,6 +2903,7 @@ async function executeOperation(props) {
2491
2903
  permissionSetArn,
2492
2904
  name: operation.permissionSetName,
2493
2905
  description: operation.description,
2906
+ sessionDuration: operation.sessionDuration,
2494
2907
  inlinePolicy: null,
2495
2908
  awsManagedPolicies: [],
2496
2909
  customerManagedPolicies: []
@@ -2521,6 +2934,30 @@ async function executeOperation(props) {
2521
2934
  }
2522
2935
  });
2523
2936
  }
2937
+ if (operation.kind === "updateIdcPermissionSetSessionDuration") {
2938
+ const permissionSet = resolvePermissionSetByName({
2939
+ state: props.state,
2940
+ permissionSetName: operation.permissionSetName
2941
+ });
2942
+ props.logger.log(
2943
+ `Updating IdC permission set session duration for "${operation.permissionSetName}"...`
2944
+ );
2945
+ await props.ssoAdminClient.send(
2946
+ new UpdatePermissionSetCommand({
2947
+ InstanceArn: props.state.identityCenter.instanceArn,
2948
+ PermissionSetArn: permissionSet.permissionSetArn,
2949
+ SessionDuration: operation.sessionDuration ?? void 0
2950
+ })
2951
+ );
2952
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2953
+ return upsertIdcPermissionSetInWorkingState({
2954
+ workingState: props.state,
2955
+ permissionSet: {
2956
+ ...permissionSet,
2957
+ sessionDuration: operation.sessionDuration
2958
+ }
2959
+ });
2960
+ }
2524
2961
  if (operation.kind === "deleteIdcPermissionSet") {
2525
2962
  const permissionSet = resolvePermissionSetByName({
2526
2963
  state: props.state,
@@ -2882,6 +3319,218 @@ async function executeOperation(props) {
2882
3319
  }
2883
3320
  });
2884
3321
  }
3322
+ if (operation.kind === "createOrgPolicy") {
3323
+ props.logger.log(
3324
+ `Creating org policy "${operation.policyName}" (${operation.policyType})...`
3325
+ );
3326
+ const response = await props.organizationsClient.send(
3327
+ new CreatePolicyCommand({
3328
+ Name: operation.policyName,
3329
+ Description: operation.description.length > 0 ? operation.description : void 0,
3330
+ Content: operation.content,
3331
+ Type: operation.policyType
3332
+ })
3333
+ );
3334
+ const policy = response.Policy?.PolicySummary;
3335
+ if (policy?.Id == null || policy.Arn == null) {
3336
+ throw new Error(
3337
+ `CreatePolicy for "${operation.policyName}" returned incomplete data.`
3338
+ );
3339
+ }
3340
+ props.logger.log(`Done: "${operation.policyName}"`);
3341
+ return upsertOrgPolicyInWorkingState({
3342
+ workingState: props.state,
3343
+ policy: {
3344
+ id: policy.Id,
3345
+ arn: policy.Arn,
3346
+ name: operation.policyName,
3347
+ description: operation.description,
3348
+ type: operation.policyType,
3349
+ content: operation.content
3350
+ }
3351
+ });
3352
+ }
3353
+ if (operation.kind === "updateOrgPolicyContent") {
3354
+ props.logger.log(`Updating org policy content "${operation.policyName}"...`);
3355
+ await props.organizationsClient.send(
3356
+ new UpdatePolicyCommand({
3357
+ PolicyId: operation.policyId,
3358
+ Content: operation.content
3359
+ })
3360
+ );
3361
+ props.logger.log(`Done: "${operation.policyName}"`);
3362
+ const currentPolicy = props.state.organization.policiesById[operation.policyId];
3363
+ if (currentPolicy == null) {
3364
+ return props.state;
3365
+ }
3366
+ return upsertOrgPolicyInWorkingState({
3367
+ workingState: props.state,
3368
+ policy: { ...currentPolicy, content: operation.content }
3369
+ });
3370
+ }
3371
+ if (operation.kind === "updateOrgPolicyDescription") {
3372
+ props.logger.log(
3373
+ `Updating org policy description "${operation.policyName}"...`
3374
+ );
3375
+ await props.organizationsClient.send(
3376
+ new UpdatePolicyCommand({
3377
+ PolicyId: operation.policyId,
3378
+ Description: operation.description
3379
+ })
3380
+ );
3381
+ props.logger.log(`Done: "${operation.policyName}"`);
3382
+ const currentPolicy = props.state.organization.policiesById[operation.policyId];
3383
+ if (currentPolicy == null) {
3384
+ return props.state;
3385
+ }
3386
+ return upsertOrgPolicyInWorkingState({
3387
+ workingState: props.state,
3388
+ policy: { ...currentPolicy, description: operation.description }
3389
+ });
3390
+ }
3391
+ if (operation.kind === "attachOrgPolicy") {
3392
+ props.logger.log(
3393
+ `Attaching org policy "${operation.policyName}" to "${operation.targetName}"...`
3394
+ );
3395
+ const resolvedPolicyId = resolvePolicyId({
3396
+ state: props.state,
3397
+ policyId: operation.policyId,
3398
+ policyName: operation.policyName
3399
+ });
3400
+ await props.organizationsClient.send(
3401
+ new AttachPolicyCommand({
3402
+ PolicyId: resolvedPolicyId,
3403
+ TargetId: operation.targetId
3404
+ })
3405
+ );
3406
+ props.logger.log(`Done: "${operation.policyName}" -> "${operation.targetName}"`);
3407
+ const targetType = operation.targetId === props.context.organization.rootId ? "ROOT" : props.state.organization.organizationalUnitsById[operation.targetId] != null ? "ORGANIZATIONAL_UNIT" : "ACCOUNT";
3408
+ return addOrgPolicyAttachmentToWorkingState({
3409
+ workingState: props.state,
3410
+ attachment: {
3411
+ policyId: resolvedPolicyId,
3412
+ targetId: operation.targetId,
3413
+ targetType
3414
+ }
3415
+ });
3416
+ }
3417
+ if (operation.kind === "detachOrgPolicy") {
3418
+ props.logger.log(
3419
+ `Detaching org policy "${operation.policyName}" from "${operation.targetName}"...`
3420
+ );
3421
+ await props.organizationsClient.send(
3422
+ new DetachPolicyCommand({
3423
+ PolicyId: operation.policyId,
3424
+ TargetId: operation.targetId
3425
+ })
3426
+ );
3427
+ props.logger.log(`Done: "${operation.policyName}" x "${operation.targetName}"`);
3428
+ return removeOrgPolicyAttachmentFromWorkingState({
3429
+ workingState: props.state,
3430
+ policyId: operation.policyId,
3431
+ targetId: operation.targetId
3432
+ });
3433
+ }
3434
+ if (operation.kind === "deleteOrgPolicy") {
3435
+ props.logger.log(`Deleting org policy "${operation.policyName}"...`);
3436
+ await props.organizationsClient.send(
3437
+ new DeletePolicyCommand({ PolicyId: operation.policyId })
3438
+ );
3439
+ props.logger.log(`Done: "${operation.policyName}"`);
3440
+ return removeOrgPolicyFromWorkingState({
3441
+ workingState: props.state,
3442
+ policyId: operation.policyId
3443
+ });
3444
+ }
3445
+ if (operation.kind === "putAlternateContact") {
3446
+ props.logger.log(
3447
+ `Setting ${operation.contactType} alternate contact for "${operation.accountName}" (${operation.accountId})...`
3448
+ );
3449
+ await props.accountClient.send(
3450
+ new PutAlternateContactCommand({
3451
+ AccountId: operation.accountId,
3452
+ AlternateContactType: operation.contactType,
3453
+ Name: operation.name,
3454
+ EmailAddress: operation.email,
3455
+ PhoneNumber: operation.phone,
3456
+ Title: operation.title
3457
+ })
3458
+ );
3459
+ props.logger.log(`Done: ${operation.contactType} contact for "${operation.accountName}"`);
3460
+ const account = props.state.organization.accountsById[operation.accountId];
3461
+ if (account == null) {
3462
+ throw new Error(
3463
+ `Could not resolve account (${operation.accountId}) in working state.`
3464
+ );
3465
+ }
3466
+ const updatedContacts = [
3467
+ ...(account.alternateContacts ?? []).filter(
3468
+ (c) => c.contactType !== operation.contactType
3469
+ ),
3470
+ {
3471
+ contactType: operation.contactType,
3472
+ name: operation.name,
3473
+ email: operation.email,
3474
+ phone: operation.phone,
3475
+ title: operation.title
3476
+ }
3477
+ ];
3478
+ return upsertAccountInWorkingState({
3479
+ workingState: props.state,
3480
+ account: { ...account, alternateContacts: updatedContacts }
3481
+ });
3482
+ }
3483
+ if (operation.kind === "deleteAlternateContact") {
3484
+ props.logger.log(
3485
+ `Deleting ${operation.contactType} alternate contact for "${operation.accountName}" (${operation.accountId})...`
3486
+ );
3487
+ await props.accountClient.send(
3488
+ new DeleteAlternateContactCommand({
3489
+ AccountId: operation.accountId,
3490
+ AlternateContactType: operation.contactType
3491
+ })
3492
+ );
3493
+ props.logger.log(`Done: removed ${operation.contactType} contact for "${operation.accountName}"`);
3494
+ const account = props.state.organization.accountsById[operation.accountId];
3495
+ if (account == null) {
3496
+ throw new Error(
3497
+ `Could not resolve account (${operation.accountId}) in working state.`
3498
+ );
3499
+ }
3500
+ return upsertAccountInWorkingState({
3501
+ workingState: props.state,
3502
+ account: {
3503
+ ...account,
3504
+ alternateContacts: (account.alternateContacts ?? []).filter(
3505
+ (c) => c.contactType !== operation.contactType
3506
+ )
3507
+ }
3508
+ });
3509
+ }
3510
+ if (operation.kind === "setIdcAccessControlAttributes") {
3511
+ props.logger.log(
3512
+ `Setting IdC access control attributes (${operation.attributes.length} attribute(s))...`
3513
+ );
3514
+ await props.ssoAdminClient.send(
3515
+ new UpdateInstanceAccessControlAttributeConfigurationCommand({
3516
+ InstanceArn: props.state.identityCenter.instanceArn,
3517
+ InstanceAccessControlAttributeConfiguration: {
3518
+ AccessControlAttributes: operation.attributes.map((attr) => ({
3519
+ Key: attr.key,
3520
+ Value: { Source: attr.source }
3521
+ }))
3522
+ }
3523
+ })
3524
+ );
3525
+ props.logger.log(`Done: access control attributes updated`);
3526
+ return {
3527
+ ...props.state,
3528
+ identityCenter: {
3529
+ ...props.state.identityCenter,
3530
+ accessControlAttributes: operation.attributes
3531
+ }
3532
+ };
3533
+ }
2885
3534
  assertUnreachable(operation, "Unsupported operation kind in apply.");
2886
3535
  }
2887
3536
  function resolveAssignmentDependencies(props) {
@@ -2942,6 +3591,16 @@ function resolveGroupByDisplayName(props) {
2942
3591
  }
2943
3592
  return group;
2944
3593
  }
3594
+ function resolvePolicyId(props) {
3595
+ if (props.policyId !== "__pending_creation__") return props.policyId;
3596
+ const policy = props.state.organization.policiesByName[props.policyName];
3597
+ if (policy == null) {
3598
+ throw new Error(
3599
+ `Could not resolve policy "${props.policyName}" in working state.`
3600
+ );
3601
+ }
3602
+ return policy.id;
3603
+ }
2945
3604
  function resolvePermissionSetByName(props) {
2946
3605
  const permissionSet = props.state.identityCenter.permissionSetsByName[props.permissionSetName];
2947
3606
  if (permissionSet == null) {
@@ -2961,9 +3620,9 @@ function upsertPermissionSetPolicyState(props) {
2961
3620
  workingState: props.state,
2962
3621
  permissionSet: {
2963
3622
  ...nextPermissionSet,
2964
- awsManagedPolicies: [...new Set(nextPermissionSet.awsManagedPolicies)].sort(
2965
- (left, right) => left.localeCompare(right)
2966
- ),
3623
+ awsManagedPolicies: [
3624
+ ...new Set(nextPermissionSet.awsManagedPolicies)
3625
+ ].sort((left, right) => left.localeCompare(right)),
2967
3626
  customerManagedPolicies: [
2968
3627
  ...nextPermissionSet.customerManagedPolicies
2969
3628
  ].sort((left, right) => {
@@ -2994,11 +3653,13 @@ async function assertOrganizationalUnitIsEmpty(props) {
2994
3653
  });
2995
3654
  if (childOrganizationalUnit != null) {
2996
3655
  throw new Error(
2997
- `Refusing to delete OU "${props.organizationalUnitName}": live AWS preflight failed [child-ou-present]: ${formatLivePreflightResource({
2998
- resourceType: "child OU",
2999
- name: childOrganizationalUnit.Name,
3000
- id: childOrganizationalUnit.Id
3001
- })} is still attached.`
3656
+ `Refusing to delete OU "${props.organizationalUnitName}": live AWS preflight failed [child-ou-present]: ${formatLivePreflightResource(
3657
+ {
3658
+ resourceType: "child OU",
3659
+ name: childOrganizationalUnit.Name,
3660
+ id: childOrganizationalUnit.Id
3661
+ }
3662
+ )} is still attached.`
3002
3663
  );
3003
3664
  }
3004
3665
  const account = await listFirstAccountForParent({
@@ -3007,11 +3668,13 @@ async function assertOrganizationalUnitIsEmpty(props) {
3007
3668
  });
3008
3669
  if (account != null) {
3009
3670
  throw new Error(
3010
- `Refusing to delete OU "${props.organizationalUnitName}": live AWS preflight failed [account-present]: ${formatLivePreflightResource({
3011
- resourceType: "account",
3012
- name: account.Name,
3013
- id: account.Id
3014
- })} is still attached.`
3671
+ `Refusing to delete OU "${props.organizationalUnitName}": live AWS preflight failed [account-present]: ${formatLivePreflightResource(
3672
+ {
3673
+ resourceType: "account",
3674
+ name: account.Name,
3675
+ id: account.Id
3676
+ }
3677
+ )} is still attached.`
3015
3678
  );
3016
3679
  }
3017
3680
  }
@@ -3227,7 +3890,9 @@ var scanResponseSchema = strictObject({
3227
3890
  users: number(),
3228
3891
  groups: number(),
3229
3892
  permissionSets: number(),
3230
- accountAssignments: number()
3893
+ accountAssignments: number(),
3894
+ policies: number(),
3895
+ policyAttachments: number()
3231
3896
  }),
3232
3897
  state: stateSchema
3233
3898
  });
@@ -3297,7 +3962,7 @@ var s3Client = new S3Client({});
3297
3962
  var organizationsClient = new OrganizationsClient3({});
3298
3963
  var ssoAdminClient = new SSOAdminClient3({});
3299
3964
  var identityStoreClient = new IdentitystoreClient3({});
3300
- var accountClient = new AccountClient2({});
3965
+ var accountClient = new AccountClient3({});
3301
3966
  async function handler(event) {
3302
3967
  try {
3303
3968
  const parseResult = safeParse(lambdaRequestSchema, event);
@@ -3322,7 +3987,7 @@ async function handler(event) {
3322
3987
  return validateResponse(response);
3323
3988
  }
3324
3989
  if (request.action === "scan") {
3325
- const response = await handleScan({ s3Client, bucket, organizationsClient, ssoAdminClient, identityStoreClient });
3990
+ const response = await handleScan({ s3Client, bucket, organizationsClient, ssoAdminClient, identityStoreClient, accountClient });
3326
3991
  return validateResponse(response);
3327
3992
  }
3328
3993
  if (request.action === "getStateUrl") {
@@ -3411,7 +4076,7 @@ function isS3PreconditionFailed(error) {
3411
4076
  async function handleScan(props) {
3412
4077
  const identityCenterInstanceArn = process.env.IDENTITY_CENTER_INSTANCE_ARN || void 0;
3413
4078
  const [organization, identityCenter] = await Promise.all([
3414
- scanOrganization({ organizationsClient: props.organizationsClient }),
4079
+ scanOrganization({ organizationsClient: props.organizationsClient, accountClient: props.accountClient }),
3415
4080
  scanIdentityCenter({
3416
4081
  ssoAdminClient: props.ssoAdminClient,
3417
4082
  identityStoreClient: props.identityStoreClient,
@@ -3438,7 +4103,9 @@ async function handleScan(props) {
3438
4103
  users: state.identityCenter.users.length,
3439
4104
  groups: state.identityCenter.groups.length,
3440
4105
  permissionSets: state.identityCenter.permissionSets.length,
3441
- accountAssignments: state.identityCenter.accountAssignments.length
4106
+ accountAssignments: state.identityCenter.accountAssignments.length,
4107
+ policies: state.organization.policies?.length ?? 0,
4108
+ policyAttachments: state.organization.policyAttachments?.length ?? 0
3442
4109
  },
3443
4110
  state
3444
4111
  };