Daniel Birowsky Popeski
Daniel Birowsky Popeski

Reputation: 9266

How to add S3 BucketPolicy with AWS CDK?

I wanna translate this CloudFormation piece into CDK:

Type: AWS::S3::BucketPolicy
Properties:
  Bucket:
    Ref: S3BucketImageUploadBuffer
  PolicyDocument:
    Version: "2012-10-17"
    Statement:
      Action:
        - s3:PutObject
        - s3:PutObjectAcl
      Effect: Allow
      Resource:
        - ...

Looking at the documentation here, I don't see a way to provide the policy document itself.

Upvotes: 23

Views: 49720

Answers (6)

djvg
djvg

Reputation: 14255

If you really need to update the bucket policy, some of the other answers probably point in the right direction. However, none of them mention the "grant" methods.

From the aws-s3 docs:

Most of the time, you won't have to manipulate the bucket policy directly. Instead, buckets have "grant" methods called to give prepackaged sets of permissions to other resources. For example:

declare const myLambda: lambda.Function;

const bucket = new s3.Bucket(this, 'MyBucket');
bucket.grantReadWrite(myLambda);

Will give the Lambda's execution role permissions to read and write from the bucket.

And from the cdk docs:

Every construct that represents a resource that can be accessed, such as an Amazon S3 bucket or Amazon DynamoDB table, has methods that grant access to another entity. All such methods have names starting with grant.

For the OP's case, there's S3.Bucket.grantPut() (and grantPutAcl()).

Note the grant methods may achieve the desired result via another route, e.g. by adding an iam policy instead of updating the bucket policy. Check the generated cfn template to see what actually happens.

Upvotes: 1

Shivam Kumar
Shivam Kumar

Reputation: 11

The following functions works for IBucket(Referred bucket) policy update. I've added the imports as well. Hope it helps.

import { Bucket, CfnBucketPolicy, IBucket } from '@aws-cdk/aws-s3';
import { PolicyDocument, PolicyStatement } from '@aws-cdk/aws-iam';
  
  const existingBucket = Bucket.fromBucketAttributes(this, 'ExistingBucket', {
      bucketArn: `arn:aws:s3:::${existingBucketName}`,
    });  
  
  const policyStatement = new iam.PolicyStatement({
    actions:    [ 's3:GetObject' ],
    resources:  [ `${existingBucket.bucketArn}`,`${existingBucket.bucketArn}/*` ],
    principals: [ new iam.StarPrincipal() ],
    });
 
  const bucketPolicy = new CfnBucketPolicy(this, 'cloudfrontAccessBucketPolicy', {
      bucket: existingBucket.bucketName,
      policyDocument: new PolicyDocument({
        statements: [
          policyStatement
        ]
      })
    });

Upvotes: 1

cam8001
cam8001

Reputation: 1641

Building on @Thomas Wagner's answer, this is how I did this. I was trying to limit the bucket to a given IP range:

import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';
import * as s3Deployment from '@aws-cdk/aws-s3-deployment';
import * as iam from '@aws-cdk/aws-iam';

export class StaticSiteStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Bucket where frontend site goes.
    const mySiteBucket = new s3.Bucket(this, 'mySiteBucket', {
      websiteIndexDocument: "index.html"
    });

    let ipLimitPolicy = new iam.PolicyStatement({
      actions: ['s3:Get*', 's3:List*'],
      resources: [mySiteBucket.arnForObjects('*')],
      principals: [new iam.AnyPrincipal()]
    });
    ipLimitPolicy.addCondition('IpAddress', {
      "aws:SourceIp": ['1.2.3.4/22']
    });
    // Allow connections from my CIDR
    mySiteBucket.addToResourcePolicy(ipLimitPolicy);


    // Deploy assets
    const mySiteDeploy = new s3Deployment.BucketDeployment(this, 'deployAdminSite', {
      sources: [s3Deployment.Source.asset("./mysite")],
      destinationBucket: mySiteBucket
    });

  }
}

I was able to use the s3.arnForObjects() and iam.AnyPrincipal() helper functions rather than specifying ARNs or Principals directly.

The assets I want to deploy to the bucket are kept in the root of my project directory in a directory called mysite, and then referenced via a call to s3Deployment.BucketDeployment. This can be any directory your build process has access to, of course.

Upvotes: 21

jackofallcode
jackofallcode

Reputation: 1996

As per the original question, then the answer from @thomas-wagner is the way to go.

If anyone comes here looking for how to create the bucket policy for a CloudFront Distribution without creating a dependency on a bucket then you need to use the L1 construct CfnBucketPolicy (rough C# example below):

    IOriginAccessIdentity originAccessIdentity = new OriginAccessIdentity(this, "origin-access-identity", new OriginAccessIdentityProps
    {
        Comment = "Origin Access Identity",
    });

    PolicyStatement bucketAccessPolicy = new PolicyStatement(new PolicyStatementProps
    {
        Effect = Effect.ALLOW,
        Principals = new[]
        {
            originAccessIdentity.GrantPrincipal
        },
        Actions = new[]
        {
            "s3:GetObject",
        },
        Resources = new[]
        {
            Props.OriginBucket.ArnForObjects("*"),
        }
    });

    _ = new CfnBucketPolicy(this, $"bucket-policy", new CfnBucketPolicyProps
    {
        Bucket = Props.OriginBucket.BucketName,
        PolicyDocument = new PolicyDocument(new PolicyDocumentProps
        {
            Statements = new[]
            {
                bucketAccessPolicy,
            },
        }),
    });

Where Props.OriginBucket is an instance of IBucket (just a bucket).

Upvotes: 4

Thomas Wagner
Thomas Wagner

Reputation: 752

This is an example from a working CDK-Stack:

   artifactBucket.addToResourcePolicy(
      new PolicyStatement({
        resources: [
          this.pipeline.artifactBucket.arnForObjects("*"), 
          this.pipeline.artifactBucket.bucketArn],
        ],
        actions: ["s3:List*", "s3:Get*"],
        principals: [new ArnPrincipal(this.deploymentRole.roleArn)]
      })
    );

Upvotes: 32

Jason Wadsworth
Jason Wadsworth

Reputation: 8887

The CDK does this a little differently. I believe you are supposed to use bucket.addToResourcePolicy, as documented here.

Upvotes: 8

Related Questions