import * as cdk from "aws-cdk-lib"; import * as iam from "aws-cdk-lib/aws-iam"; import { IConstruct } from "constructs"; /** IAM managed-policy quota, whitespace excluded. */ const MAX_POLICY_CHARS = 6144; /** * Fails synth when an HCP stack's managed policy document reaches the IAM * size quota, or when a role in that stack carries an inline policy. * Register it on seahaven-hcptf only. That stack owns payments-dashboard * in prod and dev, and seahaven-site in prod. * * `allowInlinePolicies` is only for the one-time `cdk import` template. * That template has to name the live inline policies so the following * deploy can delete them. The default template still rejects inline policies. */ export class HcptfPolicyAspect implements cdk.IAspect { constructor(private readonly allowInlinePolicies = false) {} public visit(node: IConstruct): void { if (node instanceof iam.CfnManagedPolicy) { const size = JSON.stringify(node.policyDocument).replace(/\s/g, "").length; if (size >= MAX_POLICY_CHARS) { cdk.Annotations.of(node).addError( `managed policy document is ${size} characters, whitespace excluded (limit ${MAX_POLICY_CHARS})`, ); } } if (node instanceof iam.CfnRole) { const policies = node.policies; if (Array.isArray(policies)) { for (const policy of policies) { if (cdk.Token.isUnresolved(policy) || !("policyDocument" in policy)) { continue; } const size = JSON.stringify(policy.policyDocument) .replace(/\s/g, "") .length; if (size >= MAX_POLICY_CHARS) { cdk.Annotations.of(node).addError( `inline policy document is ${size} characters, whitespace excluded (limit ${MAX_POLICY_CHARS})`, ); } } if (!this.allowInlinePolicies && policies.length > 0) { cdk.Annotations.of(node).addError( "inline policy is not allowed on this role", ); } } } if (node instanceof iam.CfnPolicy) { cdk.Annotations.of(node).addError("inline policy is not allowed in this stack"); } } }