@beesolve/aws-accounts 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/applyLogic.js +571 -225
- package/dist/awsConfig.js +441 -30
- package/dist/cli.js +86 -23
- package/dist/commands/graveyard.js +27 -0
- package/dist/commands/remote.js +206 -39
- package/dist/commands/validate.js +75 -0
- package/dist/diff.js +336 -14
- package/dist/lambda/handler.js +8 -4
- package/dist/lambdaClient.js +5 -2
- package/dist/operations.js +116 -1
- package/dist/scanLogic.js +237 -9
- package/dist/state.js +244 -8
- package/dist-lambda/handler.mjs +1164 -248
- package/dist-lambda/lambda.zip +0 -0
- package/package.json +2 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadAwsConfigModelFromTsFile } from "../awsConfig.js";
|
|
2
2
|
const INLINE_POLICY_MAX_CHARS = 10240;
|
|
3
|
+
const ORG_POLICY_CONTENT_MAX_BYTES = 5120;
|
|
3
4
|
async function runValidateCommand(input) {
|
|
4
5
|
const configPath = input.configPath ?? "aws.config.ts";
|
|
5
6
|
const typesPath = input.typesPath ?? "aws.config.types.ts";
|
|
@@ -14,6 +15,9 @@ async function runValidateCommand(input) {
|
|
|
14
15
|
checkCircularOuReferences(config, errors);
|
|
15
16
|
checkAssignmentPrincipals(config, errors);
|
|
16
17
|
checkInlinePolicySizes(config, errors);
|
|
18
|
+
checkOrgPolicySizes(config, errors);
|
|
19
|
+
checkOrgPolicyTargets(config, errors);
|
|
20
|
+
checkDelegatedAdministratorDuplicates(config, errors);
|
|
17
21
|
if (errors.length > 0) {
|
|
18
22
|
for (const error of errors) {
|
|
19
23
|
input.logger.log(`Error: ${error}`);
|
|
@@ -62,6 +66,77 @@ function checkAssignmentPrincipals(config, errors) {
|
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
}
|
|
69
|
+
function checkOrgPolicySizes(config, errors) {
|
|
70
|
+
for (const policy of config.policies.serviceControlPolicies) {
|
|
71
|
+
const contentBytes = Buffer.byteLength(JSON.stringify(policy.content), "utf8");
|
|
72
|
+
if (contentBytes > ORG_POLICY_CONTENT_MAX_BYTES) {
|
|
73
|
+
errors.push(
|
|
74
|
+
`Service control policy "${policy.name}" content is ${contentBytes} bytes (limit: ${ORG_POLICY_CONTENT_MAX_BYTES}).`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const policy of config.policies.resourceControlPolicies) {
|
|
79
|
+
const contentBytes = Buffer.byteLength(JSON.stringify(policy.content), "utf8");
|
|
80
|
+
if (contentBytes > ORG_POLICY_CONTENT_MAX_BYTES) {
|
|
81
|
+
errors.push(
|
|
82
|
+
`Resource control policy "${policy.name}" content is ${contentBytes} bytes (limit: ${ORG_POLICY_CONTENT_MAX_BYTES}).`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const policy of config.policies.backupPolicies) {
|
|
87
|
+
const contentBytes = Buffer.byteLength(JSON.stringify(policy.content), "utf8");
|
|
88
|
+
if (contentBytes > ORG_POLICY_CONTENT_MAX_BYTES) {
|
|
89
|
+
errors.push(
|
|
90
|
+
`Backup policy "${policy.name}" content is ${contentBytes} bytes (limit: ${ORG_POLICY_CONTENT_MAX_BYTES}).`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function checkOrgPolicyTargets(config, errors) {
|
|
96
|
+
const ouNames = new Set(config.organizationalUnits.map((ou) => ou.name));
|
|
97
|
+
const accountNames = new Set(
|
|
98
|
+
config.organizationalUnits.flatMap((ou) => ou.accounts.map((a) => a.name))
|
|
99
|
+
);
|
|
100
|
+
for (const policy of config.policies.serviceControlPolicies) {
|
|
101
|
+
for (const target of policy.targets) {
|
|
102
|
+
if (target !== "root" && !ouNames.has(target) && !accountNames.has(target)) {
|
|
103
|
+
errors.push(
|
|
104
|
+
`Service control policy "${policy.name}" references unknown target "${target}".`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const policy of config.policies.resourceControlPolicies) {
|
|
110
|
+
for (const target of policy.targets) {
|
|
111
|
+
if (target !== "root" && !ouNames.has(target) && !accountNames.has(target)) {
|
|
112
|
+
errors.push(
|
|
113
|
+
`Resource control policy "${policy.name}" references unknown target "${target}".`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
for (const policy of config.policies.backupPolicies) {
|
|
119
|
+
for (const target of policy.targets) {
|
|
120
|
+
if (target !== "root" && !ouNames.has(target) && !accountNames.has(target)) {
|
|
121
|
+
errors.push(
|
|
122
|
+
`Backup policy "${policy.name}" references unknown target "${target}".`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function checkDelegatedAdministratorDuplicates(config, errors) {
|
|
129
|
+
const seen = /* @__PURE__ */ new Set();
|
|
130
|
+
for (const da of config.delegatedAdministrators) {
|
|
131
|
+
const key = `${da.account}|${da.servicePrincipal}`;
|
|
132
|
+
if (seen.has(key)) {
|
|
133
|
+
errors.push(
|
|
134
|
+
`Duplicate delegated administrator entry: account "${da.account}" + service principal "${da.servicePrincipal}".`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
seen.add(key);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
65
140
|
function checkInlinePolicySizes(config, errors) {
|
|
66
141
|
for (const ps of config.permissionSets) {
|
|
67
142
|
if (ps.inlinePolicy == null) {
|
package/dist/diff.js
CHANGED
|
@@ -26,13 +26,26 @@ const operationExecutionPriority = {
|
|
|
26
26
|
attachIdcCustomerManagedPolicyReferenceToPermissionSet: 20,
|
|
27
27
|
detachIdcCustomerManagedPolicyReferenceFromPermissionSet: 21,
|
|
28
28
|
provisionIdcPermissionSet: 22,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
putIdcPermissionSetPermissionsBoundary: 23,
|
|
30
|
+
deleteIdcPermissionSetPermissionsBoundary: 24,
|
|
31
|
+
grantIdcAccountAssignment: 25,
|
|
32
|
+
removeIdcGroupMembership: 26,
|
|
33
|
+
revokeIdcAccountAssignment: 27,
|
|
34
|
+
deleteIdcUser: 28,
|
|
35
|
+
deleteIdcGroup: 29,
|
|
36
|
+
deleteIdcPermissionSet: 30,
|
|
37
|
+
deleteOu: 31,
|
|
38
|
+
createOrgPolicy: 32,
|
|
39
|
+
updateOrgPolicyContent: 33,
|
|
40
|
+
updateOrgPolicyDescription: 34,
|
|
41
|
+
attachOrgPolicy: 35,
|
|
42
|
+
detachOrgPolicy: 36,
|
|
43
|
+
deleteOrgPolicy: 37,
|
|
44
|
+
putAlternateContact: 38,
|
|
45
|
+
deleteAlternateContact: 39,
|
|
46
|
+
setIdcAccessControlAttributes: 40,
|
|
47
|
+
registerDelegatedAdministrator: 41,
|
|
48
|
+
deregisterDelegatedAdministrator: 42
|
|
36
49
|
};
|
|
37
50
|
function diffStates(props) {
|
|
38
51
|
const operations = [];
|
|
@@ -99,6 +112,13 @@ function diffStates(props) {
|
|
|
99
112
|
)
|
|
100
113
|
});
|
|
101
114
|
}
|
|
115
|
+
diffAlternateContacts({
|
|
116
|
+
operations,
|
|
117
|
+
accountId: nextAccount.id,
|
|
118
|
+
accountName: nextAccount.name,
|
|
119
|
+
currentContacts: currentAccount.alternateContacts ?? [],
|
|
120
|
+
nextContacts: nextAccount.alternateContacts ?? []
|
|
121
|
+
});
|
|
102
122
|
continue;
|
|
103
123
|
}
|
|
104
124
|
if (currentAccount.id === pendingCreationId || nextAccount.id === pendingCreationId || currentAccount.parentId === pendingCreationId || nextAccount.parentId === pendingCreationId) {
|
|
@@ -131,6 +151,13 @@ function diffStates(props) {
|
|
|
131
151
|
toOuId: nextAccount.parentId,
|
|
132
152
|
toOuName
|
|
133
153
|
});
|
|
154
|
+
diffAlternateContacts({
|
|
155
|
+
operations,
|
|
156
|
+
accountId: nextAccount.id,
|
|
157
|
+
accountName: nextAccount.name,
|
|
158
|
+
currentContacts: currentAccount.alternateContacts ?? [],
|
|
159
|
+
nextContacts: nextAccount.alternateContacts ?? []
|
|
160
|
+
});
|
|
134
161
|
}
|
|
135
162
|
const graveyardOrganizationalUnit = currentOrganization.organizationalUnitByName.get("Graveyard");
|
|
136
163
|
for (const currentAccount of currentOrganization.accounts) {
|
|
@@ -192,13 +219,17 @@ function diffStates(props) {
|
|
|
192
219
|
const addedOrganizationalUnits = [];
|
|
193
220
|
const removedOrganizationalUnits = [];
|
|
194
221
|
for (const nextOrganizationalUnit of nextOrganization.organizationalUnits) {
|
|
195
|
-
if (currentOrganization.organizationalUnitByName.has(
|
|
222
|
+
if (currentOrganization.organizationalUnitByName.has(
|
|
223
|
+
nextOrganizationalUnit.name
|
|
224
|
+
)) {
|
|
196
225
|
continue;
|
|
197
226
|
}
|
|
198
227
|
addedOrganizationalUnits.push(nextOrganizationalUnit);
|
|
199
228
|
}
|
|
200
229
|
for (const currentOrganizationalUnit of currentOrganization.organizationalUnits) {
|
|
201
|
-
if (nextOrganization.organizationalUnitByName.has(
|
|
230
|
+
if (nextOrganization.organizationalUnitByName.has(
|
|
231
|
+
currentOrganizationalUnit.name
|
|
232
|
+
)) {
|
|
202
233
|
continue;
|
|
203
234
|
}
|
|
204
235
|
removedOrganizationalUnits.push(currentOrganizationalUnit);
|
|
@@ -436,7 +467,9 @@ function diffStates(props) {
|
|
|
436
467
|
});
|
|
437
468
|
}
|
|
438
469
|
for (const nextPermissionSet of props.next.identityCenter.permissionSets) {
|
|
439
|
-
const currentPermissionSet = currentIdcView.permissionSetsByName.get(
|
|
470
|
+
const currentPermissionSet = currentIdcView.permissionSetsByName.get(
|
|
471
|
+
nextPermissionSet.name
|
|
472
|
+
);
|
|
440
473
|
if (currentPermissionSet == null) {
|
|
441
474
|
operations.push({
|
|
442
475
|
kind: "createIdcPermissionSet",
|
|
@@ -484,7 +517,9 @@ function diffStates(props) {
|
|
|
484
517
|
const currentAwsManagedPolicies = new Set(
|
|
485
518
|
currentPermissionSet?.awsManagedPolicies ?? []
|
|
486
519
|
);
|
|
487
|
-
const nextAwsManagedPolicies = new Set(
|
|
520
|
+
const nextAwsManagedPolicies = new Set(
|
|
521
|
+
nextPermissionSet.awsManagedPolicies
|
|
522
|
+
);
|
|
488
523
|
for (const managedPolicyArn of nextAwsManagedPolicies) {
|
|
489
524
|
if (currentAwsManagedPolicies.has(managedPolicyArn)) {
|
|
490
525
|
continue;
|
|
@@ -517,7 +552,10 @@ function diffStates(props) {
|
|
|
517
552
|
policy
|
|
518
553
|
])
|
|
519
554
|
);
|
|
520
|
-
for (const [
|
|
555
|
+
for (const [
|
|
556
|
+
policyKey,
|
|
557
|
+
customerManagedPolicy
|
|
558
|
+
] of nextCustomerManagedPolicies) {
|
|
521
559
|
if (currentCustomerManagedPolicies.has(policyKey)) {
|
|
522
560
|
continue;
|
|
523
561
|
}
|
|
@@ -528,7 +566,10 @@ function diffStates(props) {
|
|
|
528
566
|
customerManagedPolicyPath: customerManagedPolicy.path
|
|
529
567
|
});
|
|
530
568
|
}
|
|
531
|
-
for (const [
|
|
569
|
+
for (const [
|
|
570
|
+
policyKey,
|
|
571
|
+
customerManagedPolicy
|
|
572
|
+
] of currentCustomerManagedPolicies) {
|
|
532
573
|
if (nextCustomerManagedPolicies.has(policyKey)) {
|
|
533
574
|
continue;
|
|
534
575
|
}
|
|
@@ -539,6 +580,23 @@ function diffStates(props) {
|
|
|
539
580
|
customerManagedPolicyPath: customerManagedPolicy.path
|
|
540
581
|
});
|
|
541
582
|
}
|
|
583
|
+
const currentBoundary = currentPermissionSet?.permissionsBoundary ?? null;
|
|
584
|
+
const nextBoundary = nextPermissionSet.permissionsBoundary ?? null;
|
|
585
|
+
if (nextBoundary != null) {
|
|
586
|
+
if (currentBoundary == null || JSON.stringify(currentBoundary) !== JSON.stringify(nextBoundary)) {
|
|
587
|
+
operations.push({
|
|
588
|
+
kind: "putIdcPermissionSetPermissionsBoundary",
|
|
589
|
+
permissionSetName: nextPermissionSet.name,
|
|
590
|
+
permissionsBoundary: nextBoundary
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (nextBoundary == null && currentBoundary != null) {
|
|
595
|
+
operations.push({
|
|
596
|
+
kind: "deleteIdcPermissionSetPermissionsBoundary",
|
|
597
|
+
permissionSetName: nextPermissionSet.name
|
|
598
|
+
});
|
|
599
|
+
}
|
|
542
600
|
if (currentPermissionSet != null && operations.length > permissionSetMutationStartIndex && permissionSetNamesWithDesiredAssignments.has(nextPermissionSet.name)) {
|
|
543
601
|
operations.push({
|
|
544
602
|
kind: "provisionIdcPermissionSet",
|
|
@@ -595,6 +653,204 @@ function diffStates(props) {
|
|
|
595
653
|
permissionSetName: removedPermissionSetName
|
|
596
654
|
});
|
|
597
655
|
}
|
|
656
|
+
const currentAccessControlAttributes = props.current.identityCenter.accessControlAttributes ?? [];
|
|
657
|
+
const nextAccessControlAttributes = props.next.identityCenter.accessControlAttributes ?? [];
|
|
658
|
+
if (JSON.stringify(
|
|
659
|
+
[...currentAccessControlAttributes].sort(
|
|
660
|
+
(a, b) => a.key.localeCompare(b.key)
|
|
661
|
+
)
|
|
662
|
+
) !== JSON.stringify(
|
|
663
|
+
[...nextAccessControlAttributes].sort(
|
|
664
|
+
(a, b) => a.key.localeCompare(b.key)
|
|
665
|
+
)
|
|
666
|
+
)) {
|
|
667
|
+
operations.push({
|
|
668
|
+
kind: "setIdcAccessControlAttributes",
|
|
669
|
+
attributes: nextAccessControlAttributes
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
const nextAccountNameById = new Map(
|
|
673
|
+
props.next.organization.accounts.map((account) => [
|
|
674
|
+
account.id,
|
|
675
|
+
account.name
|
|
676
|
+
])
|
|
677
|
+
);
|
|
678
|
+
const currentAccountNameById = new Map(
|
|
679
|
+
props.current.organization.accounts.map((account) => [
|
|
680
|
+
account.id,
|
|
681
|
+
account.name
|
|
682
|
+
])
|
|
683
|
+
);
|
|
684
|
+
const currentDelegatedAdmins = props.current.organization.delegatedAdministrators ?? [];
|
|
685
|
+
const nextDelegatedAdmins = props.next.organization.delegatedAdministrators ?? [];
|
|
686
|
+
const nextDelegatedAdminKeys = new Set(
|
|
687
|
+
nextDelegatedAdmins.map((da) => `${da.accountId}|${da.servicePrincipal}`)
|
|
688
|
+
);
|
|
689
|
+
const currentDelegatedAdminKeys = new Set(
|
|
690
|
+
currentDelegatedAdmins.map(
|
|
691
|
+
(da) => `${da.accountId}|${da.servicePrincipal}`
|
|
692
|
+
)
|
|
693
|
+
);
|
|
694
|
+
for (const nextDa of nextDelegatedAdmins) {
|
|
695
|
+
if (currentDelegatedAdminKeys.has(
|
|
696
|
+
`${nextDa.accountId}|${nextDa.servicePrincipal}`
|
|
697
|
+
)) {
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
operations.push({
|
|
701
|
+
kind: "registerDelegatedAdministrator",
|
|
702
|
+
accountId: nextDa.accountId,
|
|
703
|
+
accountName: nextAccountNameById.get(nextDa.accountId) ?? nextDa.accountId,
|
|
704
|
+
servicePrincipal: nextDa.servicePrincipal
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
for (const currentDa of currentDelegatedAdmins) {
|
|
708
|
+
if (nextDelegatedAdminKeys.has(
|
|
709
|
+
`${currentDa.accountId}|${currentDa.servicePrincipal}`
|
|
710
|
+
)) {
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
operations.push({
|
|
714
|
+
kind: "deregisterDelegatedAdministrator",
|
|
715
|
+
accountId: currentDa.accountId,
|
|
716
|
+
accountName: currentAccountNameById.get(currentDa.accountId) ?? currentDa.accountId,
|
|
717
|
+
servicePrincipal: currentDa.servicePrincipal
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
const currentPolicies = props.current.organization.policies ?? [];
|
|
721
|
+
const nextPolicies = props.next.organization.policies ?? [];
|
|
722
|
+
const currentPolicyAttachments = props.current.organization.policyAttachments ?? [];
|
|
723
|
+
const nextPolicyAttachments = props.next.organization.policyAttachments ?? [];
|
|
724
|
+
const currentPoliciesByName = new Map(
|
|
725
|
+
currentPolicies.map((p) => [`${p.type}|${p.name}`, p])
|
|
726
|
+
);
|
|
727
|
+
const nextPoliciesByName = new Map(
|
|
728
|
+
nextPolicies.map((p) => [`${p.type}|${p.name}`, p])
|
|
729
|
+
);
|
|
730
|
+
const currentAttachmentsByKey = new Set(
|
|
731
|
+
currentPolicyAttachments.map((a) => `${a.policyId}|${a.targetId}`)
|
|
732
|
+
);
|
|
733
|
+
const nextPoliciesByPendingId = /* @__PURE__ */ new Map();
|
|
734
|
+
for (const nextPolicy of nextPolicies) {
|
|
735
|
+
const currentPolicy = currentPoliciesByName.get(
|
|
736
|
+
`${nextPolicy.type}|${nextPolicy.name}`
|
|
737
|
+
);
|
|
738
|
+
if (currentPolicy == null) {
|
|
739
|
+
operations.push({
|
|
740
|
+
kind: "createOrgPolicy",
|
|
741
|
+
policyName: nextPolicy.name,
|
|
742
|
+
policyType: nextPolicy.type,
|
|
743
|
+
description: nextPolicy.description,
|
|
744
|
+
content: nextPolicy.content
|
|
745
|
+
});
|
|
746
|
+
nextPoliciesByPendingId.set(nextPolicy.id, nextPolicy);
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
if (normalizeJsonContent(currentPolicy.content) !== normalizeJsonContent(nextPolicy.content)) {
|
|
750
|
+
operations.push({
|
|
751
|
+
kind: "updateOrgPolicyContent",
|
|
752
|
+
policyId: currentPolicy.id,
|
|
753
|
+
policyName: currentPolicy.name,
|
|
754
|
+
content: nextPolicy.content
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
if (currentPolicy.description !== nextPolicy.description) {
|
|
758
|
+
operations.push({
|
|
759
|
+
kind: "updateOrgPolicyDescription",
|
|
760
|
+
policyId: currentPolicy.id,
|
|
761
|
+
policyName: currentPolicy.name,
|
|
762
|
+
description: nextPolicy.description
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
const nextPoliciesById = new Map(nextPolicies.map((p) => [p.id, p]));
|
|
767
|
+
const currentPoliciesById = new Map(currentPolicies.map((p) => [p.id, p]));
|
|
768
|
+
const nextOuNameById = new Map(
|
|
769
|
+
props.next.organization.organizationalUnits.map((ou) => [ou.id, ou.name])
|
|
770
|
+
);
|
|
771
|
+
const currentOuNameById = new Map(
|
|
772
|
+
props.current.organization.organizationalUnits.map((ou) => [
|
|
773
|
+
ou.id,
|
|
774
|
+
ou.name
|
|
775
|
+
])
|
|
776
|
+
);
|
|
777
|
+
function resolveNextTargetName(targetId, targetType) {
|
|
778
|
+
if (targetType === "ROOT") return "root";
|
|
779
|
+
if (targetType === "ORGANIZATIONAL_UNIT")
|
|
780
|
+
return nextOuNameById.get(targetId) ?? "unknown";
|
|
781
|
+
return nextAccountNameById.get(targetId) ?? "unknown";
|
|
782
|
+
}
|
|
783
|
+
function resolveCurrentTargetName(targetId, targetType) {
|
|
784
|
+
if (targetType === "ROOT") return "root";
|
|
785
|
+
if (targetType === "ORGANIZATIONAL_UNIT")
|
|
786
|
+
return currentOuNameById.get(targetId) ?? "unknown";
|
|
787
|
+
return currentAccountNameById.get(targetId) ?? "unknown";
|
|
788
|
+
}
|
|
789
|
+
for (const nextAttachment of nextPolicyAttachments) {
|
|
790
|
+
if (nextAttachment.policyId === pendingCreationId) {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
if (nextAttachment.targetId === pendingCreationId) {
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
const attachmentKey = `${nextAttachment.policyId}|${nextAttachment.targetId}`;
|
|
797
|
+
if (currentAttachmentsByKey.has(attachmentKey)) {
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
const policy = nextPoliciesById.get(nextAttachment.policyId) ?? currentPoliciesById.get(nextAttachment.policyId);
|
|
801
|
+
if (policy == null) {
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
operations.push({
|
|
805
|
+
kind: "attachOrgPolicy",
|
|
806
|
+
policyId: nextAttachment.policyId,
|
|
807
|
+
policyName: policy.name,
|
|
808
|
+
targetId: nextAttachment.targetId,
|
|
809
|
+
targetName: resolveNextTargetName(
|
|
810
|
+
nextAttachment.targetId,
|
|
811
|
+
nextAttachment.targetType
|
|
812
|
+
)
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
const nextAttachmentKeys = new Set(
|
|
816
|
+
nextPolicyAttachments.filter(
|
|
817
|
+
(a) => a.policyId !== pendingCreationId && a.targetId !== pendingCreationId
|
|
818
|
+
).map((a) => `${a.policyId}|${a.targetId}`)
|
|
819
|
+
);
|
|
820
|
+
const nextPolicyIds = new Set(
|
|
821
|
+
nextPolicies.filter((p) => p.id !== pendingCreationId).map((p) => p.id)
|
|
822
|
+
);
|
|
823
|
+
for (const currentAttachment of currentPolicyAttachments) {
|
|
824
|
+
const attachmentKey = `${currentAttachment.policyId}|${currentAttachment.targetId}`;
|
|
825
|
+
const policyBeingDeleted = !nextPolicyIds.has(currentAttachment.policyId) && currentPoliciesById.has(currentAttachment.policyId);
|
|
826
|
+
if (nextAttachmentKeys.has(attachmentKey) && !policyBeingDeleted) {
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
const policy = currentPoliciesById.get(currentAttachment.policyId);
|
|
830
|
+
if (policy == null) {
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
operations.push({
|
|
834
|
+
kind: "detachOrgPolicy",
|
|
835
|
+
policyId: currentAttachment.policyId,
|
|
836
|
+
policyName: policy.name,
|
|
837
|
+
targetId: currentAttachment.targetId,
|
|
838
|
+
targetName: resolveCurrentTargetName(
|
|
839
|
+
currentAttachment.targetId,
|
|
840
|
+
currentAttachment.targetType
|
|
841
|
+
)
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
for (const currentPolicy of currentPolicies) {
|
|
845
|
+
if (nextPoliciesByName.has(`${currentPolicy.type}|${currentPolicy.name}`)) {
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
operations.push({
|
|
849
|
+
kind: "deleteOrgPolicy",
|
|
850
|
+
policyId: currentPolicy.id,
|
|
851
|
+
policyName: currentPolicy.name
|
|
852
|
+
});
|
|
853
|
+
}
|
|
598
854
|
operations.sort((left, right) => {
|
|
599
855
|
const priorityComparison = getOperationExecutionPriority(left) - getOperationExecutionPriority(right);
|
|
600
856
|
if (priorityComparison !== 0) {
|
|
@@ -830,8 +1086,41 @@ function getOperationSortKey(operation) {
|
|
|
830
1086
|
operation.principalName
|
|
831
1087
|
].join("|");
|
|
832
1088
|
}
|
|
1089
|
+
if (operation.kind === "createOrgPolicy") {
|
|
1090
|
+
return `${operation.kind}|${operation.policyType}|${operation.policyName}`;
|
|
1091
|
+
}
|
|
1092
|
+
if (operation.kind === "updateOrgPolicyContent" || operation.kind === "updateOrgPolicyDescription" || operation.kind === "deleteOrgPolicy") {
|
|
1093
|
+
return `${operation.kind}|${operation.policyName}`;
|
|
1094
|
+
}
|
|
1095
|
+
if (operation.kind === "attachOrgPolicy" || operation.kind === "detachOrgPolicy") {
|
|
1096
|
+
return [operation.kind, operation.policyName, operation.targetName].join(
|
|
1097
|
+
"|"
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
if (operation.kind === "putAlternateContact" || operation.kind === "deleteAlternateContact") {
|
|
1101
|
+
return [operation.kind, operation.accountName, operation.contactType].join(
|
|
1102
|
+
"|"
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
if (operation.kind === "setIdcAccessControlAttributes") {
|
|
1106
|
+
return operation.kind;
|
|
1107
|
+
}
|
|
1108
|
+
if (operation.kind === "registerDelegatedAdministrator" || operation.kind === "deregisterDelegatedAdministrator") {
|
|
1109
|
+
return [
|
|
1110
|
+
operation.kind,
|
|
1111
|
+
operation.accountName,
|
|
1112
|
+
operation.servicePrincipal
|
|
1113
|
+
].join("|");
|
|
1114
|
+
}
|
|
833
1115
|
return "zzzz";
|
|
834
1116
|
}
|
|
1117
|
+
function normalizeJsonContent(content) {
|
|
1118
|
+
try {
|
|
1119
|
+
return JSON.stringify(sortJsonValue(JSON.parse(content)));
|
|
1120
|
+
} catch {
|
|
1121
|
+
return content;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
835
1124
|
function normalizeAccountTags(tags) {
|
|
836
1125
|
if (tags == null || tags.length === 0) {
|
|
837
1126
|
return [];
|
|
@@ -949,7 +1238,9 @@ function normalizeIdentityCenterState(props) {
|
|
|
949
1238
|
};
|
|
950
1239
|
}
|
|
951
1240
|
function createNormalizedIdcMembershipKey(props) {
|
|
952
|
-
return [props.membership.groupDisplayName, props.membership.userName].join(
|
|
1241
|
+
return [props.membership.groupDisplayName, props.membership.userName].join(
|
|
1242
|
+
"|"
|
|
1243
|
+
);
|
|
953
1244
|
}
|
|
954
1245
|
function resolveAssignmentPrincipalName(props) {
|
|
955
1246
|
if (props.principalType === "GROUP") {
|
|
@@ -1016,6 +1307,37 @@ function isResolvableOrganizationalUnitId(props) {
|
|
|
1016
1307
|
}
|
|
1017
1308
|
return props.organizationalUnitNameById.has(props.organizationalUnitId);
|
|
1018
1309
|
}
|
|
1310
|
+
function diffAlternateContacts(props) {
|
|
1311
|
+
const currentByType = new Map(
|
|
1312
|
+
props.currentContacts.map((c) => [c.contactType, c])
|
|
1313
|
+
);
|
|
1314
|
+
const nextByType = new Map(props.nextContacts.map((c) => [c.contactType, c]));
|
|
1315
|
+
for (const next of props.nextContacts) {
|
|
1316
|
+
const current = currentByType.get(next.contactType);
|
|
1317
|
+
if (current == null || current.name !== next.name || current.email !== next.email || current.phone !== next.phone || current.title !== next.title) {
|
|
1318
|
+
props.operations.push({
|
|
1319
|
+
kind: "putAlternateContact",
|
|
1320
|
+
accountId: props.accountId,
|
|
1321
|
+
accountName: props.accountName,
|
|
1322
|
+
contactType: next.contactType,
|
|
1323
|
+
name: next.name,
|
|
1324
|
+
email: next.email,
|
|
1325
|
+
phone: next.phone,
|
|
1326
|
+
title: next.title
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
for (const current of props.currentContacts) {
|
|
1331
|
+
if (!nextByType.has(current.contactType)) {
|
|
1332
|
+
props.operations.push({
|
|
1333
|
+
kind: "deleteAlternateContact",
|
|
1334
|
+
accountId: props.accountId,
|
|
1335
|
+
accountName: props.accountName,
|
|
1336
|
+
contactType: current.contactType
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1019
1341
|
export {
|
|
1020
1342
|
diffStates
|
|
1021
1343
|
};
|
package/dist/lambda/handler.js
CHANGED
|
@@ -44,7 +44,9 @@ const scanResponseSchema = v.strictObject({
|
|
|
44
44
|
users: v.number(),
|
|
45
45
|
groups: v.number(),
|
|
46
46
|
permissionSets: v.number(),
|
|
47
|
-
accountAssignments: v.number()
|
|
47
|
+
accountAssignments: v.number(),
|
|
48
|
+
policies: v.number(),
|
|
49
|
+
policyAttachments: v.number()
|
|
48
50
|
}),
|
|
49
51
|
state: stateSchema
|
|
50
52
|
});
|
|
@@ -139,7 +141,7 @@ async function handler(event) {
|
|
|
139
141
|
return validateResponse(response);
|
|
140
142
|
}
|
|
141
143
|
if (request.action === "scan") {
|
|
142
|
-
const response = await handleScan({ s3Client, bucket, organizationsClient, ssoAdminClient, identityStoreClient });
|
|
144
|
+
const response = await handleScan({ s3Client, bucket, organizationsClient, ssoAdminClient, identityStoreClient, accountClient });
|
|
143
145
|
return validateResponse(response);
|
|
144
146
|
}
|
|
145
147
|
if (request.action === "getStateUrl") {
|
|
@@ -228,7 +230,7 @@ function isS3PreconditionFailed(error) {
|
|
|
228
230
|
async function handleScan(props) {
|
|
229
231
|
const identityCenterInstanceArn = process.env.IDENTITY_CENTER_INSTANCE_ARN || void 0;
|
|
230
232
|
const [organization, identityCenter] = await Promise.all([
|
|
231
|
-
scanOrganization({ organizationsClient: props.organizationsClient }),
|
|
233
|
+
scanOrganization({ organizationsClient: props.organizationsClient, accountClient: props.accountClient }),
|
|
232
234
|
scanIdentityCenter({
|
|
233
235
|
ssoAdminClient: props.ssoAdminClient,
|
|
234
236
|
identityStoreClient: props.identityStoreClient,
|
|
@@ -255,7 +257,9 @@ async function handleScan(props) {
|
|
|
255
257
|
users: state.identityCenter.users.length,
|
|
256
258
|
groups: state.identityCenter.groups.length,
|
|
257
259
|
permissionSets: state.identityCenter.permissionSets.length,
|
|
258
|
-
accountAssignments: state.identityCenter.accountAssignments.length
|
|
260
|
+
accountAssignments: state.identityCenter.accountAssignments.length,
|
|
261
|
+
policies: state.organization.policies?.length ?? 0,
|
|
262
|
+
policyAttachments: state.organization.policyAttachments?.length ?? 0
|
|
259
263
|
},
|
|
260
264
|
state
|
|
261
265
|
};
|
package/dist/lambdaClient.js
CHANGED
|
@@ -31,7 +31,9 @@ const scanResponseSchema = v.strictObject({
|
|
|
31
31
|
users: v.number(),
|
|
32
32
|
groups: v.number(),
|
|
33
33
|
permissionSets: v.number(),
|
|
34
|
-
accountAssignments: v.number()
|
|
34
|
+
accountAssignments: v.number(),
|
|
35
|
+
policies: v.number(),
|
|
36
|
+
policyAttachments: v.number()
|
|
35
37
|
}),
|
|
36
38
|
state: stateSchema
|
|
37
39
|
});
|
|
@@ -209,7 +211,8 @@ function buildEmptyStateForError() {
|
|
|
209
211
|
groupMemberships: [],
|
|
210
212
|
permissionSets: [],
|
|
211
213
|
accountAssignments: [],
|
|
212
|
-
accessRoles: []
|
|
214
|
+
accessRoles: [],
|
|
215
|
+
accessControlAttributes: []
|
|
213
216
|
}
|
|
214
217
|
};
|
|
215
218
|
}
|