Adopt external-dev OU and its 3 SCPs via resource import (#45)

cdk import by Id (non-mutating), content byte-exact from
describe-policy, targetIds = exact live attachments. Post-import drift
detection: IN_SYNC, 0 drifted. All four Retain — the full org guardrail
set is now drift-checked IaC.
This commit is contained in:
Adam Moussa 2026-07-14 14:10:10 -04:00 • committed by GitHub
parent 22b04c4e75
commit 10e66c92c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 147 additions and 0 deletions

View file

@ -1,7 +1,15 @@
import * as fs from "fs";
import * as path from "path";
import * as cdk from "aws-cdk-lib";
import * as organizations from "aws-cdk-lib/aws-organizations";
import { Construct } from "constructs";
/** Byte-exact live SCP content (lib/scp/*.json) — see import block below. */
const scpContent = (name: string): Record<string, unknown> =>
JSON.parse(
fs.readFileSync(path.join(__dirname, "scp", `${name}.json`), "utf8")
);
/**
* AWS Organizations structure for org o-9kufuzz6b4: OU skeleton + generalized
* service-control policies (multi-account segregation plan Phase 2,
@ -226,6 +234,51 @@ export class OrgGovernanceStack extends cdk.Stack {
});
retain(denyRootUser);
// ── Adopted (cdk-imported) external-dev OU + its 3 SCPs ─────────────────
// Imported 2026-07-14 by Id (ou-nbuj-q34yz3ql, p-i59g24mz, p-8yty5mnd,
// p-ivmwtipw). Properties are byte-exact to the live resources at import
// time (content JSON in lib/scp/, descriptions/targets verified via
// describe-policy). RULES: content stays a JSON object (string form kills
// drift detection); targetIds is the exact live attachment set — any edit
// here detaches/attaches a LIVE guardrail on the isolated contractor
// account and requires the mandatory review gates; never rename these
// logical ids (rename = delete+create; Retain would orphan, not detach,
// but the stack would lose the resource).
const externalDevOu = new organizations.CfnOrganizationalUnit(this, "ExternalDevOu", {
name: "external-dev",
parentId: ROOT_ID,
});
retain(externalDevOu);
const externalDevRegionLock = new organizations.CfnPolicy(this, "ExternalDevRegionLock", {
name: "external-dev-region-lock",
type: "SERVICE_CONTROL_POLICY",
description: "external-dev OU guardrail: external-dev-region-lock",
targetIds: ["ou-nbuj-q34yz3ql"],
content: scpContent("external-dev-region-lock"),
});
retain(externalDevRegionLock);
const externalDevIamGuardrails = new organizations.CfnPolicy(this, "ExternalDevIamGuardrails", {
name: "external-dev-iam-guardrails",
type: "SERVICE_CONTROL_POLICY",
description: "external-dev OU guardrail: external-dev-iam-guardrails",
targetIds: ["ou-nbuj-q34yz3ql"],
content: scpContent("external-dev-iam-guardrails"),
});
retain(externalDevIamGuardrails);
const externalDevProtectSecurity = new organizations.CfnPolicy(this, "ExternalDevProtectSecurity", {
name: "external-dev-protect-security",
type: "SERVICE_CONTROL_POLICY",
description: "external-dev OU guardrail: external-dev-protect-security",
targetIds: ["ou-nbuj-q34yz3ql"],
content: scpContent("external-dev-protect-security"),
});
retain(externalDevProtectSecurity);
new cdk.CfnOutput(this, "ExternalDevOuId", { value: externalDevOu.attrId });
new cdk.CfnOutput(this, "WorkloadsOuId", { value: workloadsOu.attrId });
new cdk.CfnOutput(this, "ProdOuId", { value: prodOu.attrId });
new cdk.CfnOutput(this, "NonprodOuId", { value: nonprodOu.attrId });

View file

@ -0,0 +1,50 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RequireBoundaryOnRoleAndUserCreate",
"Effect": "Deny",
"Action": ["iam:CreateRole", "iam:CreateUser"],
"Resource": "*",
"Condition": {
"StringNotEquals": { "iam:PermissionsBoundary": "arn:aws:iam::396287094661:policy/external-dev-execution-boundary" },
"ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole", "arn:aws:iam::396287094661:role/cdk-hnb659fds-*", "arn:aws:iam::396287094661:role/githubdeploy-seahaven-external-dev-baseline"] }
}
},
{
"Sid": "ProtectBoundaryPolicyFromEdits",
"Effect": "Deny",
"Action": ["iam:CreatePolicyVersion", "iam:SetDefaultPolicyVersion", "iam:DeletePolicy", "iam:DeletePolicyVersion"],
"Resource": "arn:aws:iam::396287094661:policy/external-dev-execution-boundary"
},
{
"Sid": "DenyAlteringPermissionsBoundaries",
"Effect": "Deny",
"Action": ["iam:DeleteRolePermissionsBoundary", "iam:DeleteUserPermissionsBoundary", "iam:PutRolePermissionsBoundary", "iam:PutUserPermissionsBoundary"],
"Resource": "*",
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole", "arn:aws:iam::396287094661:role/cdk-hnb659fds-*"] } }
},
{
"Sid": "DenyIamUserAndAccessKeyCreation",
"Effect": "Deny",
"Action": ["iam:CreateUser", "iam:CreateAccessKey", "iam:CreateLoginProfile"],
"Resource": "*",
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole"] } }
},
{
"Sid": "ProtectPrivilegedRoles",
"Effect": "Deny",
"Action": ["iam:UpdateAssumeRolePolicy", "iam:AttachRolePolicy", "iam:DetachRolePolicy", "iam:PutRolePolicy", "iam:DeleteRolePolicy", "iam:DeleteRole", "iam:UpdateRole", "iam:TagRole", "iam:UntagRole"],
"Resource": [
"arn:aws:iam::396287094661:role/OrganizationAccountAccessRole",
"arn:aws:iam::396287094661:role/cdk-hnb659fds-*",
"arn:aws:iam::396287094661:role/githubdeploy-*",
"arn:aws:iam::396287094661:role/seahaven-extdev-config-recorder-role",
"arn:aws:iam::396287094661:role/seahaven-extdev-config-custom-resource-role",
"arn:aws:iam::396287094661:role/aws-service-role/*"
],
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole", "arn:aws:iam::396287094661:role/cdk-hnb659fds-*"] } }
}
]
}

View file

@ -0,0 +1,26 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyDisablingSecurityServices",
"Effect": "Deny",
"Action": [
"cloudtrail:StopLogging", "cloudtrail:DeleteTrail", "cloudtrail:UpdateTrail",
"guardduty:DeleteDetector", "guardduty:UpdateDetector", "guardduty:DisassociateFromMasterAccount",
"config:StopConfigurationRecorder", "config:DeleteConfigurationRecorder", "config:DeleteDeliveryChannel",
"securityhub:DisableSecurityHub", "securityhub:BatchDisableStandards",
"accessanalyzer:DeleteAnalyzer", "inspector2:Disable"
],
"Resource": "*",
"Condition": {
"ArnNotLike": { "aws:PrincipalArn": [
"arn:aws:iam::396287094661:role/OrganizationAccountAccessRole",
"arn:aws:iam::396287094661:role/cdk-hnb659fds-*",
"arn:aws:iam::396287094661:role/seahaven-extdev-config-custom-resource-role"
]}
}
},
{ "Sid": "DenyLeavingOrganization", "Effect": "Deny", "Action": ["organizations:LeaveOrganization"], "Resource": "*" }
]
}

View file

@ -0,0 +1,18 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRegionsOutsideUsEast1",
"Effect": "Deny",
"NotAction": [
"iam:*", "organizations:*", "account:*", "sts:*", "route53:*", "route53domains:*",
"cloudfront:*", "waf:*", "shield:*", "globalaccelerator:*", "budgets:*", "ce:*",
"cur:*", "health:*", "support:*", "supportplans:*", "trustedadvisor:*", "artifact:*",
"aws-portal:*"
],
"Resource": "*",
"Condition": { "StringNotEquals": { "aws:RequestedRegion": "us-east-1" } }
}
]
}