-
Notifications
You must be signed in to change notification settings - Fork 4k
/
notifications-resource-handler.ts
113 lines (96 loc) · 3.77 KB
/
notifications-resource-handler.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import * as fs from 'fs';
import * as path from 'path';
import { Construct } from 'constructs';
import * as iam from '../../../aws-iam';
import * as cdk from '../../../core';
export class NotificationsResourceHandlerProps {
role?: iam.IRole;
}
/**
* A Lambda-based custom resource handler that provisions S3 bucket
* notifications for a bucket.
*
* The resource property schema is:
*
* {
* BucketName: string, NotificationConfiguration: { see
* PutBucketNotificationConfiguration }
* }
*
* For 'Delete' operations, we send an empty NotificationConfiguration as
* required. We propagate errors and results as-is.
*
* Sadly, we can't use aws-cdk-lib/aws-lambda as it will introduce a dependency
* cycle, so this uses raw `cdk.Resource`s.
*/
export class NotificationsResourceHandler extends Construct {
/**
* Defines a stack-singleton lambda function with the logic for a CloudFormation custom
* resource that provisions bucket notification configuration for a bucket.
*
* @returns The ARN of the custom resource lambda function.
*/
public static singleton(context: Construct, props: NotificationsResourceHandlerProps = {}) {
const root = cdk.Stack.of(context);
// well-known logical id to ensure stack singletonity
const logicalId = 'BucketNotificationsHandler050a0587b7544547bf325f094a3db834';
let lambda = root.node.tryFindChild(logicalId) as NotificationsResourceHandler;
if (!lambda) {
lambda = new NotificationsResourceHandler(root, logicalId, props);
}
return lambda;
}
/**
* The ARN of the handler's lambda function. Used as a service token in the
* custom resource.
*/
public readonly functionArn: string;
/**
* The role of the handler's lambda function.
*/
public readonly role: iam.IRole;
constructor(scope: Construct, id: string, props: NotificationsResourceHandlerProps = {}) {
super(scope, id);
this.role = props.role ?? new iam.Role(this, 'Role', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
});
this.role.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
);
this.role.addToPrincipalPolicy(new iam.PolicyStatement({
actions: ['s3:PutBucketNotification'],
resources: ['*'],
}));
const resourceType = 'AWS::Lambda::Function';
class InLineLambda extends cdk.CfnResource {
public readonly tags: cdk.TagManager = new cdk.TagManager(cdk.TagType.STANDARD, resourceType);
protected renderProperties(properties: any): { [key: string]: any } {
properties.Tags = cdk.listMapper(cdk.cfnTagToCloudFormation)(this.tags.renderTags());
delete properties.tags;
return properties;
}
}
const handlerSource = fs.readFileSync(path.join(__dirname, 'lambda/index.py'), 'utf8');
// Removing lines that starts with '#' (comment lines) in order to fit the 4096 limit
const handlerSourceWithoutComments = handlerSource.replace(/^ *#.*\n?/gm, '');
if (handlerSourceWithoutComments.length > 4096) {
throw new Error(`Source of Notifications Resource Handler is too large (${handlerSourceWithoutComments.length} > 4096)`);
}
const resource = new InLineLambda(this, 'Resource', {
type: resourceType,
properties: {
Description: 'AWS CloudFormation handler for "Custom::S3BucketNotifications" resources (@aws-cdk/aws-s3)',
Code: { ZipFile: handlerSourceWithoutComments },
Handler: 'index.handler',
Role: this.role.roleArn,
Runtime: 'python3.9',
Timeout: 300,
},
});
resource.node.addDependency(this.role);
this.functionArn = resource.getAtt('Arn').toString();
}
public addToRolePolicy(statement: iam.PolicyStatement) {
this.role.addToPrincipalPolicy(statement);
}
}