这是 AWS CDK v2 开发者指南。旧版 CDK v1 于 2022 年 6 月 1 日进入维护阶段,并于 2023 年 6 月 1 日终止支持。
使用 AWS CDK 定义 L2 构造的权限
使用 AWS Cloud Development Kit (AWS CDK) 时,为 L2 构造定义 AWS Identity and Access Management(IAM)角色和策略。
使用授予方法定义权限
使用 AWS 构造库中的 L2 构造定义基础设施时,可以使用所提供的 grant 方法指定资源将需要的权限。AWS CDK 将自动为所有需要这些角色的 AWS 资源创建所需的 IAM 角色。
以下是在 AWS Lambda 函数和 Amazon Simple Storage Service(Amazon S3)存储桶之间定义权限的示例。在本例中,存储桶 L2 构造的 grantRead
方法将用于定义以下权限:
- 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
}
// ...
当您使用 L2 构造的 grant 方法定义资源之间的权限时,AWS CDK 将根据您指定的方法创建具有最低权限策略的角色。作为一项安全最佳实践,我们建议您使用仅应用您所需权限的方法。例如,如果您只需要授予 Lambda 函数从 Amazon S3 存储桶读取数据的权限,请使用 grantRead
方法而不是 grantReadWrite
。
对于您使用的每种方法,CDK 都会为指定资源创建唯一的 IAM 角色。如有必要,您还可以直接修改将附加到该角色的策略。以下是 示例:
- 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("*")),
}))
// ...
但是,我们建议您使用 grant
方法(如可用)。
手动创建和使用 IAM 角色
如果您不想使用 CDK grant
方法创建和管理权限,则必须手动创建和配置权限。您可以使用 AWS Management Console、AWS CLI 或 AWS SDK 创建 IAM 角色。然后,您可以手动将它们传入 CDK 应用程序中,也可以使用角色自定义功能。
手动引用和管理所有角色
需要角色的构造有一个可选 role
属性,可用于传入角色对象。
手动引用角色
-
使用 Role.fromRoleName()
引用已有色。以下是 示例:
const existingRole = Role.fromRoleName(stack, 'Role', 'my-pre-existing-role', {
mutable: false // Prevent CDK from attempting to add policies to this role
}
-
在定义资源时传递已有角色。以下是 示例:
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, });
使用角色自定义功能
AWS CDK 角色自定义功能会生成 CDK 应用程序中的角色和策略的报告。您可以使用此功能生成报告。然后您可以用预先创建的角色代替它们。
使用角色自定义功能
-
在 CDK 应用程序顶部某处添加 Role.customizeRoles()
。以下是 示例:
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);
-
合成应用程序时,CDK 会抛出一个错误,表示您需要向 Role.customizeRoles()
提供预先创建的角色名。以下是生成的报告的示例:
<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)/*"
]
}
]
-
创建角色后,您可以将其传入应用程序中以应用于资源。例如,如果为 LambdaStack/Handler/ServiceRole
创建的角色名称为 lambda-service-role
,则应按如下方式更新 CDK 应用程序:
const stack = new Stack(app, 'LambdaStack');
// Add this to pass in the role
iam.Role.customizeRoles(stack, {
usePrecreatedRoles: {
'LambdaStack/Handler/ServiceRole': 'lambda-service-role',
},
});
现在,CDK 将在 CDK 应用程序中引用角色的任何位置使用预先创建的角色名称。它还将继续生成报告,以便可以参考未来的任何策略更改。
您会注意到,报告中对 Amazon S3 存储桶 ARN 的引用显示为 (LambdaStack/Bucket/Resource.Arn
),而不是存储桶的实际 ARN。这是因为存储桶 ARN 是一个部署时间值,在合成时(存储桶尚未创建)未知。这是我们为何建议允许 CDK 使用提供的 grant
方法管理 IAM 角色和权限的又一个示例。要使用初始策略创建角色,管理员必须创建具有更广泛权限的策略(例如,arn:aws:s3:::*
)。