Skip to content

Commit 122a232

Browse files
authored
feat(events): EventBus.grantPutEventsTo method for granular grants (#13429)
Right now EventBus has a static method `grantPutEvents()` which grants PutEvents to all EventBridge buses in the account. Adding a `grantPutEventsTo()` method to the IEventBus interface that grants PutEvents to the specific event bus. We are also deprecating `grantPutEvents()` in favor to `grantAllPutEvents()` which has the same behavior. Closes #11228. *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent f5a6647 commit 122a232

File tree

7 files changed

+217
-60
lines changed

7 files changed

+217
-60
lines changed

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

+14
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,17 @@ bus.archive('MyArchive', {
186186
retention: cdk.Duration.days(365),
187187
});
188188
```
189+
190+
## Granting PutEvents to an existing EventBus
191+
192+
To import an existing EventBus into your CDK application, use `EventBus.fromEventBusArn` or `EventBus.fromEventBusAttributes`
193+
factory method.
194+
195+
Then, you can use the `grantPutEventsTo` method to grant `event:PutEvents` to the eventBus.
196+
197+
```ts
198+
const eventBus = EventBus.fromEventBusArn(this, 'ImportedEventBus', 'arn:aws:events:us-east-1:111111111:event-bus/my-event-bus');
199+
200+
// now you can just call methods on the eventbus
201+
eventBus.grantPutEventsTo(lambdaFunction);
202+
```

packages/@aws-cdk/aws-events/lib/event-bus.ts

+31
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ export interface IEventBus extends IResource {
4747
* @param props Properties of the archive
4848
*/
4949
archive(id: string, props: BaseArchiveProps): Archive;
50+
51+
/**
52+
* Grants an IAM Principal to send custom events to the eventBus
53+
* so that they can be matched to rules.
54+
*
55+
* @param grantee The principal (no-op if undefined)
56+
*/
57+
grantPutEventsTo(grantee: iam.IGrantable): iam.Grant;
5058
}
5159

5260
/**
@@ -137,6 +145,14 @@ abstract class EventBusBase extends Resource implements IEventBus {
137145
archiveName: props.archiveName,
138146
});
139147
}
148+
149+
public grantPutEventsTo(grantee: iam.IGrantable): iam.Grant {
150+
return iam.Grant.addToPrincipal({
151+
grantee,
152+
actions: ['events:PutEvents'],
153+
resourceArns: [this.eventBusArn],
154+
});
155+
}
140156
}
141157

142158
/**
@@ -177,6 +193,7 @@ export class EventBus extends EventBusBase {
177193
* so that they can be matched to rules.
178194
*
179195
* @param grantee The principal (no-op if undefined)
196+
* @deprecated use grantAllPutEvents instead
180197
*/
181198
public static grantPutEvents(grantee: iam.IGrantable): iam.Grant {
182199
// It's currently not possible to restrict PutEvents to specific resources.
@@ -188,6 +205,20 @@ export class EventBus extends EventBusBase {
188205
});
189206
}
190207

208+
/**
209+
* Permits an IAM Principal to send custom events to EventBridge
210+
* so that they can be matched to rules.
211+
*
212+
* @param grantee The principal (no-op if undefined)
213+
*/
214+
public static grantAllPutEvents(grantee: iam.IGrantable): iam.Grant {
215+
return iam.Grant.addToPrincipal({
216+
grantee,
217+
actions: ['events:PutEvents'],
218+
resourceArns: ['*'],
219+
});
220+
}
221+
191222
private static eventBusProps(defaultEventBusName: string, props?: EventBusProps) {
192223
if (props) {
193224
const { eventBusName, eventSourceName } = props;

packages/@aws-cdk/aws-events/test/test.event-bus.ts

+70
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,76 @@ export = {
306306

307307
test.done();
308308
},
309+
310+
'can grant PutEvents using grantAllPutEvents'(test: Test) {
311+
// GIVEN
312+
const stack = new Stack();
313+
const role = new iam.Role(stack, 'Role', {
314+
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
315+
});
316+
317+
// WHEN
318+
EventBus.grantAllPutEvents(role);
319+
320+
// THEN
321+
expect(stack).to(haveResource('AWS::IAM::Policy', {
322+
PolicyDocument: {
323+
Statement: [
324+
{
325+
Action: 'events:PutEvents',
326+
Effect: 'Allow',
327+
Resource: '*',
328+
},
329+
],
330+
Version: '2012-10-17',
331+
},
332+
Roles: [
333+
{
334+
Ref: 'Role1ABCC5F0',
335+
},
336+
],
337+
}));
338+
339+
test.done();
340+
},
341+
'can grant PutEvents to a specific event bus'(test: Test) {
342+
// GIVEN
343+
const stack = new Stack();
344+
const role = new iam.Role(stack, 'Role', {
345+
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
346+
});
347+
348+
const eventBus = new EventBus(stack, 'EventBus');
349+
350+
// WHEN
351+
eventBus.grantPutEventsTo(role);
352+
353+
// THEN
354+
expect(stack).to(haveResource('AWS::IAM::Policy', {
355+
PolicyDocument: {
356+
Statement: [
357+
{
358+
Action: 'events:PutEvents',
359+
Effect: 'Allow',
360+
Resource: {
361+
'Fn::GetAtt': [
362+
'EventBus7B8748AA',
363+
'Arn',
364+
],
365+
},
366+
},
367+
],
368+
Version: '2012-10-17',
369+
},
370+
Roles: [
371+
{
372+
Ref: 'Role1ABCC5F0',
373+
},
374+
],
375+
}));
376+
377+
test.done();
378+
},
309379
'can archive events'(test: Test) {
310380
// GIVEN
311381
const stack = new Stack();

packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts

+17-7
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,25 @@ export class EventBridgeDestination implements lambda.IDestination {
2222
* Returns a destination configuration
2323
*/
2424
public bind(_scope: Construct, fn: lambda.IFunction, _options?: lambda.DestinationOptions): lambda.DestinationConfig {
25-
// deduplicated automatically
26-
events.EventBus.grantPutEvents(fn); // Cannot restrict to a specific resource
25+
if (this.eventBus) {
26+
this.eventBus.grantPutEventsTo(fn);
27+
28+
return {
29+
destination: this.eventBus.eventBusArn,
30+
};
31+
}
32+
33+
const existingDefaultEventBus = _scope.node.tryFindChild('DefaultEventBus');
34+
let eventBus = (existingDefaultEventBus as events.EventBus) || events.EventBus.fromEventBusArn(_scope, 'DefaultEventBus', Stack.of(fn).formatArn({
35+
service: 'events',
36+
resource: 'event-bus',
37+
resourceName: 'default',
38+
}));
39+
40+
eventBus.grantPutEventsTo(fn);
2741

2842
return {
29-
destination: this.eventBus && this.eventBus.eventBusArn || Stack.of(fn).formatArn({
30-
service: 'events',
31-
resource: 'event-bus',
32-
resourceName: 'default',
33-
}),
43+
destination: eventBus.eventBusArn,
3444
};
3545
}
3646
}

packages/@aws-cdk/aws-lambda-destinations/test/destinations.test.ts

+25-50
Original file line numberDiff line numberDiff line change
@@ -47,56 +47,12 @@ test('event bus as destination', () => {
4747
{
4848
Action: 'events:PutEvents',
4949
Effect: 'Allow',
50-
Resource: '*',
51-
},
52-
],
53-
Version: '2012-10-17',
54-
},
55-
});
56-
});
57-
58-
test('event bus as destination defaults to default event bus', () => {
59-
// WHEN
60-
new lambda.Function(stack, 'Function', {
61-
...lambdaProps,
62-
onSuccess: new destinations.EventBridgeDestination(),
63-
});
64-
65-
// THEN
66-
expect(stack).toHaveResource('AWS::Lambda::EventInvokeConfig', {
67-
DestinationConfig: {
68-
OnSuccess: {
69-
Destination: {
70-
'Fn::Join': [
71-
'',
72-
[
73-
'arn:',
74-
{
75-
Ref: 'AWS::Partition',
76-
},
77-
':events:',
78-
{
79-
Ref: 'AWS::Region',
80-
},
81-
':',
82-
{
83-
Ref: 'AWS::AccountId',
84-
},
85-
':event-bus/default',
50+
Resource: {
51+
'Fn::GetAtt': [
52+
'EventBus7B8748AA',
53+
'Arn',
8654
],
87-
],
88-
},
89-
},
90-
},
91-
});
92-
93-
expect(stack).toHaveResource('AWS::IAM::Policy', {
94-
PolicyDocument: {
95-
Statement: [
96-
{
97-
Action: 'events:PutEvents',
98-
Effect: 'Allow',
99-
Resource: '*',
55+
},
10056
},
10157
],
10258
Version: '2012-10-17',
@@ -215,7 +171,26 @@ test('lambda payload as destination', () => {
215171
{
216172
Action: 'events:PutEvents',
217173
Effect: 'Allow',
218-
Resource: '*',
174+
Resource: {
175+
'Fn::Join': [
176+
'',
177+
[
178+
'arn:',
179+
{
180+
Ref: 'AWS::Partition',
181+
},
182+
':events:',
183+
{
184+
Ref: 'AWS::Region',
185+
},
186+
':',
187+
{
188+
Ref: 'AWS::AccountId',
189+
},
190+
':event-bus/default',
191+
],
192+
],
193+
},
219194
},
220195
],
221196
Version: '2012-10-17',

packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.expected.json

+20-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,26 @@
219219
{
220220
"Action": "events:PutEvents",
221221
"Effect": "Allow",
222-
"Resource": "*"
222+
"Resource": {
223+
"Fn::Join": [
224+
"",
225+
[
226+
"arn:",
227+
{
228+
"Ref": "AWS::Partition"
229+
},
230+
":events:",
231+
{
232+
"Ref": "AWS::Region"
233+
},
234+
":",
235+
{
236+
"Ref": "AWS::AccountId"
237+
},
238+
":event-bus/default"
239+
]
240+
]
241+
}
223242
},
224243
{
225244
"Action": "lambda:InvokeFunction",

packages/@aws-cdk/aws-lambda-destinations/test/integ.lambda-chain.expected.json

+40-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,26 @@
3939
{
4040
"Action": "events:PutEvents",
4141
"Effect": "Allow",
42-
"Resource": "*"
42+
"Resource": {
43+
"Fn::Join": [
44+
"",
45+
[
46+
"arn:",
47+
{
48+
"Ref": "AWS::Partition"
49+
},
50+
":events:",
51+
{
52+
"Ref": "AWS::Region"
53+
},
54+
":",
55+
{
56+
"Ref": "AWS::AccountId"
57+
},
58+
":event-bus/default"
59+
]
60+
]
61+
}
4362
}
4463
],
4564
"Version": "2012-10-17"
@@ -289,7 +308,26 @@
289308
{
290309
"Action": "events:PutEvents",
291310
"Effect": "Allow",
292-
"Resource": "*"
311+
"Resource": {
312+
"Fn::Join": [
313+
"",
314+
[
315+
"arn:",
316+
{
317+
"Ref": "AWS::Partition"
318+
},
319+
":events:",
320+
{
321+
"Ref": "AWS::Region"
322+
},
323+
":",
324+
{
325+
"Ref": "AWS::AccountId"
326+
},
327+
":event-bus/default"
328+
]
329+
]
330+
}
293331
}
294332
],
295333
"Version": "2012-10-17"

0 commit comments

Comments
 (0)