Add AWS Powertools
Tags: aws, serverless, observability, devops
Published: 2021-11-27 11:36:25

---
title: Add AWS Powertools
series: [Lambda Observability]
published: true
description: Add Observability layer to your Lambda
tags: #AWS #Serverless #Observability #DevOps
cover_image: https://www.pawelpiwosz.net/assets/lambdaObservability/sherlock-holmes.jpg
---

## Recap

We have definitely better logging ability now. But notice one thing - what we have, allows us to understand the global information about Lambda. We still do not understand the internals. We know that execution suffered by cold start. But.. how much?

## AWS Powertools

Fortunately, we have tooling which we can use to know more.

Unfortunatelly though, this tool is available for Python and Java only. At least, at this moment.

| Python | Java |
|--------|------|
| https://github.com/awslabs/aws-lambda-powertools-python | https://github.com/awslabs/aws-lambda-powertools-java|

According to [Roadmap](https://github.com/awslabs/aws-lambda-powertools-roadmap) there is some work on .Net support. Hopefully we will see it soon. Personally, I would like to see nodejs and golang support too.

But we are talking about Python. 

We already added one layer, for Lambda Insights. This time we need to do exactly the same thing, add new layer, but we will do it completelly differently. And we will work with the SAM template only.

Ok, First, as you remember, adding Lambda Insights in SAM template was quite easy:

```yaml
      Layers:
        - !Sub "arn:aws:lambda:${AWS::Region}:580247275435:layer:LambdaInsightsExtension:14"
```

We used `!Sub` to make substitution of `${AWS::Region}` into actual region. It will not work with Powertools, as it is available in a few locations only.  
We also have the version specified = `:14` on the end. This time we will specify it differently too.  
To say more, we will not reffer to `lambda`, but `serverlessrepo`.

In orger to have it completed, we create the new resource in our template. I placed it just below `Resources:` statement.

```yaml
Resources:

  AwsLambdaPowertoolsPythonLayer:
    Type: AWS::Serverless::Application
    Properties:
      Location:
        ApplicationId: arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer
        SemanticVersion: 1.22.0 # change to latest semantic version available in SAR
```

At the time of writting this tutorial, the available version was `1.22.0`

***

Obviously, we can check which versions we can use and which one is the last one.

```bash
aws serverlessrepo list-application-versions --application-id arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer
```

Sometimes we need to add `--region <region>` (in my case it will be eu-west-1) and `--profile <profile_name>`. Let's see it:

```bash
aws serverlessrepo list-application-versions --application-id arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer --region eu-west-1 --profile demo 
```

Output should look similar to this:

```JSON
{
    "Versions": [
        {
            "ApplicationId": "arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer",
            "CreationTime": "2020-08-25T11:36:35.772Z",
            "SemanticVersion": "1.3.0",
            "SourceCodeUrl": "https://github.com/awslabs/aws-lambda-powertools-python"
        },
```

It is not too handy to go through it. Let's make it better to select the last version.

```bash
aws serverlessrepo list-application-versions --application-id arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer --region eu-west-1 --profile demo --query 'Versions[*].SemanticVersion'
```

And now we have:

```JSON
[
    "1.3.0",
    "1.3.1",
    "1.4.0",
    "1.5.0",
    "1.6.0",
    "1.7.0",
    "1.8.0",
    "1.9.0",
    "1.10.0",
    "1.10.1",
    "1.10.2",
    "1.10.3",
    "1.10.4",
    "1.10.5",
    "1.11.0",
    "1.12.0",
    "1.13.0",
    "1.14.0",
    "1.15.0",
    "1.15.1",
    "1.16.0",
    "1.16.1",
    "1.17.0",
    "1.17.1",
    "1.18.0",
    "1.18.1",
    "1.19.0",
    "1.20.0",
-- More  --
```

Better. But still... We have all versions and we need to scroll to grab the last one. Also, not perfect for scripting.

Let's nail it.

```bash
aws serverlessrepo list-application-versions --application-id arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer --region eu-west-1 --profile demo --query 'Versions[::-1].SemanticVersion | [0]'
```

And now we have:

```JSON
"1.22.0"
```

Perfect.

What we have done? we used `---query` in order to go through `Versions` and select all in reverse order `[::-1]`. We took into consideration only one field of `Versions`, which is `.SemanticVersion`. And on the end we piped it with `|` and took only the first value `[0]`. That's it :)

***

Ok, so we confirmed the version. We can safely use it in our resource description, shown on the beginning of this article.

But it is not the end!

***

Now it is time to add this layer to definition of our Lambda function.

```yaml
Layers:
        - !GetAtt AwsLambdaPowertoolsPythonLayer.Outputs.LayerVersionArn
        - !Sub "arn:aws:lambda:${AWS::Region}:580247275435:layer:LambdaInsightsExtension:14"
```

Notice, we added it before Insights, what is not that important. What is important, is the difference between these two definitions. In case of Powertools we used `!GetAtt` in order to get the attribute of the defined resource. In case of `AWS::Serverless::Application` we can use `!Ref` which is simply the id of the resource, or `'!GetAtt` to collect `Outputs.ApplicationOutputName` which is the `ApplicationOutputName`. Another words, we "anchor" the layer definition to created application.

***

We need to complete last step here, the configuration of Powertools. According to documentation, Lambda needs to have some Variables set. We will do it directly in SAM template.

```yaml
      Layers:
        - !GetAtt AwsLambdaPowertoolsPythonLayer.Outputs.LayerVersionArn
        - !Sub "arn:aws:lambda:${AWS::Region}:580247275435:layer:LambdaInsightsExtension:14"
      Environment:
        Variables:
          LOG_LEVEL: "INFO"
          POWERTOOLS_SERVICE_NAME: simpleFunctionService
          POWERTOOLS_METRICS_NAMESPACE: simpleFunctionMetrics
```

I attached the snipped with previously added layer to show where to place variables.

What we have here?

We say what `LOG_LEVEL` we want. In our case it is "INFO" and it is enough. Also, it is the default value (another is DEBUG). We also set the `POWERTOOLS_SERVICE_NAME`, it is simply the name of the service which will be visible through all logs entries, and `POWERTOOLS_METRICS_NAMESPACE` which will be used to construct the namespace for the metrics in CloudWatch.

These three are the basic for our setup.

***

Last but not least, we need to remember that the IAM Role which we use (well, IAM Policy) needs to have access, therefore the easiest ay which you can go is just simply add `serverlessrepo:*` to your policy. It is ok for tests and plays, but for "professional use" please narrow it down.  
Also, if you are creating the setup through CloudFormation, remember about `CAPABILITY_AUTO_EXPAND` capability. It is needed in order to operate with layer added the way we did.

***

More about it you can read in [documentation](https://awslabs.github.io/aws-lambda-powertools-python/latest/)

***

![Lambda Powertools layer installed](https://www.pawelpiwosz.net/assets/lambdaObservability/lambdaPowertoolsInstalled.jpg)
Ok, we addedd the Powertools layer. But doing simply this we achieved nothing. What we need to do, is implementation in the code, what we will do in the next episode.

***

Cover image by <a href="https://pixabay.com/users/422737-422737/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=462957">Hebi B.</a> from <a href="https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=462957">Pixabay</a>