@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/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
- state
125
- });
198
+ const mappedConfig = mapStateToAwsConfig({ state });
199
+ const mergedConfig = props.existingConfig != null ? props.existingConfig : mappedConfig;
126
200
  const sortedConfig = sortAwsConfigModel({
127
- config: mappedConfig
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: typesModule.awsConfigSchema
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
- (left, right) => left.name.localeCompare(right.name)
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: [...permissionSet.customerManagedPolicies].sort(
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("Undefined values must be handled before TypeScript rendering.");
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(([, entryValue]) => entryValue !== void 0);
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