@beesolve/aws-accounts 1.2.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/cli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { parseArgs } from "node:util";
2
2
  import { createInterface } from "node:readline/promises";
3
+ import { basename } from "node:path";
3
4
  import { S3Client } from "@aws-sdk/client-s3";
4
5
  import { IAMClient } from "@aws-sdk/client-iam";
5
6
  import { LambdaClient } from "@aws-sdk/client-lambda";
@@ -24,13 +25,15 @@ import {
24
25
  runRemoteInit,
25
26
  runRemotePlan,
26
27
  runRemoteApply,
27
- runRemoteUpgrade
28
+ runRemoteUpgrade,
29
+ runRemoteDrift
28
30
  } from "./commands/remote.js";
29
31
  import {
30
32
  classifyCliError,
31
33
  exitCodeForCliErrorKind,
32
34
  toUsageError
33
35
  } from "./error.js";
36
+ import { assertUnreachable } from "./helpers.js";
34
37
  import { readAwsContextFromFile, readPackageVersion } from "./awsConfig.js";
35
38
  const commands = [
36
39
  "bootstrap",
@@ -42,7 +45,8 @@ const commands = [
42
45
  "profile",
43
46
  "plan",
44
47
  "apply",
45
- "upgrade"
48
+ "upgrade",
49
+ "drift"
46
50
  ];
47
51
  function isCommandName(value) {
48
52
  return commands.includes(value);
@@ -171,51 +175,73 @@ async function main() {
171
175
  };
172
176
  if (command === "bootstrap") {
173
177
  await runRemoteBootstrap(remoteInput);
174
- } else if (command === "scan") {
178
+ await printVersionBannerIfNeeded(logger);
179
+ return;
180
+ }
181
+ if (command === "scan") {
175
182
  await runRemoteScan(remoteInput);
176
- } else if (command === "init") {
183
+ await printVersionBannerIfNeeded(logger);
184
+ return;
185
+ }
186
+ if (command === "init") {
177
187
  await runRemoteInit(remoteInput);
178
- } else if (command === "plan") {
188
+ await printVersionBannerIfNeeded(logger);
189
+ return;
190
+ }
191
+ if (command === "plan") {
179
192
  await runRemotePlan(remoteInput);
180
- } else if (command === "apply") {
193
+ await printVersionBannerIfNeeded(logger);
194
+ return;
195
+ }
196
+ if (command === "apply") {
181
197
  await runRemoteApply(remoteInput);
182
- } else if (command === "upgrade") {
198
+ await printVersionBannerIfNeeded(logger);
199
+ return;
200
+ }
201
+ if (command === "upgrade") {
183
202
  await runRemoteUpgrade(remoteInput);
184
- } else {
185
- printHelp(logger);
186
- process.exitCode = 1;
203
+ await printVersionBannerIfNeeded(logger);
187
204
  return;
188
205
  }
189
- await printVersionBannerIfNeeded(logger);
206
+ if (command === "drift") {
207
+ await runRemoteDrift(remoteInput);
208
+ await printVersionBannerIfNeeded(logger);
209
+ return;
210
+ }
211
+ assertUnreachable(command, `Unhandled remote command: "${command}"`);
190
212
  }
191
213
  function printHelp(logger) {
214
+ const cmd = basename(process.argv[1], ".js");
192
215
  logger.log("@beesolve/aws-accounts");
193
216
  logger.log("");
194
217
  logger.log("Usage:");
195
218
  logger.log(
196
- " npm run cli -- bootstrap [--profile <name>] [--region <region>] [--yes]"
219
+ ` ${cmd} bootstrap [--profile <name>] [--region <region>] [--yes]`
220
+ );
221
+ logger.log(` ${cmd} scan [--profile <name>] [--region <region>]`);
222
+ logger.log(
223
+ ` ${cmd} init [--profile <name>] [--region <region>] [--yes]`
197
224
  );
198
- logger.log(" npm run cli -- scan [--profile <name>] [--region <region>]");
199
225
  logger.log(
200
- " npm run cli -- init [--profile <name>] [--region <region>] [--yes]"
226
+ ` ${cmd} init --update [--profile <name>] [--region <region>] [--yes]`
201
227
  );
228
+ logger.log(` ${cmd} regenerate [--yes]`);
229
+ logger.log(` ${cmd} validate`);
230
+ logger.log(` ${cmd} graveyard`);
231
+ logger.log(` ${cmd} graveyard close`);
202
232
  logger.log(
203
- " npm run cli -- init --update [--profile <name>] [--region <region>] [--yes]"
233
+ ` ${cmd} profile --sso-start-url <url> [--sso-session <name>] (env: AWS_SSO_START_URL)`
204
234
  );
205
- logger.log(" npm run cli -- regenerate [--yes]");
206
- logger.log(" npm run cli -- validate");
207
- logger.log(" npm run cli -- graveyard");
208
- logger.log(" npm run cli -- graveyard close");
209
235
  logger.log(
210
- " npm run cli -- profile --sso-start-url <url> [--sso-session <name>] (env: AWS_SSO_START_URL)"
236
+ ` ${cmd} plan [--profile <name>] [--region <region>] [--refresh]`
211
237
  );
212
238
  logger.log(
213
- " npm run cli -- plan [--profile <name>] [--region <region>] [--refresh]"
239
+ ` ${cmd} apply [--profile <name>] [--region <region>] [--yes] [--allow-destructive] [--ignore-unsupported]`
214
240
  );
241
+ logger.log(` ${cmd} upgrade [--profile <name>] [--region <region>]`);
215
242
  logger.log(
216
- " npm run cli -- apply [--profile <name>] [--region <region>] [--yes] [--allow-destructive] [--ignore-unsupported]"
243
+ ` ${cmd} drift [--profile <name>] [--region <region>] [--refresh]`
217
244
  );
218
- logger.log(" npm run cli -- upgrade [--profile <name>] [--region <region>]");
219
245
  logger.log("");
220
246
  logger.log("Environment fallback:");
221
247
  logger.log(" AWS_PROFILE, AWS_REGION, AWS_DEFAULT_REGION");
@@ -55,7 +55,7 @@ import { assertUnreachable, delay } from "../helpers.js";
55
55
  import { toPreconditionError } from "../error.js";
56
56
  import { sts, organizations, sso, identitystore, s3, logs, account, iam, lambda } from "@beesolve/iam-policy-ts";
57
57
  const remoteCommandSchema = v.object({
58
- subcommand: v.picklist(["bootstrap", "scan", "init", "plan", "apply", "upgrade"]),
58
+ subcommand: v.picklist(["bootstrap", "scan", "init", "plan", "apply", "upgrade", "drift"]),
59
59
  profile: v.optional(v.string()),
60
60
  region: v.optional(v.string()),
61
61
  flags: v.object({
@@ -657,10 +657,61 @@ async function runRemoteUpgrade(input) {
657
657
  input.logger.log("");
658
658
  input.logger.log("Run init --update to sync your config with new remote features before using plan/apply.");
659
659
  }
660
+ async function runRemoteDrift(input) {
661
+ const deployment = await readDeploymentFromContext();
662
+ const baseline = await fetchCurrentState({
663
+ input,
664
+ deployment
665
+ });
666
+ const clientConfig = buildAwsClientConfig({
667
+ profile: input.profile ?? (deployment.profile || void 0),
668
+ region: input.region ?? (deployment.region || void 0)
669
+ });
670
+ const lambdaClient = new LambdaClient(clientConfig);
671
+ input.logger.log("Scanning live AWS state...");
672
+ const result = await invokeLambda({
673
+ lambdaClient,
674
+ lambdaArn: deployment.lambdaArn,
675
+ payload: { action: "scan" }
676
+ });
677
+ if (!result.ok) {
678
+ throw new Error(formatLambdaError(result.error));
679
+ }
680
+ const response = result.response;
681
+ if (!("action" in response) || response.action !== "scan") {
682
+ throw new Error("Unexpected response from Lambda scan action.");
683
+ }
684
+ const liveState = response.state;
685
+ await writeStateCache(cachePath, liveState);
686
+ const plan = diffStates({
687
+ current: baseline,
688
+ next: liveState
689
+ });
690
+ displayDrift({ plan, logger: input.logger });
691
+ }
692
+ function displayDrift(props) {
693
+ const driftOperations = props.plan.operations.filter(
694
+ (operation) => operation.kind !== "provisionIdcPermissionSet"
695
+ );
696
+ if (driftOperations.length === 0 && props.plan.unsupported.length === 0) {
697
+ props.logger.log("No drift.");
698
+ return;
699
+ }
700
+ props.logger.log(`Drift: ${driftOperations.length} change(s) detected since last scan`);
701
+ for (const operation of driftOperations) {
702
+ props.logger.log(formatOperationLine(operation));
703
+ }
704
+ if (props.plan.unsupported.length > 0) {
705
+ props.logger.log("Unsupported diffs:");
706
+ for (const diff of props.plan.unsupported) {
707
+ props.logger.log(` - ${diff.description} [${diff.category}]`);
708
+ }
709
+ }
710
+ }
660
711
  function warnIfRemotePoliciesNotInConfig(props) {
661
712
  const remotePolicies = props.currentState.organization.policies ?? [];
662
713
  const hasRemotePolicies = remotePolicies.length > 0;
663
- const hasLocalPolicies = (props.config.policies?.serviceControlPolicies?.length ?? 0) > 0 || (props.config.policies?.resourceControlPolicies?.length ?? 0) > 0;
714
+ const hasLocalPolicies = props.config.policies.serviceControlPolicies.length > 0 || props.config.policies.resourceControlPolicies.length > 0;
664
715
  if (hasRemotePolicies && !hasLocalPolicies) {
665
716
  props.logger.log("");
666
717
  props.logger.log("Warning: remote state contains SCPs/RCPs not present in your config. Proceeding could delete them.");
@@ -748,7 +799,7 @@ function displayPlan(props) {
748
799
  }
749
800
  }
750
801
  function isDestructiveOperation(operation) {
751
- return operation.kind === "deleteOu" || operation.kind === "removeAccount" || operation.kind === "deleteIdcUser" || operation.kind === "deleteIdcGroup" || operation.kind === "deleteIdcPermissionSet" || operation.kind === "detachOrgPolicy" || operation.kind === "deleteOrgPolicy";
802
+ return operation.kind === "deleteOu" || operation.kind === "removeAccount" || operation.kind === "deleteIdcUser" || operation.kind === "deleteIdcGroup" || operation.kind === "deleteIdcPermissionSet" || operation.kind === "deleteIdcPermissionSetPermissionsBoundary" || operation.kind === "detachOrgPolicy" || operation.kind === "deleteOrgPolicy" || operation.kind === "deregisterDelegatedAdministrator";
752
803
  }
753
804
  function formatOperationLine(operation) {
754
805
  if (operation.kind === "moveAccount") {
@@ -826,6 +877,14 @@ function formatOperationLine(operation) {
826
877
  if (operation.kind === "provisionIdcPermissionSet") {
827
878
  return ` provision IdC permission set "${operation.permissionSetName}" to all provisioned accounts`;
828
879
  }
880
+ if (operation.kind === "putIdcPermissionSetPermissionsBoundary") {
881
+ const b = operation.permissionsBoundary;
882
+ const label = "managedPolicyArn" in b ? b.managedPolicyArn : `${b.customerManagedPolicyPath}${b.customerManagedPolicyName}`;
883
+ return ` put permissions boundary "${label}" on IdC permission set "${operation.permissionSetName}"`;
884
+ }
885
+ if (operation.kind === "deleteIdcPermissionSetPermissionsBoundary") {
886
+ return ` [destructive] delete permissions boundary from IdC permission set "${operation.permissionSetName}"`;
887
+ }
829
888
  if (operation.kind === "removeIdcGroupMembership") {
830
889
  return ` remove user "${operation.userName}" from IdC group "${operation.groupDisplayName}"`;
831
890
  }
@@ -866,6 +925,12 @@ function formatOperationLine(operation) {
866
925
  if (operation.kind === "setIdcAccessControlAttributes") {
867
926
  return ` set IdC access control attributes (${operation.attributes.length} attribute(s))`;
868
927
  }
928
+ if (operation.kind === "registerDelegatedAdministrator") {
929
+ return ` register delegated administrator "${operation.accountName}" (${operation.accountId}) for ${operation.servicePrincipal}`;
930
+ }
931
+ if (operation.kind === "deregisterDelegatedAdministrator") {
932
+ return ` [destructive] deregister delegated administrator "${operation.accountName}" (${operation.accountId}) for ${operation.servicePrincipal}`;
933
+ }
869
934
  assertUnreachable(operation, "Unsupported operation kind in formatOperationLine.");
870
935
  }
871
936
  function formatPrincipalLabel(principalType, principalName) {
@@ -1077,6 +1142,7 @@ async function waitForLambdaReady(lambdaClient, functionName) {
1077
1142
  export {
1078
1143
  runRemoteApply,
1079
1144
  runRemoteBootstrap,
1145
+ runRemoteDrift,
1080
1146
  runRemoteInit,
1081
1147
  runRemotePlan,
1082
1148
  runRemoteScan,
@@ -17,6 +17,7 @@ async function runValidateCommand(input) {
17
17
  checkInlinePolicySizes(config, errors);
18
18
  checkOrgPolicySizes(config, errors);
19
19
  checkOrgPolicyTargets(config, errors);
20
+ checkDelegatedAdministratorDuplicates(config, errors);
20
21
  if (errors.length > 0) {
21
22
  for (const error of errors) {
22
23
  input.logger.log(`Error: ${error}`);
@@ -66,7 +67,7 @@ function checkAssignmentPrincipals(config, errors) {
66
67
  }
67
68
  }
68
69
  function checkOrgPolicySizes(config, errors) {
69
- for (const policy of config.policies?.serviceControlPolicies ?? []) {
70
+ for (const policy of config.policies.serviceControlPolicies) {
70
71
  const contentBytes = Buffer.byteLength(JSON.stringify(policy.content), "utf8");
71
72
  if (contentBytes > ORG_POLICY_CONTENT_MAX_BYTES) {
72
73
  errors.push(
@@ -74,7 +75,7 @@ function checkOrgPolicySizes(config, errors) {
74
75
  );
75
76
  }
76
77
  }
77
- for (const policy of config.policies?.resourceControlPolicies ?? []) {
78
+ for (const policy of config.policies.resourceControlPolicies) {
78
79
  const contentBytes = Buffer.byteLength(JSON.stringify(policy.content), "utf8");
79
80
  if (contentBytes > ORG_POLICY_CONTENT_MAX_BYTES) {
80
81
  errors.push(
@@ -82,13 +83,21 @@ function checkOrgPolicySizes(config, errors) {
82
83
  );
83
84
  }
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
+ }
85
94
  }
86
95
  function checkOrgPolicyTargets(config, errors) {
87
96
  const ouNames = new Set(config.organizationalUnits.map((ou) => ou.name));
88
97
  const accountNames = new Set(
89
98
  config.organizationalUnits.flatMap((ou) => ou.accounts.map((a) => a.name))
90
99
  );
91
- for (const policy of config.policies?.serviceControlPolicies ?? []) {
100
+ for (const policy of config.policies.serviceControlPolicies) {
92
101
  for (const target of policy.targets) {
93
102
  if (target !== "root" && !ouNames.has(target) && !accountNames.has(target)) {
94
103
  errors.push(
@@ -97,7 +106,7 @@ function checkOrgPolicyTargets(config, errors) {
97
106
  }
98
107
  }
99
108
  }
100
- for (const policy of config.policies?.resourceControlPolicies ?? []) {
109
+ for (const policy of config.policies.resourceControlPolicies) {
101
110
  for (const target of policy.targets) {
102
111
  if (target !== "root" && !ouNames.has(target) && !accountNames.has(target)) {
103
112
  errors.push(
@@ -106,6 +115,27 @@ function checkOrgPolicyTargets(config, errors) {
106
115
  }
107
116
  }
108
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
+ }
109
139
  }
110
140
  function checkInlinePolicySizes(config, errors) {
111
141
  for (const ps of config.permissionSets) {
package/dist/diff.js CHANGED
@@ -26,22 +26,26 @@ const operationExecutionPriority = {
26
26
  attachIdcCustomerManagedPolicyReferenceToPermissionSet: 20,
27
27
  detachIdcCustomerManagedPolicyReferenceFromPermissionSet: 21,
28
28
  provisionIdcPermissionSet: 22,
29
- grantIdcAccountAssignment: 23,
30
- removeIdcGroupMembership: 24,
31
- revokeIdcAccountAssignment: 25,
32
- deleteIdcUser: 26,
33
- deleteIdcGroup: 27,
34
- deleteIdcPermissionSet: 28,
35
- deleteOu: 29,
36
- createOrgPolicy: 30,
37
- updateOrgPolicyContent: 31,
38
- updateOrgPolicyDescription: 32,
39
- attachOrgPolicy: 33,
40
- detachOrgPolicy: 34,
41
- deleteOrgPolicy: 35,
42
- putAlternateContact: 36,
43
- deleteAlternateContact: 37,
44
- setIdcAccessControlAttributes: 38
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
45
49
  };
46
50
  function diffStates(props) {
47
51
  const operations = [];
@@ -576,6 +580,23 @@ function diffStates(props) {
576
580
  customerManagedPolicyPath: customerManagedPolicy.path
577
581
  });
578
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
+ }
579
600
  if (currentPermissionSet != null && operations.length > permissionSetMutationStartIndex && permissionSetNamesWithDesiredAssignments.has(nextPermissionSet.name)) {
580
601
  operations.push({
581
602
  kind: "provisionIdcPermissionSet",
@@ -648,6 +669,54 @@ function diffStates(props) {
648
669
  attributes: nextAccessControlAttributes
649
670
  });
650
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
+ }
651
720
  const currentPolicies = props.current.organization.policies ?? [];
652
721
  const nextPolicies = props.next.organization.policies ?? [];
653
722
  const currentPolicyAttachments = props.current.organization.policyAttachments ?? [];
@@ -699,23 +768,22 @@ function diffStates(props) {
699
768
  const nextOuNameById = new Map(
700
769
  props.next.organization.organizationalUnits.map((ou) => [ou.id, ou.name])
701
770
  );
702
- const nextAccountNameById = new Map(
703
- props.next.organization.accounts.map((account) => [account.id, account.name])
704
- );
705
771
  const currentOuNameById = new Map(
706
- props.current.organization.organizationalUnits.map((ou) => [ou.id, ou.name])
707
- );
708
- const currentAccountNameById = new Map(
709
- props.current.organization.accounts.map((account) => [account.id, account.name])
772
+ props.current.organization.organizationalUnits.map((ou) => [
773
+ ou.id,
774
+ ou.name
775
+ ])
710
776
  );
711
777
  function resolveNextTargetName(targetId, targetType) {
712
778
  if (targetType === "ROOT") return "root";
713
- if (targetType === "ORGANIZATIONAL_UNIT") return nextOuNameById.get(targetId) ?? "unknown";
779
+ if (targetType === "ORGANIZATIONAL_UNIT")
780
+ return nextOuNameById.get(targetId) ?? "unknown";
714
781
  return nextAccountNameById.get(targetId) ?? "unknown";
715
782
  }
716
783
  function resolveCurrentTargetName(targetId, targetType) {
717
784
  if (targetType === "ROOT") return "root";
718
- if (targetType === "ORGANIZATIONAL_UNIT") return currentOuNameById.get(targetId) ?? "unknown";
785
+ if (targetType === "ORGANIZATIONAL_UNIT")
786
+ return currentOuNameById.get(targetId) ?? "unknown";
719
787
  return currentAccountNameById.get(targetId) ?? "unknown";
720
788
  }
721
789
  for (const nextAttachment of nextPolicyAttachments) {
@@ -1037,6 +1105,13 @@ function getOperationSortKey(operation) {
1037
1105
  if (operation.kind === "setIdcAccessControlAttributes") {
1038
1106
  return operation.kind;
1039
1107
  }
1108
+ if (operation.kind === "registerDelegatedAdministrator" || operation.kind === "deregisterDelegatedAdministrator") {
1109
+ return [
1110
+ operation.kind,
1111
+ operation.accountName,
1112
+ operation.servicePrincipal
1113
+ ].join("|");
1114
+ }
1040
1115
  return "zzzz";
1041
1116
  }
1042
1117
  function normalizeJsonContent(content) {
@@ -153,6 +153,22 @@ const provisionIdcPermissionSetOperationSchema = v.strictObject({
153
153
  permissionSetName: v.string(),
154
154
  targetScope: v.literal("ALL_PROVISIONED_ACCOUNTS")
155
155
  });
156
+ const permissionsBoundaryOperationValueSchema = v.union([
157
+ v.strictObject({ managedPolicyArn: v.string() }),
158
+ v.strictObject({
159
+ customerManagedPolicyName: v.string(),
160
+ customerManagedPolicyPath: v.string()
161
+ })
162
+ ]);
163
+ const putIdcPermissionSetPermissionsBoundaryOperationSchema = v.strictObject({
164
+ kind: v.literal("putIdcPermissionSetPermissionsBoundary"),
165
+ permissionSetName: v.string(),
166
+ permissionsBoundary: permissionsBoundaryOperationValueSchema
167
+ });
168
+ const deleteIdcPermissionSetPermissionsBoundaryOperationSchema = v.strictObject({
169
+ kind: v.literal("deleteIdcPermissionSetPermissionsBoundary"),
170
+ permissionSetName: v.string()
171
+ });
156
172
  const grantIdcAccountAssignmentOperationSchema = v.strictObject({
157
173
  kind: v.literal("grantIdcAccountAssignment"),
158
174
  accountName: v.string(),
@@ -197,6 +213,18 @@ const deleteAlternateContactOperationSchema = v.strictObject({
197
213
  accountName: v.string(),
198
214
  contactType: alternateContactTypeSchema
199
215
  });
216
+ const registerDelegatedAdministratorOperationSchema = v.strictObject({
217
+ kind: v.literal("registerDelegatedAdministrator"),
218
+ accountId: v.string(),
219
+ accountName: v.string(),
220
+ servicePrincipal: v.string()
221
+ });
222
+ const deregisterDelegatedAdministratorOperationSchema = v.strictObject({
223
+ kind: v.literal("deregisterDelegatedAdministrator"),
224
+ accountId: v.string(),
225
+ accountName: v.string(),
226
+ servicePrincipal: v.string()
227
+ });
200
228
  const createOrgPolicyOperationSchema = v.strictObject({
201
229
  kind: v.literal("createOrgPolicy"),
202
230
  policyName: v.string(),
@@ -204,7 +232,8 @@ const createOrgPolicyOperationSchema = v.strictObject({
204
232
  "SERVICE_CONTROL_POLICY",
205
233
  "RESOURCE_CONTROL_POLICY",
206
234
  "TAG_POLICY",
207
- "AISERVICES_OPT_OUT_POLICY"
235
+ "AISERVICES_OPT_OUT_POLICY",
236
+ "BACKUP_POLICY"
208
237
  ]),
209
238
  description: v.string(),
210
239
  content: v.string()
@@ -268,6 +297,8 @@ const operationSchema = v.variant("kind", [
268
297
  attachIdcCustomerManagedPolicyReferenceToPermissionSetOperationSchema,
269
298
  detachIdcCustomerManagedPolicyReferenceFromPermissionSetOperationSchema,
270
299
  provisionIdcPermissionSetOperationSchema,
300
+ putIdcPermissionSetPermissionsBoundaryOperationSchema,
301
+ deleteIdcPermissionSetPermissionsBoundaryOperationSchema,
271
302
  grantIdcAccountAssignmentOperationSchema,
272
303
  revokeIdcAccountAssignmentOperationSchema,
273
304
  createOrgPolicyOperationSchema,
@@ -278,7 +309,9 @@ const operationSchema = v.variant("kind", [
278
309
  deleteOrgPolicyOperationSchema,
279
310
  putAlternateContactOperationSchema,
280
311
  deleteAlternateContactOperationSchema,
281
- setIdcAccessControlAttributesOperationSchema
312
+ setIdcAccessControlAttributesOperationSchema,
313
+ registerDelegatedAdministratorOperationSchema,
314
+ deregisterDelegatedAdministratorOperationSchema
282
315
  ]);
283
316
  const unsupportedDiffKindSchema = v.picklist([
284
317
  "ambiguousOuRename",