@beesolve/aws-accounts 1.0.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.
@@ -0,0 +1,3558 @@
1
+ // src/lambda/handler.ts
2
+ import {
3
+ GetObjectCommand,
4
+ PutObjectCommand,
5
+ S3Client,
6
+ S3ServiceException
7
+ } from "@aws-sdk/client-s3";
8
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
9
+ import { OrganizationsClient as OrganizationsClient3 } from "@aws-sdk/client-organizations";
10
+ import { SSOAdminClient as SSOAdminClient3 } from "@aws-sdk/client-sso-admin";
11
+ import { IdentitystoreClient as IdentitystoreClient3 } from "@aws-sdk/client-identitystore";
12
+ import { AccountClient as AccountClient2 } from "@aws-sdk/client-account";
13
+
14
+ // node_modules/valibot/dist/index.mjs
15
+ var store$4;
16
+ var DEFAULT_CONFIG = {
17
+ lang: void 0,
18
+ message: void 0,
19
+ abortEarly: void 0,
20
+ abortPipeEarly: void 0
21
+ };
22
+ // @__NO_SIDE_EFFECTS__
23
+ function getGlobalConfig(config$1) {
24
+ if (!config$1 && !store$4) return DEFAULT_CONFIG;
25
+ return {
26
+ lang: config$1?.lang ?? store$4?.lang,
27
+ message: config$1?.message,
28
+ abortEarly: config$1?.abortEarly ?? store$4?.abortEarly,
29
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$4?.abortPipeEarly
30
+ };
31
+ }
32
+ var store$3;
33
+ // @__NO_SIDE_EFFECTS__
34
+ function getGlobalMessage(lang) {
35
+ return store$3?.get(lang);
36
+ }
37
+ var store$2;
38
+ // @__NO_SIDE_EFFECTS__
39
+ function getSchemaMessage(lang) {
40
+ return store$2?.get(lang);
41
+ }
42
+ var store$1;
43
+ // @__NO_SIDE_EFFECTS__
44
+ function getSpecificMessage(reference, lang) {
45
+ return store$1?.get(reference)?.get(lang);
46
+ }
47
+ // @__NO_SIDE_EFFECTS__
48
+ function _stringify(input) {
49
+ const type = typeof input;
50
+ if (type === "string") return `"${input}"`;
51
+ if (type === "number" || type === "bigint" || type === "boolean") return `${input}`;
52
+ if (type === "object" || type === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
53
+ return type;
54
+ }
55
+ function _addIssue(context, label, dataset, config$1, other) {
56
+ const input = other && "input" in other ? other.input : dataset.value;
57
+ const expected = other?.expected ?? context.expects ?? null;
58
+ const received = other?.received ?? /* @__PURE__ */ _stringify(input);
59
+ const issue = {
60
+ kind: context.kind,
61
+ type: context.type,
62
+ input,
63
+ expected,
64
+ received,
65
+ message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
66
+ requirement: context.requirement,
67
+ path: other?.path,
68
+ issues: other?.issues,
69
+ lang: config$1.lang,
70
+ abortEarly: config$1.abortEarly,
71
+ abortPipeEarly: config$1.abortPipeEarly
72
+ };
73
+ const isSchema = context.kind === "schema";
74
+ const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage(issue.lang);
75
+ if (message$1 !== void 0) issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
76
+ if (isSchema) dataset.typed = false;
77
+ if (dataset.issues) dataset.issues.push(issue);
78
+ else dataset.issues = [issue];
79
+ }
80
+ var _standardCache = /* @__PURE__ */ new WeakMap();
81
+ // @__NO_SIDE_EFFECTS__
82
+ function _getStandardProps(context) {
83
+ let cached = _standardCache.get(context);
84
+ if (!cached) {
85
+ cached = {
86
+ version: 1,
87
+ vendor: "valibot",
88
+ validate(value$1) {
89
+ return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig());
90
+ }
91
+ };
92
+ _standardCache.set(context, cached);
93
+ }
94
+ return cached;
95
+ }
96
+ // @__NO_SIDE_EFFECTS__
97
+ function _isValidObjectKey(object$1, key) {
98
+ return Object.prototype.hasOwnProperty.call(object$1, key) && key !== "__proto__" && key !== "prototype" && key !== "constructor";
99
+ }
100
+ // @__NO_SIDE_EFFECTS__
101
+ function _joinExpects(values$1, separator) {
102
+ const list = [...new Set(values$1)];
103
+ if (list.length > 1) return `(${list.join(` ${separator} `)})`;
104
+ return list[0] ?? "never";
105
+ }
106
+ var ValiError = class extends Error {
107
+ /**
108
+ * Creates a Valibot error with useful information.
109
+ *
110
+ * @param issues The error issues.
111
+ */
112
+ constructor(issues) {
113
+ super(issues[0].message);
114
+ this.name = "ValiError";
115
+ this.issues = issues;
116
+ }
117
+ };
118
+ var EMOJI_REGEX = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
119
+ // @__NO_SIDE_EFFECTS__
120
+ function minLength(requirement, message$1) {
121
+ return {
122
+ kind: "validation",
123
+ type: "min_length",
124
+ reference: minLength,
125
+ async: false,
126
+ expects: `>=${requirement}`,
127
+ requirement,
128
+ message: message$1,
129
+ "~run"(dataset, config$1) {
130
+ if (dataset.typed && dataset.value.length < this.requirement) _addIssue(this, "length", dataset, config$1, { received: `${dataset.value.length}` });
131
+ return dataset;
132
+ }
133
+ };
134
+ }
135
+ var ABORT_EARLY_CONFIG = { abortEarly: true };
136
+ // @__NO_SIDE_EFFECTS__
137
+ function getFallback(schema, dataset, config$1) {
138
+ return typeof schema.fallback === "function" ? schema.fallback(dataset, config$1) : schema.fallback;
139
+ }
140
+ // @__NO_SIDE_EFFECTS__
141
+ function getDefault(schema, dataset, config$1) {
142
+ return typeof schema.default === "function" ? schema.default(dataset, config$1) : schema.default;
143
+ }
144
+ // @__NO_SIDE_EFFECTS__
145
+ function array(item, message$1) {
146
+ return {
147
+ kind: "schema",
148
+ type: "array",
149
+ reference: array,
150
+ expects: "Array",
151
+ async: false,
152
+ item,
153
+ message: message$1,
154
+ get "~standard"() {
155
+ return /* @__PURE__ */ _getStandardProps(this);
156
+ },
157
+ "~run"(dataset, config$1) {
158
+ const input = dataset.value;
159
+ if (Array.isArray(input)) {
160
+ dataset.typed = true;
161
+ dataset.value = [];
162
+ for (let key = 0; key < input.length; key++) {
163
+ const value$1 = input[key];
164
+ const itemDataset = this.item["~run"]({ value: value$1 }, config$1);
165
+ if (itemDataset.issues) {
166
+ const pathItem = {
167
+ type: "array",
168
+ origin: "value",
169
+ input,
170
+ key,
171
+ value: value$1
172
+ };
173
+ for (const issue of itemDataset.issues) {
174
+ if (issue.path) issue.path.unshift(pathItem);
175
+ else issue.path = [pathItem];
176
+ dataset.issues?.push(issue);
177
+ }
178
+ if (!dataset.issues) dataset.issues = itemDataset.issues;
179
+ if (config$1.abortEarly) {
180
+ dataset.typed = false;
181
+ break;
182
+ }
183
+ }
184
+ if (!itemDataset.typed) dataset.typed = false;
185
+ dataset.value.push(itemDataset.value);
186
+ }
187
+ } else _addIssue(this, "type", dataset, config$1);
188
+ return dataset;
189
+ }
190
+ };
191
+ }
192
+ // @__NO_SIDE_EFFECTS__
193
+ function boolean(message$1) {
194
+ return {
195
+ kind: "schema",
196
+ type: "boolean",
197
+ reference: boolean,
198
+ expects: "boolean",
199
+ async: false,
200
+ message: message$1,
201
+ get "~standard"() {
202
+ return /* @__PURE__ */ _getStandardProps(this);
203
+ },
204
+ "~run"(dataset, config$1) {
205
+ if (typeof dataset.value === "boolean") dataset.typed = true;
206
+ else _addIssue(this, "type", dataset, config$1);
207
+ return dataset;
208
+ }
209
+ };
210
+ }
211
+ // @__NO_SIDE_EFFECTS__
212
+ function literal(literal_, message$1) {
213
+ return {
214
+ kind: "schema",
215
+ type: "literal",
216
+ reference: literal,
217
+ expects: /* @__PURE__ */ _stringify(literal_),
218
+ async: false,
219
+ literal: literal_,
220
+ message: message$1,
221
+ get "~standard"() {
222
+ return /* @__PURE__ */ _getStandardProps(this);
223
+ },
224
+ "~run"(dataset, config$1) {
225
+ if (dataset.value === this.literal) dataset.typed = true;
226
+ else _addIssue(this, "type", dataset, config$1);
227
+ return dataset;
228
+ }
229
+ };
230
+ }
231
+ // @__NO_SIDE_EFFECTS__
232
+ function nullable(wrapped, default_) {
233
+ return {
234
+ kind: "schema",
235
+ type: "nullable",
236
+ reference: nullable,
237
+ expects: `(${wrapped.expects} | null)`,
238
+ async: false,
239
+ wrapped,
240
+ default: default_,
241
+ get "~standard"() {
242
+ return /* @__PURE__ */ _getStandardProps(this);
243
+ },
244
+ "~run"(dataset, config$1) {
245
+ if (dataset.value === null) {
246
+ if (this.default !== void 0) dataset.value = /* @__PURE__ */ getDefault(this, dataset, config$1);
247
+ if (dataset.value === null) {
248
+ dataset.typed = true;
249
+ return dataset;
250
+ }
251
+ }
252
+ return this.wrapped["~run"](dataset, config$1);
253
+ }
254
+ };
255
+ }
256
+ // @__NO_SIDE_EFFECTS__
257
+ function number(message$1) {
258
+ return {
259
+ kind: "schema",
260
+ type: "number",
261
+ reference: number,
262
+ expects: "number",
263
+ async: false,
264
+ message: message$1,
265
+ get "~standard"() {
266
+ return /* @__PURE__ */ _getStandardProps(this);
267
+ },
268
+ "~run"(dataset, config$1) {
269
+ if (typeof dataset.value === "number" && !isNaN(dataset.value)) dataset.typed = true;
270
+ else _addIssue(this, "type", dataset, config$1);
271
+ return dataset;
272
+ }
273
+ };
274
+ }
275
+ // @__NO_SIDE_EFFECTS__
276
+ function optional(wrapped, default_) {
277
+ return {
278
+ kind: "schema",
279
+ type: "optional",
280
+ reference: optional,
281
+ expects: `(${wrapped.expects} | undefined)`,
282
+ async: false,
283
+ wrapped,
284
+ default: default_,
285
+ get "~standard"() {
286
+ return /* @__PURE__ */ _getStandardProps(this);
287
+ },
288
+ "~run"(dataset, config$1) {
289
+ if (dataset.value === void 0) {
290
+ if (this.default !== void 0) dataset.value = /* @__PURE__ */ getDefault(this, dataset, config$1);
291
+ if (dataset.value === void 0) {
292
+ dataset.typed = true;
293
+ return dataset;
294
+ }
295
+ }
296
+ return this.wrapped["~run"](dataset, config$1);
297
+ }
298
+ };
299
+ }
300
+ // @__NO_SIDE_EFFECTS__
301
+ function picklist(options, message$1) {
302
+ return {
303
+ kind: "schema",
304
+ type: "picklist",
305
+ reference: picklist,
306
+ expects: /* @__PURE__ */ _joinExpects(options.map(_stringify), "|"),
307
+ async: false,
308
+ options,
309
+ message: message$1,
310
+ get "~standard"() {
311
+ return /* @__PURE__ */ _getStandardProps(this);
312
+ },
313
+ "~run"(dataset, config$1) {
314
+ if (this.options.includes(dataset.value)) dataset.typed = true;
315
+ else _addIssue(this, "type", dataset, config$1);
316
+ return dataset;
317
+ }
318
+ };
319
+ }
320
+ // @__NO_SIDE_EFFECTS__
321
+ function record(key, value$1, message$1) {
322
+ return {
323
+ kind: "schema",
324
+ type: "record",
325
+ reference: record,
326
+ expects: "Object",
327
+ async: false,
328
+ key,
329
+ value: value$1,
330
+ message: message$1,
331
+ get "~standard"() {
332
+ return /* @__PURE__ */ _getStandardProps(this);
333
+ },
334
+ "~run"(dataset, config$1) {
335
+ const input = dataset.value;
336
+ if (input && typeof input === "object") {
337
+ dataset.typed = true;
338
+ dataset.value = {};
339
+ for (const entryKey in input) if (/* @__PURE__ */ _isValidObjectKey(input, entryKey)) {
340
+ const entryValue = input[entryKey];
341
+ const keyDataset = this.key["~run"]({ value: entryKey }, config$1);
342
+ if (keyDataset.issues) {
343
+ const pathItem = {
344
+ type: "object",
345
+ origin: "key",
346
+ input,
347
+ key: entryKey,
348
+ value: entryValue
349
+ };
350
+ for (const issue of keyDataset.issues) {
351
+ issue.path = [pathItem];
352
+ dataset.issues?.push(issue);
353
+ }
354
+ if (!dataset.issues) dataset.issues = keyDataset.issues;
355
+ if (config$1.abortEarly) {
356
+ dataset.typed = false;
357
+ break;
358
+ }
359
+ }
360
+ const valueDataset = this.value["~run"]({ value: entryValue }, config$1);
361
+ if (valueDataset.issues) {
362
+ const pathItem = {
363
+ type: "object",
364
+ origin: "value",
365
+ input,
366
+ key: entryKey,
367
+ value: entryValue
368
+ };
369
+ for (const issue of valueDataset.issues) {
370
+ if (issue.path) issue.path.unshift(pathItem);
371
+ else issue.path = [pathItem];
372
+ dataset.issues?.push(issue);
373
+ }
374
+ if (!dataset.issues) dataset.issues = valueDataset.issues;
375
+ if (config$1.abortEarly) {
376
+ dataset.typed = false;
377
+ break;
378
+ }
379
+ }
380
+ if (!keyDataset.typed || !valueDataset.typed) dataset.typed = false;
381
+ if (keyDataset.typed) dataset.value[keyDataset.value] = valueDataset.value;
382
+ }
383
+ } else _addIssue(this, "type", dataset, config$1);
384
+ return dataset;
385
+ }
386
+ };
387
+ }
388
+ // @__NO_SIDE_EFFECTS__
389
+ function strictObject(entries$1, message$1) {
390
+ return {
391
+ kind: "schema",
392
+ type: "strict_object",
393
+ reference: strictObject,
394
+ expects: "Object",
395
+ async: false,
396
+ entries: entries$1,
397
+ message: message$1,
398
+ get "~standard"() {
399
+ return /* @__PURE__ */ _getStandardProps(this);
400
+ },
401
+ "~run"(dataset, config$1) {
402
+ const input = dataset.value;
403
+ if (input && typeof input === "object") {
404
+ dataset.typed = true;
405
+ dataset.value = {};
406
+ for (const key in this.entries) {
407
+ const valueSchema = this.entries[key];
408
+ if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && valueSchema.default !== void 0) {
409
+ const value$1 = key in input ? input[key] : /* @__PURE__ */ getDefault(valueSchema);
410
+ const valueDataset = valueSchema["~run"]({ value: value$1 }, config$1);
411
+ if (valueDataset.issues) {
412
+ const pathItem = {
413
+ type: "object",
414
+ origin: "value",
415
+ input,
416
+ key,
417
+ value: value$1
418
+ };
419
+ for (const issue of valueDataset.issues) {
420
+ if (issue.path) issue.path.unshift(pathItem);
421
+ else issue.path = [pathItem];
422
+ dataset.issues?.push(issue);
423
+ }
424
+ if (!dataset.issues) dataset.issues = valueDataset.issues;
425
+ if (config$1.abortEarly) {
426
+ dataset.typed = false;
427
+ break;
428
+ }
429
+ }
430
+ if (!valueDataset.typed) dataset.typed = false;
431
+ dataset.value[key] = valueDataset.value;
432
+ } else if (valueSchema.fallback !== void 0) dataset.value[key] = /* @__PURE__ */ getFallback(valueSchema);
433
+ else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
434
+ _addIssue(this, "key", dataset, config$1, {
435
+ input: void 0,
436
+ expected: `"${key}"`,
437
+ path: [{
438
+ type: "object",
439
+ origin: "key",
440
+ input,
441
+ key,
442
+ value: input[key]
443
+ }]
444
+ });
445
+ if (config$1.abortEarly) break;
446
+ }
447
+ }
448
+ if (!dataset.issues || !config$1.abortEarly) {
449
+ for (const key in input) if (!(key in this.entries)) {
450
+ _addIssue(this, "key", dataset, config$1, {
451
+ input: key,
452
+ expected: "never",
453
+ path: [{
454
+ type: "object",
455
+ origin: "key",
456
+ input,
457
+ key,
458
+ value: input[key]
459
+ }]
460
+ });
461
+ break;
462
+ }
463
+ }
464
+ } else _addIssue(this, "type", dataset, config$1);
465
+ return dataset;
466
+ }
467
+ };
468
+ }
469
+ // @__NO_SIDE_EFFECTS__
470
+ function string(message$1) {
471
+ return {
472
+ kind: "schema",
473
+ type: "string",
474
+ reference: string,
475
+ expects: "string",
476
+ async: false,
477
+ message: message$1,
478
+ get "~standard"() {
479
+ return /* @__PURE__ */ _getStandardProps(this);
480
+ },
481
+ "~run"(dataset, config$1) {
482
+ if (typeof dataset.value === "string") dataset.typed = true;
483
+ else _addIssue(this, "type", dataset, config$1);
484
+ return dataset;
485
+ }
486
+ };
487
+ }
488
+ // @__NO_SIDE_EFFECTS__
489
+ function _subIssues(datasets) {
490
+ let issues;
491
+ if (datasets) for (const dataset of datasets) if (issues) for (const issue of dataset.issues) issues.push(issue);
492
+ else issues = dataset.issues;
493
+ return issues;
494
+ }
495
+ // @__NO_SIDE_EFFECTS__
496
+ function union(options, message$1) {
497
+ return {
498
+ kind: "schema",
499
+ type: "union",
500
+ reference: union,
501
+ expects: /* @__PURE__ */ _joinExpects(options.map((option) => option.expects), "|"),
502
+ async: false,
503
+ options,
504
+ message: message$1,
505
+ get "~standard"() {
506
+ return /* @__PURE__ */ _getStandardProps(this);
507
+ },
508
+ "~run"(dataset, config$1) {
509
+ let validDataset;
510
+ let typedDatasets;
511
+ let untypedDatasets;
512
+ for (const schema of this.options) {
513
+ const optionDataset = schema["~run"]({ value: dataset.value }, config$1);
514
+ if (optionDataset.typed) if (optionDataset.issues) if (typedDatasets) typedDatasets.push(optionDataset);
515
+ else typedDatasets = [optionDataset];
516
+ else {
517
+ validDataset = optionDataset;
518
+ break;
519
+ }
520
+ else if (untypedDatasets) untypedDatasets.push(optionDataset);
521
+ else untypedDatasets = [optionDataset];
522
+ }
523
+ if (validDataset) return validDataset;
524
+ if (typedDatasets) {
525
+ if (typedDatasets.length === 1) return typedDatasets[0];
526
+ _addIssue(this, "type", dataset, config$1, { issues: /* @__PURE__ */ _subIssues(typedDatasets) });
527
+ dataset.typed = true;
528
+ } else if (untypedDatasets?.length === 1) return untypedDatasets[0];
529
+ else _addIssue(this, "type", dataset, config$1, { issues: /* @__PURE__ */ _subIssues(untypedDatasets) });
530
+ return dataset;
531
+ }
532
+ };
533
+ }
534
+ // @__NO_SIDE_EFFECTS__
535
+ function variant(key, options, message$1) {
536
+ return {
537
+ kind: "schema",
538
+ type: "variant",
539
+ reference: variant,
540
+ expects: "Object",
541
+ async: false,
542
+ key,
543
+ options,
544
+ message: message$1,
545
+ get "~standard"() {
546
+ return /* @__PURE__ */ _getStandardProps(this);
547
+ },
548
+ "~run"(dataset, config$1) {
549
+ const input = dataset.value;
550
+ if (input && typeof input === "object") {
551
+ let outputDataset;
552
+ let maxDiscriminatorPriority = 0;
553
+ let invalidDiscriminatorKey = this.key;
554
+ let expectedDiscriminators = [];
555
+ const parseOptions = (variant$1, allKeys) => {
556
+ for (const schema of variant$1.options) {
557
+ if (schema.type === "variant") parseOptions(schema, new Set(allKeys).add(schema.key));
558
+ else {
559
+ let keysAreValid = true;
560
+ let currentPriority = 0;
561
+ for (const currentKey of allKeys) {
562
+ const discriminatorSchema = schema.entries[currentKey];
563
+ if (currentKey in input ? discriminatorSchema["~run"]({
564
+ typed: false,
565
+ value: input[currentKey]
566
+ }, ABORT_EARLY_CONFIG).issues : discriminatorSchema.type !== "exact_optional" && discriminatorSchema.type !== "optional" && discriminatorSchema.type !== "nullish") {
567
+ keysAreValid = false;
568
+ if (invalidDiscriminatorKey !== currentKey && (maxDiscriminatorPriority < currentPriority || maxDiscriminatorPriority === currentPriority && currentKey in input && !(invalidDiscriminatorKey in input))) {
569
+ maxDiscriminatorPriority = currentPriority;
570
+ invalidDiscriminatorKey = currentKey;
571
+ expectedDiscriminators = [];
572
+ }
573
+ if (invalidDiscriminatorKey === currentKey) expectedDiscriminators.push(schema.entries[currentKey].expects);
574
+ break;
575
+ }
576
+ currentPriority++;
577
+ }
578
+ if (keysAreValid) {
579
+ const optionDataset = schema["~run"]({ value: input }, config$1);
580
+ if (!outputDataset || !outputDataset.typed && optionDataset.typed) outputDataset = optionDataset;
581
+ }
582
+ }
583
+ if (outputDataset && !outputDataset.issues) break;
584
+ }
585
+ };
586
+ parseOptions(this, /* @__PURE__ */ new Set([this.key]));
587
+ if (outputDataset) return outputDataset;
588
+ _addIssue(this, "type", dataset, config$1, {
589
+ input: input[invalidDiscriminatorKey],
590
+ expected: /* @__PURE__ */ _joinExpects(expectedDiscriminators, "|"),
591
+ path: [{
592
+ type: "object",
593
+ origin: "value",
594
+ input,
595
+ key: invalidDiscriminatorKey,
596
+ value: input[invalidDiscriminatorKey]
597
+ }]
598
+ });
599
+ } else _addIssue(this, "type", dataset, config$1);
600
+ return dataset;
601
+ }
602
+ };
603
+ }
604
+ function parse(schema, input, config$1) {
605
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config$1));
606
+ if (dataset.issues) throw new ValiError(dataset.issues);
607
+ return dataset.value;
608
+ }
609
+ // @__NO_SIDE_EFFECTS__
610
+ function pipe(...pipe$1) {
611
+ return {
612
+ ...pipe$1[0],
613
+ pipe: pipe$1,
614
+ get "~standard"() {
615
+ return /* @__PURE__ */ _getStandardProps(this);
616
+ },
617
+ "~run"(dataset, config$1) {
618
+ for (const item of pipe$1) if (item.kind !== "metadata") {
619
+ if (dataset.issues && (item.kind === "schema" || item.kind === "transformation")) {
620
+ dataset.typed = false;
621
+ break;
622
+ }
623
+ if (!dataset.issues || !config$1.abortEarly && !config$1.abortPipeEarly) dataset = item["~run"](dataset, config$1);
624
+ }
625
+ return dataset;
626
+ }
627
+ };
628
+ }
629
+ // @__NO_SIDE_EFFECTS__
630
+ function safeParse(schema, input, config$1) {
631
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config$1));
632
+ return {
633
+ typed: dataset.typed,
634
+ success: !dataset.issues,
635
+ output: dataset.value,
636
+ issues: dataset.issues
637
+ };
638
+ }
639
+
640
+ // src/operations.ts
641
+ var moveAccountOperationSchema = strictObject({
642
+ kind: literal("moveAccount"),
643
+ accountId: string(),
644
+ accountName: string(),
645
+ fromOuId: string(),
646
+ fromOuName: string(),
647
+ toOuId: string(),
648
+ toOuName: string()
649
+ });
650
+ var createOuOperationSchema = strictObject({
651
+ kind: literal("createOu"),
652
+ ouName: string(),
653
+ parentOuId: string(),
654
+ parentOuName: string()
655
+ });
656
+ var renameOuOperationSchema = strictObject({
657
+ kind: literal("renameOu"),
658
+ ouId: string(),
659
+ fromOuName: string(),
660
+ toOuName: string(),
661
+ parentOuId: string(),
662
+ parentOuName: string()
663
+ });
664
+ var deleteOuOperationSchema = strictObject({
665
+ kind: literal("deleteOu"),
666
+ ouId: string(),
667
+ ouName: string(),
668
+ parentOuId: string(),
669
+ parentOuName: string()
670
+ });
671
+ var createAccountOperationSchema = strictObject({
672
+ kind: literal("createAccount"),
673
+ accountName: string(),
674
+ accountEmail: string(),
675
+ targetOuId: string(),
676
+ targetOuName: string()
677
+ });
678
+ var updateAccountTagsOperationSchema = strictObject({
679
+ kind: literal("updateAccountTags"),
680
+ accountId: string(),
681
+ accountName: string(),
682
+ tags: record(string(), string())
683
+ });
684
+ var updateAccountNameOperationSchema = strictObject({
685
+ kind: literal("updateAccountName"),
686
+ accountId: string(),
687
+ fromAccountName: string(),
688
+ toAccountName: string()
689
+ });
690
+ var removeAccountOperationSchema = strictObject({
691
+ kind: literal("removeAccount"),
692
+ accountId: string(),
693
+ accountName: string(),
694
+ fromOuId: string(),
695
+ fromOuName: string(),
696
+ toOuId: string(),
697
+ toOuName: string()
698
+ });
699
+ var createIdcUserOperationSchema = strictObject({
700
+ kind: literal("createIdcUser"),
701
+ userName: string(),
702
+ displayName: string(),
703
+ email: string()
704
+ });
705
+ var updateIdcUserOperationSchema = strictObject({
706
+ kind: literal("updateIdcUser"),
707
+ userName: string(),
708
+ displayName: string(),
709
+ email: string()
710
+ });
711
+ var deleteIdcUserOperationSchema = strictObject({
712
+ kind: literal("deleteIdcUser"),
713
+ userName: string()
714
+ });
715
+ var createIdcGroupOperationSchema = strictObject({
716
+ kind: literal("createIdcGroup"),
717
+ groupDisplayName: string(),
718
+ description: string()
719
+ });
720
+ var updateIdcGroupDescriptionOperationSchema = strictObject({
721
+ kind: literal("updateIdcGroupDescription"),
722
+ groupDisplayName: string(),
723
+ description: string()
724
+ });
725
+ var deleteIdcGroupOperationSchema = strictObject({
726
+ kind: literal("deleteIdcGroup"),
727
+ groupDisplayName: string()
728
+ });
729
+ var addIdcGroupMembershipOperationSchema = strictObject({
730
+ kind: literal("addIdcGroupMembership"),
731
+ groupDisplayName: string(),
732
+ userName: string()
733
+ });
734
+ var removeIdcGroupMembershipOperationSchema = strictObject({
735
+ kind: literal("removeIdcGroupMembership"),
736
+ groupDisplayName: string(),
737
+ userName: string()
738
+ });
739
+ var createIdcPermissionSetOperationSchema = strictObject({
740
+ kind: literal("createIdcPermissionSet"),
741
+ permissionSetName: string(),
742
+ description: string()
743
+ });
744
+ var updateIdcPermissionSetDescriptionOperationSchema = strictObject({
745
+ kind: literal("updateIdcPermissionSetDescription"),
746
+ permissionSetName: string(),
747
+ description: string()
748
+ });
749
+ var deleteIdcPermissionSetOperationSchema = strictObject({
750
+ kind: literal("deleteIdcPermissionSet"),
751
+ permissionSetName: string()
752
+ });
753
+ var putIdcPermissionSetInlinePolicyOperationSchema = strictObject({
754
+ kind: literal("putIdcPermissionSetInlinePolicy"),
755
+ permissionSetName: string(),
756
+ inlinePolicy: string()
757
+ });
758
+ var deleteIdcPermissionSetInlinePolicyOperationSchema = strictObject({
759
+ kind: literal("deleteIdcPermissionSetInlinePolicy"),
760
+ permissionSetName: string()
761
+ });
762
+ var attachIdcManagedPolicyToPermissionSetOperationSchema = strictObject({
763
+ kind: literal("attachIdcManagedPolicyToPermissionSet"),
764
+ permissionSetName: string(),
765
+ managedPolicyArn: string()
766
+ });
767
+ var detachIdcManagedPolicyFromPermissionSetOperationSchema = strictObject({
768
+ kind: literal("detachIdcManagedPolicyFromPermissionSet"),
769
+ permissionSetName: string(),
770
+ managedPolicyArn: string()
771
+ });
772
+ var attachIdcCustomerManagedPolicyReferenceToPermissionSetOperationSchema = strictObject({
773
+ kind: literal("attachIdcCustomerManagedPolicyReferenceToPermissionSet"),
774
+ permissionSetName: string(),
775
+ customerManagedPolicyName: string(),
776
+ customerManagedPolicyPath: string()
777
+ });
778
+ var detachIdcCustomerManagedPolicyReferenceFromPermissionSetOperationSchema = strictObject({
779
+ kind: literal("detachIdcCustomerManagedPolicyReferenceFromPermissionSet"),
780
+ permissionSetName: string(),
781
+ customerManagedPolicyName: string(),
782
+ customerManagedPolicyPath: string()
783
+ });
784
+ var provisionIdcPermissionSetOperationSchema = strictObject({
785
+ kind: literal("provisionIdcPermissionSet"),
786
+ permissionSetName: string(),
787
+ targetScope: literal("ALL_PROVISIONED_ACCOUNTS")
788
+ });
789
+ var grantIdcAccountAssignmentOperationSchema = strictObject({
790
+ kind: literal("grantIdcAccountAssignment"),
791
+ accountName: string(),
792
+ permissionSetName: string(),
793
+ principalType: picklist(["GROUP", "USER"]),
794
+ principalName: string()
795
+ });
796
+ var revokeIdcAccountAssignmentOperationSchema = strictObject({
797
+ kind: literal("revokeIdcAccountAssignment"),
798
+ accountName: string(),
799
+ permissionSetName: string(),
800
+ principalType: picklist(["GROUP", "USER"]),
801
+ principalName: string()
802
+ });
803
+ var operationSchema = variant("kind", [
804
+ moveAccountOperationSchema,
805
+ createOuOperationSchema,
806
+ renameOuOperationSchema,
807
+ deleteOuOperationSchema,
808
+ createAccountOperationSchema,
809
+ updateAccountTagsOperationSchema,
810
+ updateAccountNameOperationSchema,
811
+ removeAccountOperationSchema,
812
+ createIdcUserOperationSchema,
813
+ updateIdcUserOperationSchema,
814
+ deleteIdcUserOperationSchema,
815
+ createIdcGroupOperationSchema,
816
+ updateIdcGroupDescriptionOperationSchema,
817
+ deleteIdcGroupOperationSchema,
818
+ addIdcGroupMembershipOperationSchema,
819
+ removeIdcGroupMembershipOperationSchema,
820
+ createIdcPermissionSetOperationSchema,
821
+ updateIdcPermissionSetDescriptionOperationSchema,
822
+ deleteIdcPermissionSetOperationSchema,
823
+ putIdcPermissionSetInlinePolicyOperationSchema,
824
+ deleteIdcPermissionSetInlinePolicyOperationSchema,
825
+ attachIdcManagedPolicyToPermissionSetOperationSchema,
826
+ detachIdcManagedPolicyFromPermissionSetOperationSchema,
827
+ attachIdcCustomerManagedPolicyReferenceToPermissionSetOperationSchema,
828
+ detachIdcCustomerManagedPolicyReferenceFromPermissionSetOperationSchema,
829
+ provisionIdcPermissionSetOperationSchema,
830
+ grantIdcAccountAssignmentOperationSchema,
831
+ revokeIdcAccountAssignmentOperationSchema
832
+ ]);
833
+ var unsupportedDiffKindSchema = picklist([
834
+ "ambiguousOuRename",
835
+ "reparentedOu",
836
+ "newOuWithUnknownParent",
837
+ "newAccountWithUnknownOu",
838
+ "removedOu"
839
+ ]);
840
+ var unsupportedDiffCategorySchema = picklist([
841
+ "destructive",
842
+ "unsupportedMutation"
843
+ ]);
844
+ var unsupportedDiffSchema = strictObject({
845
+ kind: unsupportedDiffKindSchema,
846
+ category: unsupportedDiffCategorySchema,
847
+ description: string()
848
+ });
849
+ var planSchema = strictObject({
850
+ operations: array(operationSchema),
851
+ unsupported: array(unsupportedDiffSchema)
852
+ });
853
+
854
+ // src/helpers.ts
855
+ function assertUnreachable(value, message = JSON.stringify(value)) {
856
+ throw Error("An unreachable state reached!\n" + message);
857
+ }
858
+ function toRecordByProperty(input, key, keyTransformer = (key2) => key2) {
859
+ return Object.fromEntries(
860
+ input.map((item) => [
861
+ keyTransformer(typeof key === "function" ? key(item) : item[key]),
862
+ item
863
+ ])
864
+ );
865
+ }
866
+ async function delay(ms) {
867
+ await new Promise((resolve) => {
868
+ setTimeout(resolve, ms);
869
+ });
870
+ }
871
+
872
+ // src/state.ts
873
+ var nonEmptyString = pipe(string(), minLength(1));
874
+ var principalTypeSchema = picklist(["GROUP", "USER"]);
875
+ var organizationalUnitSchema = strictObject({
876
+ id: nonEmptyString,
877
+ parentId: nonEmptyString,
878
+ arn: nonEmptyString,
879
+ name: nonEmptyString
880
+ });
881
+ var accountTagSchema = strictObject({
882
+ key: nonEmptyString,
883
+ value: string()
884
+ });
885
+ var accountSchema = strictObject({
886
+ id: nonEmptyString,
887
+ arn: nonEmptyString,
888
+ name: nonEmptyString,
889
+ email: nonEmptyString,
890
+ status: nonEmptyString,
891
+ parentId: nonEmptyString,
892
+ tags: array(accountTagSchema)
893
+ });
894
+ var userSchema = strictObject({
895
+ userId: nonEmptyString,
896
+ userName: nonEmptyString,
897
+ displayName: string(),
898
+ email: string()
899
+ });
900
+ var groupSchema = strictObject({
901
+ groupId: nonEmptyString,
902
+ displayName: nonEmptyString,
903
+ description: optional(string())
904
+ });
905
+ var groupMembershipSchema = strictObject({
906
+ membershipId: nonEmptyString,
907
+ groupId: nonEmptyString,
908
+ userId: nonEmptyString
909
+ });
910
+ var customerManagedPolicyReferenceSchema = strictObject({
911
+ name: nonEmptyString,
912
+ path: nonEmptyString
913
+ });
914
+ var permissionSetSchema = strictObject({
915
+ permissionSetArn: nonEmptyString,
916
+ name: nonEmptyString,
917
+ description: string(),
918
+ inlinePolicy: nullable(nonEmptyString),
919
+ awsManagedPolicies: array(nonEmptyString),
920
+ customerManagedPolicies: array(customerManagedPolicyReferenceSchema)
921
+ });
922
+ var accountAssignmentSchema = strictObject({
923
+ accountId: nonEmptyString,
924
+ permissionSetArn: nonEmptyString,
925
+ principalId: nonEmptyString,
926
+ principalType: principalTypeSchema
927
+ });
928
+ var accessRoleSchema = strictObject({
929
+ accountId: nonEmptyString,
930
+ permissionSetArn: nonEmptyString,
931
+ principalId: nonEmptyString,
932
+ principalType: principalTypeSchema,
933
+ roleName: nonEmptyString
934
+ });
935
+ var stateSchema = strictObject({
936
+ version: nonEmptyString,
937
+ generatedAt: nonEmptyString,
938
+ organization: strictObject({
939
+ rootId: nonEmptyString,
940
+ organizationalUnits: array(organizationalUnitSchema),
941
+ accounts: array(accountSchema)
942
+ }),
943
+ identityCenter: strictObject({
944
+ instanceArn: nonEmptyString,
945
+ identityStoreId: nonEmptyString,
946
+ users: array(userSchema),
947
+ groups: array(groupSchema),
948
+ groupMemberships: array(groupMembershipSchema),
949
+ permissionSets: array(permissionSetSchema),
950
+ accountAssignments: array(accountAssignmentSchema),
951
+ accessRoles: array(accessRoleSchema)
952
+ })
953
+ });
954
+ function createWorkingState(props) {
955
+ return {
956
+ version: props.state.version,
957
+ generatedAt: props.state.generatedAt,
958
+ organization: {
959
+ rootId: props.state.organization.rootId,
960
+ organizationalUnitsById: toRecordByProperty(
961
+ props.state.organization.organizationalUnits,
962
+ "id"
963
+ ),
964
+ accountsById: toRecordByProperty(props.state.organization.accounts, "id"),
965
+ accountsByName: toRecordByProperty(
966
+ props.state.organization.accounts,
967
+ "name"
968
+ )
969
+ },
970
+ identityCenter: createWorkingIdentityCenterState({
971
+ identityCenter: props.state.identityCenter
972
+ })
973
+ };
974
+ }
975
+ function materializeWorkingState(props) {
976
+ return {
977
+ version: props.workingState.version,
978
+ generatedAt: props.workingState.generatedAt,
979
+ organization: {
980
+ rootId: props.workingState.organization.rootId,
981
+ organizationalUnits: Object.values(
982
+ props.workingState.organization.organizationalUnitsById
983
+ ),
984
+ accounts: Object.values(props.workingState.organization.accountsById)
985
+ },
986
+ identityCenter: {
987
+ instanceArn: props.workingState.identityCenter.instanceArn,
988
+ identityStoreId: props.workingState.identityCenter.identityStoreId,
989
+ users: structuredClone(props.workingState.identityCenter.users),
990
+ groups: structuredClone(props.workingState.identityCenter.groups),
991
+ groupMemberships: structuredClone(
992
+ props.workingState.identityCenter.groupMemberships
993
+ ),
994
+ permissionSets: structuredClone(
995
+ props.workingState.identityCenter.permissionSets
996
+ ),
997
+ accountAssignments: structuredClone(
998
+ props.workingState.identityCenter.accountAssignments
999
+ ),
1000
+ accessRoles: structuredClone(
1001
+ props.workingState.identityCenter.accessRoles
1002
+ )
1003
+ }
1004
+ };
1005
+ }
1006
+ function moveAccountInWorkingState(props) {
1007
+ const currentAccount = props.workingState.organization.accountsById[props.accountId];
1008
+ if (currentAccount == null || currentAccount.parentId === props.parentId) {
1009
+ return props.workingState;
1010
+ }
1011
+ return {
1012
+ ...props.workingState,
1013
+ organization: {
1014
+ ...props.workingState.organization,
1015
+ accountsById: {
1016
+ ...props.workingState.organization.accountsById,
1017
+ [props.accountId]: {
1018
+ ...currentAccount,
1019
+ parentId: props.parentId
1020
+ }
1021
+ },
1022
+ accountsByName: {
1023
+ ...props.workingState.organization.accountsByName,
1024
+ [currentAccount.name]: {
1025
+ ...currentAccount,
1026
+ parentId: props.parentId
1027
+ }
1028
+ }
1029
+ }
1030
+ };
1031
+ }
1032
+ function upsertAccountInWorkingState(props) {
1033
+ const currentAccount = props.workingState.organization.accountsById[props.account.id];
1034
+ if (currentAccount != null && currentAccount.id === props.account.id && currentAccount.arn === props.account.arn && currentAccount.name === props.account.name && currentAccount.email === props.account.email && currentAccount.status === props.account.status && currentAccount.parentId === props.account.parentId && JSON.stringify(normalizeAccountTags(currentAccount.tags)) === JSON.stringify(normalizeAccountTags(props.account.tags))) {
1035
+ return props.workingState;
1036
+ }
1037
+ let accountsByName = {
1038
+ ...props.workingState.organization.accountsByName
1039
+ };
1040
+ if (currentAccount != null && currentAccount.name !== props.account.name) {
1041
+ const { [currentAccount.name]: _removed, ...rest } = accountsByName;
1042
+ accountsByName = rest;
1043
+ }
1044
+ accountsByName = {
1045
+ ...accountsByName,
1046
+ [props.account.name]: props.account
1047
+ };
1048
+ return {
1049
+ ...props.workingState,
1050
+ organization: {
1051
+ ...props.workingState.organization,
1052
+ accountsById: {
1053
+ ...props.workingState.organization.accountsById,
1054
+ [props.account.id]: props.account
1055
+ },
1056
+ accountsByName
1057
+ }
1058
+ };
1059
+ }
1060
+ function upsertOrganizationalUnitInWorkingState(props) {
1061
+ const currentOrganizationalUnit = props.workingState.organization.organizationalUnitsById[props.organizationalUnit.id];
1062
+ if (currentOrganizationalUnit != null && currentOrganizationalUnit.id === props.organizationalUnit.id && currentOrganizationalUnit.parentId === props.organizationalUnit.parentId && currentOrganizationalUnit.arn === props.organizationalUnit.arn && currentOrganizationalUnit.name === props.organizationalUnit.name) {
1063
+ return props.workingState;
1064
+ }
1065
+ return {
1066
+ ...props.workingState,
1067
+ organization: {
1068
+ ...props.workingState.organization,
1069
+ organizationalUnitsById: {
1070
+ ...props.workingState.organization.organizationalUnitsById,
1071
+ [props.organizationalUnit.id]: props.organizationalUnit
1072
+ }
1073
+ }
1074
+ };
1075
+ }
1076
+ function renameOrganizationalUnitInWorkingState(props) {
1077
+ const currentOrganizationalUnit = props.workingState.organization.organizationalUnitsById[props.organizationalUnitId];
1078
+ if (currentOrganizationalUnit == null || currentOrganizationalUnit.name === props.name) {
1079
+ return props.workingState;
1080
+ }
1081
+ return {
1082
+ ...props.workingState,
1083
+ organization: {
1084
+ ...props.workingState.organization,
1085
+ organizationalUnitsById: {
1086
+ ...props.workingState.organization.organizationalUnitsById,
1087
+ [props.organizationalUnitId]: {
1088
+ ...currentOrganizationalUnit,
1089
+ name: props.name
1090
+ }
1091
+ }
1092
+ }
1093
+ };
1094
+ }
1095
+ function removeOrganizationalUnitFromWorkingState(props) {
1096
+ if (props.workingState.organization.organizationalUnitsById[props.organizationalUnitId] == null) {
1097
+ return props.workingState;
1098
+ }
1099
+ const nextOrganizationalUnitsById = {
1100
+ ...props.workingState.organization.organizationalUnitsById
1101
+ };
1102
+ delete nextOrganizationalUnitsById[props.organizationalUnitId];
1103
+ return {
1104
+ ...props.workingState,
1105
+ organization: {
1106
+ ...props.workingState.organization,
1107
+ organizationalUnitsById: nextOrganizationalUnitsById
1108
+ }
1109
+ };
1110
+ }
1111
+ function createAccountAssignmentKey(props) {
1112
+ return [
1113
+ props.accountId,
1114
+ props.permissionSetArn,
1115
+ props.principalId,
1116
+ props.principalType
1117
+ ].join("|");
1118
+ }
1119
+ function createGroupMembershipKey(props) {
1120
+ return [props.groupId, props.userId].join("|");
1121
+ }
1122
+ function upsertIdcUserInWorkingState(props) {
1123
+ const currentUser = props.workingState.identityCenter.usersByUserName[props.user.userName];
1124
+ if (currentUser != null && currentUser.userId === props.user.userId && currentUser.displayName === props.user.displayName && currentUser.userName === props.user.userName && currentUser.email === props.user.email) {
1125
+ return props.workingState;
1126
+ }
1127
+ const remainingUsers = props.workingState.identityCenter.users.filter(
1128
+ (user) => user.userName !== props.user.userName
1129
+ );
1130
+ return {
1131
+ ...props.workingState,
1132
+ identityCenter: createWorkingIdentityCenterState({
1133
+ identityCenter: {
1134
+ ...materializeWorkingIdentityCenterState({
1135
+ identityCenter: props.workingState.identityCenter
1136
+ }),
1137
+ users: [...remainingUsers, props.user]
1138
+ }
1139
+ })
1140
+ };
1141
+ }
1142
+ function removeIdcUserFromWorkingState(props) {
1143
+ const user = props.workingState.identityCenter.usersByUserName[props.userName];
1144
+ if (user == null) {
1145
+ return props.workingState;
1146
+ }
1147
+ return {
1148
+ ...props.workingState,
1149
+ identityCenter: createWorkingIdentityCenterState({
1150
+ identityCenter: {
1151
+ ...materializeWorkingIdentityCenterState({
1152
+ identityCenter: props.workingState.identityCenter
1153
+ }),
1154
+ users: props.workingState.identityCenter.users.filter(
1155
+ (currentUser) => currentUser.userName !== props.userName
1156
+ ),
1157
+ groupMemberships: props.workingState.identityCenter.groupMemberships.filter(
1158
+ (groupMembership) => groupMembership.userId !== user.userId
1159
+ ),
1160
+ accountAssignments: props.workingState.identityCenter.accountAssignments.filter(
1161
+ (accountAssignment) => accountAssignment.principalType !== "USER" || accountAssignment.principalId !== user.userId
1162
+ )
1163
+ }
1164
+ })
1165
+ };
1166
+ }
1167
+ function upsertIdcGroupInWorkingState(props) {
1168
+ const currentGroup = props.workingState.identityCenter.groupsByDisplayName[props.group.displayName];
1169
+ if (currentGroup != null && currentGroup.groupId === props.group.groupId && currentGroup.displayName === props.group.displayName && (currentGroup.description ?? "") === (props.group.description ?? "")) {
1170
+ return props.workingState;
1171
+ }
1172
+ const remainingGroups = props.workingState.identityCenter.groups.filter(
1173
+ (group) => group.displayName !== props.group.displayName
1174
+ );
1175
+ return {
1176
+ ...props.workingState,
1177
+ identityCenter: createWorkingIdentityCenterState({
1178
+ identityCenter: {
1179
+ ...materializeWorkingIdentityCenterState({
1180
+ identityCenter: props.workingState.identityCenter
1181
+ }),
1182
+ groups: [...remainingGroups, props.group]
1183
+ }
1184
+ })
1185
+ };
1186
+ }
1187
+ function removeIdcGroupFromWorkingState(props) {
1188
+ const group = props.workingState.identityCenter.groupsByDisplayName[props.groupDisplayName];
1189
+ if (group == null) {
1190
+ return props.workingState;
1191
+ }
1192
+ return {
1193
+ ...props.workingState,
1194
+ identityCenter: createWorkingIdentityCenterState({
1195
+ identityCenter: {
1196
+ ...materializeWorkingIdentityCenterState({
1197
+ identityCenter: props.workingState.identityCenter
1198
+ }),
1199
+ groups: props.workingState.identityCenter.groups.filter(
1200
+ (currentGroup) => currentGroup.displayName !== props.groupDisplayName
1201
+ ),
1202
+ groupMemberships: props.workingState.identityCenter.groupMemberships.filter(
1203
+ (groupMembership) => groupMembership.groupId !== group.groupId
1204
+ ),
1205
+ accountAssignments: props.workingState.identityCenter.accountAssignments.filter(
1206
+ (accountAssignment) => accountAssignment.principalType !== "GROUP" || accountAssignment.principalId !== group.groupId
1207
+ )
1208
+ }
1209
+ })
1210
+ };
1211
+ }
1212
+ function upsertIdcPermissionSetInWorkingState(props) {
1213
+ const currentPermissionSet = props.workingState.identityCenter.permissionSetsByName[props.permissionSet.name];
1214
+ if (currentPermissionSet != null && currentPermissionSet.permissionSetArn === props.permissionSet.permissionSetArn && currentPermissionSet.name === props.permissionSet.name && currentPermissionSet.description === props.permissionSet.description && currentPermissionSet.inlinePolicy === props.permissionSet.inlinePolicy && JSON.stringify(currentPermissionSet.awsManagedPolicies) === JSON.stringify(props.permissionSet.awsManagedPolicies) && JSON.stringify(currentPermissionSet.customerManagedPolicies) === JSON.stringify(props.permissionSet.customerManagedPolicies)) {
1215
+ return props.workingState;
1216
+ }
1217
+ const remainingPermissionSets = props.workingState.identityCenter.permissionSets.filter(
1218
+ (permissionSet) => permissionSet.name !== props.permissionSet.name
1219
+ );
1220
+ return {
1221
+ ...props.workingState,
1222
+ identityCenter: createWorkingIdentityCenterState({
1223
+ identityCenter: {
1224
+ ...materializeWorkingIdentityCenterState({
1225
+ identityCenter: props.workingState.identityCenter
1226
+ }),
1227
+ permissionSets: [...remainingPermissionSets, props.permissionSet]
1228
+ }
1229
+ })
1230
+ };
1231
+ }
1232
+ function removeIdcPermissionSetFromWorkingState(props) {
1233
+ const permissionSet = props.workingState.identityCenter.permissionSetsByName[props.permissionSetName];
1234
+ if (permissionSet == null) {
1235
+ return props.workingState;
1236
+ }
1237
+ return {
1238
+ ...props.workingState,
1239
+ identityCenter: createWorkingIdentityCenterState({
1240
+ identityCenter: {
1241
+ ...materializeWorkingIdentityCenterState({
1242
+ identityCenter: props.workingState.identityCenter
1243
+ }),
1244
+ permissionSets: props.workingState.identityCenter.permissionSets.filter(
1245
+ (currentPermissionSet) => currentPermissionSet.name !== props.permissionSetName
1246
+ ),
1247
+ accountAssignments: props.workingState.identityCenter.accountAssignments.filter(
1248
+ (accountAssignment) => accountAssignment.permissionSetArn !== permissionSet.permissionSetArn
1249
+ )
1250
+ }
1251
+ })
1252
+ };
1253
+ }
1254
+ function addAccountAssignmentToWorkingState(props) {
1255
+ const assignmentKey = createAccountAssignmentKey({
1256
+ accountId: props.accountAssignment.accountId,
1257
+ permissionSetArn: props.accountAssignment.permissionSetArn,
1258
+ principalId: props.accountAssignment.principalId,
1259
+ principalType: props.accountAssignment.principalType
1260
+ });
1261
+ if (props.workingState.identityCenter.accountAssignmentsByKey[assignmentKey] != null) {
1262
+ return props.workingState;
1263
+ }
1264
+ return {
1265
+ ...props.workingState,
1266
+ identityCenter: createWorkingIdentityCenterState({
1267
+ identityCenter: {
1268
+ ...materializeWorkingIdentityCenterState({
1269
+ identityCenter: props.workingState.identityCenter
1270
+ }),
1271
+ accountAssignments: [
1272
+ ...props.workingState.identityCenter.accountAssignments,
1273
+ props.accountAssignment
1274
+ ]
1275
+ }
1276
+ })
1277
+ };
1278
+ }
1279
+ function addGroupMembershipToWorkingState(props) {
1280
+ const membershipKey = createGroupMembershipKey({
1281
+ groupId: props.groupMembership.groupId,
1282
+ userId: props.groupMembership.userId
1283
+ });
1284
+ if (props.workingState.identityCenter.groupMembershipsByKey[membershipKey] != null) {
1285
+ return props.workingState;
1286
+ }
1287
+ return {
1288
+ ...props.workingState,
1289
+ identityCenter: createWorkingIdentityCenterState({
1290
+ identityCenter: {
1291
+ ...materializeWorkingIdentityCenterState({
1292
+ identityCenter: props.workingState.identityCenter
1293
+ }),
1294
+ groupMemberships: [
1295
+ ...props.workingState.identityCenter.groupMemberships,
1296
+ props.groupMembership
1297
+ ]
1298
+ }
1299
+ })
1300
+ };
1301
+ }
1302
+ function removeGroupMembershipFromWorkingState(props) {
1303
+ const membershipKey = createGroupMembershipKey({
1304
+ groupId: props.groupMembership.groupId,
1305
+ userId: props.groupMembership.userId
1306
+ });
1307
+ if (props.workingState.identityCenter.groupMembershipsByKey[membershipKey] == null) {
1308
+ return props.workingState;
1309
+ }
1310
+ return {
1311
+ ...props.workingState,
1312
+ identityCenter: createWorkingIdentityCenterState({
1313
+ identityCenter: {
1314
+ ...materializeWorkingIdentityCenterState({
1315
+ identityCenter: props.workingState.identityCenter
1316
+ }),
1317
+ groupMemberships: props.workingState.identityCenter.groupMemberships.filter(
1318
+ (groupMembership) => createGroupMembershipKey({
1319
+ groupId: groupMembership.groupId,
1320
+ userId: groupMembership.userId
1321
+ }) !== membershipKey
1322
+ )
1323
+ }
1324
+ })
1325
+ };
1326
+ }
1327
+ function removeAccountAssignmentFromWorkingState(props) {
1328
+ const assignmentKey = createAccountAssignmentKey({
1329
+ accountId: props.accountAssignment.accountId,
1330
+ permissionSetArn: props.accountAssignment.permissionSetArn,
1331
+ principalId: props.accountAssignment.principalId,
1332
+ principalType: props.accountAssignment.principalType
1333
+ });
1334
+ if (props.workingState.identityCenter.accountAssignmentsByKey[assignmentKey] == null) {
1335
+ return props.workingState;
1336
+ }
1337
+ return {
1338
+ ...props.workingState,
1339
+ identityCenter: createWorkingIdentityCenterState({
1340
+ identityCenter: {
1341
+ ...materializeWorkingIdentityCenterState({
1342
+ identityCenter: props.workingState.identityCenter
1343
+ }),
1344
+ accountAssignments: props.workingState.identityCenter.accountAssignments.filter(
1345
+ (accountAssignment) => createAccountAssignmentKey({
1346
+ accountId: accountAssignment.accountId,
1347
+ permissionSetArn: accountAssignment.permissionSetArn,
1348
+ principalId: accountAssignment.principalId,
1349
+ principalType: accountAssignment.principalType
1350
+ }) !== assignmentKey
1351
+ )
1352
+ }
1353
+ })
1354
+ };
1355
+ }
1356
+ function createAccessRoleName(assignment) {
1357
+ return `AWSReservedSSO_${assignment.permissionSetArn.split("/").at(-1) ?? "PermissionSet"}_${assignment.accountId}`;
1358
+ }
1359
+ function createWorkingIdentityCenterState(props) {
1360
+ const users = structuredClone(props.identityCenter.users);
1361
+ const groups = structuredClone(props.identityCenter.groups);
1362
+ const groupMemberships = structuredClone(
1363
+ props.identityCenter.groupMemberships
1364
+ );
1365
+ const permissionSets = structuredClone(props.identityCenter.permissionSets);
1366
+ const accountAssignments = structuredClone(
1367
+ props.identityCenter.accountAssignments
1368
+ );
1369
+ return {
1370
+ instanceArn: props.identityCenter.instanceArn,
1371
+ identityStoreId: props.identityCenter.identityStoreId,
1372
+ users,
1373
+ usersByUserName: toRecordByProperty(users, "userName"),
1374
+ groups,
1375
+ groupsByDisplayName: toRecordByProperty(groups, "displayName"),
1376
+ groupMemberships,
1377
+ groupMembershipsByKey: toRecordByProperty(
1378
+ groupMemberships,
1379
+ createGroupMembershipKey
1380
+ ),
1381
+ permissionSets,
1382
+ permissionSetsByName: toRecordByProperty(permissionSets, "name"),
1383
+ accountAssignments,
1384
+ accountAssignmentsByKey: toRecordByProperty(
1385
+ accountAssignments,
1386
+ createAccountAssignmentKey
1387
+ ),
1388
+ accessRoles: createAccessRoles({
1389
+ accountAssignments
1390
+ })
1391
+ };
1392
+ }
1393
+ function materializeWorkingIdentityCenterState(props) {
1394
+ return {
1395
+ instanceArn: props.identityCenter.instanceArn,
1396
+ identityStoreId: props.identityCenter.identityStoreId,
1397
+ users: structuredClone(props.identityCenter.users),
1398
+ groups: structuredClone(props.identityCenter.groups),
1399
+ groupMemberships: structuredClone(props.identityCenter.groupMemberships),
1400
+ permissionSets: structuredClone(props.identityCenter.permissionSets),
1401
+ accountAssignments: structuredClone(
1402
+ props.identityCenter.accountAssignments
1403
+ ),
1404
+ accessRoles: structuredClone(props.identityCenter.accessRoles)
1405
+ };
1406
+ }
1407
+ function createAccessRoles(props) {
1408
+ return props.accountAssignments.map((accountAssignment) => ({
1409
+ accountId: accountAssignment.accountId,
1410
+ permissionSetArn: accountAssignment.permissionSetArn,
1411
+ principalId: accountAssignment.principalId,
1412
+ principalType: accountAssignment.principalType,
1413
+ roleName: createAccessRoleName(accountAssignment)
1414
+ }));
1415
+ }
1416
+ function compareByKeys(...values) {
1417
+ for (let index = 0; index < values.length; index += 2) {
1418
+ const left = values[index] ?? "";
1419
+ const right = values[index + 1] ?? "";
1420
+ const compared = left.localeCompare(right);
1421
+ if (compared !== 0) {
1422
+ return compared;
1423
+ }
1424
+ }
1425
+ return 0;
1426
+ }
1427
+ function normalizeAccountTags(tags) {
1428
+ if (tags == null || tags.length === 0) {
1429
+ return [];
1430
+ }
1431
+ return [...tags].sort(
1432
+ (left, right) => compareByKeys(left.key, right.key, left.value, right.value)
1433
+ );
1434
+ }
1435
+
1436
+ // src/scanLogic.ts
1437
+ import {
1438
+ ListGroupMembershipsCommand,
1439
+ ListGroupsCommand,
1440
+ ListUsersCommand
1441
+ } from "@aws-sdk/client-identitystore";
1442
+ import {
1443
+ ListAccountsCommand,
1444
+ ListOrganizationalUnitsForParentCommand,
1445
+ ListParentsCommand,
1446
+ ListRootsCommand,
1447
+ ListTagsForResourceCommand
1448
+ } from "@aws-sdk/client-organizations";
1449
+ import {
1450
+ DescribePermissionSetCommand,
1451
+ GetInlinePolicyForPermissionSetCommand,
1452
+ ListAccountAssignmentsCommand,
1453
+ ListAccountsForProvisionedPermissionSetCommand,
1454
+ ListCustomerManagedPolicyReferencesInPermissionSetCommand,
1455
+ ListInstancesCommand,
1456
+ ListManagedPoliciesInPermissionSetCommand,
1457
+ ListPermissionSetsCommand
1458
+ } from "@aws-sdk/client-sso-admin";
1459
+ async function scanOrganization(props) {
1460
+ const roots = await props.organizationsClient.send(new ListRootsCommand({}));
1461
+ const root = roots.Roots?.[0];
1462
+ if (root?.Id == null) {
1463
+ throw new Error("No organization root found.");
1464
+ }
1465
+ const organizationalUnits = await collectOrganizationalUnits({
1466
+ organizationsClient: props.organizationsClient,
1467
+ parentId: root.Id
1468
+ });
1469
+ const accounts = [];
1470
+ let nextToken;
1471
+ do {
1472
+ const response = await props.organizationsClient.send(
1473
+ new ListAccountsCommand({ NextToken: nextToken })
1474
+ );
1475
+ for (const account of response.Accounts ?? []) {
1476
+ if (account.Id == null || account.Arn == null || account.Name == null || account.Email == null || account.Status == null) {
1477
+ continue;
1478
+ }
1479
+ const parents = await props.organizationsClient.send(
1480
+ new ListParentsCommand({ ChildId: account.Id })
1481
+ );
1482
+ const parentId = parents.Parents?.[0]?.Id ?? root.Id;
1483
+ const tagsResponse = await props.organizationsClient.send(
1484
+ new ListTagsForResourceCommand({
1485
+ ResourceId: account.Id
1486
+ })
1487
+ );
1488
+ accounts.push({
1489
+ id: account.Id,
1490
+ arn: account.Arn,
1491
+ name: account.Name,
1492
+ email: account.Email,
1493
+ status: account.Status,
1494
+ parentId,
1495
+ tags: (tagsResponse.Tags ?? []).flatMap((tag) => {
1496
+ if (tag.Key == null) {
1497
+ return [];
1498
+ }
1499
+ return [
1500
+ {
1501
+ key: tag.Key,
1502
+ value: tag.Value ?? ""
1503
+ }
1504
+ ];
1505
+ })
1506
+ });
1507
+ }
1508
+ nextToken = response.NextToken;
1509
+ } while (nextToken != null);
1510
+ return {
1511
+ rootId: root.Id,
1512
+ organizationalUnits,
1513
+ accounts
1514
+ };
1515
+ }
1516
+ async function collectOrganizationalUnits(props) {
1517
+ const children = [];
1518
+ let nextToken;
1519
+ do {
1520
+ const response = await props.organizationsClient.send(
1521
+ new ListOrganizationalUnitsForParentCommand({
1522
+ ParentId: props.parentId,
1523
+ NextToken: nextToken
1524
+ })
1525
+ );
1526
+ for (const ou of response.OrganizationalUnits ?? []) {
1527
+ if (ou.Id == null || ou.Arn == null || ou.Name == null) {
1528
+ continue;
1529
+ }
1530
+ children.push({
1531
+ id: ou.Id,
1532
+ parentId: props.parentId,
1533
+ arn: ou.Arn,
1534
+ name: ou.Name
1535
+ });
1536
+ const descendants = await collectOrganizationalUnits({
1537
+ organizationsClient: props.organizationsClient,
1538
+ parentId: ou.Id
1539
+ });
1540
+ children.push(...descendants);
1541
+ }
1542
+ nextToken = response.NextToken;
1543
+ } while (nextToken != null);
1544
+ return children;
1545
+ }
1546
+ async function scanIdentityCenter(props) {
1547
+ const instancesResponse = await props.ssoAdminClient.send(
1548
+ new ListInstancesCommand({})
1549
+ );
1550
+ const instances = instancesResponse.Instances ?? [];
1551
+ if (instances.length === 0) {
1552
+ throw new Error("No IAM Identity Center instance found.");
1553
+ }
1554
+ const instance = selectIdentityCenterInstance({
1555
+ instances,
1556
+ requestedInstanceArn: props.requestedInstanceArn
1557
+ });
1558
+ const [users, groups, permissionSets] = await Promise.all([
1559
+ listIdentityStoreUsers({
1560
+ identityStoreClient: props.identityStoreClient,
1561
+ identityStoreId: instance.identityStoreId
1562
+ }),
1563
+ listIdentityStoreGroups({
1564
+ identityStoreClient: props.identityStoreClient,
1565
+ identityStoreId: instance.identityStoreId
1566
+ }),
1567
+ listPermissionSets({
1568
+ ssoAdminClient: props.ssoAdminClient,
1569
+ instanceArn: instance.instanceArn
1570
+ })
1571
+ ]);
1572
+ const groupMemberships = await listGroupMemberships({
1573
+ identityStoreClient: props.identityStoreClient,
1574
+ identityStoreId: instance.identityStoreId,
1575
+ groups
1576
+ });
1577
+ const accountAssignments = await listAccountAssignments({
1578
+ ssoAdminClient: props.ssoAdminClient,
1579
+ instanceArn: instance.instanceArn,
1580
+ permissionSets
1581
+ });
1582
+ const accessRoles = accountAssignments.map((assignment) => ({
1583
+ ...assignment,
1584
+ roleName: createAccessRoleName(assignment)
1585
+ }));
1586
+ return {
1587
+ instanceArn: instance.instanceArn,
1588
+ identityStoreId: instance.identityStoreId,
1589
+ users,
1590
+ groups,
1591
+ groupMemberships,
1592
+ permissionSets,
1593
+ accountAssignments,
1594
+ accessRoles
1595
+ };
1596
+ }
1597
+ function selectIdentityCenterInstance(props) {
1598
+ if (props.requestedInstanceArn != null) {
1599
+ const selected2 = props.instances.find(
1600
+ (instance) => instance.InstanceArn === props.requestedInstanceArn
1601
+ );
1602
+ if (selected2?.InstanceArn == null || selected2.IdentityStoreId == null) {
1603
+ throw new Error(
1604
+ `Identity Center instance not found for --instance-arn: ${props.requestedInstanceArn}`
1605
+ );
1606
+ }
1607
+ return {
1608
+ instanceArn: selected2.InstanceArn,
1609
+ identityStoreId: selected2.IdentityStoreId
1610
+ };
1611
+ }
1612
+ if (props.instances.length > 1) {
1613
+ const knownArns = props.instances.map((instance) => instance.InstanceArn).filter((value) => value != null).join(", ");
1614
+ throw new Error(
1615
+ `Multiple IAM Identity Center instances found. Re-run scan with --instance-arn. Available: ${knownArns}`
1616
+ );
1617
+ }
1618
+ const selected = props.instances[0];
1619
+ if (selected?.InstanceArn == null || selected.IdentityStoreId == null) {
1620
+ throw new Error("IAM Identity Center instance is missing required fields.");
1621
+ }
1622
+ return {
1623
+ instanceArn: selected.InstanceArn,
1624
+ identityStoreId: selected.IdentityStoreId
1625
+ };
1626
+ }
1627
+ async function listIdentityStoreUsers(props) {
1628
+ const users = [];
1629
+ let nextToken;
1630
+ do {
1631
+ const response = await props.identityStoreClient.send(
1632
+ new ListUsersCommand({
1633
+ IdentityStoreId: props.identityStoreId,
1634
+ NextToken: nextToken
1635
+ })
1636
+ );
1637
+ for (const user of response.Users ?? []) {
1638
+ if (user.UserId == null || user.UserName == null) {
1639
+ continue;
1640
+ }
1641
+ users.push({
1642
+ userId: user.UserId,
1643
+ userName: user.UserName,
1644
+ displayName: user.DisplayName ?? "",
1645
+ email: resolveIdentityStoreUserEmail({
1646
+ emails: user.Emails ?? []
1647
+ })
1648
+ });
1649
+ }
1650
+ nextToken = response.NextToken;
1651
+ } while (nextToken != null);
1652
+ return users;
1653
+ }
1654
+ async function listIdentityStoreGroups(props) {
1655
+ const groups = [];
1656
+ let nextToken;
1657
+ do {
1658
+ const response = await props.identityStoreClient.send(
1659
+ new ListGroupsCommand({
1660
+ IdentityStoreId: props.identityStoreId,
1661
+ NextToken: nextToken
1662
+ })
1663
+ );
1664
+ for (const group of response.Groups ?? []) {
1665
+ if (group.GroupId == null || group.DisplayName == null) {
1666
+ continue;
1667
+ }
1668
+ groups.push({
1669
+ groupId: group.GroupId,
1670
+ displayName: group.DisplayName,
1671
+ description: group.Description ?? ""
1672
+ });
1673
+ }
1674
+ nextToken = response.NextToken;
1675
+ } while (nextToken != null);
1676
+ return groups;
1677
+ }
1678
+ async function listGroupMemberships(props) {
1679
+ const groupMemberships = [];
1680
+ for (const group of props.groups) {
1681
+ let nextToken;
1682
+ do {
1683
+ const response = await props.identityStoreClient.send(
1684
+ new ListGroupMembershipsCommand({
1685
+ IdentityStoreId: props.identityStoreId,
1686
+ GroupId: group.groupId,
1687
+ NextToken: nextToken
1688
+ })
1689
+ );
1690
+ for (const groupMembership of response.GroupMemberships ?? []) {
1691
+ const userId = groupMembership.MemberId?.UserId;
1692
+ if (groupMembership.MembershipId == null || groupMembership.GroupId == null || userId == null) {
1693
+ continue;
1694
+ }
1695
+ groupMemberships.push({
1696
+ membershipId: groupMembership.MembershipId,
1697
+ groupId: groupMembership.GroupId,
1698
+ userId
1699
+ });
1700
+ }
1701
+ nextToken = response.NextToken;
1702
+ } while (nextToken != null);
1703
+ }
1704
+ return groupMemberships;
1705
+ }
1706
+ function resolveIdentityStoreUserEmail(props) {
1707
+ const primaryEmail = props.emails.find(
1708
+ (email) => email.Primary === true && email.Value != null && email.Value.length > 0
1709
+ );
1710
+ if (primaryEmail?.Value != null) {
1711
+ return primaryEmail.Value;
1712
+ }
1713
+ return props.emails.find((email) => email.Value != null && email.Value.length > 0)?.Value ?? "";
1714
+ }
1715
+ async function listPermissionSets(props) {
1716
+ const permissionSetArns = [];
1717
+ let nextToken;
1718
+ do {
1719
+ const response = await props.ssoAdminClient.send(
1720
+ new ListPermissionSetsCommand({
1721
+ InstanceArn: props.instanceArn,
1722
+ NextToken: nextToken
1723
+ })
1724
+ );
1725
+ permissionSetArns.push(...response.PermissionSets ?? []);
1726
+ nextToken = response.NextToken;
1727
+ } while (nextToken != null);
1728
+ const describeResponses = await Promise.all(
1729
+ permissionSetArns.map(
1730
+ (permissionSetArn) => props.ssoAdminClient.send(
1731
+ new DescribePermissionSetCommand({
1732
+ InstanceArn: props.instanceArn,
1733
+ PermissionSetArn: permissionSetArn
1734
+ })
1735
+ )
1736
+ )
1737
+ );
1738
+ const permissionSets = await Promise.all(
1739
+ describeResponses.map(async (response) => {
1740
+ const permissionSet = response.PermissionSet;
1741
+ if (permissionSet?.PermissionSetArn == null || permissionSet.Name == null) {
1742
+ return void 0;
1743
+ }
1744
+ const [
1745
+ inlinePolicy,
1746
+ awsManagedPolicies,
1747
+ customerManagedPolicies
1748
+ ] = await Promise.all([
1749
+ getInlinePolicyForPermissionSet({
1750
+ ssoAdminClient: props.ssoAdminClient,
1751
+ instanceArn: props.instanceArn,
1752
+ permissionSetArn: permissionSet.PermissionSetArn
1753
+ }),
1754
+ listManagedPoliciesInPermissionSet({
1755
+ ssoAdminClient: props.ssoAdminClient,
1756
+ instanceArn: props.instanceArn,
1757
+ permissionSetArn: permissionSet.PermissionSetArn
1758
+ }),
1759
+ listCustomerManagedPoliciesInPermissionSet({
1760
+ ssoAdminClient: props.ssoAdminClient,
1761
+ instanceArn: props.instanceArn,
1762
+ permissionSetArn: permissionSet.PermissionSetArn
1763
+ })
1764
+ ]);
1765
+ return {
1766
+ permissionSetArn: permissionSet.PermissionSetArn,
1767
+ name: permissionSet.Name,
1768
+ description: permissionSet.Description ?? "",
1769
+ inlinePolicy,
1770
+ awsManagedPolicies,
1771
+ customerManagedPolicies
1772
+ };
1773
+ })
1774
+ );
1775
+ return permissionSets.filter(
1776
+ (permissionSet) => permissionSet != null
1777
+ );
1778
+ }
1779
+ async function getInlinePolicyForPermissionSet(props) {
1780
+ const response = await props.ssoAdminClient.send(
1781
+ new GetInlinePolicyForPermissionSetCommand({
1782
+ InstanceArn: props.instanceArn,
1783
+ PermissionSetArn: props.permissionSetArn
1784
+ })
1785
+ );
1786
+ const inlinePolicy = response.InlinePolicy?.trim();
1787
+ return inlinePolicy != null && inlinePolicy.length > 0 ? inlinePolicy : null;
1788
+ }
1789
+ async function listManagedPoliciesInPermissionSet(props) {
1790
+ const managedPolicies = [];
1791
+ let nextToken;
1792
+ do {
1793
+ const response = await props.ssoAdminClient.send(
1794
+ new ListManagedPoliciesInPermissionSetCommand({
1795
+ InstanceArn: props.instanceArn,
1796
+ PermissionSetArn: props.permissionSetArn,
1797
+ NextToken: nextToken
1798
+ })
1799
+ );
1800
+ for (const attachedManagedPolicy of response.AttachedManagedPolicies ?? []) {
1801
+ if (attachedManagedPolicy.Arn == null) {
1802
+ continue;
1803
+ }
1804
+ managedPolicies.push(attachedManagedPolicy.Arn);
1805
+ }
1806
+ nextToken = response.NextToken;
1807
+ } while (nextToken != null);
1808
+ return managedPolicies;
1809
+ }
1810
+ async function listCustomerManagedPoliciesInPermissionSet(props) {
1811
+ const customerManagedPolicies = [];
1812
+ let nextToken;
1813
+ do {
1814
+ const response = await props.ssoAdminClient.send(
1815
+ new ListCustomerManagedPolicyReferencesInPermissionSetCommand({
1816
+ InstanceArn: props.instanceArn,
1817
+ PermissionSetArn: props.permissionSetArn,
1818
+ NextToken: nextToken
1819
+ })
1820
+ );
1821
+ for (const customerManagedPolicyReference of response.CustomerManagedPolicyReferences ?? []) {
1822
+ if (customerManagedPolicyReference.Name == null || customerManagedPolicyReference.Path == null) {
1823
+ continue;
1824
+ }
1825
+ customerManagedPolicies.push({
1826
+ name: customerManagedPolicyReference.Name,
1827
+ path: customerManagedPolicyReference.Path
1828
+ });
1829
+ }
1830
+ nextToken = response.NextToken;
1831
+ } while (nextToken != null);
1832
+ return customerManagedPolicies;
1833
+ }
1834
+ async function listAccountAssignments(props) {
1835
+ const assignments = [];
1836
+ for (const permissionSet of props.permissionSets) {
1837
+ const accountIds = await listAccountsForPermissionSet({
1838
+ ssoAdminClient: props.ssoAdminClient,
1839
+ instanceArn: props.instanceArn,
1840
+ permissionSetArn: permissionSet.permissionSetArn
1841
+ });
1842
+ for (const accountId of accountIds) {
1843
+ let nextToken;
1844
+ do {
1845
+ const response = await props.ssoAdminClient.send(
1846
+ new ListAccountAssignmentsCommand({
1847
+ InstanceArn: props.instanceArn,
1848
+ AccountId: accountId,
1849
+ PermissionSetArn: permissionSet.permissionSetArn,
1850
+ NextToken: nextToken
1851
+ })
1852
+ );
1853
+ for (const assignment of response.AccountAssignments ?? []) {
1854
+ if (assignment.AccountId == null || assignment.PermissionSetArn == null || assignment.PrincipalId == null || assignment.PrincipalType == null) {
1855
+ continue;
1856
+ }
1857
+ assignments.push({
1858
+ accountId: assignment.AccountId,
1859
+ permissionSetArn: assignment.PermissionSetArn,
1860
+ principalId: assignment.PrincipalId,
1861
+ principalType: assignment.PrincipalType
1862
+ });
1863
+ }
1864
+ nextToken = response.NextToken;
1865
+ } while (nextToken != null);
1866
+ }
1867
+ }
1868
+ return assignments;
1869
+ }
1870
+ async function listAccountsForPermissionSet(props) {
1871
+ const accountIds = [];
1872
+ let nextToken;
1873
+ do {
1874
+ const response = await props.ssoAdminClient.send(
1875
+ new ListAccountsForProvisionedPermissionSetCommand({
1876
+ InstanceArn: props.instanceArn,
1877
+ PermissionSetArn: props.permissionSetArn,
1878
+ NextToken: nextToken
1879
+ })
1880
+ );
1881
+ accountIds.push(...response.AccountIds ?? []);
1882
+ nextToken = response.NextToken;
1883
+ } while (nextToken != null);
1884
+ return accountIds;
1885
+ }
1886
+
1887
+ // src/applyLogic.ts
1888
+ import {
1889
+ PutAccountNameCommand
1890
+ } from "@aws-sdk/client-account";
1891
+ import {
1892
+ CreateOrganizationalUnitCommand,
1893
+ DeleteOrganizationalUnitCommand,
1894
+ ListAccountsForParentCommand,
1895
+ ListOrganizationalUnitsForParentCommand as ListOrganizationalUnitsForParentCommand2,
1896
+ MoveAccountCommand as MoveAccountCommand2,
1897
+ TagResourceCommand,
1898
+ UntagResourceCommand,
1899
+ UpdateOrganizationalUnitCommand
1900
+ } from "@aws-sdk/client-organizations";
1901
+ import {
1902
+ CreateGroupMembershipCommand,
1903
+ CreateGroupCommand,
1904
+ CreateUserCommand,
1905
+ DeleteGroupCommand,
1906
+ DeleteGroupMembershipCommand,
1907
+ DeleteUserCommand,
1908
+ GetGroupMembershipIdCommand,
1909
+ UpdateGroupCommand,
1910
+ UpdateUserCommand
1911
+ } from "@aws-sdk/client-identitystore";
1912
+ import {
1913
+ CreateAccountAssignmentCommand,
1914
+ AttachCustomerManagedPolicyReferenceToPermissionSetCommand,
1915
+ AttachManagedPolicyToPermissionSetCommand,
1916
+ CreatePermissionSetCommand,
1917
+ DeleteAccountAssignmentCommand,
1918
+ DeleteInlinePolicyFromPermissionSetCommand,
1919
+ DeletePermissionSetCommand,
1920
+ DescribeAccountAssignmentCreationStatusCommand,
1921
+ DescribeAccountAssignmentDeletionStatusCommand,
1922
+ DescribePermissionSetProvisioningStatusCommand,
1923
+ DetachCustomerManagedPolicyReferenceFromPermissionSetCommand,
1924
+ DetachManagedPolicyFromPermissionSetCommand,
1925
+ ProvisionPermissionSetCommand,
1926
+ PutInlinePolicyToPermissionSetCommand,
1927
+ UpdatePermissionSetCommand
1928
+ } from "@aws-sdk/client-sso-admin";
1929
+
1930
+ // src/accountCreation.ts
1931
+ import {
1932
+ CreateAccountCommand,
1933
+ DescribeCreateAccountStatusCommand,
1934
+ ListAccountsCommand as ListAccountsCommand2,
1935
+ MoveAccountCommand
1936
+ } from "@aws-sdk/client-organizations";
1937
+ async function createAccountAndMoveToOu(props) {
1938
+ props.logger.log(
1939
+ `Creating account "${props.accountName}" (${props.accountEmail})...`
1940
+ );
1941
+ const createResponse = await props.organizationsClient.send(
1942
+ new CreateAccountCommand({
1943
+ AccountName: props.accountName,
1944
+ Email: props.accountEmail
1945
+ })
1946
+ );
1947
+ const createRequestId = createResponse.CreateAccountStatus?.Id;
1948
+ if (createRequestId == null) {
1949
+ throw new Error("CreateAccount did not return a request id.");
1950
+ }
1951
+ const accountId = await pollCreateAccountStatusUntilTerminal({
1952
+ organizationsClient: props.organizationsClient,
1953
+ logger: props.logger,
1954
+ createRequestId,
1955
+ timeoutInMs: props.timeoutInMs,
1956
+ pollIntervalInMs: props.pollIntervalInMs
1957
+ });
1958
+ props.logger.log(
1959
+ `Moving account "${props.accountName}" (${accountId}) to destination OU (${props.destinationParentId})...`
1960
+ );
1961
+ await props.organizationsClient.send(
1962
+ new MoveAccountCommand({
1963
+ AccountId: accountId,
1964
+ SourceParentId: props.sourceParentId,
1965
+ DestinationParentId: props.destinationParentId
1966
+ })
1967
+ );
1968
+ const account = await resolveCreatedAccountRecord({
1969
+ organizationsClient: props.organizationsClient,
1970
+ accountId,
1971
+ destinationParentId: props.destinationParentId
1972
+ });
1973
+ return {
1974
+ accountId,
1975
+ account
1976
+ };
1977
+ }
1978
+ async function pollCreateAccountStatusUntilTerminal(props) {
1979
+ const startedAt = Date.now();
1980
+ let lastStatus;
1981
+ while (Date.now() - startedAt < props.timeoutInMs) {
1982
+ const response = await props.organizationsClient.send(
1983
+ new DescribeCreateAccountStatusCommand({
1984
+ CreateAccountRequestId: props.createRequestId
1985
+ })
1986
+ );
1987
+ const status = response.CreateAccountStatus;
1988
+ const state = status?.State ?? "UNKNOWN";
1989
+ if (state !== lastStatus) {
1990
+ props.logger.log(`CreateAccount status: ${state}`);
1991
+ lastStatus = state;
1992
+ }
1993
+ if (state === "SUCCEEDED") {
1994
+ if (status?.AccountId == null) {
1995
+ throw new Error(
1996
+ "CreateAccount succeeded but response did not include AccountId."
1997
+ );
1998
+ }
1999
+ return status.AccountId;
2000
+ }
2001
+ if (state === "FAILED") {
2002
+ throw new Error(
2003
+ `CreateAccount failed: ${status?.FailureReason ?? "unknown reason"}.`
2004
+ );
2005
+ }
2006
+ await delay(props.pollIntervalInMs);
2007
+ }
2008
+ throw new Error(
2009
+ `CreateAccount timed out after ${props.timeoutInMs}ms. Check AWS Organizations and retry.`
2010
+ );
2011
+ }
2012
+ async function resolveCreatedAccountRecord(props) {
2013
+ const account = await findAccountById({
2014
+ organizationsClient: props.organizationsClient,
2015
+ accountId: props.accountId
2016
+ });
2017
+ if (account == null) {
2018
+ throw new Error(
2019
+ `Created account "${props.accountId}" could not be resolved from AWS Organizations list.`
2020
+ );
2021
+ }
2022
+ return {
2023
+ id: account.id,
2024
+ arn: account.arn,
2025
+ name: account.name,
2026
+ email: account.email,
2027
+ status: account.status,
2028
+ parentId: props.destinationParentId
2029
+ };
2030
+ }
2031
+ async function findAccountById(props) {
2032
+ let nextToken;
2033
+ do {
2034
+ const response = await props.organizationsClient.send(
2035
+ new ListAccountsCommand2({ NextToken: nextToken })
2036
+ );
2037
+ const matched = (response.Accounts ?? []).find(
2038
+ (account) => isCompleteAccountWithStatus(account, props.accountId)
2039
+ );
2040
+ if (matched?.Id != null && matched.Arn != null && matched.Name != null && matched.Email != null && matched.Status != null) {
2041
+ return {
2042
+ id: matched.Id,
2043
+ arn: matched.Arn,
2044
+ name: matched.Name,
2045
+ email: matched.Email,
2046
+ status: matched.Status
2047
+ };
2048
+ }
2049
+ nextToken = response.NextToken;
2050
+ } while (nextToken != null);
2051
+ return void 0;
2052
+ }
2053
+ function isCompleteAccountWithStatus(account, expectedAccountId) {
2054
+ if (account.Id == null || account.Arn == null || account.Name == null || account.Email == null || account.Status == null) {
2055
+ return false;
2056
+ }
2057
+ if (expectedAccountId == null) {
2058
+ return true;
2059
+ }
2060
+ return account.Id === expectedAccountId;
2061
+ }
2062
+
2063
+ // src/applyLogic.ts
2064
+ async function executeOperation(props) {
2065
+ const operation = props.operation;
2066
+ if (operation.kind === "moveAccount") {
2067
+ props.logger.log(
2068
+ `Moving "${operation.accountName}" (${operation.accountId}): ${operation.fromOuName} -> ${operation.toOuName}`
2069
+ );
2070
+ await props.organizationsClient.send(
2071
+ new MoveAccountCommand2({
2072
+ AccountId: operation.accountId,
2073
+ SourceParentId: operation.fromOuId,
2074
+ DestinationParentId: operation.toOuId
2075
+ })
2076
+ );
2077
+ props.logger.log(`Done: "${operation.accountName}"`);
2078
+ return moveAccountInWorkingState({
2079
+ workingState: props.state,
2080
+ accountId: operation.accountId,
2081
+ parentId: operation.toOuId
2082
+ });
2083
+ }
2084
+ if (operation.kind === "createOu") {
2085
+ props.logger.log(
2086
+ `Creating OU "${operation.ouName}" under ${operation.parentOuName}...`
2087
+ );
2088
+ const response = await props.organizationsClient.send(
2089
+ new CreateOrganizationalUnitCommand({
2090
+ ParentId: operation.parentOuId,
2091
+ Name: operation.ouName
2092
+ })
2093
+ );
2094
+ const createdOu = response.OrganizationalUnit;
2095
+ if (createdOu?.Id == null || createdOu.Arn == null || createdOu.Name == null) {
2096
+ throw new Error(
2097
+ `CreateOrganizationalUnit for "${operation.ouName}" returned incomplete OU data.`
2098
+ );
2099
+ }
2100
+ props.logger.log(`Done: "${createdOu.Name}"`);
2101
+ return upsertOrganizationalUnitInWorkingState({
2102
+ workingState: props.state,
2103
+ organizationalUnit: {
2104
+ id: createdOu.Id,
2105
+ parentId: operation.parentOuId,
2106
+ arn: createdOu.Arn,
2107
+ name: createdOu.Name
2108
+ }
2109
+ });
2110
+ }
2111
+ if (operation.kind === "renameOu") {
2112
+ props.logger.log(
2113
+ `Renaming OU "${operation.fromOuName}" -> "${operation.toOuName}"...`
2114
+ );
2115
+ await props.organizationsClient.send(
2116
+ new UpdateOrganizationalUnitCommand({
2117
+ OrganizationalUnitId: operation.ouId,
2118
+ Name: operation.toOuName
2119
+ })
2120
+ );
2121
+ props.logger.log(`Done: "${operation.toOuName}"`);
2122
+ return renameOrganizationalUnitInWorkingState({
2123
+ workingState: props.state,
2124
+ organizationalUnitId: operation.ouId,
2125
+ name: operation.toOuName
2126
+ });
2127
+ }
2128
+ if (operation.kind === "deleteOu") {
2129
+ props.logger.log(`Deleting OU "${operation.ouName}"...`);
2130
+ await assertOrganizationalUnitIsEmpty({
2131
+ organizationsClient: props.organizationsClient,
2132
+ organizationalUnitId: operation.ouId,
2133
+ organizationalUnitName: operation.ouName
2134
+ });
2135
+ await props.organizationsClient.send(
2136
+ new DeleteOrganizationalUnitCommand({
2137
+ OrganizationalUnitId: operation.ouId
2138
+ })
2139
+ );
2140
+ props.logger.log(`Done: "${operation.ouName}"`);
2141
+ return removeOrganizationalUnitFromWorkingState({
2142
+ workingState: props.state,
2143
+ organizationalUnitId: operation.ouId
2144
+ });
2145
+ }
2146
+ if (operation.kind === "createAccount") {
2147
+ const result = await createAccountAndMoveToOu({
2148
+ organizationsClient: props.organizationsClient,
2149
+ logger: props.logger,
2150
+ accountName: operation.accountName,
2151
+ accountEmail: operation.accountEmail,
2152
+ sourceParentId: props.context.organization.rootId,
2153
+ destinationParentId: operation.targetOuId,
2154
+ timeoutInMs: props.runtime.createAccount.timeoutInMs,
2155
+ pollIntervalInMs: props.runtime.createAccount.pollIntervalInMs
2156
+ });
2157
+ return upsertAccountInWorkingState({
2158
+ workingState: props.state,
2159
+ account: {
2160
+ id: result.account.id,
2161
+ arn: result.account.arn,
2162
+ name: result.account.name,
2163
+ email: result.account.email,
2164
+ status: result.account.status,
2165
+ parentId: operation.targetOuId,
2166
+ tags: []
2167
+ }
2168
+ });
2169
+ }
2170
+ if (operation.kind === "updateAccountTags") {
2171
+ const account = props.state.organization.accountsById[operation.accountId];
2172
+ if (account == null) {
2173
+ throw new Error(
2174
+ `Could not resolve account "${operation.accountName}" (${operation.accountId}) in working state.`
2175
+ );
2176
+ }
2177
+ const currentTags = new Map(
2178
+ (account.tags ?? []).map((tag) => [tag.key, tag.value])
2179
+ );
2180
+ const desiredTags = new Map(Object.entries(operation.tags));
2181
+ const tagsToApply = [...desiredTags.entries()].filter(([key, value]) => currentTags.get(key) !== value).map(([Key, Value]) => ({ Key, Value }));
2182
+ const tagKeysToRemove = [...currentTags.keys()].filter(
2183
+ (key) => desiredTags.has(key) === false
2184
+ );
2185
+ props.logger.log(
2186
+ `Updating account tags "${operation.accountName}" (${operation.accountId})...`
2187
+ );
2188
+ if (tagsToApply.length > 0) {
2189
+ await props.organizationsClient.send(
2190
+ new TagResourceCommand({
2191
+ ResourceId: operation.accountId,
2192
+ Tags: tagsToApply
2193
+ })
2194
+ );
2195
+ }
2196
+ if (tagKeysToRemove.length > 0) {
2197
+ await props.organizationsClient.send(
2198
+ new UntagResourceCommand({
2199
+ ResourceId: operation.accountId,
2200
+ TagKeys: tagKeysToRemove
2201
+ })
2202
+ );
2203
+ }
2204
+ props.logger.log(`Done: tags updated for "${operation.accountName}"`);
2205
+ return upsertAccountInWorkingState({
2206
+ workingState: props.state,
2207
+ account: {
2208
+ ...account,
2209
+ tags: Object.entries(operation.tags).map(([key, value]) => ({ key, value }))
2210
+ }
2211
+ });
2212
+ }
2213
+ if (operation.kind === "updateAccountName") {
2214
+ props.logger.log(
2215
+ `Renaming account (${operation.accountId}): "${operation.fromAccountName}" -> "${operation.toAccountName}"...`
2216
+ );
2217
+ await props.accountClient.send(
2218
+ new PutAccountNameCommand({
2219
+ AccountId: operation.accountId,
2220
+ AccountName: operation.toAccountName
2221
+ })
2222
+ );
2223
+ props.logger.log(
2224
+ `Done: account "${operation.toAccountName}" (${operation.accountId})`
2225
+ );
2226
+ const account = props.state.organization.accountsById[operation.accountId];
2227
+ if (account == null) {
2228
+ throw new Error(
2229
+ `Could not resolve account (${operation.accountId}) in working state after rename.`
2230
+ );
2231
+ }
2232
+ return upsertAccountInWorkingState({
2233
+ workingState: props.state,
2234
+ account: {
2235
+ ...account,
2236
+ name: operation.toAccountName
2237
+ }
2238
+ });
2239
+ }
2240
+ if (operation.kind === "removeAccount") {
2241
+ props.logger.log(
2242
+ `Moving removed account "${operation.accountName}" (${operation.accountId}) to ${operation.toOuName}...`
2243
+ );
2244
+ await props.organizationsClient.send(
2245
+ new MoveAccountCommand2({
2246
+ AccountId: operation.accountId,
2247
+ SourceParentId: operation.fromOuId,
2248
+ DestinationParentId: operation.toOuId
2249
+ })
2250
+ );
2251
+ props.logger.log(`Done: "${operation.accountName}" -> ${operation.toOuName}`);
2252
+ return moveAccountInWorkingState({
2253
+ workingState: props.state,
2254
+ accountId: operation.accountId,
2255
+ parentId: operation.toOuId
2256
+ });
2257
+ }
2258
+ if (operation.kind === "createIdcUser") {
2259
+ props.logger.log(`Creating IdC user "${operation.userName}"...`);
2260
+ const response = await props.identityStoreClient.send(
2261
+ new CreateUserCommand({
2262
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
2263
+ UserName: operation.userName,
2264
+ DisplayName: operation.displayName,
2265
+ Name: buildIdentityStoreUserName({
2266
+ userName: operation.userName,
2267
+ displayName: operation.displayName
2268
+ }),
2269
+ Emails: operation.email.length > 0 ? [
2270
+ {
2271
+ Value: operation.email,
2272
+ Type: "Work",
2273
+ Primary: true
2274
+ }
2275
+ ] : void 0
2276
+ })
2277
+ );
2278
+ if (response.UserId == null) {
2279
+ throw new Error(
2280
+ `CreateUser for "${operation.userName}" returned no user id.`
2281
+ );
2282
+ }
2283
+ props.logger.log(`Done: "${operation.userName}"`);
2284
+ return upsertIdcUserInWorkingState({
2285
+ workingState: props.state,
2286
+ user: {
2287
+ userId: response.UserId,
2288
+ userName: operation.userName,
2289
+ displayName: operation.displayName,
2290
+ email: operation.email
2291
+ }
2292
+ });
2293
+ }
2294
+ if (operation.kind === "updateIdcUser") {
2295
+ const user = resolveUserByName({
2296
+ state: props.state,
2297
+ userName: operation.userName
2298
+ });
2299
+ const operations = [];
2300
+ if (user.displayName !== operation.displayName) {
2301
+ operations.push({
2302
+ AttributePath: "displayName",
2303
+ AttributeValue: operation.displayName
2304
+ });
2305
+ operations.push({
2306
+ AttributePath: "name",
2307
+ AttributeValue: buildIdentityStoreUserName({
2308
+ userName: operation.userName,
2309
+ displayName: operation.displayName
2310
+ })
2311
+ });
2312
+ }
2313
+ if (user.email !== operation.email && operation.email.length > 0) {
2314
+ operations.push({
2315
+ AttributePath: "emails",
2316
+ AttributeValue: [
2317
+ {
2318
+ Value: operation.email,
2319
+ Type: "Work",
2320
+ Primary: true
2321
+ }
2322
+ ]
2323
+ });
2324
+ }
2325
+ if (operations.length === 0) {
2326
+ return props.state;
2327
+ }
2328
+ props.logger.log(`Updating IdC user "${operation.userName}"...`);
2329
+ await props.identityStoreClient.send(
2330
+ new UpdateUserCommand({
2331
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
2332
+ UserId: user.userId,
2333
+ Operations: operations
2334
+ })
2335
+ );
2336
+ props.logger.log(`Done: "${operation.userName}"`);
2337
+ return upsertIdcUserInWorkingState({
2338
+ workingState: props.state,
2339
+ user: {
2340
+ ...user,
2341
+ displayName: operation.displayName,
2342
+ email: operation.email.length > 0 ? operation.email : user.email
2343
+ }
2344
+ });
2345
+ }
2346
+ if (operation.kind === "deleteIdcUser") {
2347
+ const user = resolveUserByName({
2348
+ state: props.state,
2349
+ userName: operation.userName
2350
+ });
2351
+ props.logger.log(`Deleting IdC user "${operation.userName}"...`);
2352
+ await props.identityStoreClient.send(
2353
+ new DeleteUserCommand({
2354
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
2355
+ UserId: user.userId
2356
+ })
2357
+ );
2358
+ props.logger.log(`Done: "${operation.userName}"`);
2359
+ return removeIdcUserFromWorkingState({
2360
+ workingState: props.state,
2361
+ userName: operation.userName
2362
+ });
2363
+ }
2364
+ if (operation.kind === "createIdcGroup") {
2365
+ props.logger.log(`Creating IdC group "${operation.groupDisplayName}"...`);
2366
+ const response = await props.identityStoreClient.send(
2367
+ new CreateGroupCommand({
2368
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
2369
+ DisplayName: operation.groupDisplayName,
2370
+ Description: operation.description.trim().length > 0 ? operation.description : void 0
2371
+ })
2372
+ );
2373
+ if (response.GroupId == null) {
2374
+ throw new Error(
2375
+ `CreateGroup for "${operation.groupDisplayName}" returned no group id.`
2376
+ );
2377
+ }
2378
+ props.logger.log(`Done: "${operation.groupDisplayName}"`);
2379
+ return upsertIdcGroupInWorkingState({
2380
+ workingState: props.state,
2381
+ group: {
2382
+ groupId: response.GroupId,
2383
+ displayName: operation.groupDisplayName,
2384
+ description: operation.description
2385
+ }
2386
+ });
2387
+ }
2388
+ if (operation.kind === "updateIdcGroupDescription") {
2389
+ const group = resolveGroupByDisplayName({
2390
+ state: props.state,
2391
+ groupDisplayName: operation.groupDisplayName
2392
+ });
2393
+ props.logger.log(
2394
+ `Updating IdC group description for "${operation.groupDisplayName}"...`
2395
+ );
2396
+ await props.identityStoreClient.send(
2397
+ new UpdateGroupCommand({
2398
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
2399
+ GroupId: group.groupId,
2400
+ Operations: [
2401
+ {
2402
+ AttributePath: "description",
2403
+ AttributeValue: operation.description
2404
+ }
2405
+ ]
2406
+ })
2407
+ );
2408
+ props.logger.log(`Done: group "${operation.groupDisplayName}"`);
2409
+ return upsertIdcGroupInWorkingState({
2410
+ workingState: props.state,
2411
+ group: {
2412
+ ...group,
2413
+ description: operation.description
2414
+ }
2415
+ });
2416
+ }
2417
+ if (operation.kind === "deleteIdcGroup") {
2418
+ const group = resolveGroupByDisplayName({
2419
+ state: props.state,
2420
+ groupDisplayName: operation.groupDisplayName
2421
+ });
2422
+ props.logger.log(`Deleting IdC group "${operation.groupDisplayName}"...`);
2423
+ await props.identityStoreClient.send(
2424
+ new DeleteGroupCommand({
2425
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
2426
+ GroupId: group.groupId
2427
+ })
2428
+ );
2429
+ props.logger.log(`Done: "${operation.groupDisplayName}"`);
2430
+ return removeIdcGroupFromWorkingState({
2431
+ workingState: props.state,
2432
+ groupDisplayName: operation.groupDisplayName
2433
+ });
2434
+ }
2435
+ if (operation.kind === "addIdcGroupMembership") {
2436
+ const resolvedMembership = resolveGroupMembershipDependencies({
2437
+ state: props.state,
2438
+ groupDisplayName: operation.groupDisplayName,
2439
+ userName: operation.userName
2440
+ });
2441
+ props.logger.log(
2442
+ `Adding user "${operation.userName}" to IdC group "${operation.groupDisplayName}"...`
2443
+ );
2444
+ const response = await props.identityStoreClient.send(
2445
+ new CreateGroupMembershipCommand({
2446
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
2447
+ GroupId: resolvedMembership.groupId,
2448
+ MemberId: {
2449
+ UserId: resolvedMembership.userId
2450
+ }
2451
+ })
2452
+ );
2453
+ if (response.MembershipId == null) {
2454
+ throw new Error(
2455
+ `CreateGroupMembership for group "${operation.groupDisplayName}" and user "${operation.userName}" returned no membership id.`
2456
+ );
2457
+ }
2458
+ props.logger.log(
2459
+ `Done: user "${operation.userName}" -> group "${operation.groupDisplayName}"`
2460
+ );
2461
+ return addGroupMembershipToWorkingState({
2462
+ workingState: props.state,
2463
+ groupMembership: {
2464
+ membershipId: response.MembershipId,
2465
+ groupId: resolvedMembership.groupId,
2466
+ userId: resolvedMembership.userId
2467
+ }
2468
+ });
2469
+ }
2470
+ if (operation.kind === "createIdcPermissionSet") {
2471
+ props.logger.log(
2472
+ `Creating IdC permission set "${operation.permissionSetName}"...`
2473
+ );
2474
+ const response = await props.ssoAdminClient.send(
2475
+ new CreatePermissionSetCommand({
2476
+ InstanceArn: props.state.identityCenter.instanceArn,
2477
+ Name: operation.permissionSetName,
2478
+ Description: operation.description.length > 0 ? operation.description : void 0
2479
+ })
2480
+ );
2481
+ const permissionSetArn = response.PermissionSet?.PermissionSetArn;
2482
+ if (permissionSetArn == null) {
2483
+ throw new Error(
2484
+ `CreatePermissionSet for "${operation.permissionSetName}" returned no permission set arn.`
2485
+ );
2486
+ }
2487
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2488
+ return upsertIdcPermissionSetInWorkingState({
2489
+ workingState: props.state,
2490
+ permissionSet: {
2491
+ permissionSetArn,
2492
+ name: operation.permissionSetName,
2493
+ description: operation.description,
2494
+ inlinePolicy: null,
2495
+ awsManagedPolicies: [],
2496
+ customerManagedPolicies: []
2497
+ }
2498
+ });
2499
+ }
2500
+ if (operation.kind === "updateIdcPermissionSetDescription") {
2501
+ const permissionSet = resolvePermissionSetByName({
2502
+ state: props.state,
2503
+ permissionSetName: operation.permissionSetName
2504
+ });
2505
+ props.logger.log(
2506
+ `Updating IdC permission set description for "${operation.permissionSetName}"...`
2507
+ );
2508
+ await props.ssoAdminClient.send(
2509
+ new UpdatePermissionSetCommand({
2510
+ InstanceArn: props.state.identityCenter.instanceArn,
2511
+ PermissionSetArn: permissionSet.permissionSetArn,
2512
+ Description: operation.description.trim().length > 0 ? operation.description : void 0
2513
+ })
2514
+ );
2515
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2516
+ return upsertIdcPermissionSetInWorkingState({
2517
+ workingState: props.state,
2518
+ permissionSet: {
2519
+ ...permissionSet,
2520
+ description: operation.description
2521
+ }
2522
+ });
2523
+ }
2524
+ if (operation.kind === "deleteIdcPermissionSet") {
2525
+ const permissionSet = resolvePermissionSetByName({
2526
+ state: props.state,
2527
+ permissionSetName: operation.permissionSetName
2528
+ });
2529
+ props.logger.log(
2530
+ `Deleting IdC permission set "${operation.permissionSetName}"...`
2531
+ );
2532
+ await props.ssoAdminClient.send(
2533
+ new DeletePermissionSetCommand({
2534
+ InstanceArn: props.state.identityCenter.instanceArn,
2535
+ PermissionSetArn: permissionSet.permissionSetArn
2536
+ })
2537
+ );
2538
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2539
+ return removeIdcPermissionSetFromWorkingState({
2540
+ workingState: props.state,
2541
+ permissionSetName: operation.permissionSetName
2542
+ });
2543
+ }
2544
+ if (operation.kind === "putIdcPermissionSetInlinePolicy") {
2545
+ const permissionSet = resolvePermissionSetByName({
2546
+ state: props.state,
2547
+ permissionSetName: operation.permissionSetName
2548
+ });
2549
+ props.logger.log(
2550
+ `Putting inline policy on IdC permission set "${operation.permissionSetName}"...`
2551
+ );
2552
+ await props.ssoAdminClient.send(
2553
+ new PutInlinePolicyToPermissionSetCommand({
2554
+ InstanceArn: props.state.identityCenter.instanceArn,
2555
+ PermissionSetArn: permissionSet.permissionSetArn,
2556
+ InlinePolicy: operation.inlinePolicy
2557
+ })
2558
+ );
2559
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2560
+ return upsertPermissionSetPolicyState({
2561
+ state: props.state,
2562
+ permissionSetName: operation.permissionSetName,
2563
+ update: (currentPermissionSet) => ({
2564
+ ...currentPermissionSet,
2565
+ inlinePolicy: operation.inlinePolicy
2566
+ })
2567
+ });
2568
+ }
2569
+ if (operation.kind === "deleteIdcPermissionSetInlinePolicy") {
2570
+ const permissionSet = resolvePermissionSetByName({
2571
+ state: props.state,
2572
+ permissionSetName: operation.permissionSetName
2573
+ });
2574
+ props.logger.log(
2575
+ `Deleting inline policy from IdC permission set "${operation.permissionSetName}"...`
2576
+ );
2577
+ await props.ssoAdminClient.send(
2578
+ new DeleteInlinePolicyFromPermissionSetCommand({
2579
+ InstanceArn: props.state.identityCenter.instanceArn,
2580
+ PermissionSetArn: permissionSet.permissionSetArn
2581
+ })
2582
+ );
2583
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2584
+ return upsertPermissionSetPolicyState({
2585
+ state: props.state,
2586
+ permissionSetName: operation.permissionSetName,
2587
+ update: (currentPermissionSet) => ({
2588
+ ...currentPermissionSet,
2589
+ inlinePolicy: null
2590
+ })
2591
+ });
2592
+ }
2593
+ if (operation.kind === "attachIdcManagedPolicyToPermissionSet") {
2594
+ const permissionSet = resolvePermissionSetByName({
2595
+ state: props.state,
2596
+ permissionSetName: operation.permissionSetName
2597
+ });
2598
+ props.logger.log(
2599
+ `Attaching managed policy "${operation.managedPolicyArn}" to IdC permission set "${operation.permissionSetName}"...`
2600
+ );
2601
+ await props.ssoAdminClient.send(
2602
+ new AttachManagedPolicyToPermissionSetCommand({
2603
+ InstanceArn: props.state.identityCenter.instanceArn,
2604
+ PermissionSetArn: permissionSet.permissionSetArn,
2605
+ ManagedPolicyArn: operation.managedPolicyArn
2606
+ })
2607
+ );
2608
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2609
+ return upsertPermissionSetPolicyState({
2610
+ state: props.state,
2611
+ permissionSetName: operation.permissionSetName,
2612
+ update: (currentPermissionSet) => ({
2613
+ ...currentPermissionSet,
2614
+ awsManagedPolicies: [
2615
+ ...currentPermissionSet.awsManagedPolicies,
2616
+ operation.managedPolicyArn
2617
+ ]
2618
+ })
2619
+ });
2620
+ }
2621
+ if (operation.kind === "detachIdcManagedPolicyFromPermissionSet") {
2622
+ const permissionSet = resolvePermissionSetByName({
2623
+ state: props.state,
2624
+ permissionSetName: operation.permissionSetName
2625
+ });
2626
+ props.logger.log(
2627
+ `Detaching managed policy "${operation.managedPolicyArn}" from IdC permission set "${operation.permissionSetName}"...`
2628
+ );
2629
+ await props.ssoAdminClient.send(
2630
+ new DetachManagedPolicyFromPermissionSetCommand({
2631
+ InstanceArn: props.state.identityCenter.instanceArn,
2632
+ PermissionSetArn: permissionSet.permissionSetArn,
2633
+ ManagedPolicyArn: operation.managedPolicyArn
2634
+ })
2635
+ );
2636
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2637
+ return upsertPermissionSetPolicyState({
2638
+ state: props.state,
2639
+ permissionSetName: operation.permissionSetName,
2640
+ update: (currentPermissionSet) => ({
2641
+ ...currentPermissionSet,
2642
+ awsManagedPolicies: currentPermissionSet.awsManagedPolicies.filter(
2643
+ (managedPolicyArn) => managedPolicyArn !== operation.managedPolicyArn
2644
+ )
2645
+ })
2646
+ });
2647
+ }
2648
+ if (operation.kind === "attachIdcCustomerManagedPolicyReferenceToPermissionSet") {
2649
+ const permissionSet = resolvePermissionSetByName({
2650
+ state: props.state,
2651
+ permissionSetName: operation.permissionSetName
2652
+ });
2653
+ props.logger.log(
2654
+ `Attaching customer-managed policy "${operation.customerManagedPolicyPath}${operation.customerManagedPolicyName}" to IdC permission set "${operation.permissionSetName}"...`
2655
+ );
2656
+ await props.ssoAdminClient.send(
2657
+ new AttachCustomerManagedPolicyReferenceToPermissionSetCommand({
2658
+ InstanceArn: props.state.identityCenter.instanceArn,
2659
+ PermissionSetArn: permissionSet.permissionSetArn,
2660
+ CustomerManagedPolicyReference: {
2661
+ Name: operation.customerManagedPolicyName,
2662
+ Path: operation.customerManagedPolicyPath
2663
+ }
2664
+ })
2665
+ );
2666
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2667
+ return upsertPermissionSetPolicyState({
2668
+ state: props.state,
2669
+ permissionSetName: operation.permissionSetName,
2670
+ update: (currentPermissionSet) => ({
2671
+ ...currentPermissionSet,
2672
+ customerManagedPolicies: [
2673
+ ...currentPermissionSet.customerManagedPolicies,
2674
+ {
2675
+ name: operation.customerManagedPolicyName,
2676
+ path: operation.customerManagedPolicyPath
2677
+ }
2678
+ ]
2679
+ })
2680
+ });
2681
+ }
2682
+ if (operation.kind === "detachIdcCustomerManagedPolicyReferenceFromPermissionSet") {
2683
+ const permissionSet = resolvePermissionSetByName({
2684
+ state: props.state,
2685
+ permissionSetName: operation.permissionSetName
2686
+ });
2687
+ props.logger.log(
2688
+ `Detaching customer-managed policy "${operation.customerManagedPolicyPath}${operation.customerManagedPolicyName}" from IdC permission set "${operation.permissionSetName}"...`
2689
+ );
2690
+ await props.ssoAdminClient.send(
2691
+ new DetachCustomerManagedPolicyReferenceFromPermissionSetCommand({
2692
+ InstanceArn: props.state.identityCenter.instanceArn,
2693
+ PermissionSetArn: permissionSet.permissionSetArn,
2694
+ CustomerManagedPolicyReference: {
2695
+ Name: operation.customerManagedPolicyName,
2696
+ Path: operation.customerManagedPolicyPath
2697
+ }
2698
+ })
2699
+ );
2700
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2701
+ return upsertPermissionSetPolicyState({
2702
+ state: props.state,
2703
+ permissionSetName: operation.permissionSetName,
2704
+ update: (currentPermissionSet) => ({
2705
+ ...currentPermissionSet,
2706
+ customerManagedPolicies: currentPermissionSet.customerManagedPolicies.filter(
2707
+ (customerManagedPolicy) => customerManagedPolicy.name !== operation.customerManagedPolicyName || customerManagedPolicy.path !== operation.customerManagedPolicyPath
2708
+ )
2709
+ })
2710
+ });
2711
+ }
2712
+ if (operation.kind === "provisionIdcPermissionSet") {
2713
+ const permissionSet = resolvePermissionSetByName({
2714
+ state: props.state,
2715
+ permissionSetName: operation.permissionSetName
2716
+ });
2717
+ props.logger.log(
2718
+ `Provisioning IdC permission set "${operation.permissionSetName}" to all provisioned accounts...`
2719
+ );
2720
+ const response = await props.ssoAdminClient.send(
2721
+ new ProvisionPermissionSetCommand({
2722
+ InstanceArn: props.state.identityCenter.instanceArn,
2723
+ PermissionSetArn: permissionSet.permissionSetArn,
2724
+ TargetType: operation.targetScope
2725
+ })
2726
+ );
2727
+ const requestId = response.PermissionSetProvisioningStatus?.RequestId ?? void 0;
2728
+ if (requestId == null) {
2729
+ throw new Error(
2730
+ `ProvisionPermissionSet for "${operation.permissionSetName}" returned no request id.`
2731
+ );
2732
+ }
2733
+ await waitForPermissionSetProvisioningSuccess({
2734
+ ssoAdminClient: props.ssoAdminClient,
2735
+ logger: props.logger,
2736
+ instanceArn: props.state.identityCenter.instanceArn,
2737
+ requestId,
2738
+ timeoutInMs: props.runtime.permissionSetProvisioning.timeoutInMs,
2739
+ pollIntervalInMs: props.runtime.permissionSetProvisioning.pollIntervalInMs,
2740
+ operationLabel: `"${operation.permissionSetName}"`
2741
+ });
2742
+ props.logger.log(`Done: "${operation.permissionSetName}"`);
2743
+ return props.state;
2744
+ }
2745
+ if (operation.kind === "removeIdcGroupMembership") {
2746
+ const resolvedMembership = resolveGroupMembershipDependencies({
2747
+ state: props.state,
2748
+ groupDisplayName: operation.groupDisplayName,
2749
+ userName: operation.userName
2750
+ });
2751
+ const membershipId = await resolveGroupMembershipId({
2752
+ state: props.state,
2753
+ identityStoreClient: props.identityStoreClient,
2754
+ groupId: resolvedMembership.groupId,
2755
+ userId: resolvedMembership.userId
2756
+ });
2757
+ props.logger.log(
2758
+ `Removing user "${operation.userName}" from IdC group "${operation.groupDisplayName}"...`
2759
+ );
2760
+ await props.identityStoreClient.send(
2761
+ new DeleteGroupMembershipCommand({
2762
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
2763
+ MembershipId: membershipId
2764
+ })
2765
+ );
2766
+ props.logger.log(
2767
+ `Done: user "${operation.userName}" x group "${operation.groupDisplayName}"`
2768
+ );
2769
+ return removeGroupMembershipFromWorkingState({
2770
+ workingState: props.state,
2771
+ groupMembership: {
2772
+ groupId: resolvedMembership.groupId,
2773
+ userId: resolvedMembership.userId
2774
+ }
2775
+ });
2776
+ }
2777
+ if (operation.kind === "grantIdcAccountAssignment") {
2778
+ const resolvedAssignment = resolveAssignmentDependencies({
2779
+ state: props.state,
2780
+ accountName: operation.accountName,
2781
+ permissionSetName: operation.permissionSetName,
2782
+ principalType: operation.principalType,
2783
+ principalName: operation.principalName
2784
+ });
2785
+ props.logger.log(
2786
+ `Granting IdC assignment "${operation.permissionSetName}" to ${formatPrincipalLabel(
2787
+ {
2788
+ principalType: operation.principalType,
2789
+ principalName: operation.principalName
2790
+ }
2791
+ )} on "${operation.accountName}"...`
2792
+ );
2793
+ const response = await props.ssoAdminClient.send(
2794
+ new CreateAccountAssignmentCommand({
2795
+ InstanceArn: props.state.identityCenter.instanceArn,
2796
+ TargetId: resolvedAssignment.accountId,
2797
+ TargetType: "AWS_ACCOUNT",
2798
+ PermissionSetArn: resolvedAssignment.permissionSetArn,
2799
+ PrincipalType: resolvedAssignment.principalType,
2800
+ PrincipalId: resolvedAssignment.principalId
2801
+ })
2802
+ );
2803
+ const requestId = response.AccountAssignmentCreationStatus?.RequestId;
2804
+ if (requestId == null) {
2805
+ throw new Error(
2806
+ `CreateAccountAssignment for "${operation.permissionSetName}" on "${operation.accountName}" returned no request id.`
2807
+ );
2808
+ }
2809
+ await waitForAccountAssignmentCreationSuccess({
2810
+ ssoAdminClient: props.ssoAdminClient,
2811
+ logger: props.logger,
2812
+ instanceArn: props.state.identityCenter.instanceArn,
2813
+ requestId,
2814
+ timeoutInMs: props.runtime.accountAssignment.timeoutInMs,
2815
+ pollIntervalInMs: props.runtime.accountAssignment.pollIntervalInMs,
2816
+ operationLabel: `"${operation.permissionSetName}" on "${operation.accountName}"`
2817
+ });
2818
+ props.logger.log(
2819
+ `Done: "${operation.permissionSetName}" -> "${operation.accountName}"`
2820
+ );
2821
+ return addAccountAssignmentToWorkingState({
2822
+ workingState: props.state,
2823
+ accountAssignment: {
2824
+ accountId: resolvedAssignment.accountId,
2825
+ permissionSetArn: resolvedAssignment.permissionSetArn,
2826
+ principalId: resolvedAssignment.principalId,
2827
+ principalType: resolvedAssignment.principalType
2828
+ }
2829
+ });
2830
+ }
2831
+ if (operation.kind === "revokeIdcAccountAssignment") {
2832
+ const resolvedAssignment = resolveAssignmentDependencies({
2833
+ state: props.state,
2834
+ accountName: operation.accountName,
2835
+ permissionSetName: operation.permissionSetName,
2836
+ principalType: operation.principalType,
2837
+ principalName: operation.principalName
2838
+ });
2839
+ props.logger.log(
2840
+ `Revoking IdC assignment "${operation.permissionSetName}" from ${formatPrincipalLabel(
2841
+ {
2842
+ principalType: operation.principalType,
2843
+ principalName: operation.principalName
2844
+ }
2845
+ )} on "${operation.accountName}"...`
2846
+ );
2847
+ const response = await props.ssoAdminClient.send(
2848
+ new DeleteAccountAssignmentCommand({
2849
+ InstanceArn: props.state.identityCenter.instanceArn,
2850
+ TargetId: resolvedAssignment.accountId,
2851
+ TargetType: "AWS_ACCOUNT",
2852
+ PermissionSetArn: resolvedAssignment.permissionSetArn,
2853
+ PrincipalType: resolvedAssignment.principalType,
2854
+ PrincipalId: resolvedAssignment.principalId
2855
+ })
2856
+ );
2857
+ const requestId = response.AccountAssignmentDeletionStatus?.RequestId;
2858
+ if (requestId == null) {
2859
+ throw new Error(
2860
+ `DeleteAccountAssignment for "${operation.permissionSetName}" on "${operation.accountName}" returned no request id.`
2861
+ );
2862
+ }
2863
+ await waitForAccountAssignmentDeletionSuccess({
2864
+ ssoAdminClient: props.ssoAdminClient,
2865
+ logger: props.logger,
2866
+ instanceArn: props.state.identityCenter.instanceArn,
2867
+ requestId,
2868
+ timeoutInMs: props.runtime.accountAssignment.timeoutInMs,
2869
+ pollIntervalInMs: props.runtime.accountAssignment.pollIntervalInMs,
2870
+ operationLabel: `"${operation.permissionSetName}" on "${operation.accountName}"`
2871
+ });
2872
+ props.logger.log(
2873
+ `Done: "${operation.permissionSetName}" x "${operation.accountName}"`
2874
+ );
2875
+ return removeAccountAssignmentFromWorkingState({
2876
+ workingState: props.state,
2877
+ accountAssignment: {
2878
+ accountId: resolvedAssignment.accountId,
2879
+ permissionSetArn: resolvedAssignment.permissionSetArn,
2880
+ principalId: resolvedAssignment.principalId,
2881
+ principalType: resolvedAssignment.principalType
2882
+ }
2883
+ });
2884
+ }
2885
+ assertUnreachable(operation, "Unsupported operation kind in apply.");
2886
+ }
2887
+ function resolveAssignmentDependencies(props) {
2888
+ const account = props.state.organization.accountsByName[props.accountName];
2889
+ if (account == null) {
2890
+ throw new Error(
2891
+ `Could not resolve account "${props.accountName}" in working state.`
2892
+ );
2893
+ }
2894
+ const permissionSet = props.state.identityCenter.permissionSetsByName[props.permissionSetName];
2895
+ if (permissionSet == null) {
2896
+ throw new Error(
2897
+ `Could not resolve permission set "${props.permissionSetName}" in working state.`
2898
+ );
2899
+ }
2900
+ if (props.principalType === "GROUP") {
2901
+ const group = props.state.identityCenter.groupsByDisplayName[props.principalName];
2902
+ if (group == null) {
2903
+ throw new Error(
2904
+ `Could not resolve group "${props.principalName}" in working state.`
2905
+ );
2906
+ }
2907
+ return {
2908
+ accountId: account.id,
2909
+ permissionSetArn: permissionSet.permissionSetArn,
2910
+ principalId: group.groupId,
2911
+ principalType: props.principalType
2912
+ };
2913
+ }
2914
+ const user = props.state.identityCenter.usersByUserName[props.principalName];
2915
+ if (user == null) {
2916
+ throw new Error(
2917
+ `Could not resolve user "${props.principalName}" in working state.`
2918
+ );
2919
+ }
2920
+ return {
2921
+ accountId: account.id,
2922
+ permissionSetArn: permissionSet.permissionSetArn,
2923
+ principalId: user.userId,
2924
+ principalType: props.principalType
2925
+ };
2926
+ }
2927
+ function resolveUserByName(props) {
2928
+ const user = props.state.identityCenter.usersByUserName[props.userName];
2929
+ if (user == null) {
2930
+ throw new Error(
2931
+ `Could not resolve user "${props.userName}" in working state.`
2932
+ );
2933
+ }
2934
+ return user;
2935
+ }
2936
+ function resolveGroupByDisplayName(props) {
2937
+ const group = props.state.identityCenter.groupsByDisplayName[props.groupDisplayName];
2938
+ if (group == null) {
2939
+ throw new Error(
2940
+ `Could not resolve group "${props.groupDisplayName}" in working state.`
2941
+ );
2942
+ }
2943
+ return group;
2944
+ }
2945
+ function resolvePermissionSetByName(props) {
2946
+ const permissionSet = props.state.identityCenter.permissionSetsByName[props.permissionSetName];
2947
+ if (permissionSet == null) {
2948
+ throw new Error(
2949
+ `Could not resolve permission set "${props.permissionSetName}" in working state.`
2950
+ );
2951
+ }
2952
+ return permissionSet;
2953
+ }
2954
+ function upsertPermissionSetPolicyState(props) {
2955
+ const permissionSet = resolvePermissionSetByName({
2956
+ state: props.state,
2957
+ permissionSetName: props.permissionSetName
2958
+ });
2959
+ const nextPermissionSet = props.update(permissionSet);
2960
+ return upsertIdcPermissionSetInWorkingState({
2961
+ workingState: props.state,
2962
+ permissionSet: {
2963
+ ...nextPermissionSet,
2964
+ awsManagedPolicies: [...new Set(nextPermissionSet.awsManagedPolicies)].sort(
2965
+ (left, right) => left.localeCompare(right)
2966
+ ),
2967
+ customerManagedPolicies: [
2968
+ ...nextPermissionSet.customerManagedPolicies
2969
+ ].sort((left, right) => {
2970
+ const pathComparison = left.path.localeCompare(right.path);
2971
+ if (pathComparison !== 0) {
2972
+ return pathComparison;
2973
+ }
2974
+ return left.name.localeCompare(right.name);
2975
+ })
2976
+ }
2977
+ });
2978
+ }
2979
+ function buildIdentityStoreUserName(props) {
2980
+ const normalizedDisplayName = props.displayName.trim();
2981
+ const fallbackName = normalizedDisplayName.length > 0 ? normalizedDisplayName : props.userName;
2982
+ const [givenName, ...familyNameParts] = fallbackName.split(/\s+/).filter((part) => part.length > 0);
2983
+ const familyName = familyNameParts.join(" ");
2984
+ return {
2985
+ Formatted: fallbackName,
2986
+ GivenName: givenName ?? fallbackName,
2987
+ FamilyName: familyName.length > 0 ? familyName : fallbackName
2988
+ };
2989
+ }
2990
+ async function assertOrganizationalUnitIsEmpty(props) {
2991
+ const childOrganizationalUnit = await listFirstChildOrganizationalUnit({
2992
+ organizationsClient: props.organizationsClient,
2993
+ parentId: props.organizationalUnitId
2994
+ });
2995
+ if (childOrganizationalUnit != null) {
2996
+ throw new Error(
2997
+ `Refusing to delete OU "${props.organizationalUnitName}": live AWS preflight failed [child-ou-present]: ${formatLivePreflightResource({
2998
+ resourceType: "child OU",
2999
+ name: childOrganizationalUnit.Name,
3000
+ id: childOrganizationalUnit.Id
3001
+ })} is still attached.`
3002
+ );
3003
+ }
3004
+ const account = await listFirstAccountForParent({
3005
+ organizationsClient: props.organizationsClient,
3006
+ parentId: props.organizationalUnitId
3007
+ });
3008
+ if (account != null) {
3009
+ throw new Error(
3010
+ `Refusing to delete OU "${props.organizationalUnitName}": live AWS preflight failed [account-present]: ${formatLivePreflightResource({
3011
+ resourceType: "account",
3012
+ name: account.Name,
3013
+ id: account.Id
3014
+ })} is still attached.`
3015
+ );
3016
+ }
3017
+ }
3018
+ function formatLivePreflightResource(props) {
3019
+ const quotedName = props.name != null ? `"${props.name}"` : void 0;
3020
+ if (quotedName != null && props.id != null) {
3021
+ return `${props.resourceType} ${quotedName} (${props.id})`;
3022
+ }
3023
+ if (quotedName != null) {
3024
+ return `${props.resourceType} ${quotedName}`;
3025
+ }
3026
+ if (props.id != null) {
3027
+ return `${props.resourceType} (${props.id})`;
3028
+ }
3029
+ return `${props.resourceType} "unknown"`;
3030
+ }
3031
+ async function listFirstChildOrganizationalUnit(props) {
3032
+ let nextToken;
3033
+ do {
3034
+ const response = await props.organizationsClient.send(
3035
+ new ListOrganizationalUnitsForParentCommand2({
3036
+ ParentId: props.parentId,
3037
+ NextToken: nextToken
3038
+ })
3039
+ );
3040
+ const organizationalUnit = response.OrganizationalUnits?.[0];
3041
+ if (organizationalUnit != null) {
3042
+ return organizationalUnit;
3043
+ }
3044
+ nextToken = response.NextToken;
3045
+ } while (nextToken != null);
3046
+ return void 0;
3047
+ }
3048
+ async function listFirstAccountForParent(props) {
3049
+ let nextToken;
3050
+ do {
3051
+ const response = await props.organizationsClient.send(
3052
+ new ListAccountsForParentCommand({
3053
+ ParentId: props.parentId,
3054
+ NextToken: nextToken
3055
+ })
3056
+ );
3057
+ const account = response.Accounts?.[0];
3058
+ if (account != null) {
3059
+ return account;
3060
+ }
3061
+ nextToken = response.NextToken;
3062
+ } while (nextToken != null);
3063
+ return void 0;
3064
+ }
3065
+ async function waitForAccountAssignmentCreationSuccess(props) {
3066
+ const startedAt = Date.now();
3067
+ let lastStatus;
3068
+ while (Date.now() - startedAt < props.timeoutInMs) {
3069
+ const response = await props.ssoAdminClient.send(
3070
+ new DescribeAccountAssignmentCreationStatusCommand({
3071
+ InstanceArn: props.instanceArn,
3072
+ AccountAssignmentCreationRequestId: props.requestId
3073
+ })
3074
+ );
3075
+ const status = response.AccountAssignmentCreationStatus;
3076
+ const state = status?.Status ?? "UNKNOWN";
3077
+ if (state !== lastStatus) {
3078
+ props.logger.log(`CreateAccountAssignment status: ${state}`);
3079
+ lastStatus = state;
3080
+ }
3081
+ if (state === "SUCCEEDED") {
3082
+ return;
3083
+ }
3084
+ if (state === "FAILED") {
3085
+ throw new Error(
3086
+ `CreateAccountAssignment failed for ${props.operationLabel}: ${status?.FailureReason ?? "unknown reason"}.`
3087
+ );
3088
+ }
3089
+ await delay(props.pollIntervalInMs);
3090
+ }
3091
+ throw new Error(
3092
+ `CreateAccountAssignment timed out after ${props.timeoutInMs}ms for ${props.operationLabel}.`
3093
+ );
3094
+ }
3095
+ async function waitForAccountAssignmentDeletionSuccess(props) {
3096
+ const startedAt = Date.now();
3097
+ let lastStatus;
3098
+ while (Date.now() - startedAt < props.timeoutInMs) {
3099
+ const response = await props.ssoAdminClient.send(
3100
+ new DescribeAccountAssignmentDeletionStatusCommand({
3101
+ InstanceArn: props.instanceArn,
3102
+ AccountAssignmentDeletionRequestId: props.requestId
3103
+ })
3104
+ );
3105
+ const status = response.AccountAssignmentDeletionStatus;
3106
+ const state = status?.Status ?? "UNKNOWN";
3107
+ if (state !== lastStatus) {
3108
+ props.logger.log(`DeleteAccountAssignment status: ${state}`);
3109
+ lastStatus = state;
3110
+ }
3111
+ if (state === "SUCCEEDED") {
3112
+ return;
3113
+ }
3114
+ if (state === "FAILED") {
3115
+ throw new Error(
3116
+ `DeleteAccountAssignment failed for ${props.operationLabel}: ${status?.FailureReason ?? "unknown reason"}.`
3117
+ );
3118
+ }
3119
+ await delay(props.pollIntervalInMs);
3120
+ }
3121
+ throw new Error(
3122
+ `DeleteAccountAssignment timed out after ${props.timeoutInMs}ms for ${props.operationLabel}.`
3123
+ );
3124
+ }
3125
+ async function waitForPermissionSetProvisioningSuccess(props) {
3126
+ const startedAt = Date.now();
3127
+ let lastStatus;
3128
+ while (Date.now() - startedAt < props.timeoutInMs) {
3129
+ const response = await props.ssoAdminClient.send(
3130
+ new DescribePermissionSetProvisioningStatusCommand({
3131
+ InstanceArn: props.instanceArn,
3132
+ ProvisionPermissionSetRequestId: props.requestId
3133
+ })
3134
+ );
3135
+ const status = response.PermissionSetProvisioningStatus;
3136
+ const state = status?.Status ?? "UNKNOWN";
3137
+ if (state !== lastStatus) {
3138
+ props.logger.log(`ProvisionPermissionSet status: ${state}`);
3139
+ lastStatus = state;
3140
+ }
3141
+ if (state === "SUCCEEDED") {
3142
+ return;
3143
+ }
3144
+ if (state === "FAILED") {
3145
+ throw new Error(
3146
+ `ProvisionPermissionSet failed for ${props.operationLabel}: ${status?.FailureReason ?? "unknown reason"}.`
3147
+ );
3148
+ }
3149
+ await delay(props.pollIntervalInMs);
3150
+ }
3151
+ throw new Error(
3152
+ `ProvisionPermissionSet timed out after ${props.timeoutInMs}ms for ${props.operationLabel}.`
3153
+ );
3154
+ }
3155
+ function formatPrincipalLabel(props) {
3156
+ if (props.principalType === "GROUP") {
3157
+ return `group "${props.principalName}"`;
3158
+ }
3159
+ return `user "${props.principalName}"`;
3160
+ }
3161
+ function resolveGroupMembershipDependencies(props) {
3162
+ const group = props.state.identityCenter.groupsByDisplayName[props.groupDisplayName];
3163
+ if (group == null) {
3164
+ throw new Error(
3165
+ `Could not resolve group "${props.groupDisplayName}" in working state.`
3166
+ );
3167
+ }
3168
+ const user = props.state.identityCenter.usersByUserName[props.userName];
3169
+ if (user == null) {
3170
+ throw new Error(
3171
+ `Could not resolve user "${props.userName}" in working state.`
3172
+ );
3173
+ }
3174
+ return {
3175
+ groupId: group.groupId,
3176
+ userId: user.userId
3177
+ };
3178
+ }
3179
+ async function resolveGroupMembershipId(props) {
3180
+ const existingMembership = props.state.identityCenter.groupMembershipsByKey[createGroupMembershipKey({
3181
+ groupId: props.groupId,
3182
+ userId: props.userId
3183
+ })];
3184
+ if (existingMembership?.membershipId != null) {
3185
+ return existingMembership.membershipId;
3186
+ }
3187
+ const response = await props.identityStoreClient.send(
3188
+ new GetGroupMembershipIdCommand({
3189
+ IdentityStoreId: props.state.identityCenter.identityStoreId,
3190
+ GroupId: props.groupId,
3191
+ MemberId: {
3192
+ UserId: props.userId
3193
+ }
3194
+ })
3195
+ );
3196
+ if (response.MembershipId == null) {
3197
+ throw new Error(
3198
+ `GetGroupMembershipId returned no membership id for group "${props.groupId}" and user "${props.userId}".`
3199
+ );
3200
+ }
3201
+ return response.MembershipId;
3202
+ }
3203
+
3204
+ // src/lambda/handler.ts
3205
+ var scanRequestSchema = strictObject({
3206
+ action: literal("scan")
3207
+ });
3208
+ var getStateUrlRequestSchema = strictObject({
3209
+ action: literal("getStateUrl")
3210
+ });
3211
+ var applyRequestSchema = strictObject({
3212
+ action: literal("apply"),
3213
+ operations: pipe(array(operationSchema), minLength(1)),
3214
+ allowDestructive: boolean()
3215
+ });
3216
+ var lambdaRequestSchema = variant("action", [
3217
+ scanRequestSchema,
3218
+ getStateUrlRequestSchema,
3219
+ applyRequestSchema
3220
+ ]);
3221
+ var scanResponseSchema = strictObject({
3222
+ action: literal("scan"),
3223
+ success: literal(true),
3224
+ summary: strictObject({
3225
+ organizationalUnits: number(),
3226
+ accounts: number(),
3227
+ users: number(),
3228
+ groups: number(),
3229
+ permissionSets: number(),
3230
+ accountAssignments: number()
3231
+ }),
3232
+ state: stateSchema
3233
+ });
3234
+ var getStateUrlResponseSchema = strictObject({
3235
+ action: literal("getStateUrl"),
3236
+ success: literal(true),
3237
+ url: string(),
3238
+ expiresInSeconds: number()
3239
+ });
3240
+ var applySuccessResponseSchema = strictObject({
3241
+ action: literal("apply"),
3242
+ success: literal(true),
3243
+ operationsCompleted: number(),
3244
+ state: stateSchema
3245
+ });
3246
+ var errorResponseSchema = strictObject({
3247
+ success: literal(false),
3248
+ error: strictObject({
3249
+ kind: picklist([
3250
+ "validation",
3251
+ "concurrencyConflict",
3252
+ "operationFailed",
3253
+ "internal"
3254
+ ]),
3255
+ message: string(),
3256
+ details: optional(
3257
+ strictObject({
3258
+ failedOperation: optional(number()),
3259
+ operationsCompleted: optional(number()),
3260
+ partialState: optional(stateSchema),
3261
+ validationIssues: optional(array(string()))
3262
+ })
3263
+ )
3264
+ })
3265
+ });
3266
+ var lambdaResponseSchema = union([
3267
+ scanResponseSchema,
3268
+ getStateUrlResponseSchema,
3269
+ applySuccessResponseSchema,
3270
+ errorResponseSchema
3271
+ ]);
3272
+ var STATE_KEY = "state.json";
3273
+ var PRESIGNED_URL_EXPIRY_SECONDS = 3600;
3274
+ var RUNTIME_DEFAULTS = {
3275
+ createAccount: {
3276
+ timeoutInMs: 3e5,
3277
+ pollIntervalInMs: 5e3
3278
+ },
3279
+ accountAssignment: {
3280
+ timeoutInMs: 6e4,
3281
+ pollIntervalInMs: 2e3
3282
+ },
3283
+ permissionSetProvisioning: {
3284
+ timeoutInMs: 6e4,
3285
+ pollIntervalInMs: 2e3
3286
+ }
3287
+ };
3288
+ var lambdaLogger = {
3289
+ log: (...args) => console.log(...args),
3290
+ info: (...args) => console.info(...args),
3291
+ warn: (...args) => console.warn(...args),
3292
+ error: (...args) => console.error(...args),
3293
+ debug: (...args) => console.debug(...args),
3294
+ trace: (...args) => console.trace(...args)
3295
+ };
3296
+ var s3Client = new S3Client({});
3297
+ var organizationsClient = new OrganizationsClient3({});
3298
+ var ssoAdminClient = new SSOAdminClient3({});
3299
+ var identityStoreClient = new IdentitystoreClient3({});
3300
+ var accountClient = new AccountClient2({});
3301
+ async function handler(event) {
3302
+ try {
3303
+ const parseResult = safeParse(lambdaRequestSchema, event);
3304
+ if (!parseResult.success) {
3305
+ const issues = parseResult.issues.map(
3306
+ (issue) => `${issue.path?.map((p) => p.key).join(".") ?? "root"}: ${issue.message}`
3307
+ );
3308
+ const response = buildErrorResponse(
3309
+ "validation",
3310
+ "Invalid request payload.",
3311
+ { validationIssues: issues }
3312
+ );
3313
+ return validateResponse(response);
3314
+ }
3315
+ const request = parseResult.output;
3316
+ const bucket = process.env.STATE_BUCKET_NAME;
3317
+ if (bucket == null || bucket.length === 0) {
3318
+ const response = buildErrorResponse(
3319
+ "internal",
3320
+ "STATE_BUCKET_NAME environment variable is not configured."
3321
+ );
3322
+ return validateResponse(response);
3323
+ }
3324
+ if (request.action === "scan") {
3325
+ const response = await handleScan({ s3Client, bucket, organizationsClient, ssoAdminClient, identityStoreClient });
3326
+ return validateResponse(response);
3327
+ }
3328
+ if (request.action === "getStateUrl") {
3329
+ const response = await handleGetStateUrl({ s3Client, bucket });
3330
+ return validateResponse(response);
3331
+ }
3332
+ if (request.action === "apply") {
3333
+ const response = await handleApply({
3334
+ s3Client,
3335
+ bucket,
3336
+ operations: request.operations,
3337
+ allowDestructive: request.allowDestructive,
3338
+ organizationsClient,
3339
+ ssoAdminClient,
3340
+ identityStoreClient,
3341
+ accountClient
3342
+ });
3343
+ return validateResponse(response);
3344
+ }
3345
+ assertUnreachable(request, "Unsupported action in handler.");
3346
+ } catch (error) {
3347
+ const message = error instanceof Error ? error.message : "An unexpected error occurred.";
3348
+ const response = buildErrorResponse("internal", message);
3349
+ return validateResponse(response);
3350
+ }
3351
+ }
3352
+ function buildErrorResponse(kind, message, details) {
3353
+ return {
3354
+ success: false,
3355
+ error: {
3356
+ kind,
3357
+ message,
3358
+ ...details != null ? { details } : {}
3359
+ }
3360
+ };
3361
+ }
3362
+ function validateResponse(response) {
3363
+ const result = safeParse(lambdaResponseSchema, response);
3364
+ if (!result.success) {
3365
+ return {
3366
+ success: false,
3367
+ error: {
3368
+ kind: "internal",
3369
+ message: "Response validation failed before returning.",
3370
+ details: {
3371
+ validationIssues: result.issues.map(
3372
+ (issue) => `${issue.path?.map((p) => p.key).join(".") ?? "root"}: ${issue.message}`
3373
+ )
3374
+ }
3375
+ }
3376
+ };
3377
+ }
3378
+ return result.output;
3379
+ }
3380
+ async function readStateFromS3(props) {
3381
+ const response = await props.s3Client.send(
3382
+ new GetObjectCommand({
3383
+ Bucket: props.bucket,
3384
+ Key: STATE_KEY
3385
+ })
3386
+ );
3387
+ const body = await response.Body?.transformToString();
3388
+ if (body == null) {
3389
+ throw new Error("State not found. Run remote scan first.");
3390
+ }
3391
+ const parsed = JSON.parse(body);
3392
+ const state = parse(stateSchema, parsed);
3393
+ const etag = response.ETag ?? "";
3394
+ return { state, etag };
3395
+ }
3396
+ async function writeStateToS3(props) {
3397
+ await props.s3Client.send(new PutObjectCommand({
3398
+ Bucket: props.bucket,
3399
+ Key: STATE_KEY,
3400
+ Body: JSON.stringify(props.state, null, 2),
3401
+ ContentType: "application/json",
3402
+ IfMatch: props.ifMatch
3403
+ }));
3404
+ }
3405
+ function isS3PreconditionFailed(error) {
3406
+ if (error instanceof S3ServiceException) {
3407
+ return error.name === "PreconditionFailed" || error.$metadata?.httpStatusCode === 412;
3408
+ }
3409
+ return false;
3410
+ }
3411
+ async function handleScan(props) {
3412
+ const identityCenterInstanceArn = process.env.IDENTITY_CENTER_INSTANCE_ARN || void 0;
3413
+ const [organization, identityCenter] = await Promise.all([
3414
+ scanOrganization({ organizationsClient: props.organizationsClient }),
3415
+ scanIdentityCenter({
3416
+ ssoAdminClient: props.ssoAdminClient,
3417
+ identityStoreClient: props.identityStoreClient,
3418
+ requestedInstanceArn: identityCenterInstanceArn
3419
+ })
3420
+ ]);
3421
+ const state = {
3422
+ version: "1",
3423
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3424
+ organization,
3425
+ identityCenter
3426
+ };
3427
+ await writeStateToS3({
3428
+ s3Client: props.s3Client,
3429
+ bucket: props.bucket,
3430
+ state
3431
+ });
3432
+ return {
3433
+ action: "scan",
3434
+ success: true,
3435
+ summary: {
3436
+ organizationalUnits: state.organization.organizationalUnits.length,
3437
+ accounts: state.organization.accounts.length,
3438
+ users: state.identityCenter.users.length,
3439
+ groups: state.identityCenter.groups.length,
3440
+ permissionSets: state.identityCenter.permissionSets.length,
3441
+ accountAssignments: state.identityCenter.accountAssignments.length
3442
+ },
3443
+ state
3444
+ };
3445
+ }
3446
+ async function handleGetStateUrl(props) {
3447
+ const command = new GetObjectCommand({
3448
+ Bucket: props.bucket,
3449
+ Key: STATE_KEY
3450
+ });
3451
+ const url = await getSignedUrl(props.s3Client, command, {
3452
+ expiresIn: PRESIGNED_URL_EXPIRY_SECONDS
3453
+ });
3454
+ return {
3455
+ action: "getStateUrl",
3456
+ success: true,
3457
+ url,
3458
+ expiresInSeconds: PRESIGNED_URL_EXPIRY_SECONDS
3459
+ };
3460
+ }
3461
+ async function handleApply(props) {
3462
+ const stateResult = await loadStateForApply({
3463
+ s3Client: props.s3Client,
3464
+ bucket: props.bucket
3465
+ });
3466
+ if (!stateResult.ok) {
3467
+ return stateResult.response;
3468
+ }
3469
+ const { state: currentState, etag } = stateResult;
3470
+ let workingState = createWorkingState({ state: currentState });
3471
+ let operationsCompleted = 0;
3472
+ for (let i = 0; i < props.operations.length; i++) {
3473
+ const operation = props.operations[i];
3474
+ try {
3475
+ workingState = await executeOperation({
3476
+ state: workingState,
3477
+ organizationsClient: props.organizationsClient,
3478
+ accountClient: props.accountClient,
3479
+ ssoAdminClient: props.ssoAdminClient,
3480
+ identityStoreClient: props.identityStoreClient,
3481
+ logger: lambdaLogger,
3482
+ context: {
3483
+ organization: {
3484
+ rootId: workingState.organization.rootId
3485
+ }
3486
+ },
3487
+ runtime: RUNTIME_DEFAULTS,
3488
+ operation
3489
+ });
3490
+ operationsCompleted++;
3491
+ } catch (error) {
3492
+ const partialState = materializeWorkingState({ workingState });
3493
+ try {
3494
+ await writeStateToS3({
3495
+ s3Client: props.s3Client,
3496
+ bucket: props.bucket,
3497
+ state: partialState,
3498
+ ifMatch: etag
3499
+ });
3500
+ } catch (writeError) {
3501
+ if (isS3PreconditionFailed(writeError)) {
3502
+ return buildErrorResponse(
3503
+ "concurrencyConflict",
3504
+ "Concurrent state modification detected while writing partial state."
3505
+ );
3506
+ }
3507
+ lambdaLogger.error(
3508
+ "Failed to write partial state after operation failure:",
3509
+ writeError
3510
+ );
3511
+ }
3512
+ const errorMessage = error instanceof Error ? error.message : "Unknown operation error";
3513
+ return buildErrorResponse("operationFailed", errorMessage, {
3514
+ failedOperation: i,
3515
+ operationsCompleted,
3516
+ partialState
3517
+ });
3518
+ }
3519
+ }
3520
+ const finalState = materializeWorkingState({ workingState });
3521
+ try {
3522
+ await writeStateToS3({
3523
+ s3Client: props.s3Client,
3524
+ bucket: props.bucket,
3525
+ state: finalState,
3526
+ ifMatch: etag
3527
+ });
3528
+ } catch (error) {
3529
+ if (isS3PreconditionFailed(error)) {
3530
+ return buildErrorResponse(
3531
+ "concurrencyConflict",
3532
+ "Concurrent state modification detected. Another apply may have completed while this one was running."
3533
+ );
3534
+ }
3535
+ throw error;
3536
+ }
3537
+ return {
3538
+ action: "apply",
3539
+ success: true,
3540
+ operationsCompleted,
3541
+ state: finalState
3542
+ };
3543
+ }
3544
+ async function loadStateForApply(props) {
3545
+ try {
3546
+ const result = await readStateFromS3({
3547
+ s3Client: props.s3Client,
3548
+ bucket: props.bucket
3549
+ });
3550
+ return { ok: true, state: result.state, etag: result.etag };
3551
+ } catch (error) {
3552
+ const message = error instanceof Error ? error.message : "Failed to read state from S3.";
3553
+ return { ok: false, response: buildErrorResponse("internal", message) };
3554
+ }
3555
+ }
3556
+ export {
3557
+ handler
3558
+ };