This is the AWS CDK v2 Developer Guide. The older CDK v1 entered maintenance on June 1,
2022 and ended support on June 1, 2023.
Define permissions for L2 constructs with the AWS CDK
Define AWS Identity and Access Management (IAM) roles and policies for L2 constructs when using the AWS Cloud Development Kit (AWS CDK).
Use grant methods to define permissions
When you define your infrastructure using L2 constructs from the AWS Construct Library, you can use the provided
grant methods to specify the permissions your resources will require. The AWS CDK will
automatically create the IAM roles needed for all AWS resources that require them.
The following is an example that defines permissions between an AWS Lambda function and Amazon Simple Storage Service (Amazon S3) bucket.
Here, the grantRead
method of the Bucket L2 construct is used to define these permissions:
- TypeScript
-
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as kms from 'aws-cdk-lib/aws-kms';
export class CdkDemoStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const key = new kms.Key(this, 'BucketKey');
const bucket = new s3.Bucket(this, 'Bucket', {
encryptionKey: key,
});
const handler = new lambda.Function(this, 'Handler', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda'),
});
// Define permissions between function and S3 bucket using grantRead method
bucket.grantRead(handler);
}
}
- JavaScript
-
const { Stack, Duration } = require('aws-cdk-lib');
const s3 = require('aws-cdk-lib/aws-s3');
const lambda = require('aws-cdk-lib/aws-lambda');
const kms = require('aws-cdk-lib/aws-kms');
class CdkDemoStack extends Stack {
constructor(scope, id, props) {
super(scope, id, props);
const key = new kms.Key(this, 'BucketKey');
const bucket = new s3.Bucket(this, 'Bucket', {
encryptionKey: key,
});
const handler = new lambda.Function(this, 'Handler', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda'),
});
// Define permissions between function and S3 bucket using grantRead method
bucket.grantRead(handler);
}
}
// ...
- Python
-
from aws_cdk import (
Stack,
aws_s3 as s3,
aws_lambda as _lambda,
aws_kms as kms,
)
from constructs import Construct
class CdkDemoStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
key = kms.Key(self, 'BucketKey')
bucket = s3.Bucket(self, 'Bucket')
handler = _lambda.Function(
self,
'Handler',
runtime = _lambda.Runtime.NODEJS_20_X,
handler = 'index.handler',
code = _lambda.Code.from_asset('lambda'),
)
# Define permissions between function and S3 bucket using grantRead method
bucket.grantRead(handler)
- Java
-
package com.myorg;
import software.amazon.awscdk.core.App;
import software.amazon.awscdk.core.Stack;
import software.amazon.awscdk.core.StackProps;
import software.amazon.awscdk.services.kms.Key;
import software.amazon.awscdk.services.kms.KeyProps;
import software.amazon.awscdk.services.s3.Bucket;
import software.amazon.awscdk.services.s3.BucketProps;
import software.amazon.awscdk.services.lambda.Function;
import software.amazon.awscdk.services.lambda.FunctionProps;
import software.amazon.awscdk.services.lambda.Runtime;
import software.amazon.awscdk.services.lambda.Code;
import software.constructs.Construct;
public class CdkDemoStack extends Stack {
public CdkDemoStack(final Construct scope, final String id) {
this(scope, id, null);
}
public CdkDemoStack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);
Key key = new Key(this, "BucketKey", KeyProps.builder().build());
Bucket bucket = new Bucket(this, "Bucket", BucketProps.builder()
.encryptionKey(key)
.build());
Function handler = new Function(this, "Handler", FunctionProps.builder()
.runtime(Runtime.NODEJS_20_X)
.handler("index.handler")
.code(Code.fromAsset("lambda"))
.build());
// Define permissions between function and S3 bucket using grantRead method
bucket.grantRead(handler);
}
public static void main(final String[] args) {
App app = new App();
new CdkDemoStack(app, "CdkDemoStack");
app.synth();
}
}
- C#
-
using Amazon.CDK;
using Amazon.CDK.AWS.KMS;
using Amazon.CDK.AWS.S3;
using Amazon.CDK.AWS.Lambda;
namespace CdkDemo
{
public class CdkDemoStack : Stack
{
internal CdkDemoStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
{
var key = new Key(this, "BucketKey");
var bucket = new Bucket(this, "Bucket", new BucketProps
{
EncryptionKey = key
});
var handler = new Function(this, "Handler", new FunctionProps
{
Runtime = Runtime.NODEJS_20_X,
Handler = "index.handler",
Code = Code.FromAsset("lambda")
});
// Define permissions between function and S3 bucket using grantRead method
bucket.GrantRead(handler);
}
}
}
- Go
-
package main
import (
"github.com/aws/aws-cdk-go/awscdk/v2"
"github.com/aws/aws-cdk-go/awscdk/v2/awskms"
"github.com/aws/aws-cdk-go/awscdk/v2/awss3"
"github.com/aws/aws-cdk-go/awscdk/v2/awslambda"
"github.com/aws/constructs-go/constructs/v10"
"github.com/aws/jsii-runtime-go"
)
// ...
func NewCdkDemoStack(scope constructs.Construct, id string, props *CdkDemoStackProps) awscdk.Stack {
stack := awscdk.NewStack(scope, &id, &props.StackProps)
key := awskms.NewKey(stack, jsii.String("BucketKey"), nil)
bucket := awss3.NewBucket(stack, jsii.String("Bucket"), &awss3.BucketProps{
EncryptionKey: key,
})
handler := awslambda.NewFunction(stack, jsii.String("Handler"), &awslambda.FunctionProps{
Runtime: awslambda.Runtime_NODEJS_20_X(),
Handler: jsii.String("index.handler"),
Code: awslambda.Code_FromAsset(jsii.String("lambda"), &awss3assets.AssetOptions{}),
})
bucket.GrantRead(handler)
return stack
}
// ...
When you use grant methods of L2 constructs to define permissions between resources, the AWS CDK will create roles
with least privilege policies based on the method you specify. As a security best practice, we recommend that you use
the method that applies only the permissions that you require. For example, if you only need to grant permissions for a
Lambda function to read from an Amazon S3 bucket, use the grantRead
method instead of
grantReadWrite
.
For each method that you use, the CDK creates a unique IAM role for the specified resources. If necessary,
you can also directly modify the policy that will be attached to the role. The following is an example:
- TypeScript
-
import { aws_iam as iam } from 'aws-cdk-lib';
handler.addToRolePolicy(new iam.PolicyStatement({
actions: ['s3:GetObject', 's3:List*'],
resources: [
bucket.bucketArn,
bucket.arnForObjects('*'),
]
}));
- JavaScript
-
const iam = require('aws-cdk-lib/aws-iam');
handler.addToRolePolicy(new iam.PolicyStatement({
actions: ['s3:GetObject', 's3:List*'],
resources: [
bucket.bucketArn,
bucket.arnForObjects('*'),
]
}));
- Python
-
from aws_cdk import aws_iam as iam
handler.add_to_role_policy(iam.PolicyStatement(
actions=['s3:GetObject', 's3:List*'],
resources=[
bucket.bucket_arn,
bucket.arn_for_objects('*'),
]
))
- Java
-
import software.amazon.awscdk.services.iam.PolicyStatement;
import software.amazon.awscdk.services.iam.PolicyStatementProps;
handler.addToRolePolicy(PolicyStatement.Builder.create()
.actions(Arrays.asList("s3:GetObject", "s3:List*"))
.resources(Arrays.asList(
bucket.getBucketArn(),
bucket.arnForObjects("*")
))
.build());
- C#
-
using Amazon.CDK.AWS.IAM;
using Amazon.CDK.AWS.S3;
using Amazon.CDK.AWS.Lambda;
handler.AddToRolePolicy(new PolicyStatement(new PolicyStatementProps
{
Actions = new[] { "s3:GetObject", "s3:List*" },
Resources = new[] { bucket.BucketArn, bucket.ArnForObjects("*") }
}));
- Go
-
package main
import (
// ...
"github.com/aws/aws-cdk-go/awscdk/v2/awsiam"
// ...
)
// ...
func NewCdkDemoStack(scope constructs.Construct, id string, props *CdkDemoStackProps) awscdk.Stack {
// ...
handler.AddToRolePolicy(awsiam.NewPolicyStatement(&awsiam.PolicyStatementProps{
Actions: jsii.Strings("s3:GetObject", "s3:List*"),
Resources: jsii.Strings(bucket.BucketArn(), bucket.ArnForObjects("*")),
}))
// ...
However, we recommend that you use the grant
methods when available.
Manually create and use IAM roles
If you prefer not to use the CDK grant
methods to create and manage permissions, you must
manually create and configure them. You can create IAM roles using the AWS Management Console, AWS CLI, or AWS SDKs. Then, you can
pass them into your CDK application manually or use the role customization feature.
Reference and manage all roles manually
Constructs that require a role have an optional role
property that you can use to pass in a role
object.
To reference a role manually
-
Use Role.fromRoleName()
to reference your pre-existing role. The following is an example:
const existingRole = Role.fromRoleName(stack, 'Role', 'my-pre-existing-role', {
mutable: false // Prevent CDK from attempting to add policies to this role
}
-
Pass the pre-existing role when defining your resource. The following is an example:
const handler = new lambda.Function(stack, 'Handler', { runtime: lambda.Runtime.NODEJS_20_XZ, handler:
'index.handler', code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')), // Pass in pre-existing role
role: existingRole, });
Use the role customization feature
The AWS CDK role customization feature generates a report of roles and policies in your
CDK app. You can use this feature to generate a report. Then you can substitute pre-created roles for
them.
To use the role customization feature
-
Add Role.customizeRoles()
somewhere towards the top of your CDK application. The
following is an example:
const stack = new Stack(app, 'LambdaStack');
// Add this to use the role customization feature
iam.Role.customizeRoles(stack);
// Define your resources using L2 constructs
const key = new kms.Key(stack, 'BucketKey');
const bucket = new s3.Bucket(stack, 'Bucket', {
encryptionKey: key,
});
const handler = new lambda.Function(stack, 'Handler', {
runtime: lambda.Runtime.NODEJS_16_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')),
});
// The grantRead() is still important. Even though it actually doesn't mutate
// any policies, it indicates the need for them.
bucket.grantRead(handler);
-
When you synthesize your application, the CDK will throw an error, indicating that you need to provide
the pre-created role name to Role.customizeRoles()
. The following is an example of the generated
report:
<missing role> (LambdaStack/Handler/ServiceRole)
AssumeRole Policy:
[
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
]
Managed Policy ARNs:
[
"arn:(PARTITION):iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
]
Managed Policies Statements:
NONE
Identity Policy Statements:
[
{
"Action": [
"s3:GetObject*",
"s3:GetBucket*",
"s3:List*"
],
"Effect": "Allow",
"Resource": [
"(LambdaStack/Bucket/Resource.Arn)",
"(LambdaStack/Bucket/Resource.Arn)/*"
]
}
]
-
Once the role is created, you can pass it into your application for the resource that it applies to. For
example, if the name of the role created for LambdaStack/Handler/ServiceRole
is
lambda-service-role
, you would update your CDK app as follows:
const stack = new Stack(app, 'LambdaStack');
// Add this to pass in the role
iam.Role.customizeRoles(stack, {
usePrecreatedRoles: {
'LambdaStack/Handler/ServiceRole': 'lambda-service-role',
},
});
The CDK will now use the pre-created role name anywhere that the role is referenced in the CDK
application. It will also continue to generate the report so that any future policy changes can be
referenced.
You will notice that the reference to the Amazon S3 bucket ARN in the report is rendered as
(LambdaStack/Bucket/Resource.Arn
) instead of the actual ARN of the bucket. This is because the
bucket ARN is a deploy time value that is not known at synthesis (the bucket hasn’t been created yet). This is
another example of why we recommend allowing CDK to manage IAM roles and permissions by using the
provided grant
methods. In order to create the role with the initial policy, the admin will have to
create the policy with broader permissions (for example, arn:aws:s3:::*
).