Implementing S3 Bucket Policies Using AWS CDK

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.

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 { Effect, PolicyStatement, 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);

    // 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 ServicePrincipal('lambda.amazonaws.com')],
        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 { Effect, PolicyStatement, 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);

    // 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 ServicePrincipal('lambda.amazonaws.com')],
        actions: ['s3:GetObject'],
        resources: [`${implicitBucket.bucketArn}/*`],
      }),
    );

    // Access the bucket policy
    implicitBucket.policy?.document.addStatements(
      new PolicyStatement({
        effect: Effect.ALLOW,
        principals: [new ServicePrincipal('lambda.amazonaws.com')],
        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. Happy coding!

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

Find the complete CDK code below.

Did you find this article valuable?

Support Mikaeel Khalid by becoming a sponsor. Any amount is appreciated!