Lambda based Lambda Layer generator

This idea follows on from Lambda Layers created from a BASH script. Simply put, it doesn't seem like a cloud native way of working. Spinning up either a Linux VM (if you are stuck using windows)or ec2 instance just to build a Lambda layer is an overkill and probably difficult to build with regard to running and automating. I had the idea to use Lambda to build and import the layer, it could be triggered regularly from CloudWatch events and to allow the layer to be used, it writes the Layer ARN to Parameter Store.

import logging
import os
import shutil
import sys
import subprocess
import boto3
 
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
 
# Example test event:-
'''
{
  "package_name": "boto3",
  "layer_description": "Lambda Layer for S3 Additional Checksum.",
  "parameter_store_layer_name": "boto3_lambda_layer",
  "parameter_store_description":"ARN for boto3 lambda layer"
}
'''
 
 
def get_latest_version(package):
    # The -m flag makes sure that you are using the pip that's tied to the active Python executable.
    # sys.executable is the absolute path to the executable that your program was invoked with.
    # "dummy" forces an error which lists all the available versions.
    latest_version = str(
        subprocess.run([sys.executable, '-m', 'pip', 'install', '{}==dummy'.format(package)],
                       capture_output=True, text=True))
 
    # remove leading text and trailing parenthesis
    latest_version = latest_version[latest_version.find('(from versions:') + 1:]
    latest_version = latest_version[:latest_version.find(')')]
 
    # remove spaces, split on comma and select last item
    latest_version = latest_version.replace(' ', '').split(',')[-1]
    return latest_version
 
 
def lambda_handler(event, context):
    logger.info("Event Data:- %s", event)
    package_name = event['package_name']
    layer_description = event['layer_description']
    parameter_store_layer_name = event['parameter_store_layer_name']
    parameter_store_description = event['parameter_store_description']
 
    latest_version = get_latest_version(package_name)
 
    package = package_name + '_' + latest_version
    install_path = os.path.join('/tmp/', package)
    logger.info("Install Path:- %s", install_path)
    os.mkdir(install_path)
 
    # run pip and tell it to use /tmp as /tmp in lambda function as it is the only writable path
    installresponse = subprocess.run(["pip", "install", package, "-t", install_path], capture_output=True, text=True)
 
    logger.info("Start ZIP process.")
    shutil.make_archive("/tmp/" + package_name + latest_version, 'zip', install_path)
    logger.info("End ZIP process.")
 
    # This sets the variables to push layer to AWS
    zipfilename = package_name + latest_version + ".zip"
    zipfilepath = os.path.join('/tmp/', zipfilename)
 
    logger.info("zipfile and full name:- %s", zipfilename, zipfilepath)
 
    # Can only use underscores as "." is not allowed by AWS here
    latest_version = latest_version.replace(".", "_")
    logger.info("version (with underscore replacement):- %s", latest_version)
 
    # Open zip file for 'r'eading as a 'b'yte stream (rb)
    with open(zipfilepath, 'rb') as zf:
        zipbytes = zf.read()
 
    logger.info("publish layer version - zip package")
    lambda_client = boto3.client('lambda')
    response = lambda_client.publish_layer_version(
        CompatibleRuntimes=[
            'python3.7',
            'python3.8',
            'python3.9',
        ],
        Content={
            'ZipFile': zipbytes,
        },
        Description=layer_description,
        LayerName=package_name + '_' + latest_version,
        LicenseInfo='MIT',
    )
 
    logger.info("LayerArn ", response['LayerArn'])
    logger.info("LayerVersionArn ", response['LayerVersionArn'])
 
    # Write ARN out to Parameter Store.
    ssm_client = boto3.client("ssm")
    ssmresponse = ssm_client.put_parameter(
        Name=parameter_store_layer_name,
        Description=parameter_store_description,
        Value=response['LayerVersionArn'],
        Type='String',
        Overwrite=True,
        Tier='Standard',
        DataType='text')
 
    logger.info("ssm response:- %s", ssmresponse)

layerpublishpermission.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "lambda:PublishLayerVersion",
            "Resource": "arn:aws:lambda:*:456712349876:layer:*"
        }
    ]
}

ssm_parameter_permission

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:PutParameter",
                "ssm:DescribeParameters"
            ],
            "Resource": "*"
        }
    ]
}
 
aws/lambdabased-lambdalayer.txt · Last modified: 27/03/2024 11:08 by andrew