How to add permissions to Lambda Functions in AWS CDK

avatar

Borislav Hadzhiev

Thu Apr 22 20213 min read

Updated on Thu Apr 22 2021

To add permissions to our Lambda function we have to attach an inline Policy to the function's role

Adding Permissions to a Lambda Function in AWS CDK #

In order to add permissions to a Lambda Function we have to attach a policy to the function's role.

The code for this article is available on GitHub

Let's take a look at a complete example where we:

  1. Create a Lambda function
  2. Create an IAM Policy statement
  3. Attach an inline policy to the function's role, passing in the policy statement we created
lib/cdk-starter-stack.ts
import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda';
import {NodejsFunction} from '@aws-cdk/aws-lambda-nodejs';
import * as cdk from '@aws-cdk/core';
import * as path from 'path';

export class CdkStarterStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // ๐Ÿ‘‡ define the Lambda
    const myFunction = new NodejsFunction(this, 'my-function', {
      runtime: lambda.Runtime.NODEJS_14_X,
      handler: 'main',
      entry: path.join(__dirname, `/../src/my-lambda/index.ts`),
    });

    // ๐Ÿ‘‡ create a policy statement
    const s3ListBucketsPolicy = new iam.PolicyStatement({
      actions: ['s3:ListAllMyBuckets'],
      resources: ['arn:aws:s3:::*'],
    });

    // ๐Ÿ‘‡ add the policy to the Function's role
    myFunction.role?.attachInlinePolicy(
      new iam.Policy(this, 'list-buckets-policy', {
        statements: [s3ListBucketsPolicy],
      }),
    );
  }
}

Let's go over what we did in the code snippet:

  1. we defined a Lambda function via the NodejsFunction construct
  2. we created an IAM Policy Statement with the PolicyStatement class. The policy grants the Lambda function access to list the S3 buckets in the account.
  3. we attached an inline IAM Policy to the function's role and we passed in the statement we created earlier in the statements array

Deploying Additional Permissions for Lambdas in CDK #

I'll execute the deploy command:

shell
npx cdk deploy

The CloudFormation console shows that our list-buckets-policy has been provisioned:

cloudformation lambda permissions

The IAM role of the lambda function now has 2 policies:

  1. The default AWSLambdaBasicExecutionRole policy that is managed by AWS
  2. The list-buckets-policy - an inline policy, managed by us

lambda iam role

At this point, we've successfully added permissions to a Lambda function's role.

Lambda Permissions in AWS CDK - Discussion #

When we define a Lambda function, it comes with an automatically generated Role (unless we explicitly provide one). This role will then be assumed by the Lambda function during execution.

The automatically generated policy included in the role is called AWSLambdaBasicExecutionRole. It grants permissions related to logging:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    }
  ]
}

In most of our lambda functions we will want to be able to log information to Cloud Watch, so extending the default lambda role is a good option.

If we were to provide our own IAM role, we would then have to manually edit the Trust relationship of the role to allow the service lambda.amazonaws.com to assume the role and add the logging permissions ourselves.

The default trust relationship looks like:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

By attaching an inline policy to the function's role we were able to keep the default Lambda execution role and attach policies to extend it.

If we were to pass in our own role, we could add the AWSLambdaBasicExecutionRole policy to it in the following way:

const myFunction = new NodejsFunction(this, 'my-function', {
  // ... rest
  role: someRole,
});

myFunction.role?.addManagedPolicy(
  iam.ManagedPolicy.fromAwsManagedPolicyName(
    'service-role/AWSLambdaBasicExecutionRole',
  ),
);

// only required if your function is in a VPC
myFunction.role?.addManagedPolicy(
  iam.ManagedPolicy.fromAwsManagedPolicyName(
    'service-role/AWSLambdaVPCAccessExecutionRole',
  ),
);

Further Reading #

Join my newsletter

I'll send you 1 email a week with links to all of the articles I've written that week

Buy Me A Coffee