Skip to content

Commit 5c3431b

Browse files
authored
fix(cloudtrail): addS3EventSelector does not expose all options (#1854)
The documentation suggested one could pass an object as the second argument to `CloudTrail.addS3EventSelector`, however the real argument was `ReadWriteType`. Additionally the documentation suggested incorrect uses of the API that would lead to invalid templates being generated. BREAKING CHANGE: The `CloudTrail.addS3EventSelector` accepts an options object instead of only a `ReadWriteType` value. Fixes #1841
1 parent f974531 commit 5c3431b

File tree

8 files changed

+181
-13
lines changed

8 files changed

+181
-13
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ const trail = new cloudtrail.CloudTrail(this, 'MyAmazingCloudTrail');
4949

5050
// Adds an event selector to the bucket magic-bucket.
5151
// By default, this includes management events and all operations (Read + Write)
52-
trail.addS3EventSelector(["arn:aws:s3:::magic-bucket/"]);
52+
trail.addS3EventSelector(["arn:aws:s3:::magic-bucket/"]);
5353

5454
// Adds an event selector to the bucket foo, with a specific configuration
55-
trail.addS3EventSelector(["arn:aws:s3:::foo"], {
55+
trail.addS3EventSelector(["arn:aws:s3:::foo/"], {
5656
includeManagementEvents: false,
5757
readWriteType: ReadWriteType.All,
5858
});

packages/@aws-cdk/aws-cloudtrail/lib/index.ts

+25-5
Original file line numberDiff line numberDiff line change
@@ -191,19 +191,20 @@ export class CloudTrail extends cdk.Construct {
191191
*
192192
* Data events: These events provide insight into the resource operations performed on or within a resource.
193193
* These are also known as data plane operations.
194-
* @param readWriteType the configuration type to log for this data event
195-
* Eg, ReadWriteType.ReadOnly will only log "read" events for S3 objects that match a filter)
194+
*
195+
* @param prefixes the list of object ARN prefixes to include in logging (maximum 250 entries).
196+
* @param options the options to configure logging of management and data events.
196197
*/
197-
public addS3EventSelector(prefixes: string[], readWriteType: ReadWriteType) {
198+
public addS3EventSelector(prefixes: string[], options: AddS3EventSelectorOptions = {}) {
198199
if (prefixes.length > 250) {
199200
throw new Error("A maximum of 250 data elements can be in one event selector");
200201
}
201202
if (this.eventSelectors.length > 5) {
202203
throw new Error("A maximum of 5 event selectors are supported per trail.");
203204
}
204205
this.eventSelectors.push({
205-
includeManagementEvents: false,
206-
readWriteType,
206+
includeManagementEvents: options.includeManagementEvents,
207+
readWriteType: options.readWriteType,
207208
dataResources: [{
208209
type: "AWS::S3::Object",
209210
values: prefixes
@@ -212,6 +213,25 @@ export class CloudTrail extends cdk.Construct {
212213
}
213214
}
214215

216+
/**
217+
* Options for adding an S3 event selector.
218+
*/
219+
export interface AddS3EventSelectorOptions {
220+
/**
221+
* Specifies whether to log read-only events, write-only events, or all events.
222+
*
223+
* @default ReadWriteType.All
224+
*/
225+
readWriteType?: ReadWriteType;
226+
227+
/**
228+
* Specifies whether the event selector includes management events for the trail.
229+
*
230+
* @default true
231+
*/
232+
includeManagementEvents?: boolean;
233+
}
234+
215235
interface EventSelector {
216236
readonly includeManagementEvents?: boolean;
217237
readonly readWriteType?: ReadWriteType;

packages/@aws-cdk/aws-cloudtrail/package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
"pkglint": "pkglint -f",
3636
"package": "cdk-package",
3737
"awslint": "cdk-awslint",
38-
"cfn2ts": "cfn2ts"
38+
"cfn2ts": "cfn2ts",
39+
"integ": "cdk-integ"
3940
},
4041
"cdk-build": {
4142
"cloudformation": "AWS::CloudTrail"
@@ -58,7 +59,8 @@
5859
"cdk-build-tools": "^0.24.1",
5960
"cfn2ts": "^0.24.1",
6061
"colors": "^1.2.1",
61-
"pkglint": "^0.24.1"
62+
"pkglint": "^0.24.1",
63+
"cdk-integ-tools": "^0.24.1"
6264
},
6365
"dependencies": {
6466
"@aws-cdk/aws-iam": "^0.24.1",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
{
2+
"Resources": {
3+
"Bucket83908E77": {
4+
"Type": "AWS::S3::Bucket"
5+
},
6+
"TrailS30071F172": {
7+
"Type": "AWS::S3::Bucket",
8+
"DeletionPolicy": "Retain"
9+
},
10+
"TrailS3PolicyE42170FE": {
11+
"Type": "AWS::S3::BucketPolicy",
12+
"Properties": {
13+
"Bucket": {
14+
"Ref": "TrailS30071F172"
15+
},
16+
"PolicyDocument": {
17+
"Statement": [
18+
{
19+
"Action": "s3:GetBucketAcl",
20+
"Effect": "Allow",
21+
"Principal": {
22+
"Service": "cloudtrail.amazonaws.com"
23+
},
24+
"Resource": {
25+
"Fn::GetAtt": [
26+
"TrailS30071F172",
27+
"Arn"
28+
]
29+
}
30+
},
31+
{
32+
"Action": "s3:PutObject",
33+
"Condition": {
34+
"StringEquals": {
35+
"s3:x-amz-acl": "bucket-owner-full-control"
36+
}
37+
},
38+
"Effect": "Allow",
39+
"Principal": {
40+
"Service": "cloudtrail.amazonaws.com"
41+
},
42+
"Resource": {
43+
"Fn::Join": [
44+
"",
45+
[
46+
{
47+
"Fn::GetAtt": [
48+
"TrailS30071F172",
49+
"Arn"
50+
]
51+
},
52+
"/AWSLogs/",
53+
{
54+
"Ref": "AWS::AccountId"
55+
},
56+
"/*"
57+
]
58+
]
59+
}
60+
}
61+
],
62+
"Version": "2012-10-17"
63+
}
64+
}
65+
},
66+
"Trail022F0CF2": {
67+
"Type": "AWS::CloudTrail::Trail",
68+
"Properties": {
69+
"IsLogging": true,
70+
"S3BucketName": {
71+
"Ref": "TrailS30071F172"
72+
},
73+
"EnableLogFileValidation": true,
74+
"EventSelectors": [
75+
{
76+
"DataResources": [
77+
{
78+
"Type": "AWS::S3::Object",
79+
"Values": [
80+
{
81+
"Fn::Join": [
82+
"",
83+
[
84+
{
85+
"Fn::GetAtt": [
86+
"Bucket83908E77",
87+
"Arn"
88+
]
89+
},
90+
"/"
91+
]
92+
]
93+
}
94+
]
95+
}
96+
]
97+
}
98+
],
99+
"IncludeGlobalServiceEvents": true,
100+
"IsMultiRegionTrail": true
101+
},
102+
"DependsOn": [
103+
"TrailS3PolicyE42170FE"
104+
]
105+
}
106+
}
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import s3 = require('@aws-cdk/aws-s3');
2+
import cdk = require('@aws-cdk/cdk');
3+
import cloudtrail = require('../lib');
4+
5+
const app = new cdk.App();
6+
const stack = new cdk.Stack(app, 'integ-cloudtrail');
7+
8+
const bucket = new s3.Bucket(stack, 'Bucket', { removalPolicy: cdk.RemovalPolicy.Destroy });
9+
10+
const trail = new cloudtrail.CloudTrail(stack, 'Trail');
11+
trail.addS3EventSelector([bucket.arnForObjects('')]);
12+
13+
app.run();

packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts

+28-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export = {
119119
const stack = getTestStack();
120120

121121
const cloudTrail = new CloudTrail(stack, 'MyAmazingCloudTrail');
122-
cloudTrail.addS3EventSelector(["arn:aws:s3:::"], ReadWriteType.All);
122+
cloudTrail.addS3EventSelector(["arn:aws:s3:::"]);
123123

124124
expect(stack).to(haveResource("AWS::CloudTrail::Trail"));
125125
expect(stack).to(haveResource("AWS::S3::Bucket"));
@@ -130,7 +130,33 @@ export = {
130130
const trail: any = stack.toCloudFormation().Resources.MyAmazingCloudTrail54516E8D;
131131
test.equals(trail.Properties.EventSelectors.length, 1);
132132
const selector = trail.Properties.EventSelectors[0];
133-
test.equals(selector.ReadWriteType, "All", "Expected selector read write type to be All");
133+
test.equals(selector.ReadWriteType, null, "Expected selector read write type to be undefined");
134+
test.equals(selector.IncludeManagementEvents, null, "Expected management events to be undefined");
135+
test.equals(selector.DataResources.length, 1, "Expected there to be one data resource");
136+
const dataResource = selector.DataResources[0];
137+
test.equals(dataResource.Type, "AWS::S3::Object", "Expected the data resrouce type to be AWS::S3::Object");
138+
test.equals(dataResource.Values.length, 1, "Expected there to be one value");
139+
test.equals(dataResource.Values[0], "arn:aws:s3:::", "Expected the first type value to be the S3 type");
140+
test.deepEqual(trail.DependsOn, ['MyAmazingCloudTrailS3Policy39C120B0']);
141+
test.done();
142+
},
143+
144+
'with hand-specified props'(test: Test) {
145+
const stack = getTestStack();
146+
147+
const cloudTrail = new CloudTrail(stack, 'MyAmazingCloudTrail');
148+
cloudTrail.addS3EventSelector(["arn:aws:s3:::"], { includeManagementEvents: false, readWriteType: ReadWriteType.ReadOnly });
149+
150+
expect(stack).to(haveResource("AWS::CloudTrail::Trail"));
151+
expect(stack).to(haveResource("AWS::S3::Bucket"));
152+
expect(stack).to(haveResource("AWS::S3::BucketPolicy", ExpectedBucketPolicyProperties));
153+
expect(stack).to(not(haveResource("AWS::Logs::LogGroup")));
154+
expect(stack).to(not(haveResource("AWS::IAM::Role")));
155+
156+
const trail: any = stack.toCloudFormation().Resources.MyAmazingCloudTrail54516E8D;
157+
test.equals(trail.Properties.EventSelectors.length, 1);
158+
const selector = trail.Properties.EventSelectors[0];
159+
test.equals(selector.ReadWriteType, "ReadOnly", "Expected selector read write type to be Read");
134160
test.equals(selector.IncludeManagementEvents, false, "Expected management events to be false");
135161
test.equals(selector.DataResources.length, 1, "Expected there to be one data resource");
136162
const dataResource = selector.DataResources[0];

packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const bucket = new s3.Bucket(stack, 'PipelineBucket', {
1717
});
1818
const key = 'key';
1919
const trail = new cloudtrail.CloudTrail(stack, 'CloudTrail');
20-
trail.addS3EventSelector([bucket.arnForObjects(key)], cloudtrail.ReadWriteType.WriteOnly);
20+
trail.addS3EventSelector([bucket.arnForObjects(key)], { readWriteType: cloudtrail.ReadWriteType.WriteOnly, includeManagementEvents: false });
2121
sourceStage.addAction(new s3.PipelineSourceAction({
2222
actionName: 'Source',
2323
outputArtifactName: 'SourceArtifact',

tools/decdk/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,4 @@
142142
"engines": {
143143
"node": ">= 8.10.0"
144144
}
145-
}
145+
}

0 commit comments

Comments
 (0)