Reputation: 2569
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
Reputation: 2569
The following aws CDK Python code deploys a layer and attaches it to an aws lambda function.
--+
+-app.py
+-cdk_layers_deploy.py
+--/functions+
+-testLambda.py
+--/layers+
+-custom_func.py
#!/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()
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
# -------------------------------------------------
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
}
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
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
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
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
Reputation: 1
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