Skip to content

Commit 319bbf9

Browse files
authored
Merge branch 'master' into benisrae/implicit-app-root-stack
2 parents 68796a1 + 5e087e5 commit 319bbf9

File tree

6 files changed

+491
-4
lines changed

6 files changed

+491
-4
lines changed

packages/@aws-cdk/aws-apigateway/lib/json-schema.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export interface JsonSchema {
6363
readonly minProperties?: number;
6464
readonly required?: string[];
6565
readonly properties?: { [name: string]: JsonSchema };
66-
readonly additionalProperties?: boolean;
66+
readonly additionalProperties?: JsonSchema | boolean;
6767
readonly patternProperties?: { [name: string]: JsonSchema };
6868
readonly dependencies?: { [name: string]: JsonSchema | string[] };
6969
readonly propertyNames?: JsonSchema;

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

+48
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,54 @@ const bucket = new Bucket(this, 'MyBucket', {
224224

225225
[S3 Server access logging]: https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerLogs.html
226226

227+
### S3 Inventory
228+
229+
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.
230+
231+
You can configure multiple inventory lists for a bucket. You can configure what object metadata to include in the inventory, whether to list all object versions or only current versions, where to store the inventory list file output, and whether to generate the inventory on a daily or weekly basis.
232+
233+
```ts
234+
const inventoryBucket = new s3.Bucket(this, 'InventoryBucket');
235+
236+
const dataBucket = new s3.Bucket(this, 'DataBucket', {
237+
inventories: [
238+
{
239+
frequency: s3.InventoryFrequency.DAILY,
240+
includeObjectVersions: s3.InventoryObjectVersion.CURRENT,
241+
destination: {
242+
bucket: inventoryBucket,
243+
},
244+
},
245+
{
246+
frequency: s3.InventoryFrequency.WEEKLY,
247+
includeObjectVersions: s3.InventoryObjectVersion.ALL,
248+
destination: {
249+
bucket: inventoryBucket,
250+
prefix: 'with-all-versions',
251+
},
252+
}
253+
]
254+
});
255+
```
256+
257+
If the destination bucket is created as part of the same CDK application, the necessary permissions will be automatically added to the bucket policy.
258+
However, if you use an imported bucket (i.e `Bucket.fromXXX()`), you'll have to make sure it contains the following policy document:
259+
260+
```
261+
{
262+
"Version": "2012-10-17",
263+
"Statement": [
264+
{
265+
"Sid": "InventoryAndAnalyticsExamplePolicy",
266+
"Effect": "Allow",
267+
"Principal": { "Service": "s3.amazonaws.com" },
268+
"Action": "s3:PutObject",
269+
"Resource": ["arn:aws:s3:::destinationBucket/*"]
270+
}
271+
]
272+
}
273+
```
274+
227275
### Website redirection
228276

229277
You can use the two following properties to specify the bucket [redirection policy]. Please note that these methods cannot both be applied to the same bucket.

packages/@aws-cdk/aws-s3/lib/bucket.ts

+193-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
import { EOL } from 'os';
12
import * as events from '@aws-cdk/aws-events';
23
import * as iam from '@aws-cdk/aws-iam';
34
import * as kms from '@aws-cdk/aws-kms';
45
import { Construct, Fn, IResource, Lazy, RemovalPolicy, Resource, Stack, Token } from '@aws-cdk/core';
5-
import { EOL } from 'os';
66
import { BucketPolicy } from './bucket-policy';
77
import { IBucketNotificationDestination } from './destination';
88
import { BucketNotifications } from './notifications-resource';
@@ -828,6 +828,130 @@ export interface RedirectTarget {
828828
readonly protocol?: RedirectProtocol;
829829
}
830830

831+
/**
832+
* All supported inventory list formats.
833+
*/
834+
export enum InventoryFormat {
835+
/**
836+
* Generate the inventory list as CSV.
837+
*/
838+
CSV = 'CSV',
839+
/**
840+
* Generate the inventory list as Parquet.
841+
*/
842+
PARQUET = 'Parquet',
843+
/**
844+
* Generate the inventory list as Parquet.
845+
*/
846+
ORC = 'ORC',
847+
}
848+
849+
/**
850+
* All supported inventory frequencies.
851+
*/
852+
export enum InventoryFrequency {
853+
/**
854+
* A report is generated every day.
855+
*/
856+
DAILY = 'Daily',
857+
/**
858+
* A report is generated every Sunday (UTC timezone) after the initial report.
859+
*/
860+
WEEKLY = 'Weekly'
861+
}
862+
863+
/**
864+
* Inventory version support.
865+
*/
866+
export enum InventoryObjectVersion {
867+
/**
868+
* Includes all versions of each object in the report.
869+
*/
870+
ALL = 'All',
871+
/**
872+
* Includes only the current version of each object in the report.
873+
*/
874+
CURRENT = 'Current',
875+
}
876+
877+
/**
878+
* The destination of the inventory.
879+
*/
880+
export interface InventoryDestination {
881+
/**
882+
* Bucket where all inventories will be saved in.
883+
*/
884+
readonly bucket: IBucket;
885+
/**
886+
* The prefix to be used when saving the inventory.
887+
*
888+
* @default - No prefix.
889+
*/
890+
readonly prefix?: string;
891+
/**
892+
* The account ID that owns the destination S3 bucket.
893+
* If no account ID is provided, the owner is not validated before exporting data.
894+
* It's recommended to set an account ID to prevent problems if the destination bucket ownership changes.
895+
*
896+
* @default - No account ID.
897+
*/
898+
readonly bucketOwner?: string;
899+
}
900+
901+
/**
902+
* Specifies the inventory configuration of an S3 Bucket.
903+
*
904+
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/storage-inventory.html
905+
*/
906+
export interface Inventory {
907+
/**
908+
* The destination of the inventory.
909+
*/
910+
readonly destination: InventoryDestination;
911+
/**
912+
* The inventory will only include objects that meet the prefix filter criteria.
913+
*
914+
* @default - No objects prefix
915+
*/
916+
readonly objectsPrefix?: string;
917+
/**
918+
* The format of the inventory.
919+
*
920+
* @default InventoryFormat.CSV
921+
*/
922+
readonly format?: InventoryFormat;
923+
/**
924+
* Whether the inventory is enabled or disabled.
925+
*
926+
* @default true
927+
*/
928+
readonly enabled?: boolean;
929+
/**
930+
* The inventory configuration ID.
931+
*
932+
* @default - generated ID.
933+
*/
934+
readonly inventoryId?: string;
935+
/**
936+
* Frequency at which the inventory should be generated.
937+
*
938+
* @default InventoryFrequency.WEEKLY
939+
*/
940+
readonly frequency?: InventoryFrequency;
941+
/**
942+
* If the inventory should contain all the object versions or only the current one.
943+
*
944+
* @default InventoryObjectVersion.ALL
945+
*/
946+
readonly includeObjectVersions?: InventoryObjectVersion;
947+
/**
948+
* A list of optional fields to be included in the inventory result.
949+
*
950+
* @default - No optional fields.
951+
*/
952+
readonly optionalFields?: string[];
953+
}
954+
831955
export interface BucketProps {
832956
/**
833957
* The kind of server-side encryption to apply to this bucket.
@@ -966,6 +1090,15 @@ export interface BucketProps {
9661090
* @default - No log file prefix
9671091
*/
9681092
readonly serverAccessLogsPrefix?: string;
1093+
1094+
/**
1095+
* The inventory configuration of the bucket.
1096+
*
1097+
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/storage-inventory.html
1098+
*
1099+
* @default - No inventory configuration
1100+
*/
1101+
readonly inventories?: Inventory[];
9691102
}
9701103

9711104
/**
@@ -1055,6 +1188,7 @@ export class Bucket extends BucketBase {
10551188
private readonly notifications: BucketNotifications;
10561189
private readonly metrics: BucketMetrics[] = [];
10571190
private readonly cors: CorsRule[] = [];
1191+
private readonly inventories: Inventory[] = [];
10581192

10591193
constructor(scope: Construct, id: string, props: BucketProps = {}) {
10601194
super(scope, id, {
@@ -1079,6 +1213,7 @@ export class Bucket extends BucketBase {
10791213
corsConfiguration: Lazy.anyValue({ produce: () => this.parseCorsConfiguration() }),
10801214
accessControl: Lazy.stringValue({ produce: () => this.accessControl }),
10811215
loggingConfiguration: this.parseServerAccessLogs(props),
1216+
inventoryConfigurations: Lazy.anyValue({ produce: () => this.parseInventoryConfiguration() }),
10821217
});
10831218

10841219
resource.applyRemovalPolicy(props.removalPolicy);
@@ -1107,6 +1242,10 @@ export class Bucket extends BucketBase {
11071242
props.serverAccessLogsBucket.allowLogDelivery();
11081243
}
11091244

1245+
for (const inventory of props.inventories ?? []) {
1246+
this.addInventory(inventory);
1247+
}
1248+
11101249
// Add all bucket metric configurations rules
11111250
(props.metrics || []).forEach(this.addMetric.bind(this));
11121251
// Add all cors configuration rules
@@ -1204,6 +1343,15 @@ export class Bucket extends BucketBase {
12041343
return this.addEventNotification(EventType.OBJECT_REMOVED, dest, ...filters);
12051344
}
12061345

1346+
/**
1347+
* Add an inventory configuration.
1348+
*
1349+
* @param inventory configuration to add
1350+
*/
1351+
public addInventory(inventory: Inventory): void {
1352+
this.inventories.push(inventory);
1353+
}
1354+
12071355
private validateBucketName(physicalName: string): void {
12081356
const bucketName = physicalName;
12091357
if (!bucketName || Token.isUnresolved(bucketName)) {
@@ -1460,6 +1608,50 @@ export class Bucket extends BucketBase {
14601608

14611609
this.accessControl = BucketAccessControl.LOG_DELIVERY_WRITE;
14621610
}
1611+
1612+
private parseInventoryConfiguration(): CfnBucket.InventoryConfigurationProperty[] | undefined {
1613+
if (!this.inventories || this.inventories.length === 0) {
1614+
return undefined;
1615+
}
1616+
1617+
return this.inventories.map((inventory, index) => {
1618+
const format = inventory.format ?? InventoryFormat.CSV;
1619+
const frequency = inventory.frequency ?? InventoryFrequency.WEEKLY;
1620+
const id = inventory.inventoryId ?? `${this.node.id}Inventory${index}`;
1621+
1622+
if (inventory.destination.bucket instanceof Bucket) {
1623+
inventory.destination.bucket.addToResourcePolicy(new iam.PolicyStatement({
1624+
effect: iam.Effect.ALLOW,
1625+
actions: ['s3:PutObject'],
1626+
resources: [
1627+
inventory.destination.bucket.bucketArn,
1628+
inventory.destination.bucket.arnForObjects(`${inventory.destination.prefix ?? ''}*`),
1629+
],
1630+
principals: [new iam.ServicePrincipal('s3.amazonaws.com')],
1631+
conditions: {
1632+
ArnLike: {
1633+
'aws:SourceArn': this.bucketArn,
1634+
},
1635+
},
1636+
}));
1637+
}
1638+
1639+
return {
1640+
id,
1641+
destination: {
1642+
bucketArn: inventory.destination.bucket.bucketArn,
1643+
bucketAccountId: inventory.destination.bucketOwner,
1644+
prefix: inventory.destination.prefix,
1645+
format,
1646+
},
1647+
enabled: inventory.enabled ?? true,
1648+
includedObjectVersions: inventory.includeObjectVersions ?? InventoryObjectVersion.ALL,
1649+
scheduleFrequency: frequency,
1650+
optionalFields: inventory.optionalFields,
1651+
prefix: inventory.objectsPrefix,
1652+
};
1653+
});
1654+
}
14631655
}
14641656

14651657
/**

0 commit comments

Comments
 (0)