Skip to content

Commit

Permalink
chore(s3): readme update with mixing L1 and L2 bucket policy (#31437)
Browse files Browse the repository at this point in the history
### Issue # (if applicable)

Closes #30148

### Reason for this change

Users using L1 and L2 bucket policy with `serverAccessLogsBucket` would cause bucket policy overwrite instead of append.

### Description of changes

No behavioural change, only readme update to explain the issues and the workaround.

### Description of how you validated changes

No behavioural change.

### Checklist
- [ ] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
GavinZZ authored Sep 13, 2024
1 parent 36baf51 commit 84701d6
Showing 1 changed file with 111 additions and 0 deletions.
111 changes: 111 additions & 0 deletions packages/aws-cdk-lib/aws-s3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,117 @@ const bucket = new s3.Bucket(this, 'MyBucket', {
});
```

The above code will create a new bucket policy if none exists or update the
existing bucket policy to allow access log delivery.

However, there could be an edge case if the `accessLogsBucket` also defines a bucket
policy resource using the L1 Construct. Although the mixing of L1 and L2 Constructs is not
recommended, there are no mechanisms in place to prevent users from doing this at the moment.

```ts
const bucketName = "my-favorite-bucket-name";
const accessLogsBucket = new s3.Bucket(this, 'AccessLogsBucket', {
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED,
bucketName,
});

// Creating a bucket policy using L1
const bucketPolicy = new s3.CfnBucketPolicy(this, "BucketPolicy", {
bucket: bucketName,
policyDocument: {
Statement: [
{
Action: 's3:*',
Effect: 'Deny',
Principal: {
AWS: '*',
},
Resource: [
accessLogsBucket.bucketArn,
`${accessLogsBucket.bucketArn}/*`
],
},
],
Version: '2012-10-17',
},
});

// 'serverAccessLogsBucket' will create a new L2 bucket policy
// to allow log delivery and overwrite the L1 bucket policy.
const bucket = new s3.Bucket(this, 'MyBucket', {
serverAccessLogsBucket: accessLogsBucket,
serverAccessLogsPrefix: 'logs',
});
```

The above example uses the L2 Bucket Construct with the L1 CfnBucketPolicy Construct. However,
when `serverAccessLogsBucket` is set, a new L2 Bucket Policy resource will be created
which overwrites the permissions defined in the L1 Bucket Policy causing unintended
behaviours.

As noted above, we highly discourage the mixed usage of L1 and L2 Constructs. The recommended
approach would to define the bucket policy using `addToResourcePolicy` method.

```ts
const accessLogsBucket = new s3.Bucket(this, 'AccessLogsBucket', {
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED,
});

accessLogsBucket.addToResourcePolicy(
new iam.PolicyStatement({
actions: ['s3:*'],
resources: [accessLogsBucket.bucketArn, accessLogsBucket.arnForObjects('*')],
principals: [new iam.AnyPrincipal()],
})
)

const bucket = new s3.Bucket(this, 'MyBucket', {
serverAccessLogsBucket: accessLogsBucket,
serverAccessLogsPrefix: 'logs',
});
```

Alternatively, users can use the L2 Bucket Policy Construct
`BucketPolicy.fromCfnBucketPolicy` to wrap around `CfnBucketPolicy` Construct. This will allow the subsequent bucket policy generated by `serverAccessLogsBucket` usage to append to the existing bucket policy instead of overwriting.

```ts
const bucketName = "my-favorite-bucket-name";
const accessLogsBucket = new s3.Bucket(this, 'AccessLogsBucket', {
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED,
bucketName,
});

const bucketPolicy = new s3.CfnBucketPolicy(this, "BucketPolicy", {
bucket: bucketName,
policyDocument: {
Statement: [
{
Action: 's3:*',
Effect: 'Deny',
Principal: {
AWS: '*',
},
Resource: [
accessLogsBucket.bucketArn,
`${accessLogsBucket.bucketArn}/*`
],
},
],
Version: '2012-10-17',
},
});

// Wrap L1 Construct with L2 Bucket Policy Construct. Subsequent
// generated bucket policy to allow access log delivery would append
// to the current policy.
s3.BucketPolicy.fromCfnBucketPolicy(bucketPolicy);

const bucket = new s3.Bucket(this, 'MyBucket', {
serverAccessLogsBucket: accessLogsBucket,
serverAccessLogsPrefix: 'logs',
});
```

## S3 Inventory

An [inventory](https://docs.aws.amazon.com/AmazonS3/latest/dev/storage-inventory.html) contains a list of the objects in the source bucket and metadata for each object. The inventory lists are stored in the destination bucket as a CSV file compressed with GZIP, as an Apache optimized row columnar (ORC) file compressed with ZLIB, or as an Apache Parquet (Parquet) file compressed with Snappy.
Expand Down

0 comments on commit 84701d6

Please sign in to comment.