@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.
- package/README.md +80 -11
- package/dist/applyLogic.js +288 -19
- package/dist/awsConfig.js +414 -32
- package/dist/cli.js +95 -25
- package/dist/commands/graveyard.js +27 -0
- package/dist/commands/profile.js +116 -0
- package/dist/commands/remote.js +152 -47
- package/dist/commands/validate.js +125 -0
- package/dist/diff.js +278 -22
- package/dist/lambda/handler.js +8 -4
- package/dist/lambdaClient.js +5 -2
- package/dist/operations.js +91 -2
- package/dist/scanLogic.js +164 -7
- package/dist/state.js +164 -7
- package/dist-lambda/handler.mjs +707 -40
- package/dist-lambda/lambda.zip +0 -0
- package/package.json +1 -1
package/dist-lambda/handler.mjs
CHANGED
|
@@ -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
|
|
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
|
|
1461
|
-
|
|
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
|
-
|
|
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]) => ({
|
|
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(
|
|
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: [
|
|
2965
|
-
(
|
|
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
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
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
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
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
|
|
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
|
};
|