Grant limited IAM permissions with AWS CDK - but fast!
Tags: cdk, cloudskills, aws, devops
Published: 2021-06-15 15:46:20

I love writing CDK apps but there is something I really loathe: Writing IAM policies and assigning them to resources like Lambdas. It feels like a step back because I don't have the same type-safety and syntax highlighting that I'm used to from native CDK constructs.

When I start writing an app, I want to make fast progress, so I fall back to using AWS Managed IAM policies, which are usually too broad and open. Then I add TODO's and promise myself to take care of it later... 

Like in this example where the Lambda should only be able to **read** from a single bucket, but is granted with **full** S3 permissions:

```typescript
const bucket = new s3.Bucket(...)

const executionRole = new iam.Role(this, "execution-role", {
  assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName(
      "service-role/AWSLambdaBasicExecutionRole"
    ),
    iam.ManagedPolicy.fromAwsManagedPolicyName(
      "service-role/AWSLambdaVPCAccessExecutionRole"
    ),
    //TODO lock down permissions!
  iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonS3FullAccess"),
  ],
})

const fn = new lambda.Function(this, "MyLambda", {
  runtime: lambda.Runtime.PYTHON_3_8,
  code: ...,
  handler: ...,
  timeout: ...,
  role: executionRole
})

```

Wouldn't it be nice if there was an idiomatic way of granting permissions in CDK? Turns out, there is!

Many CDK constructs representing resources that can be accessed (like S3, DynamoDB, SQS, SNS...) provide various `.grant...()` methods (see [CDK Grants](https://docs.aws.amazon.com/cdk/latest/guide/permissions.html#permissions_grants)). With this method you don't have to memorize or look up the names of any of the AWS Managed IAM policies and it's incredibly succinct:

```typescript
const bucket = new s3.Bucket(...)

const fn = new lambda.Function(this, "MyLambda", {
  runtime: lambda.Runtime.PYTHON_3_8,
  code: ...,
  handler: ...,
  timeout: ...
})

bucket.grantRead(fn)
```

Note how I was able to remove the `executionRole` completely because CDK will chose a sensible default [Execution Role](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-lambda-readme.html#execution-role) that will include permissions to invoke the Lambda.
In the last line I just use the `bucket` object and call `grantRead` to give the Lambda permissions to read from this bucket *only*.

Not only is this easier and faster to write, it also makes our infrastructure more secure! Besides, you can always fall back to explicitly using IAM Roles and Policies if you encounter CDK constructs where `grant` isn't available.

### Learn more
Other CDK modules follow similar mechanisms, for example `@aws-cdk/aws-ec2` when it comes to [allowing connections](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-ec2-readme.html#allowing-connections). No more manual writing of Security Group rules!

CDK is evolving fast and getting better by the day. If you want to dive deeper, check out the [CDK Workshop](https://cdkworkshop.com/) for some hands-on learning and the [CDK Construct Catalog](https://awscdk.io/) to find hundreds of reusable CDK constructs from AWS and other developers.

Have fun building!
-Ramon