seahaven-org-baseline/lib/member-baseline-stack.ts
Adam Moussa 0a7c1bc450
seahaven-dev account baseline with org-managed detection (Phase 4) (#49)
* Add seahaven-dev member baseline with org-managed detection

Account 710827005802 (internal dev/staging) is the first account born
after delegation: GuardDuty/Security Hub enroll it via the org admin,
so DetectiveControls gains a localDetectiveServices flag (default true
— zero diff on the three deployed consumers, verified) and the dev
instance sets orgManagedDetection to skip the colliding local
detector/hub/analyzer. Default VPC kept and flow-logged (dev runs real
workloads). Enrollment verified Enabled in both services before this
commit.

* Fix Phase-4 review findings: standards + analyzer stay CFN-owned

SH-DEV-001: org AutoEnableStandards DEFAULT gave dev legacy CIS v1.2.0
and nothing owned CIS v3.0 — org config set to NONE, standards are now
unconditional in DetectiveControls (attach fine to an org-enabled hub),
legacy ruleset disabled in dev. SH-DEVBASE-002: the ORGANIZATION
analyzer treats the whole org as trusted so it cannot flag intra-org
exposure — account analyzer restored unconditionally (coexistence
verified live). Enrollment comments corrected: manual create-members,
the automatic sweep is still unexercised. Zero diff re-verified on all
three deployed baseline stacks.
2026-07-14 16:41:36 -04:00

75 lines
3.3 KiB
TypeScript

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { DetectiveControls } from "./detective-controls";
import { FlowLogs } from "./flow-logs";
import { GovernanceToggles } from "./governance-toggles";
export interface MemberBaselineStackProps extends cdk.StackProps {
/**
* Physical-name prefix for account-scoped resources (e.g. "seahaven-extdev").
* Also names the monthly budget (`<namePrefix>-monthly-cost`). Stable per
* account — changing it on a deployed stack replaces live resources.
*/
readonly namePrefix: string;
/** Monthly cost budget ceiling in USD. */
readonly monthlyBudgetUsd: number;
/** Sea Haven ops address that receives budget alerts (not the account's tenants). */
readonly budgetAlertEmail: string;
/** Value for the Owner tag (informational; decoupled from alert routing). */
readonly ownerEmail: string;
/** VPC ids to attach flow logs to (from cdk context; may be empty). */
readonly flowLogVpcIds: string[];
/** Value for the ManagedBy tag on every resource in the stack. */
readonly managedByTag: string;
/**
* TRUE for accounts created after org delegation (2026-07-14): GuardDuty /
* Security Hub / analyzer are org-managed (auto-enrolled), so the stack must
* not create local duplicates. Default FALSE (pre-delegation accounts).
*/
readonly orgManagedDetection?: boolean;
}
/**
* Account-local security baseline for org MEMBER accounts (first tenant:
* seahaven-external-dev). Absorbed from the retired seahaven-external-dev-baseline
* repo — a stripped fork of the management-account baseline, now sharing its
* constructs (prefix-parameterized) instead of forking them.
*
* Deliberately excludes everything that is org-level or prod-specific:
* - No local CloudTrail — the management-account org trail (seahaven-org-trail)
* already captures every member account's events centrally.
* - No CIS Section-4 metric-filter alarms — the Security Hub CIS standard
* evaluates those controls against Config without a local trail log group.
* - No WAF, SES monitoring, Bedrock logging, DynamoDB CMK, or AWS Backup —
* all prod-only concerns.
*
* Contains: AWS Config, Security Hub standards (FSBP + CIS v3.0), IAM Access
* Analyzer, Inspector2 (post-deploy CLI, see README), VPC flow logs, and a
* monthly cost Budget — plus the GuardDuty detector + Security Hub hub only
* when the account predates org delegation (orgManagedDetection false); newer
* accounts get those from the delegated admin.
*/
export class MemberBaselineStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: MemberBaselineStackProps) {
super(scope, id, props);
new DetectiveControls(this, "DetectiveControls", {
namePrefix: props.namePrefix,
localDetectiveServices: !(props.orgManagedDetection ?? false),
});
new FlowLogs(this, "FlowLogs", {
namePrefix: props.namePrefix,
vpcIds: props.flowLogVpcIds,
});
new GovernanceToggles(this, "GovernanceToggles", {
budgetName: `${props.namePrefix}-monthly-cost`,
monthlyLimitUsd: props.monthlyBudgetUsd,
alertEmail: props.budgetAlertEmail,
});
cdk.Tags.of(this).add("Owner", props.ownerEmail);
cdk.Tags.of(this).add("ManagedBy", props.managedByTag);
}
}