Skip to content

Backend CDK Stack

The hello_world.hello_world_stack module defines the backend stack: API Gateway, the Lambda function, DynamoDB idempotency table, SSM parameter, and AppConfig feature flag.

API reference

hello_world.hello_world_stack

HelloWorldStack(scope, construct_id, **kwargs)

Bases: Stack

Thin wrapper stack composing the :class:HelloWorldApp construct.

Per the CDK best practice "model with constructs, deploy with stacks", the domain logic lives in the HelloWorldApp construct; this stack only applies stack-wide compliance Aspects, wires CfnOutputs, and attaches the stack-level and singleton-scoped cdk-nag suppressions that cannot be expressed on individual resources.

Source code in hello_world/hello_world_stack.py
def __init__(self, scope: Construct, construct_id: str, **kwargs: Any) -> None:
    super().__init__(scope, construct_id, **kwargs)

    apply_compliance_aspects(self)

    self.app = HelloWorldApp(self, "App")

    # Expose API URL for consumption by the frontend stack
    self.api_url = self.app.api_url

    CfnOutput(
        self,
        "HelloWorldApiOutput",
        description="API Gateway endpoint URL for Prod stage",
        value=f"{self.app.api.url}hello",
    )
    CfnOutput(
        self,
        "HelloWorldFunctionOutput",
        description="Hello World Lambda Function ARN",
        value=self.app.function.function_arn,
    )
    CfnOutput(
        self,
        "HelloWorldFunctionIamRoleOutput",
        description="IAM Role created for Hello World function",
        value=cast(iam.IRole, self.app.function.role).role_arn,
    )
    CfnOutput(
        self,
        "IdempotencyTableName",
        description="DynamoDB table used for Lambda idempotency",
        value=self.app.idempotency_table.table_name,
    )
    CfnOutput(
        self,
        "GreetingParameterName",
        description="SSM parameter name for the greeting message",
        value=self.app.greeting_param.parameter_name,
    )
    CfnOutput(
        self,
        "AppConfigAppName",
        description="AppConfig application name for feature flags",
        value=self.app.app_config_app.name,
    )
    CfnOutput(
        self,
        "CloudWatchDashboardUrl",
        description="CloudWatch dashboard URL for this stack",
        value=f"https://{self.region}.console.aws.amazon.com/cloudwatch/home#dashboards:name={self.stack_name}",
    )

    # ── Singleton-scoped cdk-nag suppressions ───────────────────────────────
    # CDK-managed singleton Lambdas (currently just the AwsCustomResource
    # provider) are created at the stack level as siblings of the construct
    # that requested them, not as children. ``suppress_cdk_singletons`` looks
    # them up via ``try_find_child`` so the suppressions keep working
    # regardless of whether the stack is at the App root or nested inside
    # a cdk.Stage. (The LogRetention singleton was eliminated when log
    # groups were made explicit via ``log_group=`` everywhere.)
    suppress_cdk_singletons(self, _CDK_SINGLETON_IDS)

    # ── Async failure destination for the AwsCustomResource provider ────────
    # CFN invokes the provider Lambda asynchronously; without an on_failure
    # destination, a crash that exhausts Lambda's two automatic retries is
    # silently dropped — only the CFN rollback error remains. Capturing the
    # failed event envelope to SQS preserves the AWS API response and full
    # request payload for post-mortem.
    self.cr_provider_dlq = attach_async_failure_destination(
        self,
        "AWS679f53fac002430cb0da5b7982bd2287",
        encryption_key=self.app.encryption_key,
        queue_id="AwsCustomResourceProviderDlq",
    )

    # ── Stack-level cdk-nag suppressions (genuinely stack-wide) ─────────────
    NagSuppressions.add_stack_suppressions(
        self,
        [
            # ── AWS Solutions ────────────────────────────────────────────────
            {"id": "AwsSolutions-APIG2", "reason": "Request validation not needed for sample app"},
            {
                "id": "AwsSolutions-APIG3",
                "reason": "WAF not attached to API Gateway — applied at CloudFront instead",
            },
            {"id": "AwsSolutions-APIG4", "reason": "Authorization not needed for sample app"},
            {"id": "AwsSolutions-COG4", "reason": "Cognito authorizer not needed for sample app"},
            # ── Serverless ───────────────────────────────────────────────────
            {
                "id": "Serverless-APIGWDefaultThrottling",
                "reason": "Custom throttling not configured for sample app",
            },
            {
                "id": "CdkNagValidationFailure",
                "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination — structured JSON logging is configured via logging_format=JSON on the Lambda",
            },
            # ── NIST 800-53 R5 ──────────────────────────────────────────────
            {
                "id": "NIST.800.53.R5-APIGWAssociatedWithWAF",
                "reason": "WAF not attached to API Gateway — applied at CloudFront instead",
            },
            {
                "id": "NIST.800.53.R5-APIGWSSLEnabled",
                "reason": "Client-side SSL certificates not required for sample app",
            },
            {
                "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted",
                "reason": (
                    "API Gateway cache cluster intentionally disabled for cost reasons — the smallest "
                    "0.5 GB cluster is ~$14/month for a sample app. Caching GET /hello would also serve "
                    "stale values across SSM parameter and AppConfig feature-flag changes."
                ),
            },
            {
                "id": "NIST.800.53.R5-DynamoDBInBackupPlan",
                "reason": "AWS Backup plan not configured for sample app — PITR is enabled for point-in-time recovery",
            },
            # ── HIPAA Security ───────────────────────────────────────────────
            {
                "id": "HIPAA.Security-APIGWSSLEnabled",
                "reason": "Client-side SSL certificates not required for sample app",
            },
            {
                "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted",
                "reason": (
                    "API Gateway cache cluster intentionally disabled for cost reasons — "
                    "see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above."
                ),
            },
            {
                "id": "HIPAA.Security-DynamoDBInBackupPlan",
                "reason": "AWS Backup plan not configured for sample app — PITR is enabled for point-in-time recovery",
            },
            # ── PCI DSS 3.2.1 ────────────────────────────────────────────────
            {
                "id": "PCI.DSS.321-APIGWAssociatedWithWAF",
                "reason": "WAF not attached to API Gateway — applied at CloudFront instead",
            },
            {
                "id": "PCI.DSS.321-APIGWSSLEnabled",
                "reason": "Client-side SSL certificates not required for sample app",
            },
            {
                "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted",
                "reason": (
                    "API Gateway cache cluster intentionally disabled for cost reasons — "
                    "see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above."
                ),
            },
        ],
    )