@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/awsConfig.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { readFile, unlink, writeFile } from "node:fs/promises";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import { build as esbuildBuild } from "esbuild";
|
|
6
6
|
import * as v from "valibot";
|
|
@@ -36,7 +36,8 @@ const deploymentSchema = v.strictObject({
|
|
|
36
36
|
region: v.string(),
|
|
37
37
|
lambdaArn: v.string(),
|
|
38
38
|
stateBucketName: v.string(),
|
|
39
|
-
stateCacheTtlSeconds: v.number()
|
|
39
|
+
stateCacheTtlSeconds: v.number(),
|
|
40
|
+
cliVersion: v.string()
|
|
40
41
|
});
|
|
41
42
|
const awsContextSchema = v.strictObject({
|
|
42
43
|
version: nonEmptyString,
|
|
@@ -66,6 +67,17 @@ const awsConfigModelSchema = v.strictObject({
|
|
|
66
67
|
key: v.string(),
|
|
67
68
|
value: v.string()
|
|
68
69
|
})
|
|
70
|
+
),
|
|
71
|
+
alternateContacts: v.optional(
|
|
72
|
+
v.array(
|
|
73
|
+
v.strictObject({
|
|
74
|
+
contactType: v.picklist(["BILLING", "OPERATIONS", "SECURITY"]),
|
|
75
|
+
name: v.string(),
|
|
76
|
+
email: v.string(),
|
|
77
|
+
phone: v.string(),
|
|
78
|
+
title: v.optional(v.string())
|
|
79
|
+
})
|
|
80
|
+
)
|
|
69
81
|
)
|
|
70
82
|
})
|
|
71
83
|
)
|
|
@@ -89,6 +101,7 @@ const awsConfigModelSchema = v.strictObject({
|
|
|
89
101
|
v.strictObject({
|
|
90
102
|
name: v.string(),
|
|
91
103
|
description: v.string(),
|
|
104
|
+
sessionDuration: v.optional(v.string()),
|
|
92
105
|
inlinePolicy: v.optional(iamPolicyDocumentSchema),
|
|
93
106
|
awsManagedPolicies: v.array(v.string()),
|
|
94
107
|
customerManagedPolicies: v.array(
|
|
@@ -106,6 +119,58 @@ const awsConfigModelSchema = v.strictObject({
|
|
|
106
119
|
user: v.optional(v.string()),
|
|
107
120
|
accounts: v.array(v.string())
|
|
108
121
|
})
|
|
122
|
+
),
|
|
123
|
+
accessControlAttributes: v.optional(
|
|
124
|
+
v.array(
|
|
125
|
+
v.strictObject({
|
|
126
|
+
key: v.string(),
|
|
127
|
+
source: v.array(v.string())
|
|
128
|
+
})
|
|
129
|
+
)
|
|
130
|
+
),
|
|
131
|
+
policies: v.optional(
|
|
132
|
+
v.strictObject({
|
|
133
|
+
serviceControlPolicies: v.optional(
|
|
134
|
+
v.array(
|
|
135
|
+
v.strictObject({
|
|
136
|
+
name: v.string(),
|
|
137
|
+
description: v.optional(v.string()),
|
|
138
|
+
content: v.record(v.string(), v.unknown()),
|
|
139
|
+
targets: v.array(v.string())
|
|
140
|
+
})
|
|
141
|
+
)
|
|
142
|
+
),
|
|
143
|
+
resourceControlPolicies: v.optional(
|
|
144
|
+
v.array(
|
|
145
|
+
v.strictObject({
|
|
146
|
+
name: v.string(),
|
|
147
|
+
description: v.optional(v.string()),
|
|
148
|
+
content: v.record(v.string(), v.unknown()),
|
|
149
|
+
targets: v.array(v.string())
|
|
150
|
+
})
|
|
151
|
+
)
|
|
152
|
+
),
|
|
153
|
+
tagPolicies: v.optional(
|
|
154
|
+
v.array(
|
|
155
|
+
v.strictObject({
|
|
156
|
+
name: v.string(),
|
|
157
|
+
description: v.optional(v.string()),
|
|
158
|
+
content: v.record(v.string(), v.unknown()),
|
|
159
|
+
targets: v.array(v.string())
|
|
160
|
+
})
|
|
161
|
+
)
|
|
162
|
+
),
|
|
163
|
+
aiServicesOptOutPolicies: v.optional(
|
|
164
|
+
v.array(
|
|
165
|
+
v.strictObject({
|
|
166
|
+
name: v.string(),
|
|
167
|
+
description: v.optional(v.string()),
|
|
168
|
+
content: v.record(v.string(), v.unknown()),
|
|
169
|
+
targets: v.array(v.string())
|
|
170
|
+
})
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
})
|
|
109
174
|
)
|
|
110
175
|
});
|
|
111
176
|
const moduleDirectoryPath = resolve(
|
|
@@ -119,11 +184,13 @@ async function writeAwsConfigFromState(props) {
|
|
|
119
184
|
state,
|
|
120
185
|
context
|
|
121
186
|
});
|
|
122
|
-
const mappedConfig = mapStateToAwsConfig({
|
|
123
|
-
|
|
124
|
-
|
|
187
|
+
const mappedConfig = mapStateToAwsConfig({ state });
|
|
188
|
+
const mergedConfig = props.existingConfig != null ? {
|
|
189
|
+
...props.existingConfig,
|
|
190
|
+
policies: props.existingConfig.policies ?? mappedConfig.policies
|
|
191
|
+
} : mappedConfig;
|
|
125
192
|
const sortedConfig = sortAwsConfigModel({
|
|
126
|
-
config:
|
|
193
|
+
config: mergedConfig
|
|
127
194
|
});
|
|
128
195
|
const nextConfigContent = renderAwsConfigTs({
|
|
129
196
|
config: sortedConfig
|
|
@@ -327,10 +394,12 @@ function mapStateToAwsConfig(props) {
|
|
|
327
394
|
`Could not map account "${account.name}" to organizational unit "${ownerOuName}".`
|
|
328
395
|
);
|
|
329
396
|
}
|
|
397
|
+
const contacts = account.alternateContacts;
|
|
330
398
|
ownerOu.accounts.push({
|
|
331
399
|
name: account.name,
|
|
332
400
|
email: account.email,
|
|
333
|
-
tags: account.tags ?? []
|
|
401
|
+
tags: account.tags ?? [],
|
|
402
|
+
alternateContacts: contacts != null && contacts.length > 0 ? contacts : void 0
|
|
334
403
|
});
|
|
335
404
|
}
|
|
336
405
|
const permissionSetByArn = toRecordByProperty(
|
|
@@ -416,6 +485,73 @@ function mapStateToAwsConfig(props) {
|
|
|
416
485
|
members.push(userName);
|
|
417
486
|
}
|
|
418
487
|
}
|
|
488
|
+
const orgPolicies = props.state.organization.policies ?? [];
|
|
489
|
+
const orgPolicyAttachments = props.state.organization.policyAttachments ?? [];
|
|
490
|
+
const ouById = toRecordByProperty(
|
|
491
|
+
props.state.organization.organizationalUnits,
|
|
492
|
+
"id"
|
|
493
|
+
);
|
|
494
|
+
const orgAccountById = toRecordByProperty(
|
|
495
|
+
props.state.organization.accounts,
|
|
496
|
+
"id"
|
|
497
|
+
);
|
|
498
|
+
function resolveTargetName(targetId, targetType) {
|
|
499
|
+
if (targetType === "ROOT") {
|
|
500
|
+
return "root";
|
|
501
|
+
}
|
|
502
|
+
if (targetType === "ORGANIZATIONAL_UNIT") {
|
|
503
|
+
return ouById[targetId]?.name ?? null;
|
|
504
|
+
}
|
|
505
|
+
if (targetType === "ACCOUNT") {
|
|
506
|
+
return orgAccountById[targetId]?.name ?? null;
|
|
507
|
+
}
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
const attachmentsByPolicyId = /* @__PURE__ */ new Map();
|
|
511
|
+
for (const attachment of orgPolicyAttachments) {
|
|
512
|
+
const targetName = resolveTargetName(
|
|
513
|
+
attachment.targetId,
|
|
514
|
+
attachment.targetType
|
|
515
|
+
);
|
|
516
|
+
if (targetName == null) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
const targets = attachmentsByPolicyId.get(attachment.policyId) ?? [];
|
|
520
|
+
targets.push(targetName);
|
|
521
|
+
attachmentsByPolicyId.set(attachment.policyId, targets);
|
|
522
|
+
}
|
|
523
|
+
const scps = orgPolicies.filter((p) => p.type === "SERVICE_CONTROL_POLICY").map((p) => ({
|
|
524
|
+
name: p.name,
|
|
525
|
+
description: p.description.length > 0 ? p.description : void 0,
|
|
526
|
+
content: JSON.parse(p.content),
|
|
527
|
+
targets: [...attachmentsByPolicyId.get(p.id) ?? []].sort(
|
|
528
|
+
(a, b) => a.localeCompare(b)
|
|
529
|
+
)
|
|
530
|
+
}));
|
|
531
|
+
const rcps = orgPolicies.filter((p) => p.type === "RESOURCE_CONTROL_POLICY").map((p) => ({
|
|
532
|
+
name: p.name,
|
|
533
|
+
description: p.description.length > 0 ? p.description : void 0,
|
|
534
|
+
content: JSON.parse(p.content),
|
|
535
|
+
targets: [...attachmentsByPolicyId.get(p.id) ?? []].sort(
|
|
536
|
+
(a, b) => a.localeCompare(b)
|
|
537
|
+
)
|
|
538
|
+
}));
|
|
539
|
+
const tagPolicies = orgPolicies.filter((p) => p.type === "TAG_POLICY").map((p) => ({
|
|
540
|
+
name: p.name,
|
|
541
|
+
description: p.description.length > 0 ? p.description : void 0,
|
|
542
|
+
content: JSON.parse(p.content),
|
|
543
|
+
targets: [...attachmentsByPolicyId.get(p.id) ?? []].sort(
|
|
544
|
+
(a, b) => a.localeCompare(b)
|
|
545
|
+
)
|
|
546
|
+
}));
|
|
547
|
+
const aiServicesOptOutPolicies = orgPolicies.filter((p) => p.type === "AISERVICES_OPT_OUT_POLICY").map((p) => ({
|
|
548
|
+
name: p.name,
|
|
549
|
+
description: p.description.length > 0 ? p.description : void 0,
|
|
550
|
+
content: JSON.parse(p.content),
|
|
551
|
+
targets: [...attachmentsByPolicyId.get(p.id) ?? []].sort(
|
|
552
|
+
(a, b) => a.localeCompare(b)
|
|
553
|
+
)
|
|
554
|
+
}));
|
|
419
555
|
const mapped = {
|
|
420
556
|
organizationalUnits,
|
|
421
557
|
users: props.state.identityCenter.users.map((user) => ({
|
|
@@ -432,6 +568,7 @@ function mapStateToAwsConfig(props) {
|
|
|
432
568
|
(permissionSet) => ({
|
|
433
569
|
name: permissionSet.name,
|
|
434
570
|
description: permissionSet.description,
|
|
571
|
+
sessionDuration: permissionSet.sessionDuration ?? void 0,
|
|
435
572
|
inlinePolicy: permissionSet.inlinePolicy == null ? void 0 : parseInlinePolicyForConfig({
|
|
436
573
|
permissionSetName: permissionSet.name,
|
|
437
574
|
inlinePolicy: permissionSet.inlinePolicy
|
|
@@ -445,7 +582,17 @@ function mapStateToAwsConfig(props) {
|
|
|
445
582
|
)
|
|
446
583
|
})
|
|
447
584
|
),
|
|
448
|
-
assignments: [...assignmentsByKey.values()]
|
|
585
|
+
assignments: [...assignmentsByKey.values()],
|
|
586
|
+
accessControlAttributes: props.state.identityCenter.accessControlAttributes.length > 0 ? props.state.identityCenter.accessControlAttributes.map((attr) => ({
|
|
587
|
+
key: attr.key,
|
|
588
|
+
source: [...attr.source]
|
|
589
|
+
})) : void 0,
|
|
590
|
+
policies: scps.length > 0 || rcps.length > 0 || tagPolicies.length > 0 || aiServicesOptOutPolicies.length > 0 ? {
|
|
591
|
+
serviceControlPolicies: scps.length > 0 ? scps : void 0,
|
|
592
|
+
resourceControlPolicies: rcps.length > 0 ? rcps : void 0,
|
|
593
|
+
tagPolicies: tagPolicies.length > 0 ? tagPolicies : void 0,
|
|
594
|
+
aiServicesOptOutPolicies: aiServicesOptOutPolicies.length > 0 ? aiServicesOptOutPolicies : void 0
|
|
595
|
+
} : void 0
|
|
449
596
|
};
|
|
450
597
|
assertUniqueNames({
|
|
451
598
|
values: mapped.organizationalUnits.map((ou) => ou.name),
|
|
@@ -612,7 +759,8 @@ function mapAwsConfigToState(props) {
|
|
|
612
759
|
email: account.email,
|
|
613
760
|
status: matchedAccount?.status ?? "ACTIVE",
|
|
614
761
|
parentId: ownerParentId,
|
|
615
|
-
tags: account.tags
|
|
762
|
+
tags: account.tags,
|
|
763
|
+
alternateContacts: account.alternateContacts != null && account.alternateContacts.length > 0 ? account.alternateContacts : void 0
|
|
616
764
|
});
|
|
617
765
|
mappedAccountIdByName.set(account.name, mappedId);
|
|
618
766
|
}
|
|
@@ -626,10 +774,7 @@ function mapAwsConfigToState(props) {
|
|
|
626
774
|
email: user.email
|
|
627
775
|
};
|
|
628
776
|
});
|
|
629
|
-
const mappedUserByUserName = toRecordByProperty(
|
|
630
|
-
mappedUsers,
|
|
631
|
-
"userName"
|
|
632
|
-
);
|
|
777
|
+
const mappedUserByUserName = toRecordByProperty(mappedUsers, "userName");
|
|
633
778
|
const mappedGroups = props.config.groups.map((group) => {
|
|
634
779
|
const matchedGroup = groupByDisplayName[group.displayName];
|
|
635
780
|
return {
|
|
@@ -667,6 +812,7 @@ function mapAwsConfigToState(props) {
|
|
|
667
812
|
permissionSetArn: matchedPermissionSet?.permissionSetArn ?? pendingCreationId,
|
|
668
813
|
name: permissionSet.name,
|
|
669
814
|
description: permissionSet.description,
|
|
815
|
+
sessionDuration: permissionSet.sessionDuration ?? null,
|
|
670
816
|
inlinePolicy: stableStringifyInlinePolicy(permissionSet.inlinePolicy),
|
|
671
817
|
awsManagedPolicies: [...permissionSet.awsManagedPolicies],
|
|
672
818
|
customerManagedPolicies: permissionSet.customerManagedPolicies.map(
|
|
@@ -707,13 +853,107 @@ function mapAwsConfigToState(props) {
|
|
|
707
853
|
});
|
|
708
854
|
}
|
|
709
855
|
}
|
|
856
|
+
const configPolicies = props.config.policies;
|
|
857
|
+
const allConfigPolicies = [];
|
|
858
|
+
const ouByName = toRecordByProperty(
|
|
859
|
+
props.currentState.organization.organizationalUnits,
|
|
860
|
+
"name"
|
|
861
|
+
);
|
|
862
|
+
const stateAccountByName = toRecordByProperty(
|
|
863
|
+
props.currentState.organization.accounts,
|
|
864
|
+
"name"
|
|
865
|
+
);
|
|
866
|
+
function resolveTargetId(targetName) {
|
|
867
|
+
if (targetName === "root") {
|
|
868
|
+
return {
|
|
869
|
+
targetId: props.context.organization.rootId,
|
|
870
|
+
targetType: "ROOT"
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
const ou = ouByName[targetName];
|
|
874
|
+
if (ou != null) {
|
|
875
|
+
return { targetId: ou.id, targetType: "ORGANIZATIONAL_UNIT" };
|
|
876
|
+
}
|
|
877
|
+
const acct = stateAccountByName[targetName];
|
|
878
|
+
if (acct != null) {
|
|
879
|
+
return { targetId: acct.id, targetType: "ACCOUNT" };
|
|
880
|
+
}
|
|
881
|
+
return { targetId: pendingCreationId, targetType: "ACCOUNT" };
|
|
882
|
+
}
|
|
883
|
+
for (const policy of configPolicies?.serviceControlPolicies ?? []) {
|
|
884
|
+
allConfigPolicies.push({
|
|
885
|
+
name: policy.name,
|
|
886
|
+
description: policy.description ?? "",
|
|
887
|
+
type: "SERVICE_CONTROL_POLICY",
|
|
888
|
+
content: JSON.stringify(policy.content),
|
|
889
|
+
targets: policy.targets.map((t) => resolveTargetId(t))
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
for (const policy of configPolicies?.resourceControlPolicies ?? []) {
|
|
893
|
+
allConfigPolicies.push({
|
|
894
|
+
name: policy.name,
|
|
895
|
+
description: policy.description ?? "",
|
|
896
|
+
type: "RESOURCE_CONTROL_POLICY",
|
|
897
|
+
content: JSON.stringify(policy.content),
|
|
898
|
+
targets: policy.targets.map((t) => resolveTargetId(t))
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
for (const policy of configPolicies?.tagPolicies ?? []) {
|
|
902
|
+
allConfigPolicies.push({
|
|
903
|
+
name: policy.name,
|
|
904
|
+
description: policy.description ?? "",
|
|
905
|
+
type: "TAG_POLICY",
|
|
906
|
+
content: JSON.stringify(policy.content),
|
|
907
|
+
targets: policy.targets.map((t) => resolveTargetId(t))
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
for (const policy of configPolicies?.aiServicesOptOutPolicies ?? []) {
|
|
911
|
+
allConfigPolicies.push({
|
|
912
|
+
name: policy.name,
|
|
913
|
+
description: policy.description ?? "",
|
|
914
|
+
type: "AISERVICES_OPT_OUT_POLICY",
|
|
915
|
+
content: JSON.stringify(policy.content),
|
|
916
|
+
targets: policy.targets.map((t) => resolveTargetId(t))
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
const currentPoliciesByNameAndType = new Map(
|
|
920
|
+
(props.currentState.organization.policies ?? []).map((p) => [
|
|
921
|
+
`${p.type}|${p.name}`,
|
|
922
|
+
p
|
|
923
|
+
])
|
|
924
|
+
);
|
|
925
|
+
const mappedPolicies = allConfigPolicies.map((p) => {
|
|
926
|
+
const current = currentPoliciesByNameAndType.get(`${p.type}|${p.name}`);
|
|
927
|
+
return {
|
|
928
|
+
id: current?.id ?? pendingCreationId,
|
|
929
|
+
arn: current?.arn ?? pendingCreationId,
|
|
930
|
+
name: p.name,
|
|
931
|
+
description: p.description,
|
|
932
|
+
type: p.type,
|
|
933
|
+
content: p.content
|
|
934
|
+
};
|
|
935
|
+
});
|
|
936
|
+
const mappedPolicyAttachments = [];
|
|
937
|
+
for (let i = 0; i < allConfigPolicies.length; i++) {
|
|
938
|
+
const configPolicy = allConfigPolicies[i];
|
|
939
|
+
const mappedPolicy = mappedPolicies[i];
|
|
940
|
+
for (const target of configPolicy.targets) {
|
|
941
|
+
mappedPolicyAttachments.push({
|
|
942
|
+
policyId: mappedPolicy.id,
|
|
943
|
+
targetId: target.targetId,
|
|
944
|
+
targetType: target.targetType
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
}
|
|
710
948
|
const mapped = {
|
|
711
949
|
version: props.currentState.version,
|
|
712
950
|
generatedAt: props.currentState.generatedAt,
|
|
713
951
|
organization: {
|
|
714
952
|
rootId: props.context.organization.rootId,
|
|
715
953
|
organizationalUnits: mappedOrganizationalUnits,
|
|
716
|
-
accounts: mappedAccounts
|
|
954
|
+
accounts: mappedAccounts,
|
|
955
|
+
policies: mappedPolicies,
|
|
956
|
+
policyAttachments: mappedPolicyAttachments
|
|
717
957
|
},
|
|
718
958
|
identityCenter: {
|
|
719
959
|
instanceArn: props.context.identityCenter.instanceArn,
|
|
@@ -729,7 +969,10 @@ function mapAwsConfigToState(props) {
|
|
|
729
969
|
principalId: assignment.principalId,
|
|
730
970
|
principalType: assignment.principalType,
|
|
731
971
|
roleName: createAccessRoleName(assignment)
|
|
732
|
-
}))
|
|
972
|
+
})),
|
|
973
|
+
accessControlAttributes: (props.config.accessControlAttributes ?? []).map(
|
|
974
|
+
(attr) => ({ key: attr.key, source: [...attr.source] })
|
|
975
|
+
)
|
|
733
976
|
}
|
|
734
977
|
};
|
|
735
978
|
assertUniqueNames({
|
|
@@ -758,6 +1001,28 @@ function mapAwsConfigToState(props) {
|
|
|
758
1001
|
),
|
|
759
1002
|
entityName: "permission set"
|
|
760
1003
|
});
|
|
1004
|
+
assertUniqueNames({
|
|
1005
|
+
values: (props.config.policies?.serviceControlPolicies ?? []).map(
|
|
1006
|
+
(p) => p.name
|
|
1007
|
+
),
|
|
1008
|
+
entityName: "SCP"
|
|
1009
|
+
});
|
|
1010
|
+
assertUniqueNames({
|
|
1011
|
+
values: (props.config.policies?.resourceControlPolicies ?? []).map(
|
|
1012
|
+
(p) => p.name
|
|
1013
|
+
),
|
|
1014
|
+
entityName: "RCP"
|
|
1015
|
+
});
|
|
1016
|
+
assertUniqueNames({
|
|
1017
|
+
values: (props.config.policies?.tagPolicies ?? []).map((p) => p.name),
|
|
1018
|
+
entityName: "tag policy"
|
|
1019
|
+
});
|
|
1020
|
+
assertUniqueNames({
|
|
1021
|
+
values: (props.config.policies?.aiServicesOptOutPolicies ?? []).map(
|
|
1022
|
+
(p) => p.name
|
|
1023
|
+
),
|
|
1024
|
+
entityName: "AI services opt-out policy"
|
|
1025
|
+
});
|
|
761
1026
|
return validateState(mapped);
|
|
762
1027
|
}
|
|
763
1028
|
function sortAwsConfigModel(props) {
|
|
@@ -792,9 +1057,12 @@ function sortAwsConfigModel(props) {
|
|
|
792
1057
|
for (const child of children) {
|
|
793
1058
|
orderedOrganizationalUnits.push({
|
|
794
1059
|
...child,
|
|
795
|
-
accounts: [...child.accounts].sort(
|
|
796
|
-
|
|
797
|
-
|
|
1060
|
+
accounts: [...child.accounts].sort((left, right) => left.name.localeCompare(right.name)).map((account) => ({
|
|
1061
|
+
...account,
|
|
1062
|
+
alternateContacts: account.alternateContacts == null ? void 0 : [...account.alternateContacts].sort(
|
|
1063
|
+
(a, b) => a.contactType.localeCompare(b.contactType)
|
|
1064
|
+
)
|
|
1065
|
+
}))
|
|
798
1066
|
});
|
|
799
1067
|
queue.push(child.name);
|
|
800
1068
|
}
|
|
@@ -816,7 +1084,9 @@ function sortAwsConfigModel(props) {
|
|
|
816
1084
|
awsManagedPolicies: [...permissionSet.awsManagedPolicies].sort(
|
|
817
1085
|
(left, right) => left.localeCompare(right)
|
|
818
1086
|
),
|
|
819
|
-
customerManagedPolicies: [
|
|
1087
|
+
customerManagedPolicies: [
|
|
1088
|
+
...permissionSet.customerManagedPolicies
|
|
1089
|
+
].sort(
|
|
820
1090
|
(left, right) => compareStringKeys(left.path, right.path, left.name, right.name)
|
|
821
1091
|
)
|
|
822
1092
|
})).sort((left, right) => left.name.localeCompare(right.name)),
|
|
@@ -833,7 +1103,41 @@ function sortAwsConfigModel(props) {
|
|
|
833
1103
|
return principalComparison;
|
|
834
1104
|
}
|
|
835
1105
|
return left.permissionSet.localeCompare(right.permissionSet);
|
|
836
|
-
})
|
|
1106
|
+
}),
|
|
1107
|
+
accessControlAttributes: props.config.accessControlAttributes == null ? void 0 : [...props.config.accessControlAttributes].map((attr) => ({
|
|
1108
|
+
...attr,
|
|
1109
|
+
source: [...attr.source].sort((a, b) => a.localeCompare(b))
|
|
1110
|
+
})).sort((a, b) => a.key.localeCompare(b.key)),
|
|
1111
|
+
policies: props.config.policies == null ? void 0 : {
|
|
1112
|
+
serviceControlPolicies: props.config.policies.serviceControlPolicies == null ? void 0 : [...props.config.policies.serviceControlPolicies].map((p) => ({
|
|
1113
|
+
...p,
|
|
1114
|
+
content: sortJsonRecord(p.content),
|
|
1115
|
+
targets: [...p.targets].sort(
|
|
1116
|
+
(a, b) => a.localeCompare(b)
|
|
1117
|
+
)
|
|
1118
|
+
})).sort((a, b) => a.name.localeCompare(b.name)),
|
|
1119
|
+
resourceControlPolicies: props.config.policies.resourceControlPolicies == null ? void 0 : [...props.config.policies.resourceControlPolicies].map((p) => ({
|
|
1120
|
+
...p,
|
|
1121
|
+
content: sortJsonRecord(p.content),
|
|
1122
|
+
targets: [...p.targets].sort(
|
|
1123
|
+
(a, b) => a.localeCompare(b)
|
|
1124
|
+
)
|
|
1125
|
+
})).sort((a, b) => a.name.localeCompare(b.name)),
|
|
1126
|
+
tagPolicies: props.config.policies.tagPolicies == null ? void 0 : [...props.config.policies.tagPolicies].map((p) => ({
|
|
1127
|
+
...p,
|
|
1128
|
+
content: sortJsonRecord(p.content),
|
|
1129
|
+
targets: [...p.targets].sort(
|
|
1130
|
+
(a, b) => a.localeCompare(b)
|
|
1131
|
+
)
|
|
1132
|
+
})).sort((a, b) => a.name.localeCompare(b.name)),
|
|
1133
|
+
aiServicesOptOutPolicies: props.config.policies.aiServicesOptOutPolicies == null ? void 0 : [...props.config.policies.aiServicesOptOutPolicies].map((p) => ({
|
|
1134
|
+
...p,
|
|
1135
|
+
content: sortJsonRecord(p.content),
|
|
1136
|
+
targets: [...p.targets].sort(
|
|
1137
|
+
(a, b) => a.localeCompare(b)
|
|
1138
|
+
)
|
|
1139
|
+
})).sort((a, b) => a.name.localeCompare(b.name))
|
|
1140
|
+
}
|
|
837
1141
|
};
|
|
838
1142
|
}
|
|
839
1143
|
function renderAwsConfigTs(props) {
|
|
@@ -864,7 +1168,9 @@ function renderTsValue(value, props) {
|
|
|
864
1168
|
return "null";
|
|
865
1169
|
}
|
|
866
1170
|
if (value === void 0) {
|
|
867
|
-
throw new Error(
|
|
1171
|
+
throw new Error(
|
|
1172
|
+
"Undefined values must be handled before TypeScript rendering."
|
|
1173
|
+
);
|
|
868
1174
|
}
|
|
869
1175
|
if (typeof value === "string") {
|
|
870
1176
|
return renderTsStringValue(value, props);
|
|
@@ -904,14 +1210,16 @@ ${renderedItems.map((item) => `${childIndent}${item}`).join(",\n")}
|
|
|
904
1210
|
${indent}]`;
|
|
905
1211
|
}
|
|
906
1212
|
function renderTsObject(value, props) {
|
|
907
|
-
const entries = Object.entries(value).filter(
|
|
1213
|
+
const entries = Object.entries(value).filter(
|
|
1214
|
+
([, entryValue]) => entryValue !== void 0
|
|
1215
|
+
);
|
|
908
1216
|
if (entries.length === 0) {
|
|
909
1217
|
return "{}";
|
|
910
1218
|
}
|
|
911
1219
|
const indent = " ".repeat(props.indentLevel);
|
|
912
1220
|
const childIndent = " ".repeat(props.indentLevel + 1);
|
|
913
1221
|
const renderedEntries = entries.map(([key, entryValue]) => {
|
|
914
|
-
const nextWithinInlinePolicy = props.withinInlinePolicy || key === "inlinePolicy";
|
|
1222
|
+
const nextWithinInlinePolicy = props.withinInlinePolicy || key === "inlinePolicy" || key === "content";
|
|
915
1223
|
const renderedValue = renderTsValue(entryValue, {
|
|
916
1224
|
indentLevel: props.indentLevel + 1,
|
|
917
1225
|
withinInlinePolicy: nextWithinInlinePolicy,
|
|
@@ -934,10 +1242,14 @@ function renderPolicyActionString(value) {
|
|
|
934
1242
|
if (knownActions == null || knownActions.includes(actionName) === false) {
|
|
935
1243
|
return JSON.stringify(value);
|
|
936
1244
|
}
|
|
937
|
-
|
|
938
|
-
|
|
1245
|
+
const fnName = servicePrefixToCamelCase(servicePrefix);
|
|
1246
|
+
if (isIdentifierSafeServicePrefix(fnName)) {
|
|
1247
|
+
return `iam.${fnName}(${JSON.stringify(actionName)})`;
|
|
939
1248
|
}
|
|
940
|
-
return `iam[${JSON.stringify(
|
|
1249
|
+
return `iam[${JSON.stringify(fnName)}](${JSON.stringify(actionName)})`;
|
|
1250
|
+
}
|
|
1251
|
+
function servicePrefixToCamelCase(value) {
|
|
1252
|
+
return value.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
941
1253
|
}
|
|
942
1254
|
function isIdentifierSafeServicePrefix(value) {
|
|
943
1255
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(value);
|
|
@@ -974,9 +1286,8 @@ function renderAwsConfigTypesTs(props) {
|
|
|
974
1286
|
});
|
|
975
1287
|
return `import * as v from "valibot";
|
|
976
1288
|
import { iamPolicyDocumentSchema } from "@beesolve/iam-policy-ts";
|
|
1289
|
+
export * as iam from "@beesolve/iam-policy-ts";
|
|
977
1290
|
export {
|
|
978
|
-
iam,
|
|
979
|
-
iamAction,
|
|
980
1291
|
iamActionCatalog,
|
|
981
1292
|
iamActionCatalogActionCount,
|
|
982
1293
|
iamActionCatalogSourceSha256,
|
|
@@ -992,10 +1303,6 @@ export {
|
|
|
992
1303
|
assertIamPolicyDocumentStrict,
|
|
993
1304
|
} from "@beesolve/iam-policy-ts";
|
|
994
1305
|
export type {
|
|
995
|
-
IamActionCatalog,
|
|
996
|
-
IamPolicyServicePrefix,
|
|
997
|
-
IamPolicyActionNameByService,
|
|
998
|
-
IamPolicyActionForService,
|
|
999
1306
|
IamPolicyVersion,
|
|
1000
1307
|
IamPolicyScalar,
|
|
1001
1308
|
IamPolicyScalarList,
|
|
@@ -1033,6 +1340,17 @@ export const awsConfigSchema = v.strictObject({
|
|
|
1033
1340
|
value: v.string(),
|
|
1034
1341
|
}),
|
|
1035
1342
|
),
|
|
1343
|
+
alternateContacts: v.optional(
|
|
1344
|
+
v.array(
|
|
1345
|
+
v.strictObject({
|
|
1346
|
+
contactType: v.picklist(["BILLING", "OPERATIONS", "SECURITY"]),
|
|
1347
|
+
name: v.string(),
|
|
1348
|
+
email: v.string(),
|
|
1349
|
+
phone: v.string(),
|
|
1350
|
+
title: v.optional(v.string()),
|
|
1351
|
+
}),
|
|
1352
|
+
),
|
|
1353
|
+
),
|
|
1036
1354
|
}),
|
|
1037
1355
|
),
|
|
1038
1356
|
}),
|
|
@@ -1055,6 +1373,7 @@ export const awsConfigSchema = v.strictObject({
|
|
|
1055
1373
|
v.strictObject({
|
|
1056
1374
|
name: v.string(),
|
|
1057
1375
|
description: v.string(),
|
|
1376
|
+
sessionDuration: v.optional(v.string()),
|
|
1058
1377
|
inlinePolicy: v.optional(iamPolicyDocumentSchema),
|
|
1059
1378
|
awsManagedPolicies: v.array(v.string()),
|
|
1060
1379
|
customerManagedPolicies: v.array(
|
|
@@ -1073,6 +1392,58 @@ export const awsConfigSchema = v.strictObject({
|
|
|
1073
1392
|
accounts: v.array(accountNameSchema),
|
|
1074
1393
|
}),
|
|
1075
1394
|
),
|
|
1395
|
+
accessControlAttributes: v.optional(
|
|
1396
|
+
v.array(
|
|
1397
|
+
v.strictObject({
|
|
1398
|
+
key: v.string(),
|
|
1399
|
+
source: v.array(v.string()),
|
|
1400
|
+
}),
|
|
1401
|
+
),
|
|
1402
|
+
),
|
|
1403
|
+
policies: v.optional(
|
|
1404
|
+
v.strictObject({
|
|
1405
|
+
serviceControlPolicies: v.optional(
|
|
1406
|
+
v.array(
|
|
1407
|
+
v.strictObject({
|
|
1408
|
+
name: v.string(),
|
|
1409
|
+
description: v.optional(v.string()),
|
|
1410
|
+
content: v.record(v.string(), v.unknown()),
|
|
1411
|
+
targets: v.array(v.union([organizationalUnitNameSchema, accountNameSchema])),
|
|
1412
|
+
}),
|
|
1413
|
+
),
|
|
1414
|
+
),
|
|
1415
|
+
resourceControlPolicies: v.optional(
|
|
1416
|
+
v.array(
|
|
1417
|
+
v.strictObject({
|
|
1418
|
+
name: v.string(),
|
|
1419
|
+
description: v.optional(v.string()),
|
|
1420
|
+
content: v.record(v.string(), v.unknown()),
|
|
1421
|
+
targets: v.array(v.union([organizationalUnitNameSchema, accountNameSchema])),
|
|
1422
|
+
}),
|
|
1423
|
+
),
|
|
1424
|
+
),
|
|
1425
|
+
tagPolicies: v.optional(
|
|
1426
|
+
v.array(
|
|
1427
|
+
v.strictObject({
|
|
1428
|
+
name: v.string(),
|
|
1429
|
+
description: v.optional(v.string()),
|
|
1430
|
+
content: v.record(v.string(), v.unknown()),
|
|
1431
|
+
targets: v.array(v.union([organizationalUnitNameSchema, accountNameSchema])),
|
|
1432
|
+
}),
|
|
1433
|
+
),
|
|
1434
|
+
),
|
|
1435
|
+
aiServicesOptOutPolicies: v.optional(
|
|
1436
|
+
v.array(
|
|
1437
|
+
v.strictObject({
|
|
1438
|
+
name: v.string(),
|
|
1439
|
+
description: v.optional(v.string()),
|
|
1440
|
+
content: v.record(v.string(), v.unknown()),
|
|
1441
|
+
targets: v.array(v.union([organizationalUnitNameSchema, accountNameSchema])),
|
|
1442
|
+
}),
|
|
1443
|
+
),
|
|
1444
|
+
),
|
|
1445
|
+
}),
|
|
1446
|
+
),
|
|
1076
1447
|
});
|
|
1077
1448
|
|
|
1078
1449
|
export type AwsConfig = v.InferOutput<typeof awsConfigSchema>;
|
|
@@ -1231,6 +1602,16 @@ async function loadAwsConfigModelFromTsFile(props) {
|
|
|
1231
1602
|
async function readAwsContextFromFile(path) {
|
|
1232
1603
|
return readAwsContextFile(path);
|
|
1233
1604
|
}
|
|
1605
|
+
async function readPackageVersion() {
|
|
1606
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
1607
|
+
const packageDir = dirname(dirname(thisFile));
|
|
1608
|
+
const raw = await readFile(join(packageDir, "package.json"), "utf8");
|
|
1609
|
+
const pkg = JSON.parse(raw);
|
|
1610
|
+
if (typeof pkg.version !== "string") {
|
|
1611
|
+
throw new Error("Could not read version from package.json.");
|
|
1612
|
+
}
|
|
1613
|
+
return pkg.version;
|
|
1614
|
+
}
|
|
1234
1615
|
async function loadAwsConfigTypesModule(props) {
|
|
1235
1616
|
const loadedModule = await loadTsModule({
|
|
1236
1617
|
modulePath: props.typesPath
|
|
@@ -1358,6 +1739,7 @@ export {
|
|
|
1358
1739
|
loadAwsConfigModelFromTsFile,
|
|
1359
1740
|
mapAwsConfigToState,
|
|
1360
1741
|
readAwsContextFromFile,
|
|
1742
|
+
readPackageVersion,
|
|
1361
1743
|
regenerateAwsConfigTypes,
|
|
1362
1744
|
regenerateTypesFromState,
|
|
1363
1745
|
writeAwsConfigFromState
|