ylev
ylev

Reputation: 2569

How to deploy and attach a layer to aws lambda function using aws CDK and Python

How to deploy and attach a layer to aws lambda function using aws CDK ?

I need a simple cdk code that deploys and attaches a layer to aws lambda function.

Upvotes: 6

Views: 7322

Answers (5)

ylev
ylev

Reputation: 2569

The following aws CDK Python code deploys a layer and attaches it to an aws lambda function.

Project Directory Structure

--+
  +-app.py
  +-cdk_layers_deploy.py
  +--/functions+
               +-testLambda.py
  +--/layers+
            +-custom_func.py

app.py file

#!/usr/bin/env python3

import sys

from aws_cdk import (core)
from cdk_layers_deploy import CdkLayersStack

app = core.App()
CdkLayersStack(app, "cdk-layers")

app.synth()

cdk_layers_deploy file

from aws_cdk import (
    aws_lambda as _lambda,
    core,
    aws_iam)
from aws_cdk.aws_iam import PolicyStatement
from aws_cdk.aws_lambda import LayerVersion, AssetCode


class CdkLayersStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # 1) deploy lambda functions
        testLambda : _lambda.Function = CdkLayersStack.cdkResourcesCreate(self)

        # 2) attach policy to function
        projectPolicy = CdkLayersStack.createPolicy(self, testLambda)

    # -----------------------------------------------------------------------------------
    @staticmethod
    def createPolicy(this, testLambda:_lambda.Function) -> None:
        projectPolicy:PolicyStatement = PolicyStatement(
            effect=aws_iam.Effect.ALLOW,
            # resources=["*"],
            resources=[testLambda.function_arn],
            actions=[ "dynamodb:Query",
                      "dynamodb:Scan",
                      "dynamodb:GetItem",
                      "dynamodb:PutItem",
                      "dynamodb:UpdateItem",
                      "states:StartExecution",
                      "states:SendTaskSuccess",
                      "states:SendTaskFailure",
                      "cognito-idp:ListUsers",
                      "ses:SendEmail"
                    ]
        )
        return projectPolicy;
    # -----------------------------------------------------------------------------------
    @staticmethod
    def cdkResourcesCreate(self) -> None:
        lambdaFunction:_lambda.Function = _lambda.Function(self, 'testLambda',
                                                           function_name='testLambda',
                                                           handler='testLambda.lambda_handler',
                                                           runtime=_lambda.Runtime.PYTHON_3_7,
                                                           code=_lambda.Code.asset('functions'),
                                                           )
        ac = AssetCode("layers")
        layer  = LayerVersion(self, "l1", code=ac, description="test-layer", layer_version_name='Test-layer-version-name')
        lambdaFunction.add_layers(layer)

        return lambdaFunction

    # -----------------------------------------------------------------------------------

testLambda.py

# -------------------------------------------------
# testLambda
# -------------------------------------------------

import custom_func as cf  # this line is errored in pyCharm -> will be fixed on aws when import the layer

def lambda_handler(event, context):
    print(f"EVENT:{event}")
    ret = cf.cust_fun()
    return {
        'statusCode': 200,
        'body': ret
    }

custom_func.py - the layer function

import datetime

def cust_fun():                           
    print("Hello from the deep layers!!")
    date_time = datetime.datetime.now().isoformat()
    print("dateTime:[%s]\n" % (date_time))

    return 1

Upvotes: 6

user2197172
user2197172

Reputation: 77

I set this up to use my site-packages in my virtual environment so it not only includes my dependencies, but also their dependencies. In order to do this when creating your environment setup your venv like this:

mkdir .venv
python3 -m venv .venv/python

Then in your stack reference your virtual environment as AssetCode(".venv"). Beause this includes the python version in the path as part of the site-packages you have to limit compatibility based on your version. The easiest way to support all python versions would be to use a different structure as defined https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html

Example:

from aws_cdk import core
from aws_cdk.aws_lambda import AssetCode, LayerVersion, Runtime


class lambda_layer_stack(core.Stack):
    def __init__(self, scope: core.Construct, construct_id: str, version: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        ac = AssetCode(path=".venv")
        au_layer = LayerVersion(
            self,
            id=construct_id,
            code=ac,
            layer_version_name=construct_id,
            description=version,
            compatible_runtimes=[Runtime.PYTHON_3_8],
        )

Upvotes: 0

Goodyear
Goodyear

Reputation: 61

You can make it by creating and deploying your layer first, then import it from aws and pass it as the layer argument for the lambda in your stack definition.

To do so, we found that the easiest solution is to create a /layer folder in your root cdk project and create a bash file to deploy the layer (You need to cd into the /layer folder to run it).

LAYER_NAME="<layer_name>"

echo <your_package>==<package.version.0> >> requirements.txt  # See PyPi for the exact version

docker run -v "$PWD":/var/task "lambci/lambda:build-python3.6" /bin/sh -c "pip install -r requirements.txt -t python/lib/python3.6/site-packages/; exit"

zip -r $LAYER_NAME.zip python > /dev/null

aws lambda publish-layer-version \
    --layer-name $LAYER_NAME \
    --zip-file fileb://$LAYER_NAME.zip \
    --compatible-runtimes "python3.6" \
    --region <your_region>

rm requirements.txt
rm -rf python
rm $LAYER_NAME.zip

You need then to search the ARN of the layer in your AWS console (Lambda>Layers) and define your layer in your _stack.py by:

layer = aws_lambda.LayerVersion.from_layer_version_arn(
    self,
    '<layer_name>',
    <layer_ARN>  # arn:aws:lambda:<your_region:<your_account_id>:layer:<layer_name>:<layer_version>
)

Then you can pass it into your lambda:

lambda = aws_lambda.Function(
    scope=self,
    id='<lambda_id>',
    handler='function.handler',
    runtime=aws_lambda.Runtime.PYTHON_3_6,
    code=aws_lambda.Code.asset(path='./lambda'),
    environment={
           
    },
    layers=[
        layer
    ]
)

Upvotes: 4

Madeesha Fernando
Madeesha Fernando

Reputation: 57

You can use it as follows,

mysql_lib = lb.LayerVersion(self, 'mysql',
    code = lb.AssetCode('lambda/layers/'),
    compatible_runtimes = [lb.Runtime.PYTHON_3_6],
)

my_lambda = lb.Function(
            self, 'core-lambda-function',
            runtime=lb.Runtime.PYTHON_3_6,
            code=lb.InlineCode(handler_code),
            function_name="lambda_function_name",
            handler="index.handler",
            layers = [mysql_lib],
            timeout=core.Duration.seconds(300),
   )

Upvotes: 0

That will not work. You need to put custom_func.py to layers/python/lib/python3.7/site-packages/custom_func.py instead to make it work (https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html#configuration-layers-path).

Upvotes: -1

Related Questions