@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
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
|
)
|
|
@@ -97,6 +109,15 @@ const awsConfigModelSchema = v.strictObject({
|
|
|
97
109
|
name: v.string(),
|
|
98
110
|
path: v.string()
|
|
99
111
|
})
|
|
112
|
+
),
|
|
113
|
+
permissionsBoundary: v.optional(
|
|
114
|
+
v.union([
|
|
115
|
+
v.strictObject({ managedPolicyArn: v.string() }),
|
|
116
|
+
v.strictObject({
|
|
117
|
+
customerManagedPolicyName: v.string(),
|
|
118
|
+
customerManagedPolicyPath: v.string()
|
|
119
|
+
})
|
|
120
|
+
])
|
|
100
121
|
)
|
|
101
122
|
})
|
|
102
123
|
),
|
|
@@ -107,7 +128,61 @@ const awsConfigModelSchema = v.strictObject({
|
|
|
107
128
|
user: v.optional(v.string()),
|
|
108
129
|
accounts: v.array(v.string())
|
|
109
130
|
})
|
|
110
|
-
)
|
|
131
|
+
),
|
|
132
|
+
accessControlAttributes: v.array(
|
|
133
|
+
v.strictObject({
|
|
134
|
+
key: v.string(),
|
|
135
|
+
source: v.array(v.string())
|
|
136
|
+
})
|
|
137
|
+
),
|
|
138
|
+
delegatedAdministrators: v.array(
|
|
139
|
+
v.strictObject({
|
|
140
|
+
account: v.string(),
|
|
141
|
+
servicePrincipal: v.string()
|
|
142
|
+
})
|
|
143
|
+
),
|
|
144
|
+
policies: v.strictObject({
|
|
145
|
+
serviceControlPolicies: v.array(
|
|
146
|
+
v.strictObject({
|
|
147
|
+
name: v.string(),
|
|
148
|
+
description: v.optional(v.string()),
|
|
149
|
+
content: v.record(v.string(), v.unknown()),
|
|
150
|
+
targets: v.array(v.string())
|
|
151
|
+
})
|
|
152
|
+
),
|
|
153
|
+
resourceControlPolicies: v.array(
|
|
154
|
+
v.strictObject({
|
|
155
|
+
name: v.string(),
|
|
156
|
+
description: v.optional(v.string()),
|
|
157
|
+
content: v.record(v.string(), v.unknown()),
|
|
158
|
+
targets: v.array(v.string())
|
|
159
|
+
})
|
|
160
|
+
),
|
|
161
|
+
tagPolicies: v.array(
|
|
162
|
+
v.strictObject({
|
|
163
|
+
name: v.string(),
|
|
164
|
+
description: v.optional(v.string()),
|
|
165
|
+
content: v.record(v.string(), v.unknown()),
|
|
166
|
+
targets: v.array(v.string())
|
|
167
|
+
})
|
|
168
|
+
),
|
|
169
|
+
aiServicesOptOutPolicies: v.array(
|
|
170
|
+
v.strictObject({
|
|
171
|
+
name: v.string(),
|
|
172
|
+
description: v.optional(v.string()),
|
|
173
|
+
content: v.record(v.string(), v.unknown()),
|
|
174
|
+
targets: v.array(v.string())
|
|
175
|
+
})
|
|
176
|
+
),
|
|
177
|
+
backupPolicies: v.array(
|
|
178
|
+
v.strictObject({
|
|
179
|
+
name: v.string(),
|
|
180
|
+
description: v.optional(v.string()),
|
|
181
|
+
content: v.record(v.string(), v.unknown()),
|
|
182
|
+
targets: v.array(v.string())
|
|
183
|
+
})
|
|
184
|
+
)
|
|
185
|
+
})
|
|
111
186
|
});
|
|
112
187
|
const moduleDirectoryPath = resolve(
|
|
113
188
|
fileURLToPath(new URL(".", import.meta.url))
|
|
@@ -120,11 +195,10 @@ async function writeAwsConfigFromState(props) {
|
|
|
120
195
|
state,
|
|
121
196
|
context
|
|
122
197
|
});
|
|
123
|
-
const mappedConfig = mapStateToAwsConfig({
|
|
124
|
-
|
|
125
|
-
});
|
|
198
|
+
const mappedConfig = mapStateToAwsConfig({ state });
|
|
199
|
+
const mergedConfig = props.existingConfig != null ? props.existingConfig : mappedConfig;
|
|
126
200
|
const sortedConfig = sortAwsConfigModel({
|
|
127
|
-
config:
|
|
201
|
+
config: mergedConfig
|
|
128
202
|
});
|
|
129
203
|
const nextConfigContent = renderAwsConfigTs({
|
|
130
204
|
config: sortedConfig
|
|
@@ -218,12 +292,9 @@ async function writeAwsConfigFromState(props) {
|
|
|
218
292
|
};
|
|
219
293
|
}
|
|
220
294
|
async function regenerateAwsConfigTypes(props) {
|
|
221
|
-
const typesModule = await loadAwsConfigTypesModule({
|
|
222
|
-
typesPath: props.typesPath
|
|
223
|
-
});
|
|
224
295
|
const loadedConfig = await loadAwsConfigFromTsFile({
|
|
225
296
|
configPath: props.configPath,
|
|
226
|
-
schema:
|
|
297
|
+
schema: awsConfigModelSchema
|
|
227
298
|
});
|
|
228
299
|
const sortedConfig = sortAwsConfigModel({
|
|
229
300
|
config: loadedConfig
|
|
@@ -328,10 +399,12 @@ function mapStateToAwsConfig(props) {
|
|
|
328
399
|
`Could not map account "${account.name}" to organizational unit "${ownerOuName}".`
|
|
329
400
|
);
|
|
330
401
|
}
|
|
402
|
+
const contacts = account.alternateContacts;
|
|
331
403
|
ownerOu.accounts.push({
|
|
332
404
|
name: account.name,
|
|
333
405
|
email: account.email,
|
|
334
|
-
tags: account.tags ?? []
|
|
406
|
+
tags: account.tags ?? [],
|
|
407
|
+
alternateContacts: contacts != null && contacts.length > 0 ? contacts : void 0
|
|
335
408
|
});
|
|
336
409
|
}
|
|
337
410
|
const permissionSetByArn = toRecordByProperty(
|
|
@@ -417,6 +490,71 @@ function mapStateToAwsConfig(props) {
|
|
|
417
490
|
members.push(userName);
|
|
418
491
|
}
|
|
419
492
|
}
|
|
493
|
+
const orgPolicies = props.state.organization.policies ?? [];
|
|
494
|
+
const orgPolicyAttachments = props.state.organization.policyAttachments ?? [];
|
|
495
|
+
const ouById = toRecordByProperty(
|
|
496
|
+
props.state.organization.organizationalUnits,
|
|
497
|
+
"id"
|
|
498
|
+
);
|
|
499
|
+
const orgAccountById = toRecordByProperty(
|
|
500
|
+
props.state.organization.accounts,
|
|
501
|
+
"id"
|
|
502
|
+
);
|
|
503
|
+
function resolveTargetName(targetId, targetType) {
|
|
504
|
+
if (targetType === "ROOT") {
|
|
505
|
+
return "root";
|
|
506
|
+
}
|
|
507
|
+
if (targetType === "ORGANIZATIONAL_UNIT") {
|
|
508
|
+
return ouById[targetId]?.name ?? null;
|
|
509
|
+
}
|
|
510
|
+
if (targetType === "ACCOUNT") {
|
|
511
|
+
return orgAccountById[targetId]?.name ?? null;
|
|
512
|
+
}
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
const attachmentsByPolicyId = /* @__PURE__ */ new Map();
|
|
516
|
+
for (const attachment of orgPolicyAttachments) {
|
|
517
|
+
const targetName = resolveTargetName(
|
|
518
|
+
attachment.targetId,
|
|
519
|
+
attachment.targetType
|
|
520
|
+
);
|
|
521
|
+
if (targetName == null) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const targets = attachmentsByPolicyId.get(attachment.policyId) ?? [];
|
|
525
|
+
targets.push(targetName);
|
|
526
|
+
attachmentsByPolicyId.set(attachment.policyId, targets);
|
|
527
|
+
}
|
|
528
|
+
const mappedOrgPolicies = orgPolicies.map((p) => ({
|
|
529
|
+
type: p.type,
|
|
530
|
+
name: p.name,
|
|
531
|
+
description: p.description.length > 0 ? p.description : void 0,
|
|
532
|
+
content: JSON.parse(p.content),
|
|
533
|
+
targets: [...attachmentsByPolicyId.get(p.id) ?? []].sort(
|
|
534
|
+
(left, right) => left.localeCompare(right)
|
|
535
|
+
)
|
|
536
|
+
}));
|
|
537
|
+
const policiesByType = /* @__PURE__ */ new Map();
|
|
538
|
+
for (const policy of mappedOrgPolicies) {
|
|
539
|
+
const bucket = policiesByType.get(policy.type) ?? new Array();
|
|
540
|
+
bucket.push({
|
|
541
|
+
name: policy.name,
|
|
542
|
+
description: policy.description,
|
|
543
|
+
content: policy.content,
|
|
544
|
+
targets: policy.targets
|
|
545
|
+
});
|
|
546
|
+
policiesByType.set(policy.type, bucket);
|
|
547
|
+
}
|
|
548
|
+
const scps = policiesByType.get("SERVICE_CONTROL_POLICY") ?? [];
|
|
549
|
+
const rcps = policiesByType.get("RESOURCE_CONTROL_POLICY") ?? [];
|
|
550
|
+
const tagPolicies = policiesByType.get("TAG_POLICY") ?? [];
|
|
551
|
+
const aiServicesOptOutPolicies = policiesByType.get("AISERVICES_OPT_OUT_POLICY") ?? [];
|
|
552
|
+
const backupPolicies = policiesByType.get("BACKUP_POLICY") ?? [];
|
|
553
|
+
const stateDelegatedAdmins = props.state.organization.delegatedAdministrators ?? [];
|
|
554
|
+
const mappedDelegatedAdministrators = stateDelegatedAdmins.map((da) => ({
|
|
555
|
+
account: accountById[da.accountId]?.name ?? da.accountId,
|
|
556
|
+
servicePrincipal: da.servicePrincipal
|
|
557
|
+
}));
|
|
420
558
|
const mapped = {
|
|
421
559
|
organizationalUnits,
|
|
422
560
|
users: props.state.identityCenter.users.map((user) => ({
|
|
@@ -444,10 +582,23 @@ function mapStateToAwsConfig(props) {
|
|
|
444
582
|
name: customerManagedPolicy.name,
|
|
445
583
|
path: customerManagedPolicy.path
|
|
446
584
|
})
|
|
447
|
-
)
|
|
585
|
+
),
|
|
586
|
+
permissionsBoundary: permissionSet.permissionsBoundary ?? void 0
|
|
448
587
|
})
|
|
449
588
|
),
|
|
450
|
-
assignments: [...assignmentsByKey.values()]
|
|
589
|
+
assignments: [...assignmentsByKey.values()],
|
|
590
|
+
accessControlAttributes: props.state.identityCenter.accessControlAttributes.map((attr) => ({
|
|
591
|
+
key: attr.key,
|
|
592
|
+
source: [...attr.source]
|
|
593
|
+
})),
|
|
594
|
+
delegatedAdministrators: mappedDelegatedAdministrators,
|
|
595
|
+
policies: {
|
|
596
|
+
serviceControlPolicies: scps,
|
|
597
|
+
resourceControlPolicies: rcps,
|
|
598
|
+
tagPolicies,
|
|
599
|
+
aiServicesOptOutPolicies,
|
|
600
|
+
backupPolicies
|
|
601
|
+
}
|
|
451
602
|
};
|
|
452
603
|
assertUniqueNames({
|
|
453
604
|
values: mapped.organizationalUnits.map((ou) => ou.name),
|
|
@@ -614,7 +765,8 @@ function mapAwsConfigToState(props) {
|
|
|
614
765
|
email: account.email,
|
|
615
766
|
status: matchedAccount?.status ?? "ACTIVE",
|
|
616
767
|
parentId: ownerParentId,
|
|
617
|
-
tags: account.tags
|
|
768
|
+
tags: account.tags,
|
|
769
|
+
alternateContacts: account.alternateContacts != null && account.alternateContacts.length > 0 ? account.alternateContacts : void 0
|
|
618
770
|
});
|
|
619
771
|
mappedAccountIdByName.set(account.name, mappedId);
|
|
620
772
|
}
|
|
@@ -628,10 +780,7 @@ function mapAwsConfigToState(props) {
|
|
|
628
780
|
email: user.email
|
|
629
781
|
};
|
|
630
782
|
});
|
|
631
|
-
const mappedUserByUserName = toRecordByProperty(
|
|
632
|
-
mappedUsers,
|
|
633
|
-
"userName"
|
|
634
|
-
);
|
|
783
|
+
const mappedUserByUserName = toRecordByProperty(mappedUsers, "userName");
|
|
635
784
|
const mappedGroups = props.config.groups.map((group) => {
|
|
636
785
|
const matchedGroup = groupByDisplayName[group.displayName];
|
|
637
786
|
return {
|
|
@@ -677,7 +826,8 @@ function mapAwsConfigToState(props) {
|
|
|
677
826
|
name: customerManagedPolicy.name,
|
|
678
827
|
path: customerManagedPolicy.path
|
|
679
828
|
})
|
|
680
|
-
)
|
|
829
|
+
),
|
|
830
|
+
permissionsBoundary: permissionSet.permissionsBoundary ?? null
|
|
681
831
|
};
|
|
682
832
|
});
|
|
683
833
|
const mappedPermissionSetByName = toRecordByProperty(
|
|
@@ -710,13 +860,122 @@ function mapAwsConfigToState(props) {
|
|
|
710
860
|
});
|
|
711
861
|
}
|
|
712
862
|
}
|
|
863
|
+
const configPolicies = props.config.policies;
|
|
864
|
+
const allConfigPolicies = [];
|
|
865
|
+
const ouByName = toRecordByProperty(
|
|
866
|
+
props.currentState.organization.organizationalUnits,
|
|
867
|
+
"name"
|
|
868
|
+
);
|
|
869
|
+
const stateAccountByName = toRecordByProperty(
|
|
870
|
+
props.currentState.organization.accounts,
|
|
871
|
+
"name"
|
|
872
|
+
);
|
|
873
|
+
function resolveTargetId(targetName) {
|
|
874
|
+
if (targetName === "root") {
|
|
875
|
+
return {
|
|
876
|
+
targetId: props.context.organization.rootId,
|
|
877
|
+
targetType: "ROOT"
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
const ou = ouByName[targetName];
|
|
881
|
+
if (ou != null) {
|
|
882
|
+
return { targetId: ou.id, targetType: "ORGANIZATIONAL_UNIT" };
|
|
883
|
+
}
|
|
884
|
+
const acct = stateAccountByName[targetName];
|
|
885
|
+
if (acct != null) {
|
|
886
|
+
return { targetId: acct.id, targetType: "ACCOUNT" };
|
|
887
|
+
}
|
|
888
|
+
return { targetId: pendingCreationId, targetType: "ACCOUNT" };
|
|
889
|
+
}
|
|
890
|
+
for (const policy of configPolicies.serviceControlPolicies) {
|
|
891
|
+
allConfigPolicies.push({
|
|
892
|
+
name: policy.name,
|
|
893
|
+
description: policy.description ?? "",
|
|
894
|
+
type: "SERVICE_CONTROL_POLICY",
|
|
895
|
+
content: JSON.stringify(policy.content),
|
|
896
|
+
targets: policy.targets.map((t) => resolveTargetId(t))
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
for (const policy of configPolicies.resourceControlPolicies) {
|
|
900
|
+
allConfigPolicies.push({
|
|
901
|
+
name: policy.name,
|
|
902
|
+
description: policy.description ?? "",
|
|
903
|
+
type: "RESOURCE_CONTROL_POLICY",
|
|
904
|
+
content: JSON.stringify(policy.content),
|
|
905
|
+
targets: policy.targets.map((t) => resolveTargetId(t))
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
for (const policy of configPolicies.tagPolicies) {
|
|
909
|
+
allConfigPolicies.push({
|
|
910
|
+
name: policy.name,
|
|
911
|
+
description: policy.description ?? "",
|
|
912
|
+
type: "TAG_POLICY",
|
|
913
|
+
content: JSON.stringify(policy.content),
|
|
914
|
+
targets: policy.targets.map((t) => resolveTargetId(t))
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
for (const policy of configPolicies.aiServicesOptOutPolicies) {
|
|
918
|
+
allConfigPolicies.push({
|
|
919
|
+
name: policy.name,
|
|
920
|
+
description: policy.description ?? "",
|
|
921
|
+
type: "AISERVICES_OPT_OUT_POLICY",
|
|
922
|
+
content: JSON.stringify(policy.content),
|
|
923
|
+
targets: policy.targets.map((t) => resolveTargetId(t))
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
for (const policy of configPolicies.backupPolicies) {
|
|
927
|
+
allConfigPolicies.push({
|
|
928
|
+
name: policy.name,
|
|
929
|
+
description: policy.description ?? "",
|
|
930
|
+
type: "BACKUP_POLICY",
|
|
931
|
+
content: JSON.stringify(policy.content),
|
|
932
|
+
targets: policy.targets.map((t) => resolveTargetId(t))
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
const currentPoliciesByNameAndType = new Map(
|
|
936
|
+
(props.currentState.organization.policies ?? []).map((p) => [
|
|
937
|
+
`${p.type}|${p.name}`,
|
|
938
|
+
p
|
|
939
|
+
])
|
|
940
|
+
);
|
|
941
|
+
const mappedPolicies = allConfigPolicies.map((p) => {
|
|
942
|
+
const current = currentPoliciesByNameAndType.get(`${p.type}|${p.name}`);
|
|
943
|
+
return {
|
|
944
|
+
id: current?.id ?? pendingCreationId,
|
|
945
|
+
arn: current?.arn ?? pendingCreationId,
|
|
946
|
+
name: p.name,
|
|
947
|
+
description: p.description,
|
|
948
|
+
type: p.type,
|
|
949
|
+
content: p.content
|
|
950
|
+
};
|
|
951
|
+
});
|
|
952
|
+
const mappedPolicyAttachments = [];
|
|
953
|
+
for (let i = 0; i < allConfigPolicies.length; i++) {
|
|
954
|
+
const configPolicy = allConfigPolicies[i];
|
|
955
|
+
const mappedPolicy = mappedPolicies[i];
|
|
956
|
+
for (const target of configPolicy.targets) {
|
|
957
|
+
mappedPolicyAttachments.push({
|
|
958
|
+
policyId: mappedPolicy.id,
|
|
959
|
+
targetId: target.targetId,
|
|
960
|
+
targetType: target.targetType
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
const configDelegatedAdmins = props.config.delegatedAdministrators;
|
|
965
|
+
const mappedDelegatedAdministrators = configDelegatedAdmins.length > 0 ? configDelegatedAdmins.map(({ account, servicePrincipal }) => ({
|
|
966
|
+
accountId: stateAccountByName[account]?.id ?? pendingCreationId,
|
|
967
|
+
servicePrincipal
|
|
968
|
+
})) : void 0;
|
|
713
969
|
const mapped = {
|
|
714
970
|
version: props.currentState.version,
|
|
715
971
|
generatedAt: props.currentState.generatedAt,
|
|
716
972
|
organization: {
|
|
717
973
|
rootId: props.context.organization.rootId,
|
|
718
974
|
organizationalUnits: mappedOrganizationalUnits,
|
|
719
|
-
accounts: mappedAccounts
|
|
975
|
+
accounts: mappedAccounts,
|
|
976
|
+
policies: mappedPolicies,
|
|
977
|
+
policyAttachments: mappedPolicyAttachments,
|
|
978
|
+
delegatedAdministrators: mappedDelegatedAdministrators
|
|
720
979
|
},
|
|
721
980
|
identityCenter: {
|
|
722
981
|
instanceArn: props.context.identityCenter.instanceArn,
|
|
@@ -732,7 +991,10 @@ function mapAwsConfigToState(props) {
|
|
|
732
991
|
principalId: assignment.principalId,
|
|
733
992
|
principalType: assignment.principalType,
|
|
734
993
|
roleName: createAccessRoleName(assignment)
|
|
735
|
-
}))
|
|
994
|
+
})),
|
|
995
|
+
accessControlAttributes: (props.config.accessControlAttributes ?? []).map(
|
|
996
|
+
(attr) => ({ key: attr.key, source: [...attr.source] })
|
|
997
|
+
)
|
|
736
998
|
}
|
|
737
999
|
};
|
|
738
1000
|
assertUniqueNames({
|
|
@@ -761,8 +1023,35 @@ function mapAwsConfigToState(props) {
|
|
|
761
1023
|
),
|
|
762
1024
|
entityName: "permission set"
|
|
763
1025
|
});
|
|
1026
|
+
assertUniqueNames({
|
|
1027
|
+
values: props.config.policies.serviceControlPolicies.map((p) => p.name),
|
|
1028
|
+
entityName: "SCP"
|
|
1029
|
+
});
|
|
1030
|
+
assertUniqueNames({
|
|
1031
|
+
values: props.config.policies.resourceControlPolicies.map((p) => p.name),
|
|
1032
|
+
entityName: "RCP"
|
|
1033
|
+
});
|
|
1034
|
+
assertUniqueNames({
|
|
1035
|
+
values: props.config.policies.tagPolicies.map((p) => p.name),
|
|
1036
|
+
entityName: "tag policy"
|
|
1037
|
+
});
|
|
1038
|
+
assertUniqueNames({
|
|
1039
|
+
values: props.config.policies.aiServicesOptOutPolicies.map((p) => p.name),
|
|
1040
|
+
entityName: "AI services opt-out policy"
|
|
1041
|
+
});
|
|
1042
|
+
assertUniqueNames({
|
|
1043
|
+
values: props.config.policies.backupPolicies.map((p) => p.name),
|
|
1044
|
+
entityName: "backup policy"
|
|
1045
|
+
});
|
|
764
1046
|
return validateState(mapped);
|
|
765
1047
|
}
|
|
1048
|
+
function sortConfigPolicies(policies) {
|
|
1049
|
+
return [...policies].map((p) => ({
|
|
1050
|
+
...p,
|
|
1051
|
+
content: sortJsonRecord(p.content),
|
|
1052
|
+
targets: [...p.targets].sort((left, right) => left.localeCompare(right))
|
|
1053
|
+
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
1054
|
+
}
|
|
766
1055
|
function sortAwsConfigModel(props) {
|
|
767
1056
|
const childrenByParentName = /* @__PURE__ */ new Map();
|
|
768
1057
|
for (const organizationalUnit of props.config.organizationalUnits) {
|
|
@@ -795,9 +1084,12 @@ function sortAwsConfigModel(props) {
|
|
|
795
1084
|
for (const child of children) {
|
|
796
1085
|
orderedOrganizationalUnits.push({
|
|
797
1086
|
...child,
|
|
798
|
-
accounts: [...child.accounts].sort(
|
|
799
|
-
|
|
800
|
-
|
|
1087
|
+
accounts: [...child.accounts].sort((left, right) => left.name.localeCompare(right.name)).map((account) => ({
|
|
1088
|
+
...account,
|
|
1089
|
+
alternateContacts: account.alternateContacts == null ? void 0 : [...account.alternateContacts].sort(
|
|
1090
|
+
(a, b) => a.contactType.localeCompare(b.contactType)
|
|
1091
|
+
)
|
|
1092
|
+
}))
|
|
801
1093
|
});
|
|
802
1094
|
queue.push(child.name);
|
|
803
1095
|
}
|
|
@@ -819,7 +1111,9 @@ function sortAwsConfigModel(props) {
|
|
|
819
1111
|
awsManagedPolicies: [...permissionSet.awsManagedPolicies].sort(
|
|
820
1112
|
(left, right) => left.localeCompare(right)
|
|
821
1113
|
),
|
|
822
|
-
customerManagedPolicies: [
|
|
1114
|
+
customerManagedPolicies: [
|
|
1115
|
+
...permissionSet.customerManagedPolicies
|
|
1116
|
+
].sort(
|
|
823
1117
|
(left, right) => compareStringKeys(left.path, right.path, left.name, right.name)
|
|
824
1118
|
)
|
|
825
1119
|
})).sort((left, right) => left.name.localeCompare(right.name)),
|
|
@@ -836,7 +1130,35 @@ function sortAwsConfigModel(props) {
|
|
|
836
1130
|
return principalComparison;
|
|
837
1131
|
}
|
|
838
1132
|
return left.permissionSet.localeCompare(right.permissionSet);
|
|
839
|
-
})
|
|
1133
|
+
}),
|
|
1134
|
+
accessControlAttributes: [...props.config.accessControlAttributes].map((attr) => ({
|
|
1135
|
+
...attr,
|
|
1136
|
+
source: [...attr.source].sort(
|
|
1137
|
+
(left, right) => left.localeCompare(right)
|
|
1138
|
+
)
|
|
1139
|
+
})).sort((left, right) => left.key.localeCompare(right.key)),
|
|
1140
|
+
delegatedAdministrators: [...props.config.delegatedAdministrators].sort(
|
|
1141
|
+
(left, right) => {
|
|
1142
|
+
const accountComparison = left.account.localeCompare(right.account);
|
|
1143
|
+
if (accountComparison !== 0) {
|
|
1144
|
+
return accountComparison;
|
|
1145
|
+
}
|
|
1146
|
+
return left.servicePrincipal.localeCompare(right.servicePrincipal);
|
|
1147
|
+
}
|
|
1148
|
+
),
|
|
1149
|
+
policies: {
|
|
1150
|
+
serviceControlPolicies: sortConfigPolicies(
|
|
1151
|
+
props.config.policies.serviceControlPolicies
|
|
1152
|
+
),
|
|
1153
|
+
resourceControlPolicies: sortConfigPolicies(
|
|
1154
|
+
props.config.policies.resourceControlPolicies
|
|
1155
|
+
),
|
|
1156
|
+
tagPolicies: sortConfigPolicies(props.config.policies.tagPolicies),
|
|
1157
|
+
aiServicesOptOutPolicies: sortConfigPolicies(
|
|
1158
|
+
props.config.policies.aiServicesOptOutPolicies
|
|
1159
|
+
),
|
|
1160
|
+
backupPolicies: sortConfigPolicies(props.config.policies.backupPolicies)
|
|
1161
|
+
}
|
|
840
1162
|
};
|
|
841
1163
|
}
|
|
842
1164
|
function renderAwsConfigTs(props) {
|
|
@@ -867,7 +1189,9 @@ function renderTsValue(value, props) {
|
|
|
867
1189
|
return "null";
|
|
868
1190
|
}
|
|
869
1191
|
if (value === void 0) {
|
|
870
|
-
throw new Error(
|
|
1192
|
+
throw new Error(
|
|
1193
|
+
"Undefined values must be handled before TypeScript rendering."
|
|
1194
|
+
);
|
|
871
1195
|
}
|
|
872
1196
|
if (typeof value === "string") {
|
|
873
1197
|
return renderTsStringValue(value, props);
|
|
@@ -907,14 +1231,16 @@ ${renderedItems.map((item) => `${childIndent}${item}`).join(",\n")}
|
|
|
907
1231
|
${indent}]`;
|
|
908
1232
|
}
|
|
909
1233
|
function renderTsObject(value, props) {
|
|
910
|
-
const entries = Object.entries(value).filter(
|
|
1234
|
+
const entries = Object.entries(value).filter(
|
|
1235
|
+
([, entryValue]) => entryValue !== void 0
|
|
1236
|
+
);
|
|
911
1237
|
if (entries.length === 0) {
|
|
912
1238
|
return "{}";
|
|
913
1239
|
}
|
|
914
1240
|
const indent = " ".repeat(props.indentLevel);
|
|
915
1241
|
const childIndent = " ".repeat(props.indentLevel + 1);
|
|
916
1242
|
const renderedEntries = entries.map(([key, entryValue]) => {
|
|
917
|
-
const nextWithinInlinePolicy = props.withinInlinePolicy || key === "inlinePolicy";
|
|
1243
|
+
const nextWithinInlinePolicy = props.withinInlinePolicy || key === "inlinePolicy" || key === "content";
|
|
918
1244
|
const renderedValue = renderTsValue(entryValue, {
|
|
919
1245
|
indentLevel: props.indentLevel + 1,
|
|
920
1246
|
withinInlinePolicy: nextWithinInlinePolicy,
|
|
@@ -1035,6 +1361,17 @@ export const awsConfigSchema = v.strictObject({
|
|
|
1035
1361
|
value: v.string(),
|
|
1036
1362
|
}),
|
|
1037
1363
|
),
|
|
1364
|
+
alternateContacts: v.optional(
|
|
1365
|
+
v.array(
|
|
1366
|
+
v.strictObject({
|
|
1367
|
+
contactType: v.picklist(["BILLING", "OPERATIONS", "SECURITY"]),
|
|
1368
|
+
name: v.string(),
|
|
1369
|
+
email: v.string(),
|
|
1370
|
+
phone: v.string(),
|
|
1371
|
+
title: v.optional(v.string()),
|
|
1372
|
+
}),
|
|
1373
|
+
),
|
|
1374
|
+
),
|
|
1038
1375
|
}),
|
|
1039
1376
|
),
|
|
1040
1377
|
}),
|
|
@@ -1066,6 +1403,15 @@ export const awsConfigSchema = v.strictObject({
|
|
|
1066
1403
|
path: v.string(),
|
|
1067
1404
|
}),
|
|
1068
1405
|
),
|
|
1406
|
+
permissionsBoundary: v.optional(
|
|
1407
|
+
v.union([
|
|
1408
|
+
v.strictObject({ managedPolicyArn: v.string() }),
|
|
1409
|
+
v.strictObject({
|
|
1410
|
+
customerManagedPolicyName: v.string(),
|
|
1411
|
+
customerManagedPolicyPath: v.string(),
|
|
1412
|
+
}),
|
|
1413
|
+
]),
|
|
1414
|
+
),
|
|
1069
1415
|
}),
|
|
1070
1416
|
),
|
|
1071
1417
|
assignments: v.array(
|
|
@@ -1076,6 +1422,60 @@ export const awsConfigSchema = v.strictObject({
|
|
|
1076
1422
|
accounts: v.array(accountNameSchema),
|
|
1077
1423
|
}),
|
|
1078
1424
|
),
|
|
1425
|
+
accessControlAttributes: v.array(
|
|
1426
|
+
v.strictObject({
|
|
1427
|
+
key: v.string(),
|
|
1428
|
+
source: v.array(v.string()),
|
|
1429
|
+
}),
|
|
1430
|
+
),
|
|
1431
|
+
delegatedAdministrators: v.array(
|
|
1432
|
+
v.strictObject({
|
|
1433
|
+
account: accountNameSchema,
|
|
1434
|
+
servicePrincipal: v.string(),
|
|
1435
|
+
}),
|
|
1436
|
+
),
|
|
1437
|
+
policies: v.strictObject({
|
|
1438
|
+
serviceControlPolicies: v.array(
|
|
1439
|
+
v.strictObject({
|
|
1440
|
+
name: v.string(),
|
|
1441
|
+
description: v.optional(v.string()),
|
|
1442
|
+
content: v.record(v.string(), v.unknown()),
|
|
1443
|
+
targets: v.array(v.union([organizationalUnitNameSchema, accountNameSchema])),
|
|
1444
|
+
}),
|
|
1445
|
+
),
|
|
1446
|
+
resourceControlPolicies: v.array(
|
|
1447
|
+
v.strictObject({
|
|
1448
|
+
name: v.string(),
|
|
1449
|
+
description: v.optional(v.string()),
|
|
1450
|
+
content: v.record(v.string(), v.unknown()),
|
|
1451
|
+
targets: v.array(v.union([organizationalUnitNameSchema, accountNameSchema])),
|
|
1452
|
+
}),
|
|
1453
|
+
),
|
|
1454
|
+
tagPolicies: v.array(
|
|
1455
|
+
v.strictObject({
|
|
1456
|
+
name: v.string(),
|
|
1457
|
+
description: v.optional(v.string()),
|
|
1458
|
+
content: v.record(v.string(), v.unknown()),
|
|
1459
|
+
targets: v.array(v.union([organizationalUnitNameSchema, accountNameSchema])),
|
|
1460
|
+
}),
|
|
1461
|
+
),
|
|
1462
|
+
aiServicesOptOutPolicies: v.array(
|
|
1463
|
+
v.strictObject({
|
|
1464
|
+
name: v.string(),
|
|
1465
|
+
description: v.optional(v.string()),
|
|
1466
|
+
content: v.record(v.string(), v.unknown()),
|
|
1467
|
+
targets: v.array(v.union([organizationalUnitNameSchema, accountNameSchema])),
|
|
1468
|
+
}),
|
|
1469
|
+
),
|
|
1470
|
+
backupPolicies: v.array(
|
|
1471
|
+
v.strictObject({
|
|
1472
|
+
name: v.string(),
|
|
1473
|
+
description: v.optional(v.string()),
|
|
1474
|
+
content: v.record(v.string(), v.unknown()),
|
|
1475
|
+
targets: v.array(v.union([organizationalUnitNameSchema, accountNameSchema])),
|
|
1476
|
+
}),
|
|
1477
|
+
),
|
|
1478
|
+
}),
|
|
1079
1479
|
});
|
|
1080
1480
|
|
|
1081
1481
|
export type AwsConfig = v.InferOutput<typeof awsConfigSchema>;
|
|
@@ -1234,6 +1634,16 @@ async function loadAwsConfigModelFromTsFile(props) {
|
|
|
1234
1634
|
async function readAwsContextFromFile(path) {
|
|
1235
1635
|
return readAwsContextFile(path);
|
|
1236
1636
|
}
|
|
1637
|
+
async function readPackageVersion() {
|
|
1638
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
1639
|
+
const packageDir = dirname(dirname(thisFile));
|
|
1640
|
+
const raw = await readFile(join(packageDir, "package.json"), "utf8");
|
|
1641
|
+
const pkg = JSON.parse(raw);
|
|
1642
|
+
if (typeof pkg.version !== "string") {
|
|
1643
|
+
throw new Error("Could not read version from package.json.");
|
|
1644
|
+
}
|
|
1645
|
+
return pkg.version;
|
|
1646
|
+
}
|
|
1237
1647
|
async function loadAwsConfigTypesModule(props) {
|
|
1238
1648
|
const loadedModule = await loadTsModule({
|
|
1239
1649
|
modulePath: props.typesPath
|
|
@@ -1361,6 +1771,7 @@ export {
|
|
|
1361
1771
|
loadAwsConfigModelFromTsFile,
|
|
1362
1772
|
mapAwsConfigToState,
|
|
1363
1773
|
readAwsContextFromFile,
|
|
1774
|
+
readPackageVersion,
|
|
1364
1775
|
regenerateAwsConfigTypes,
|
|
1365
1776
|
regenerateTypesFromState,
|
|
1366
1777
|
writeAwsConfigFromState
|