Lambda execution role¶
Every Lambda needs a role that it can assume and that can write logs. The interesting part is the extra permissions, so that is the argument.
def lambda_role(name_prefix: str, extra: Sequence[AnyStatement]) -> iam.Role:
"""Execution role for a Lambda: log permissions plus the `extra` statements."""
log_arn = arn.log_group(f"/aws/lambda/{name_prefix}*", streams=True)
return iam.role(
AssumeRolePolicyDocument=assume_role_policy(service("lambda.amazonaws.com")),
Policies=[iam.RolePolicyProperty(
PolicyName=f"{name_prefix}-lambda",
PolicyDocument=policy_document([
allow(["logs:CreateLogStream", "logs:CreateLogGroup", "logs:PutLogEvents"], [log_arn]),
*extra,
]),
)],
)
Use it by passing the statements the function needs. The grants give you statements for common cases; they are the AWS CDK's opinions, so treat them as less permanent than the rest of this book:
lambda_role("order-processor-prod", [
*grants.Sqs.Queue.consume_messages.on(cfn.get_att("OrderQueue", "Arn")),
*grants.DynamoDb.Table.write_data.on(cfn.arn.table("orders-prod")),
])
Or write your own with the permission names, which are plain strings, so a typo is an attribute error:
Things worth noticing¶
- It returns
iam.Role, notdict. The generated type knowsPoliciestakes a list ofiam.RolePolicyProperty, so a misspelledPolicyDocumnetis an error here, in the pattern, rather than in every caller. allowandStatementcome fromcfn_typed.Effectis limited to"Allow"or"Deny", andResourceaccepts a literal or an intrinsic.arn()and its methods, likearn.table(...), build aFn::Subwith the partition, region and account filled in by CloudFormation, so the same code works in every partition.- The log group wildcard is scoped to the function prefix, not
*. Least privilege is cheaper to write once in a pattern than to remember in every stack.