Skip to content

feat: publish lib as Lambda Layer #1095

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish_layer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ on:
required: true
# Automatic trigger after release
workflow_run:
workflows: ["release"]
workflows: ["Make Release"]
types:
- completed

Expand Down
227 changes: 227 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,233 @@ Core utilities such as Tracer, Logger, Metrics, and Event Handler will be availa

## Installation

You can use Powertools through [AWS Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-layer) or install it as your dependency via NPM:

* **Lambda Layer**: [**arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:1**](#){: .copyMe}:clipboard:
* **NPM**: **`npm install @aws-lambda-powertools/tracer @aws-lambda-powertools/metrics @aws-lambda-powertools/logger`**

???+ hint "Support this project by using Lambda Layers :heart:"
Lambda Layers allow us to understand who uses this library in a non-intrusive way. This helps us justify and gain future investments for other Lambda Powertools languages.

When using Layers, you can add Lambda Powertools as a dev dependency to not impact the development process.


### Lambda Layer

[Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html){target="_blank"} is a .zip file archive that can contain additional code, pre-packaged dependencies, data, or configuration files. Layers promote code sharing and separation of responsibilities so that you can iterate faster on writing business logic.

You can include Lambda Powertools Lambda Layer using [AWS Lambda Console](https://docs.aws.amazon.com/lambda/latest/dg/invocation-layers.html#invocation-layers-using){target="_blank"}, or your preferred deployment framework.

??? note "Note: Expand to copy any regional Lambda Layer ARN"

| Region | Layer ARN
|--------------------------- | ---------------------------
| `us-east-1` | [arn:aws:lambda:us-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `us-east-2` | [arn:aws:lambda:us-east-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `us-west-1` | [arn:aws:lambda:us-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `us-west-2` | [arn:aws:lambda:us-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `ap-south-1` | [arn:aws:lambda:ap-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `eu-central-1` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `eu-west-1` | [arn:aws:lambda:eu-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `eu-west-2` | [arn:aws:lambda:eu-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `eu-west-3` | [arn:aws:lambda:eu-west-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `eu-north-1` | [arn:aws:lambda:eu-north-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `ca-central-1` | [arn:aws:lambda:ca-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:
| `sa-east-1` | [arn:aws:lambda:sa-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1](#){: .copyMe}:clipboard:

??? question "Can't find our Lambda Layer for your preferred AWS region?"
You can use our [CDK Layer Construct](https://github.com/aws-samples/cdk-lambda-powertools-python-layer){target="_blank"}, or NPM like you normally would for any other library.

Please do file a feature request with the region you'd want us to prioritize making our Lambda Layer available.

=== "SAM"

```yaml hl_lines="5"
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Layers:
- !Sub arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:1
```

=== "Serverless framework"

```yaml hl_lines="5"
functions:
hello:
handler: lambda_function.lambda_handler
layers:
- arn:aws:lambda:${aws:region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:1
```

=== "CDK"

```typescript hl_lines="11 16"
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class SampleFunctionWithLayer extends Construct {
constructor(scope: Construct, id: string) {
super(scope, id);
// Create a Layer with AWS Lambda Powertools for TypeScript
const powertoolsLayer = lambda.LayerVersion.fromLayerVersionArn(
this,
'PowertoolsLayer',
`arn:aws:lambda:${cdk.Stack.of(this).region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:1`
);
new lambda.Function(this, 'Function', {
runtime: lambda.Runtime.NODEJS_16_X,
// Add the Layer to a Lambda function
layers: [powertoolsLayer],
code: lambda.Code.fromInline(`
const { Logger } = require('@aws-lambda-powertools/logger');
const { Metrics } = require('@aws-lambda-powertools/metrics');
const { Tracer } = require('@aws-lambda-powertools/tracer');
const logger = new Logger({logLevel: 'DEBUG'});
const metrics = new Metrics();
const tracer = new Tracer();
exports.handler = function(event, ctx) {
logger.debug("Hello World!");
}`),
handler: 'index.handler',
});
}
}
```

=== "Terraform"

```terraform hl_lines="9 38"
terraform {
required_version = "~> 1.0.5"
required_providers {
aws = "~> 3.50.0"
}
}
provider "aws" {
region = "{region}"
}
resource "aws_iam_role" "iam_for_lambda" {
name = "iam_for_lambda"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow"
}
]
}
EOF
}
resource "aws_lambda_function" "test_lambda" {
filename = "lambda_function_payload.zip"
function_name = "lambda_function_name"
role = aws_iam_role.iam_for_lambda.arn
handler = "index.test"
runtime = "nodejs16.x"
layers = ["arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:1"]
source_code_hash = filebase64sha256("lambda_function_payload.zip")
}
```

=== "Amplify"

```zsh
# Create a new one with the layer
❯ amplify add function
? Select which capability you want to add: Lambda function (serverless function)
? Provide an AWS Lambda function name: <NAME-OF-FUNCTION>
? Choose the runtime that you want to use: NodeJS
? Do you want to configure advanced settings? Yes
...
? Do you want to enable Lambda layers for this function? Yes
? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1
❯ amplify push -y
# Updating an existing function and add the layer
❯ amplify update function
? Select the Lambda function you want to update test2
General information
- Name: <NAME-OF-FUNCTION>
? Which setting do you want to update? Lambda layers configuration
? Do you want to enable Lambda layers for this function? Yes
? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:1
? Do you want to edit the local lambda function now? No
```

=== "Get the Layer .zip contents"
Change `{region}` to your AWS region, e.g. `eu-west-1`

```bash title="AWS CLI"
aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:1 --region {region}
```

The pre-signed URL to download this Lambda Layer will be within `Location` key.

???+ warning "Warning: Limitations"

Container Image deployment (OCI) or inline Lambda functions do not support Lambda Layers.

If you use `esbuild` to bundle your code, make sure to exclude `@aws-lambda-powertools` from being bundled since the packages will be brought by the Layer:

=== "SAM"

```yaml hl_lines="5"
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
...
Metadata:
# Manage esbuild properties
BuildMethod: esbuild
BuildProperties:
Minify: true
External:s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this s in External:s (<- this) on purpose? If yes please resolve this comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oups , well catched

- '@aws-lambda-powertools/commons'
- '@aws-lambda-powertools/logger'
- '@aws-lambda-powertools/metrics'
- '@aws-lambda-powertools/tracer'
```

=== "Serverless framework"

```yaml hl_lines="5"
custom:
esbuild:
external:
- '@aws-lambda-powertools/commons'
- '@aws-lambda-powertools/logger'
- '@aws-lambda-powertools/metrics'
- '@aws-lambda-powertools/tracer'
```

=== "CDK"

```typescript hl_lines="11 16"
new awsLambdaNodejs.NodejsFunction(this, 'Function', {
...
bundling: {
externalModules: [
'@aws-lambda-powertools/commons',
'@aws-lambda-powertools/logger',
'@aws-lambda-powertools/metrics',
'@aws-lambda-powertools/tracer',
],
}
});
```

### NPM Modules

The AWS Lambda Powertools for TypeScript utilities (which from here will be referred as Powertools) follow a modular approach, similar to the official [AWS SDK v3 for JavaScript](https://github.com/aws/aws-sdk-js-v3).
Each TypeScript utility is installed as standalone NPM package.

Expand Down
1 change: 1 addition & 0 deletions examples/cdk/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_modules
# CDK asset staging directory
.cdk.staging
cdk.out
lib
2 changes: 1 addition & 1 deletion examples/cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The example functions, located in the `src` folder, are invoked automatically, t
## Deploying the stack

* Navigate to this location of the repo in your terminal (`examples/cdk`)
* `npm install`
* `npm ci`
* `npm run cdk deploy --all --profile <YOUR_AWS_PROFILE>`

Note: Prior to deploying you may need to run `cdk bootstrap aws://<YOU_AWS_ACCOUNT_ID>/<AWS_REGION> --profile <YOUR_AWS_PROFILE>` if you have not already bootstrapped your account for CDK.
Expand Down
14 changes: 7 additions & 7 deletions examples/cdk/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/cdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"devDependencies": {
"@types/jest": "^27.4.0",
"@types/node": "18.0.0",
"aws-cdk": "^2.0.0",
"aws-cdk": "2.27.0",
"esbuild": "^0.14.23",
"jest": "^27.5.1",
"ts-jest": "^27.1.4",
Expand Down
53 changes: 36 additions & 17 deletions examples/cdk/src/example-function.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,54 @@
import { custom_resources, aws_iam } from 'aws-cdk-lib';
import { custom_resources, aws_iam, Stack } from 'aws-cdk-lib';
import { Events } from '@aws-lambda-powertools/commons';
import { Construct } from 'constructs';
import { NodejsFunction, NodejsFunctionProps } from 'aws-cdk-lib/aws-lambda-nodejs';
import { Tracing, Runtime } from 'aws-cdk-lib/aws-lambda';
import { Tracing, Runtime, LayerVersion } from 'aws-cdk-lib/aws-lambda';

interface ExampleFunctionProps {
readonly functionName: string
readonly tracingActive?: boolean
readonly useLayer?: boolean
readonly invocations?: number
readonly fnProps?: Partial<NodejsFunctionProps>
}

class ExampleFunction extends Construct {

public constructor(scope: Construct, id: string, props: ExampleFunctionProps) {
super(scope, id);

const { functionName, tracingActive, invocations, fnProps } = Object.assign({
tracingActive: false,
invocations: 2
}, props);
const { functionName, tracingActive, invocations, fnProps } = Object.assign(
{
tracingActive: false,
invocations: 2,
},
props
);

const fn = new NodejsFunction(this, functionName, {
const fnOptions = {
tracing: tracingActive ? Tracing.ACTIVE : Tracing.DISABLED,
runtime: Runtime.NODEJS_16_X,
...fnProps
});
...fnProps,
};

if (props.useLayer) {
fnOptions.bundling = {
externalModules: [
'@aws-lambda-powertools/commons',
'@aws-lambda-powertools/logger',
'@aws-lambda-powertools/metrics',
'@aws-lambda-powertools/tracer',
],
};
fnOptions.layers = [
LayerVersion.fromLayerVersionArn(
this,
'AWSLambdaPowertoolsTypeScript',
`arn:aws:lambda:${Stack.of(this).region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:1`
),
];
}

const fn = new NodejsFunction(this, functionName, fnOptions);

for (let i = 0; i < invocations; i++) {
new custom_resources.AwsCustomResource(this, `Invoke-${functionName}-${i}`, {
Expand All @@ -37,14 +60,12 @@ class ExampleFunction extends Construct {
FunctionName: fn.functionName,
InvocationType: 'RequestResponse',
Payload: JSON.stringify(Events.Custom.CustomEvent),
}
},
},
policy: custom_resources.AwsCustomResourcePolicy.fromStatements([
new aws_iam.PolicyStatement({
effect: aws_iam.Effect.ALLOW,
resources: [
fn.functionArn,
],
resources: [fn.functionArn],
actions: ['lambda:InvokeFunction'],
}),
]),
Expand All @@ -53,6 +74,4 @@ class ExampleFunction extends Construct {
}
}

export {
ExampleFunction
};
export { ExampleFunction };
Loading