1import type * as Effect from "effect/Effect";
2import type * as Stream from "effect/Stream";
3import {
4 consumeBusEvents,
5 type EventRecord,
6 type EventRouteProps,
7} from "../EventBridge/EventSource.ts";
8
9/**
10 * The documented AWS Control Tower lifecycle event names — one per
11 * completed Control Tower action (account provisioning, guardrail
12 * enablement, landing zone setup, OU registration).
13 */
14export type ControlTowerLifecycleEventName =
15 | "CreateManagedAccount"
16 | "UpdateManagedAccount"
17 | "EnableGuardrail"
18 | "DisableGuardrail"
19 | "SetupLandingZone"
20 | "UpdateLandingZone"
21 | "RegisterOrganizationalUnit"
22 | "DeregisterOrganizationalUnit"
23 | "PrecheckOrganizationalUnit";
24
25/**
26 * The `detail` payload AWS Control Tower delivers to EventBridge when a
27 * lifecycle action completes. Control Tower publishes them as AWS service
28 * events via CloudTrail, so the detail carries the CloudTrail record shape;
29 * the interesting outcome lives under `serviceEventDetails`.
30 */
31export interface ControlTowerLifecycleEventDetail {
32 /** The lifecycle event name, e.g. `CreateManagedAccount`. */
33 eventName?: ControlTowerLifecycleEventName | (string & {});
34 /** Always `controltower.amazonaws.com` for lifecycle events. */
35 eventSource?: string;
36 /** The Control Tower home region that recorded the event. */
37 awsRegion?: string;
38 /** When the lifecycle action completed. */
39 eventTime?: string;
40 /**
41 * The lifecycle outcome, keyed by `{eventName}Status` (e.g.
42 * `createManagedAccountStatus.state` is `SUCCEEDED` or `FAILED`).
43 */
44 serviceEventDetails?: Record<string, unknown>;
45 /** Additional CloudTrail record fields (the schema grows over time). */
46 [key: string]: unknown;
47}
48
49/** An AWS Control Tower lifecycle EventBridge event delivered to the handler. */
50export type ControlTowerLifecycleEvent =
51 EventRecord<ControlTowerLifecycleEventDetail>;
52
53export interface LifecycleEventSourceProps extends EventRouteProps {
54 /**
55 * Logical id for the backing EventBridge rule.
56 * @default "ControlTowerLifecycleEvents"
57 */
58 id?: string;
59 /**
60 * Which lifecycle event names to subscribe to.
61 * @default all lifecycle events
62 */
63 events?: readonly ControlTowerLifecycleEventName[];
64}
65
66/**
67 * Event source connecting AWS Control Tower lifecycle events to the hosting
68 * compute. Control Tower publishes every completed lifecycle action —
69 * account provisioning (`CreateManagedAccount`), guardrail changes
70 * (`EnableGuardrail`), landing zone setup/upgrade (`SetupLandingZone`,
71 * `UpdateLandingZone`), and OU registration — to the management account's
72 * default EventBridge bus in the home region (source `aws.controltower`);
73 * this subscribes the host Function to those events so it can trigger
74 * account-customization or notification automation.
75 *
76 * Control Tower publishes to EventBridge automatically — no additional
77 * resource is created besides the EventBridge rule targeting the host.
78 * Provide the host-specific implementation layer (e.g.
79 * `AWS.Lambda.EventSource`) on the Function effect.
80 *
81 * ### Consuming Lifecycle Events
82 * **Example:** Customize Newly Provisioned Accounts
83 * ```typescript
84 * import * as AWS from "alchemy/AWS";
85 *
86 * export default OnboardFunction.make(
87 * { main: import.meta.url },
88 * Effect.gen(function* () {
89 * yield* AWS.ControlTower.consumeLifecycleEvents(
90 * { events: ["CreateManagedAccount"] },
91 * (events) =>
92 * Stream.runForEach(events, (event) =>
93 * Effect.log(
94 * `account provisioned: ${JSON.stringify(
95 * event.detail.serviceEventDetails,
96 * )}`,
97 * ),
98 * ),
99 * );
100 * return {};
101 * }).pipe(Effect.provide(AWS.Lambda.EventSource)),
102 * );
103 * ```
104 */
105export const consumeLifecycleEvents = <StreamReq = never, Req = never>(
106 props: LifecycleEventSourceProps,
107 process: (
108 events: Stream.Stream<ControlTowerLifecycleEvent, never, StreamReq>,
109 ) => Effect.Effect<void, never, Req>,
110) =>
111 consumeBusEvents(
112 props.id ?? "ControlTowerLifecycleEvents",
113 {
114 source: ["aws.controltower"],
115 "detail-type": ["AWS Service Event via CloudTrail"],
116 ...(props.events === undefined
117 ? {}
118 : { detail: { eventName: [...props.events] } }),
119 },
120 { description: props.description, state: props.state },
121 process,
122 );