Skip to content

Commit 84701d6

Browse files
authored
chore(s3): readme update with mixing L1 and L2 bucket policy (#31437)
### 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*
1 parent 36baf51 commit 84701d6

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed

packages/aws-cdk-lib/aws-s3/README.md

+111
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,117 @@ const bucket = new s3.Bucket(this, 'MyBucket', {
492492
});
493493
```
494494

495+
The above code will create a new bucket policy if none exists or update the
496+
existing bucket policy to allow access log delivery.
497+
498+
However, there could be an edge case if the `accessLogsBucket` also defines a bucket
499+
policy resource using the L1 Construct. Although the mixing of L1 and L2 Constructs is not
500+
recommended, there are no mechanisms in place to prevent users from doing this at the moment.
501+
502+
```ts
503+
const bucketName = "my-favorite-bucket-name";
504+
const accessLogsBucket = new s3.Bucket(this, 'AccessLogsBucket', {
505+
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED,
506+
bucketName,
507+
});
508+
509+
// Creating a bucket policy using L1
510+
const bucketPolicy = new s3.CfnBucketPolicy(this, "BucketPolicy", {
511+
bucket: bucketName,
512+
policyDocument: {
513+
Statement: [
514+
{
515+
Action: 's3:*',
516+
Effect: 'Deny',
517+
Principal: {
518+
AWS: '*',
519+
},
520+
Resource: [
521+
accessLogsBucket.bucketArn,
522+
`${accessLogsBucket.bucketArn}/*`
523+
],
524+
},
525+
],
526+
Version: '2012-10-17',
527+
},
528+
});
529+
530+
// 'serverAccessLogsBucket' will create a new L2 bucket policy
531+
// to allow log delivery and overwrite the L1 bucket policy.
532+
const bucket = new s3.Bucket(this, 'MyBucket', {
533+
serverAccessLogsBucket: accessLogsBucket,
534+
serverAccessLogsPrefix: 'logs',
535+
});
536+
```
537+
538+
The above example uses the L2 Bucket Construct with the L1 CfnBucketPolicy Construct. However,
539+
when `serverAccessLogsBucket` is set, a new L2 Bucket Policy resource will be created
540+
which overwrites the permissions defined in the L1 Bucket Policy causing unintended
541+
behaviours.
542+
543+
As noted above, we highly discourage the mixed usage of L1 and L2 Constructs. The recommended
544+
approach would to define the bucket policy using `addToResourcePolicy` method.
545+
546+
```ts
547+
const accessLogsBucket = new s3.Bucket(this, 'AccessLogsBucket', {
548+
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED,
549+
});
550+
551+
accessLogsBucket.addToResourcePolicy(
552+
new iam.PolicyStatement({
553+
actions: ['s3:*'],
554+
resources: [accessLogsBucket.bucketArn, accessLogsBucket.arnForObjects('*')],
555+
principals: [new iam.AnyPrincipal()],
556+
})
557+
)
558+
559+
const bucket = new s3.Bucket(this, 'MyBucket', {
560+
serverAccessLogsBucket: accessLogsBucket,
561+
serverAccessLogsPrefix: 'logs',
562+
});
563+
```
564+
565+
Alternatively, users can use the L2 Bucket Policy Construct
566+
`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.
567+
568+
```ts
569+
const bucketName = "my-favorite-bucket-name";
570+
const accessLogsBucket = new s3.Bucket(this, 'AccessLogsBucket', {
571+
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED,
572+
bucketName,
573+
});
574+
575+
const bucketPolicy = new s3.CfnBucketPolicy(this, "BucketPolicy", {
576+
bucket: bucketName,
577+
policyDocument: {
578+
Statement: [
579+
{
580+
Action: 's3:*',
581+
Effect: 'Deny',
582+
Principal: {
583+
AWS: '*',
584+
},
585+
Resource: [
586+
accessLogsBucket.bucketArn,
587+
`${accessLogsBucket.bucketArn}/*`
588+
],
589+
},
590+
],
591+
Version: '2012-10-17',
592+
},
593+
});
594+
595+
// Wrap L1 Construct with L2 Bucket Policy Construct. Subsequent
596+
// generated bucket policy to allow access log delivery would append
597+
// to the current policy.
598+
s3.BucketPolicy.fromCfnBucketPolicy(bucketPolicy);
599+
600+
const bucket = new s3.Bucket(this, 'MyBucket', {
601+
serverAccessLogsBucket: accessLogsBucket,
602+
serverAccessLogsPrefix: 'logs',
603+
});
604+
```
605+
495606
## S3 Inventory
496607

497608
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.

0 commit comments

Comments
 (0)