MIKAEELS

Explore the blog

Implementing S3 Bucket Policies Using AWS CDK

In this blog post, we will explore how to implement S3 bucket policies using AWS CDK (Cloud Development Kit). We'll cover two approaches: explicit and implicit. By the end of this guide, you will understand how to create S3 bucket policies in a structured and reusable manner using AWS CDK.

The caller of an S3 request made by Lambda is the function's execution role, not the generic Lambda service principal. The revised examples create a role that Lambda can assume and grant that role access. Assign it to an actual function separately, or prefer bucket.grantRead(function) when you manage both resources. See Lambda execution roles.

Prerequisites

Before we dive into the code, ensure you have the following installed:

  • AWS CDK
  • AWS CLI
  • Node.js
  • TypeScript

The folder structure for the project will look like below.

S3-BUCKET-POLICY-AWS-CDK ├── bin │ └── app.ts ├── cdk.out ├── node_modules ├── stacks │ ├── index.ts │ ├── s3-bucket-policy-explicit-stack.ts │ └── s3-bucket-policy-implicit-stack.ts ├── test ├── .gitignore ├── .npmignore ├── cdk.json ├── jest.config.js ├── package.json ├── README.md └── tsconfig.json

Link to full GitHub project.

Step 1: Setting Up the Project

First, let's initialize the project and install the necessary dependencies:

mkdir S3-BUCKET-POLICY-AWS-CDK cd S3-BUCKET-POLICY-AWS-CDK cdk init app --language=typescript

Step 2: Defining the Explicit S3 Bucket Policy Stack

We will start with the explicit method, where we create a separate BucketPolicy resource and attach it to the S3 bucket.

Create a new file stacks/s3-bucket-policy-explicit-stack.ts and add the following code:

import { RemovalPolicy, Stack, StackProps } from "aws-cdk-lib"; import { ArnPrincipal, Effect, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; import { Bucket, BucketPolicy } from "aws-cdk-lib/aws-s3"; import { Construct } from "constructs"; export class S3BucketPolicyExplicitStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const readerRole = new Role(this, "ReaderRole", { assumedBy: new ServicePrincipal("lambda.amazonaws.com"), }); // Create the S3 bucket const explicitBucket = new Bucket(this, "explicit-s3-bucket", { removalPolicy: RemovalPolicy.DESTROY, }); // Create the bucket policy const bucketPolicy = new BucketPolicy(this, "explicit-bucket-policy-id", { bucket: explicitBucket, }); // Add policy statements to the bucket policy bucketPolicy.document.addStatements( new PolicyStatement({ effect: Effect.ALLOW, principals: [new ArnPrincipal(readerRole.roleArn)], actions: ["s3:GetObject"], resources: [`${explicitBucket.bucketArn}/*`], }) ); } }

Step 3: Defining the Implicit S3 Bucket Policy Stack

Next, we will use the implicit method, where we add the bucket policy directly to the bucket resource.

Create a new file stacks/s3-bucket-policy-implicit-stack.ts and add the following code:

import { RemovalPolicy, Stack, StackProps } from "aws-cdk-lib"; import { ArnPrincipal, Effect, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; import { Bucket } from "aws-cdk-lib/aws-s3"; import { Construct } from "constructs"; export class S3BucketPolicyImplicitStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const readerRole = new Role(this, "ReaderRole", { assumedBy: new ServicePrincipal("lambda.amazonaws.com"), }); // Create the S3 bucket const implicitBucket = new Bucket(this, "implicit-s3-bucket", { removalPolicy: RemovalPolicy.DESTROY, }); // Create a bucket policy automatically implicitBucket.addToResourcePolicy( new PolicyStatement({ effect: Effect.ALLOW, principals: [new ArnPrincipal(readerRole.roleArn)], actions: ["s3:GetObject"], resources: [`${implicitBucket.bucketArn}/*`], }) ); // Access the bucket policy implicitBucket.policy?.document.addStatements( new PolicyStatement({ effect: Effect.ALLOW, principals: [new ArnPrincipal(readerRole.roleArn)], actions: ["s3:GetBucketTagging"], resources: [implicitBucket.bucketArn], }) ); } }

Step 4: Setting Up the Application Entry Point

Now, let's set up the application entry point to deploy these stacks. Update the bin/app.ts file as follows:

#!/usr/bin/env node import "source-map-support/register"; import { App } from "aws-cdk-lib"; import { S3BucketPolicyExplicitStack, S3BucketPolicyImplicitStack, } from "../stacks"; const app = new App(); new S3BucketPolicyImplicitStack(app, "S3BucketPolicyImplicitStack", {}); new S3BucketPolicyExplicitStack(app, "S3BucketPolicyExplicitStack", {});

Step 5: Deploying the Stacks

Finally, deploy the stacks using the following commands:

cdk bootstrap cdk deploy S3BucketPolicyImplicitStack cdk deploy S3BucketPolicyExplicitStack

Conclusion

In this blog post, we demonstrated how to create and manage S3 bucket policies using AWS CDK in both explicit and implicit ways. This approach provides flexibility and a clear structure for managing your infrastructure as code.

Feel free to reach out in the comments if you have any questions or run into issues.

Find the complete CDK code below.

Source repository

Continue reading

Create an IAM User with AWS CDK: Scoped S3 Permissions · Configure S3 CORS: JSON Rules, Preflight, and Troubleshooting

Quick Recap

What's the difference between explicit and implicit S3 bucket policies in CDK?

The explicit method creates a separate BucketPolicy construct and attaches it to the bucket. The implicit method calls addToResourcePolicy() directly on the bucket, which creates the underlying policy resource automatically.

How do you grant Lambda access to an S3 bucket with an explicit bucket policy?

Create a BucketPolicy construct for the bucket, then call bucketPolicy.document.addStatements() with a PolicyStatement allowing the Lambda execution role ARN the needed object actions. For a Lambda function managed in the same app, bucket.grantRead(function) is usually a simpler way to grant access.

How do you add a policy statement to an S3 bucket implicitly?

Call bucket.addToResourcePolicy() with a PolicyStatement directly on the Bucket construct. CDK creates and manages the underlying BucketPolicy resource for you.

Add Mikaeels Blog as a preferred source on Google

Comments

Share your thoughts and questions below