[{"data":1,"prerenderedAt":49},["ShallowReactive",2],{"snippet-tools-lambda-function-construct":3},{"id":4,"title":5,"contributors":6,"description":8,"extension":9,"gitHub":10,"introBox":13,"languages":17,"meta":19,"resources":20,"services":31,"snippets":33,"stem":43,"tags":44,"type":47,"__hash__":48},"snippets\u002Fsnippets\u002Ftools-lambda-function-construct.json","Standardize AWS Lambda definitions with AWS CDK Construct",[7],"content\u002Fcontributors\u002Fpete-davis.json","Use a CDK Construct to standardize and simplify Lambda function definition","json",{"template":11},{"repoURL":12},"https:\u002F\u002Fgithub.com\u002Faws-samples\u002Fserverless-snippets\u002Ftree\u002Fmain\u002Ftools-lambda-function-construct",{"headline":14,"text":15},"How it works",[16],"The CDK Construct provides a standardized blueprint for creating Lambda functions (similar to the Global section of an AWS SAM template). This enables standardization of function definitions and centralized updates, e.g., runtime updates. All parameters can be overridden as necessary for individual functions.",[18],"Python",{},{"bullets":21},[22,25,28],{"text":23,"link":24},"AWS CDK Constructs","https:\u002F\u002Fdocs.aws.amazon.com\u002Fcdk\u002Fv2\u002Fguide\u002Fconstructs.html",{"text":26,"link":27},"Working with the AWS CDK in Python","https:\u002F\u002Fdocs.aws.amazon.com\u002Fcdk\u002Fv2\u002Fguide\u002Fwork-with-cdk-python.html",{"text":29,"link":30},"Python CDK API Reference (constructs module)","https:\u002F\u002Fdocs.aws.amazon.com\u002Fcdk\u002Fapi\u002Fv2\u002Fpython\u002Fconstructs.html",[32],"lambda-event-functions",[34,39],{"title":35,"description":36,"language":37,"code":38},"Python Lambda Construct Example","A reusable CDK Construct that standardizes Lambda function creation with built-in CloudWatch Log Groups, Powertools for AWS Lambda, X-Ray tracing, and automatic dependency bundling. Supports per-function overrides for runtime, architecture, memory, timeout, etc. The construct can be easily tailored for your own needs. See the repo for a deployable example.","py","from pathlib import Path\nfrom aws_cdk import (\n    aws_lambda as lambda_,\n    aws_logs as logs,\n    BundlingOptions,\n    Duration,\n    RemovalPolicy,\n    Stack,\n)\nfrom constructs import Construct\n\n\nclass StandardLambda(Construct):\n    \"\"\"\n    A reusable CDK Construct that provides a standardized blueprint for creating\n    AWS Lambda functions — similar to the Global section of an AWS SAM template.\n\n    This construct automatically includes:\n    - A dedicated CloudWatch Log Group with configurable retention and removal policies,\n      along with IAM permissions for the Lambda function to write logs.\n    - The AWS Lambda Powertools for Python V3 layer (managed by AWS), pre-configured\n      with POWERTOOLS_SERVICE_NAME and LOG_LEVEL environment variables.\n    - X-Ray active tracing enabled by default.\n    - Container-based dependency bundling when a requirements.txt file is detected.\n\n    All default settings can be overridden per function. Environment variables and\n    layers are merged (not replaced) so that Powertools config is always preserved\n    unless explicitly overridden.\n\n    The function and its execution role are exposed as public attributes (self.function\n    and self.role) so consumers can grant additional IAM permissions after creation.\n    \"\"\"\n\n    def __init__(\n        self,\n        scope: Construct,\n        construct_id: str,\n        handler: str,\n        code_path: str,\n        runtime: lambda_.Runtime = lambda_.Runtime.PYTHON_3_14,\n        log_retention: logs.RetentionDays = logs.RetentionDays.ONE_WEEK,\n        log_removal_policy: RemovalPolicy = RemovalPolicy.DESTROY,\n        **kwargs,\n    ) -&#62; None:\n        super().__init__(scope, construct_id)\n\n        # Get the current stack reference — used to resolve region-specific ARNs\n        # (e.g. the Powertools layer ARN which varies by region)\n        stack = Stack.of(self)\n\n        # Automatically detect whether the Lambda code directory contains a\n        # requirements.txt with real dependencies. If it does, Container bundling\n        # is used to install them into the deployment package. If not, the code\n        # directory is packaged as-is.\n        code = self._build_code(code_path, runtime)\n\n        # Create a dedicated CloudWatch Log Group for this Lambda function.\n        # Let CDK auto-generate the name based on the Lambda function name — this avoids naming\n        # collisions and keeps log groups tidy.\n        # The retention and removal policy are configurable via constructor\n        # parameters, defaulting to ONE_WEEK retention and DESTROY on stack deletion.\n        log_group = logs.LogGroup(\n            self,\n            \"LogGroup\",\n            retention=log_retention,\n            removal_policy=log_removal_policy,\n        )\n\n        # Resolve the architecture — default to ARM_64 for better price\u002Fperformance,\n        # but allow the consumer to override it via kwargs.\n        architecture = kwargs.pop(\"architecture\", lambda_.Architecture.ARM_64)\n\n        # Determine the correct Powertools layer ARN based on both architecture\n        # and runtime. AWS publishes separate Powertools layers per combination:\n        #   - AWSLambdaPowertoolsPythonV3-python313-arm64\n        #   - AWSLambdaPowertoolsPythonV3-python313-x86_64\n        #   - AWSLambdaPowertoolsPythonV3-python314-arm64\n        #   - AWSLambdaPowertoolsPythonV3-python314-x86_64\n        # See: https:\u002F\u002Fdocs.powertools.aws.dev\u002Flambda\u002Fpython\u002Flatest\u002F\n        arch_suffix = \"arm64\" if architecture.name == \"arm64\" else \"x86_64\"\n\n        # Extract the Python version string from the runtime name (e.g. \"python3.14\"\n        # becomes \"python314\") to match the Powertools layer naming convention.\n        # runtime.name returns the format \"python3.14\", so we strip the dot.\n        runtime_suffix = runtime.name.replace(\".\", \"\")\n\n        powertools_layer = lambda_.LayerVersion.from_layer_version_arn(\n            self,\n            \"PowertoolsLayer\",\n            f\"arn:aws:lambda:{stack.region}:017000801446:layer:AWSLambdaPowertoolsPythonV3-{runtime_suffix}-{arch_suffix}:27\",\n        )\n\n        # Define the default configuration that every Lambda function created\n        # by this construct will inherit. These values represent sensible\n        # defaults for a serverless application:\n        # - ARM_64 architecture (or consumer-specified) for better price\u002Fperformance\n        # - 30 second timeout\n        # - 256 MB memory\n        # - Powertools layer (architecture-matched) included\n        # - X-Ray active tracing enabled\n        # - Powertools environment variables pre-configured\n        # - The dedicated log group attached\n        defaults = {\n            \"runtime\": runtime,\n            \"architecture\": architecture,\n            \"timeout\": Duration.seconds(30),\n            \"memory_size\": 256,\n            \"layers\": [powertools_layer],\n            \"environment\": {\n                \"POWERTOOLS_SERVICE_NAME\": construct_id,\n                \"LOG_LEVEL\": \"DEBUG\",\n            },\n            \"tracing\": lambda_.Tracing.ACTIVE,\n            \"log_group\": log_group,\n        }\n\n        # Handle layers and environment variables separately from other kwargs.\n        # We pop them out before the general merge so we can combine them\n        # (append\u002Fmerge) rather than replace the defaults entirely.\n        # This ensures the Powertools layer and its env vars are always present\n        # even when the consumer provides additional layers or env vars.\n        user_layers = kwargs.pop(\"layers\", None)\n        user_environment = kwargs.pop(\"environment\", None)\n\n        # Merge all remaining kwargs with defaults. User-provided values\n        # take precedence over defaults (e.g. a custom timeout or memory_size).\n        merged_config = {**defaults, **kwargs}\n\n\n        # Merge layers additively — the consumer's layers are appended\n        # after the Powertools layer so all layers are included.\n        if user_layers is not None:\n            merged_config[\"layers\"] = defaults.get(\"layers\", []) + user_layers\n            \n        # Merge environment variables additively — the consumer's env vars\n        # are added alongside the Powertools defaults, not replacing them.\n        if user_environment is not None:\n            merged_config[\"environment\"] = {\n                **defaults.get(\"environment\", {}),\n                **user_environment,\n            }\n\n        # Create the Lambda function with the merged configuration.\n        self.function = lambda_.Function(\n            self, \"Function\", handler=handler, code=code, **merged_config\n        )\n\n        # Grant the Lambda function permission to write logs to its dedicated\n        # CloudWatch Log Group. \n        log_group.grant_write(self.function)\n\n        # Expose the function's IAM execution role as a public attribute.\n        # This allows consumers to attach additional permissions after\n        # creating the construct, e.g.:\n        #   my_lambda.role.add_managed_policy(...)\n        #   my_table.grant_read_write_data(my_lambda.function)\n        self.role = self.function.role\n\n    def _build_code(self, code_path: str, runtime: lambda_.Runtime) -&#62; lambda_.Code:\n        \"\"\"\n        Build the Lambda deployment package with automatic dependency detection.\n\n        Checks for a requirements.txt in the code directory. If one exists and\n        contains real dependencies (not just comments or blank lines), Container\n        bundling is used: a container with the matching Lambda runtime image\n        runs pip install into \u002Fasset-output, then copies the function code\n        alongside the installed packages. This produces a flat deployment zip\n        where Python can import everything directly.\n\n        If no requirements.txt is found (or it's empty), the code directory\n        is simply packaged as-is with no Docker overhead.\n        \"\"\"\n        code_dir = Path(code_path)\n        requirements_file = code_dir \u002F \"requirements.txt\"\n\n        if requirements_file.exists() and self._has_dependencies(requirements_file):\n            # Use Container bundling: spin up a container with the Lambda runtime\n            # image, install dependencies into \u002Fasset-output (which becomes the\n            # root of the deployment zip), then copy the function code into the\n            # same directory so everything is flat and importable.\n            return lambda_.Code.from_asset(\n                code_path,\n                bundling=BundlingOptions(\n                    image=runtime.bundling_image,\n                    command=[\n                        \"bash\",\n                        \"-c\",\n                        \" && \".join(\n                            [\n                                # Install dependencies \n                                \"pip install -r requirements.txt -t \u002Fasset-output\u002F\",\n                                # Copy the function source code alongside the\n                                # installed dependencies\n                                \"cp -r . \u002Fasset-output\",\n                            ]\n                        ),\n                    ],\n                ),\n            )\n\n        # No dependencies found — package the code directory directly\n        # without spinning up a Docker container\n        return lambda_.Code.from_asset(code_path)\n\n    def _has_dependencies(self, requirements_file: Path) -&#62; bool:\n        \"\"\"\n        Check if a requirements.txt file contains actual package dependencies.\n        Returns False if the file is empty or contains only comments and blank lines.\n        This avoids unnecessary Docker bundling for functions with no external deps.\n        \"\"\"\n        try:\n            with open(requirements_file, \"r\", encoding=\"utf-8\") as f:\n                for line in f:\n                    line = line.strip()\n                    # Skip empty lines and comment-only lines\n                    if line and not line.startswith(\"#\"):\n                        return True\n            return False\n        except Exception:\n            return False\n",{"title":40,"description":41,"language":37,"code":42},"Usage Example","Demonstrates three patterns for consuming the StandardLambda construct: a minimal function with all defaults, a function with overridden runtime and environment variables, and a function with external dependencies, additional layers, and custom IAM permissions.","from aws_cdk import (\n    aws_iam as iam,\n    aws_lambda as lambda_,\n    Duration,\n    Stack,\n)\nfrom constructs import Construct\nfrom cdk.constructs.standard_lambda_construct import StandardLambda\n\n\nclass StandardLambdaExample(Stack):\n    \"\"\"\n    Example CDK stack demonstrating three usage patterns of the StandardLambda\n    construct: a minimal function, a function with overridden defaults, and a\n    function with external dependencies and additional layers\u002Fpermissions.\n    \"\"\"\n\n    def __init__(self, scope: Construct, construct_id: str, **kwargs) -&#62; None:\n        super().__init__(scope, construct_id, **kwargs)\n\n        stack = Stack.of(self)\n\n        # Resolve the deployment region — used below to build region-specific\n        # layer ARNs (e.g. Lambda Insights)\n        region = stack.region\n\n        # ---------------------------------------------------------------\n        # Example 1: Minimal Lambda — no external dependencies\n        # ---------------------------------------------------------------\n        # Demonstrates the simplest usage of the StandardLambda construct.\n        # The \"lambda\u002Fsimple\" directory contains only the handler code and\n        # empty requirements.txt, so CDK packages the code as a plain asset\n        # without spinning up a Container.\n        #\n        # All defaults from the construct are inherited:\n        #   - Python 3.14 runtime on ARM_64\n        #   - 256 MB memory, 30s timeout\n        #   - Powertools layer + environment variables\n        #   - Dedicated CloudWatch Log Group with 1-week retention\n        #   - X-Ray active tracing\n        simple_lambda = StandardLambda(\n            self,\n            \"SimpleLambda\",\n            handler=\"index.handler\",\n            code_path=\"lambda\u002Fsimple\",\n        )\n\n        # ---------------------------------------------------------------\n        # Example 2: Overriding defaults — custom runtime and env vars\n        # ---------------------------------------------------------------\n        # Demonstrates how to override construct defaults on a per-function\n        # basis. Here we pin the runtime to Python 3.13 and add a custom\n        # environment variable.\n        override_lambda = StandardLambda(\n            self,\n            \"OverrideLambda\",\n            handler=\"index.handler\",\n            code_path=\"lambda\u002Fsimple\",\n            runtime=lambda_.Runtime.PYTHON_3_13,\n            environment={\n                \"CUSTOM_VAR\": \"custom_value\",\n                # POWERTOOLS_SERVICE_NAME and LOG_LEVEL are still included\n                # automatically via the construct's merge logic\n            },\n        )\n\n        # ---------------------------------------------------------------\n        # Example 3: Lambda with dependencies, extra layers, and permissions\n        # ---------------------------------------------------------------\n        # Demonstrates a more complex function that:\n        #   1. Has external pip dependencies (requirements.txt exists in\n        #      \"lambda\u002Fdependency\"), so Container bundling kicks in automatically.\n        #   2. Uses X86_64 architecture instead of the default ARM_64 — the\n        #      construct automatically selects the matching Powertools layer ARN.\n        #   3. Adds an additional Lambda layer (CloudWatch Lambda Insights)\n        #      on top of the default Powertools layer — layers are merged,\n        #      not replaced.\n        #   4. Overrides timeout (60s) and memory (512 MB) for a heavier\n        #      workload.\n        #   5. Attaches an additional IAM managed policy to the function's\n        #      execution role via the exposed self.role attribute.\n\n        # Look up the AWS-managed Lambda Insights extension layer for X86_64.\n        # This provides enhanced monitoring metrics in CloudWatch.\n        lambda_insights_layer = lambda_.LayerVersion.from_layer_version_arn(\n            self,\n            \"LambdaInsightsLayer\",\n            f\"arn:aws:lambda:{region}:580247275435:layer:LambdaInsightsExtension:31\",\n        )\n\n        dependency_lambda = StandardLambda(\n            self,\n            \"DependencyLambda\",\n            handler=\"index.handler\",\n            code_path=\"lambda\u002Fdependency\",\n            architecture=lambda_.Architecture.X86_64,\n            timeout=Duration.seconds(60),\n            layers=[lambda_insights_layer],\n            memory_size=512,\n        )\n\n        # Grant the Lambda Insights managed policy to the function's role.\n        # This is needed for the Insights layer to publish enhanced metrics.\n        # Uses the exposed self.role attribute from the construct.\n        dependency_lambda.role.add_managed_policy(\n            iam.ManagedPolicy.from_aws_managed_policy_name(\n                \"CloudWatchLambdaInsightsExecutionRolePolicy\"\n            )\n        )\n","snippets\u002Ftools-lambda-function-construct",[45,46],"CDK","Lambda","Tools","Tv8uV2YZ_ZAs_CZtqGw_fExO1ADfxSFqEHrwkvidHQA",1792578864479]