diff --git a/.viperlightignore b/.viperlightignore index 844539a25..23105fd3c 100644 --- a/.viperlightignore +++ b/.viperlightignore @@ -133,3 +133,11 @@ source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wa source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.existing-waf-to-multiple-gateways.expected.json:814 # This is a test case source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/test.kinesisstream-gluejob.test.ts:127 +# aws-lambda-eventbridge. Auto generated Ids are ignored & x-ray resource permission with a `*` in test cases is ignored +source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunction.expected.json:122 +source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithNewEventBus.expected.json:149 +source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithVpc.expected.json:163 +source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingEventBus.expected.json:108 +source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingFunction.expected.json:122 +source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/aws-lambda-eventbridge.test.ts:28 +source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/aws-lambda-eventbridge.test.ts:339 \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts index e7eb7855b..2907360e5 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts @@ -181,11 +181,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-eventbus`, {}) }; - try { + const app = () => { new EventbridgeToKinesisFirehoseToS3(stack, 'test-eventbridge-firehose', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisstreams/test/eventbridge-kinesisstreams.test.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisstreams/test/eventbridge-kinesisstreams.test.ts index 099ca6241..0913ed91f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisstreams/test/eventbridge-kinesisstreams.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisstreams/test/eventbridge-kinesisstreams.test.ts @@ -122,11 +122,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-eventbus`, {}) }; - try { + const app = () => { new EventbridgeToKinesisStreams(stack, 'test-eventbridge-kinesisstreams', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-lambda/test/eventbridge-lambda.test.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-lambda/test/eventbridge-lambda.test.ts index 98dc99c88..14660b1d5 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-lambda/test/eventbridge-lambda.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-lambda/test/eventbridge-lambda.test.ts @@ -230,11 +230,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-eventbus`, {}) }; - try { + const app = () => { new EventbridgeToLambda(stack, 'test-eventbridge-lambda', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-sns/test/eventbridge-sns-topic.test.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-sns/test/eventbridge-sns-topic.test.ts index 1ad42fc09..af1d799bc 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-sns/test/eventbridge-sns-topic.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-sns/test/eventbridge-sns-topic.test.ts @@ -238,11 +238,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-new-eventbus`, {}) }; - try { + const app = () => { new EventbridgeToSns(stack, 'test-eventbridge-sns', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-sqs/test/eventbridge-sqs-queue.test.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-sqs/test/eventbridge-sqs-queue.test.ts index 684e59bd0..abdec3e61 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-sqs/test/eventbridge-sqs-queue.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-sqs/test/eventbridge-sqs-queue.test.ts @@ -299,11 +299,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-new-eventbus`, {}) }; - try { + const app = () => { new EventbridgeToSqs(stack, 'test-eventbridge-sqs', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-stepfunctions/test/eventbridge-stepfunctions.test.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-stepfunctions/test/eventbridge-stepfunctions.test.ts index 1477afdee..4aaf6bde6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-stepfunctions/test/eventbridge-stepfunctions.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-stepfunctions/test/eventbridge-stepfunctions.test.ts @@ -164,11 +164,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-new-eventbus`, {}) }; - try { + const app = () => { new EventbridgeToStepfunctions(stack, 'test-eventbridge-stepfunctions', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/events-rule-kinesisfirehose-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/events-rule-kinesisfirehose-s3.test.ts index 24c71773e..b0771826a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/events-rule-kinesisfirehose-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisfirehose-s3/test/events-rule-kinesisfirehose-s3.test.ts @@ -181,11 +181,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-eventbus`, {}) }; - try { + const app = () => { new EventsRuleToKinesisFirehoseToS3(stack, 'test-eventsrule-firehose', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/events-rule-kinesisstreams.test.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/events-rule-kinesisstreams.test.ts index b4c02fdfe..6a5c3b17f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/events-rule-kinesisstreams.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-kinesisstreams/test/events-rule-kinesisstreams.test.ts @@ -123,11 +123,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-eventbus`, {}) }; - try { + const app = () => { new EventsRuleToKinesisStreams(stack, 'test-eventsrule-kinesisstreams', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-lambda/test/events-rule-lambda.test.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-lambda/test/events-rule-lambda.test.ts index d8659d75a..94a73744f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-lambda/test/events-rule-lambda.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-lambda/test/events-rule-lambda.test.ts @@ -231,11 +231,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-eventbus`, {}) }; - try { + const app = () => { new EventsRuleToLambda(stack, 'test-eventsrule-lambda', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/test/events-rule-sns-topic.test.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/test/events-rule-sns-topic.test.ts index 9f47b3064..4de80ea44 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/test/events-rule-sns-topic.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/test/events-rule-sns-topic.test.ts @@ -238,11 +238,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-new-eventbus`, {}) }; - try { + const app = () => { new EventsRuleToSns(stack, 'test-eventsrule-sns', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/test/events-rule-sqs-queue.test.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/test/events-rule-sqs-queue.test.ts index 586fa0398..29477b2d3 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/test/events-rule-sqs-queue.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/test/events-rule-sqs-queue.test.ts @@ -299,11 +299,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-new-eventbus`, {}) }; - try { + const app = () => { new EventsRuleToSqs(stack, 'test-eventsrule-sqs', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-step-function/test/events-rule-step-function.test.ts b/source/patterns/@aws-solutions-constructs/aws-events-rule-step-function/test/events-rule-step-function.test.ts index d54c464d2..a431efc6a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-step-function/test/events-rule-step-function.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-step-function/test/events-rule-step-function.test.ts @@ -164,11 +164,10 @@ test('check exception while passing existingEventBus & eventBusProps', () => { existingEventBusInterface: new events.EventBus(stack, `test-existing-new-eventbus`, {}) }; - try { + const app = () => { new EventsRuleToStepFunction(stack, 'test-eventsrule-stepfunctions', props); - } catch (e) { - expect(e).toBeInstanceOf(Error); - } + }; + expect(app).toThrowError(); }); test('check custom event bus resource with props when deploy:true', () => { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/.eslintignore b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/.eslintignore new file mode 100755 index 000000000..0819e2e65 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/.eslintignore @@ -0,0 +1,5 @@ +lib/*.js +test/*.js +*.d.ts +coverage +test/lambda/index.js \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/.gitignore b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/.gitignore new file mode 100755 index 000000000..6773cabd2 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/.gitignore @@ -0,0 +1,15 @@ +lib/*.js +test/*.js +*.js.map +*.d.ts +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/.npmignore b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/.npmignore new file mode 100755 index 000000000..f66791629 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/.npmignore @@ -0,0 +1,21 @@ +# Exclude typescript source and config +*.ts +tsconfig.json +coverage +.nyc_output +*.tgz +*.snk +*.tsbuildinfo + +# Include javascript files and typescript declarations +!*.js +!*.d.ts + +# Exclude jsii outdir +dist + +# Include .jsii +!.jsii + +# Include .jsii +!.jsii \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/README.md b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/README.md new file mode 100755 index 000000000..a059141bb --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/README.md @@ -0,0 +1,88 @@ +# aws-lambda-eventbridge module + + +--- + +![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge) + +--- + + +| **Reference Documentation**:| https://docs.aws.amazon.com/solutions/latest/constructs/| +|:-------------|:-------------| +
+ +| **Language** | **Package** | +|:-------------|-----------------| +|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_lambda_eventbridge`| +|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-lambda-eventbridge`| +|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.lambdaeventbridge`| + +This AWS Solutions Construct implements an AWS Lambda function connected to an Amazon EventBridge. + +Here is a minimal deployable pattern definition in Typescript: + +``` typescript +import { LambdaToEventbridge, LambdaToEventbridgeProps } from "@aws-solutions-constructs/aws-lambda-eventbridge"; + +new LambdaToEventbridge(this, 'LambdaToEventbridgePattern', { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/lambda`) + } +}); + +``` + +## Initializer + +``` text +new LambdaToEventbridge(scope: Construct, id: string, props: LambdaToEventbridgeProps); +``` + +_Parameters_ + +* scope [`Construct`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html) +* id `string` +* props [`LambdaToEventbridgeProps`](#pattern-construct-props) + +## Pattern Construct Props + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|existingLambdaObj?|[`lambda.Function`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.Function.html)|An optional, existing Lambda function to be used instead of the default function. Providing both this and `lambdaFunctionProps` will cause an error.| +|lambdaFunctionProps?|[`lambda.FunctionProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.FunctionProps.html)|Optional user-provided properties to override the default properties for the Lambda function. | +|existingEventBusInterface?|[`events.IEventBus`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-events.IEventBus.html)| Optional user-provided custom EventBus for construct to use. Providing both this and `eventBusProps` results an error.| +|eventBusProps?|[`events.EventBusProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-events.EventBusProps.html)|Optional user-provided properties to override the default properties when creating a custom EventBus. Setting this value to `{}` will create a custom EventBus using all default properties. If neither this nor `existingEventBusInterface` is provided the construct will use the `default` EventBus. Providing both this and `existingEventBusInterface` results an error.| +|existingVpc?|[`ec2.IVpc`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.IVpc.html)|An optional, existing VPC into which this pattern should be deployed. When deployed in a VPC, the Lambda function will use ENIs in the VPC to access network resources and an Interface Endpoint will be created in the VPC for Amazon EventBridge. If an existing VPC is provided, the `deployVpc` property cannot be `true`. This uses `ec2.IVpc` to allow clients to supply VPCs that exist outside the stack using the [`ec2.Vpc.fromLookup()`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.Vpc.html#static-fromwbrlookupscope-id-options) method.| +|vpcProps?|[`ec2.VpcProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.VpcProps.html)|Optional user-provided properties to override the default properties for the new VPC. `enableDnsHostnames`, `enableDnsSupport`, `natGateways` and `subnetConfiguration` are set by the pattern, so any values for those properties supplied here will be overrriden. If `deployVpc` is not `true` then this property will be ignored.| +|deployVpc?|`boolean`|Whether to create a new VPC based on `vpcProps` into which to deploy this pattern. Setting this to true will deploy the minimal, most private VPC to run the pattern:If this property is `true` then `existingVpc` cannot be specified. Defaults to `false`.| +|eventBusEnvironmentVariableName?|`string`|Optional Name for the EventBus's name environment variable set for the Lambda function.| + +## Pattern Properties + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|lambdaFunction|[`lambda.Function`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.Function.html)|Returns an instance of the Lambda function created by the pattern.| +|eventBus?|[`events.IEventBus`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-events.IEventBus.html)|Returns the instance of events.IEventBus used by the construct| +|vpc?|[`ec2.IVpc`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.IVpc.html)|Returns an interface on the VPC used by the pattern (if any). This may be a VPC created by the pattern or the VPC supplied to the pattern constructor.| + +## Default settings + +Out of the box implementation of the Construct without any override will set the following defaults: + +### AWS Lambda Function +* Configure limited privilege access IAM role for Lambda function. +* Enable reusing connections with Keep-Alive for NodeJs Lambda function. +* Allow the function to put events to EventBus (custom EventBus can be used by specifying `existingEventBusInterface` or `eventBusProps` property). +* Enable X-Ray Tracing +* Set Environment Variables + * EVENTBUS_NAME + * AWS_NODEJS_CONNECTION_REUSE_ENABLED (for Node 10.x and higher functions) + +## Architecture +![Architecture Diagram](architecture.png) + +*** +© Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/architecture.png b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/architecture.png new file mode 100644 index 000000000..b2998524b Binary files /dev/null and b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/architecture.png differ diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/lib/index.ts new file mode 100755 index 000000000..e4defc145 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/lib/index.ts @@ -0,0 +1,135 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import * as defaults from "@aws-solutions-constructs/core"; +import * as lambda from "@aws-cdk/aws-lambda"; +import * as ec2 from "@aws-cdk/aws-ec2"; +import * as events from "@aws-cdk/aws-events"; +import { Construct } from "@aws-cdk/core"; + +/** + * @summary The properties for the LambdaToEventbridge class. + */ +export interface LambdaToEventbridgeProps { + /** + * Existing instance of Lambda Function object, providing both this and `lambdaFunctionProps` will cause an error. + * + * @default - None + */ + readonly existingLambdaObj?: lambda.Function; + /** + * User provided props to override the default props for the Lambda function. + * + * @default - Default properties are used. + */ + readonly lambdaFunctionProps?: lambda.FunctionProps; + /** + * Existing instance of a custom EventBus. + * + * @default - None + */ + readonly existingEventBusInterface?: events.IEventBus; + /** + * A new custom EventBus is created with provided props. + * + * @default - None + */ + readonly eventBusProps?: events.EventBusProps; + /** + * An existing VPC for the construct to use (construct will NOT create a new VPC in this case) + */ + readonly existingVpc?: ec2.IVpc; + /** + * Properties to override default properties if deployVpc is true + */ + readonly vpcProps?: ec2.VpcProps; + /** + * Whether to deploy a new VPC + * + * @default - false + */ + readonly deployVpc?: boolean; + /** + * Optional Name for the EventBus's name environment variable set for the Lambda function. + * + * @default - EVENTBUS_NAME is used + */ + readonly eventBusEnvironmentVariableName?: string; +} + +/** + * @summary The LambdaToEventbridge class. + */ +export class LambdaToEventbridge extends Construct { + public readonly lambdaFunction: lambda.Function; + public readonly eventBus?: events.IEventBus; + public readonly vpc?: ec2.IVpc; + + /** + * @summary Constructs a new instance of the LambdaToEventbridge class. + * @param {cdk.App} scope - represents the scope for all the resources. + * @param {string} id - this is a a scope-unique id. + * @param {LambdaToEventbridgeProps} props - user provided props for the construct. + * @since 1.120.0 + * @access public + */ + constructor(scope: Construct, id: string, props: LambdaToEventbridgeProps) { + super(scope, id); + defaults.CheckProps(props); + + if (props.deployVpc || props.existingVpc) { + if (props.deployVpc && props.existingVpc) { + throw new Error("More than 1 VPC specified in the properties"); + } + + this.vpc = defaults.buildVpc(scope, { + defaultVpcProps: defaults.DefaultIsolatedVpcProps(), + existingVpc: props.existingVpc, + userVpcProps: props.vpcProps, + constructVpcProps: { + enableDnsHostnames: true, + enableDnsSupport: true, + }, + }); + defaults.AddAwsServiceEndpoint(scope, this.vpc, defaults.ServiceEndpointTypes.EVENTS); + } + + // Setup the Lambda function + this.lambdaFunction = defaults.buildLambdaFunction(this, { + existingLambdaObj: props.existingLambdaObj, + lambdaFunctionProps: props.lambdaFunctionProps, + vpc: this.vpc, + }); + + // Build Event Bus + this.eventBus = defaults.buildEventBus(this, { + existingEventBusInterface: props.existingEventBusInterface, + eventBusProps: props.eventBusProps + }); + + // Configure environment variables + const eventBusEnvironmentVariableName = props.eventBusEnvironmentVariableName || 'EVENTBUS_NAME'; + const eventBusName = this.eventBus?.eventBusName || 'default'; + this.lambdaFunction.addEnvironment(eventBusEnvironmentVariableName, eventBusName); + + // Enable putevents permissions for the Lambda function by default + if (this.eventBus) { + this.eventBus.grantPutEventsTo(this.lambdaFunction); + } else { + // Since user didn't specify custom event bus, provide permissions on default event bus + const defaultEventBus = events.EventBus.fromEventBusName(this, 'default-event-bus', 'default'); + defaultEventBus.grantPutEventsTo(this.lambdaFunction); + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/package.json b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/package.json new file mode 100755 index 000000000..29c6f3ca5 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/package.json @@ -0,0 +1,98 @@ +{ + "name": "@aws-solutions-constructs/aws-lambda-eventbridge", + "version": "0.0.0", + "description": "CDK constructs for defining an interaction between an AWS Lambda function and an Amazon EventBridge.", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/awslabs/aws-solutions-constructs.git", + "directory": "source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge" + }, + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "scripts": { + "build": "tsc -b .", + "lint": "eslint -c ../eslintrc.yml --ext=.js,.ts . && tslint --project .", + "lint-fix": "eslint -c ../eslintrc.yml --ext=.js,.ts --fix .", + "test": "jest --coverage", + "clean": "tsc -b --clean", + "watch": "tsc -b -w", + "integ": "cdk-integ", + "integ-assert": "cdk-integ-assert", + "integ-no-clean": "cdk-integ --no-clean", + "jsii": "jsii", + "jsii-pacmak": "jsii-pacmak", + "build+lint+test": "npm run jsii && npm run lint && npm test && npm run integ-assert", + "snapshot-update": "npm run jsii && npm test -- -u && npm run integ-assert" + }, + "jsii": { + "outdir": "dist", + "targets": { + "java": { + "package": "software.amazon.awsconstructs.services.lambdaeventbridge", + "maven": { + "groupId": "software.amazon.awsconstructs", + "artifactId": "lambdaeventbridge" + } + }, + "dotnet": { + "namespace": "Amazon.Constructs.AWS.LambdaEventbridge", + "packageId": "Amazon.Constructs.AWS.LambdaEventbridge", + "signAssembly": true, + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "python": { + "distName": "aws-solutions-constructs.aws-lambda-eventbridge", + "module": "aws_solutions_constructs.aws_lambda_eventbridge" + } + } + }, + "dependencies": { + "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/aws-events": "0.0.0", + "@aws-cdk/aws-ec2": "0.0.0", + "@aws-cdk/core": "0.0.0", + "@aws-solutions-constructs/core": "0.0.0", + "constructs": "^3.2.0" + }, + "devDependencies": { + "@aws-cdk/assert": "0.0.0", + "@types/jest": "^26.0.22", + "@types/node": "^10.3.0" + }, + "jest": { + "moduleFileExtensions": [ + "js" + ], + "coverageReporters": [ + "text", + [ + "lcov", + { + "projectRoot": "../../../../" + } + ] + ] + }, + "peerDependencies": { + "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/core": "0.0.0", + "@aws-solutions-constructs/core": "0.0.0", + "constructs": "^3.2.0", + "@aws-cdk/aws-ec2": "0.0.0", + "@aws-cdk/aws-events": "0.0.0" + }, + "keywords": [ + "aws", + "cdk", + "awscdk", + "AWS Solutions Constructs", + "Amazon EventBridge", + "AWS Lambda" + ] +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/aws-lambda-eventbridge.test.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/aws-lambda-eventbridge.test.ts new file mode 100755 index 000000000..913827ae9 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/aws-lambda-eventbridge.test.ts @@ -0,0 +1,513 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { Stack } from "@aws-cdk/core"; +import * as lambda from "@aws-cdk/aws-lambda"; +import * as events from "@aws-cdk/aws-events"; +import * as ec2 from "@aws-cdk/aws-ec2"; +import { LambdaToEventbridge, LambdaToEventbridgeProps } from '../lib'; +import '@aws-cdk/assert/jest'; + +const xrayPolicyStatement = { + Action: [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + Effect: "Allow", + Resource: "*" +}; +const defaultEventBusPolicyStatement = { + Action: "events:PutEvents", + Effect: "Allow", + Resource: { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition" + }, + ":events:", + { + Ref: "AWS::Region" + }, + ":", + { + Ref: "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + } +}; + +const vpcConfig = { + VpcConfig: { + SecurityGroupIds: [ + { + "Fn::GetAtt": [ + "lambdatoeventbridgestackReplaceDefaultSecurityGroupsecuritygroup59EF0706", + "GroupId", + ], + }, + ], + SubnetIds: [ + { + Ref: "VpcisolatedSubnet1SubnetE62B1B9B", + }, + { + Ref: "VpcisolatedSubnet2Subnet39217055", + }, + ], + }, +}; + +// -------------------------------------------------------------- +// Tests minimal deployment with new Lambda function +// -------------------------------------------------------------- +test('Test minimal deployment with new Lambda function', () => { + // Stack + const stack = new Stack(); + // Helper declaration + const construct = new LambdaToEventbridge(stack, 'lambda-to-eventbridge-stack', { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/lambda`) + } + }); + + // Check Props + const lambdaFunction = construct.lambdaFunction; + const vpc = construct.vpc; + const eventBus = construct.eventBus; + expect(lambdaFunction).toBeDefined(); + expect(vpc).toBeUndefined(); + expect(eventBus).toBeUndefined(); + + // Check EVENTBUS_NAME Env variable + expect(stack).toHaveResource("AWS::Lambda::Function", { + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1", + EVENTBUS_NAME: "default" + } + } + }); + + // Since using default event bus, there shouldn't be any eventbus + expect(stack).not.toHaveResource('AWS::Events::EventBus'); + + // Check Lambda Function permissions to access default Event Bridge + expect(stack).toHaveResourceLike("AWS::IAM::Policy", { + PolicyDocument: { + Statement: [ + xrayPolicyStatement, + defaultEventBusPolicyStatement + ] + } + }); +}); + +// -------------------------------------------------------------- +// Test bad call with existingVpc and deployVpc +// -------------------------------------------------------------- +test("Test bad call with existingVpc and deployVpc", () => { + // Stack + const stack = new Stack(); + + const testVpc = new ec2.Vpc(stack, "test-vpc", {}); + + const app = () => { + // Helper declaration + new LambdaToEventbridge(stack, "lambda-to-eventbridge-stack", { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_10_X, + handler: "index.handler", + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + }, + existingVpc: testVpc, + deployVpc: true, + }); + }; + // Assertion + expect(app).toThrowError(); +}); + +// -------------------------------------------------------------- +// Test exception while passing existingEventBus & eventBusProps +// -------------------------------------------------------------- +test("Test bad call with existingVpc and deployVpc", () => { + // Stack + const stack = new Stack(); + + const app = () => { + // Helper declaration + new LambdaToEventbridge(stack, 'lambda-to-eventbridge-stack', { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/lambda`) + }, + eventBusProps: {}, + existingEventBusInterface: new events.EventBus(stack, `new-event-bus`, {}) + }); + }; + // Assertion + expect(app).toThrowError(); +}); + +// -------------------------------------------------------------- +// Test deployment w/ existing eventbus +// -------------------------------------------------------------- +test('Test deployment w/ existing eventbus', () => { + // Stack + const stack = new Stack(); + // Helper declaration + const existingEventBus = new events.EventBus(stack, 'existing-eventbus', { + eventBusName: 'customeventbus' + }); + + new LambdaToEventbridge(stack, 'lambda-to-eventbridge-stack', { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/lambda`) + }, + existingEventBusInterface: existingEventBus + }); + + // Check Lambda Permissions + expect(stack).toHaveResourceLike("AWS::IAM::Policy", { + PolicyDocument: { + Statement: [ + xrayPolicyStatement, + { + Action: "events:PutEvents", + Effect: "Allow", + Resource: { + "Fn::GetAtt": [ + "existingeventbus2A99FA49", + "Arn" + ] + } + } + ] + } + }); + expect(stack).toHaveResource('AWS::Events::EventBus', { + Name: `customeventbus` + }); +}); + +// -------------------------------------------------------------- +// Test minimal deployment that deploys a VPC without vpcProps +// -------------------------------------------------------------- +test("Test minimal deployment that deploys a VPC without vpcProps", () => { + // Stack + const stack = new Stack(); + // Helper declaration + new LambdaToEventbridge(stack, "lambda-to-eventbridge-stack", { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: "index.handler", + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + }, + deployVpc: true, + }); + + expect(stack).toHaveResource("AWS::Lambda::Function", vpcConfig); + + expect(stack).toHaveResource("AWS::EC2::VPC", { + EnableDnsHostnames: true, + EnableDnsSupport: true, + }); + + expect(stack).toHaveResource("AWS::EC2::VPCEndpoint", { + VpcEndpointType: "Interface", + }); + + expect(stack).toCountResources("AWS::EC2::Subnet", 2); + expect(stack).toCountResources("AWS::EC2::InternetGateway", 0); +}); + +// -------------------------------------------------------------- +// Test minimal deployment that deploys a VPC w/vpcProps +// -------------------------------------------------------------- +test("Test minimal deployment that deploys a VPC w/vpcProps", () => { + // Stack + const stack = new Stack(); + // Helper declaration + new LambdaToEventbridge(stack, "lambda-to-eventbridge-stack", { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: "index.handler", + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + }, + vpcProps: { + enableDnsHostnames: false, + enableDnsSupport: false, + cidr: "192.68.0.0/16", + }, + deployVpc: true, + }); + + expect(stack).toHaveResource("AWS::Lambda::Function", vpcConfig); + + expect(stack).toHaveResource("AWS::EC2::VPC", { + CidrBlock: "192.68.0.0/16", + EnableDnsHostnames: true, + EnableDnsSupport: true, + }); + + expect(stack).toHaveResource("AWS::EC2::VPCEndpoint", { + VpcEndpointType: "Interface", + }); + + expect(stack).toCountResources("AWS::EC2::Subnet", 2); + expect(stack).toCountResources("AWS::EC2::InternetGateway", 0); +}); + +// -------------------------------------------------------------- +// Test minimal deployment with an existing VPC +// -------------------------------------------------------------- +test("Test minimal deployment with an existing VPC", () => { + // Stack + const stack = new Stack(); + + const testVpc = new ec2.Vpc(stack, "test-vpc", {}); + + // Helper declaration + new LambdaToEventbridge(stack, "lambda-to-eventbridge-stack", { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: "index.handler", + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + }, + existingVpc: testVpc, + }); + + expect(stack).toHaveResource("AWS::Lambda::Function", { + VpcConfig: { + SecurityGroupIds: [ + { + "Fn::GetAtt": [ + "lambdatoeventbridgestackReplaceDefaultSecurityGroupsecuritygroup59EF0706", + "GroupId", + ], + }, + ], + SubnetIds: [ + { + Ref: "testvpcPrivateSubnet1Subnet865FB50A", + }, + { + Ref: "testvpcPrivateSubnet2Subnet23D3396F", + }, + ], + }, + }); + + expect(stack).toHaveResource("AWS::EC2::VPCEndpoint", { + VpcEndpointType: "Interface", + }); + + // Check Lambda Function permissions to access default Event Bridge + expect(stack).toHaveResourceLike("AWS::IAM::Policy", { + PolicyDocument: { + Statement: [ + { + Action: [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:AssignPrivateIpAddresses", + "ec2:UnassignPrivateIpAddresses" + ], + Effect: "Allow", + Resource: "*" + }, + xrayPolicyStatement, + defaultEventBusPolicyStatement + ] + } + }); +}); + +// -------------------------------------------------------------- +// Test minimal deployment with an existing VPC and existing Lambda function not in a VPC +// +// buildLambdaFunction should throw an error if the Lambda function is not +// attached to a VPC +// -------------------------------------------------------------- +test("Test minimal deployment with an existing VPC and existing Lambda function not in a VPC", () => { + // Stack + const stack = new Stack(); + + const testLambdaFunction = new lambda.Function(stack, 'test-lamba', { + runtime: lambda.Runtime.NODEJS_12_X, + handler: "index.handler", + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + }); + + const testVpc = new ec2.Vpc(stack, "test-vpc", {}); + + // Helper declaration + const app = () => { + // Helper declaration + new LambdaToEventbridge(stack, "lambda-to-eventbridge-stack", { + existingLambdaObj: testLambdaFunction, + existingVpc: testVpc, + }); + }; + + // Assertion + expect(app).toThrowError(); + +}); + +// ---------------------------------------------------------------------------------- +// Test lambda function custom environment variable and custom event bus permissions +// ---------------------------------------------------------------------------------- +test('Test lambda function custom environment variable', () => { + // Stack + const stack = new Stack(); + + // Helper declaration + new LambdaToEventbridge(stack, 'lambda-to-eventbridge-stack', { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + }, + eventBusProps: {eventBusName: 'customeventbus'}, + eventBusEnvironmentVariableName: 'CUSTOM_EVENTBUS_NAME' + }); + + // Check environment variables + expect(stack).toHaveResource('AWS::Lambda::Function', { + Handler: 'index.handler', + Runtime: 'nodejs12.x', + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + CUSTOM_EVENTBUS_NAME: { + Ref: "lambdatoeventbridgestackcustomeventbus25825BEF" + } + } + } + }); + + expect(stack).toHaveResource('AWS::Events::EventBus', { + Name: `customeventbus` + }); + + // Check lambda permissions to custom event bus + expect(stack).toHaveResourceLike("AWS::IAM::Policy", { + PolicyDocument: { + Statement: [ + xrayPolicyStatement, + { + Action: "events:PutEvents", + Effect: "Allow", + Resource: { + "Fn::GetAtt": [ + "lambdatoeventbridgestackcustomeventbus25825BEF", + "Arn" + ] + } + } + ] + } + }); +}); + +test('check multiple constructs in a single stack', () => { + const stack = new Stack(); + + const props: LambdaToEventbridgeProps = { + lambdaFunctionProps: { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler' + }, + eventBusProps: {} + }; + new LambdaToEventbridge(stack, 'test-new-lambda-eventbridge1', props); + new LambdaToEventbridge(stack, 'test-new-lambda-eventbridge2', props); + expect(stack).toCountResources('AWS::Events::EventBus', 2); +}); + +test('check multiple lambda functions publishing to single event bus', () => { + const stack = new Stack(); + + const props1: LambdaToEventbridgeProps = { + lambdaFunctionProps: { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler' + }, + eventBusProps: {} + }; + const construct = new LambdaToEventbridge(stack, 'test-new-lambda-eventbridge1', props1); + + const props2: LambdaToEventbridgeProps = { + lambdaFunctionProps: { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler' + }, + existingEventBusInterface: construct.eventBus + }; + new LambdaToEventbridge(stack, 'test-new-lambda-eventbridge2', props2); + + // Make sure only single event bus exists + expect(stack).toCountResources('AWS::Events::EventBus', 1); + + // Make sure 2 lambda functions exist + expect(stack).toCountResources('AWS::Lambda::Function', 2); + + // Check whether lambdas have permisison to publish to the event bus + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + xrayPolicyStatement, + { + Action: "events:PutEvents", + Effect: "Allow", + Resource: { + "Fn::GetAtt": [ + "testnewlambdaeventbridge1CustomEventBusA089F10A", + "Arn" + ] + } + } + ] + } + }); + + // Check environment variables + expect(stack).toHaveResource('AWS::Lambda::Function', { + Handler: 'index.handler', + Runtime: 'nodejs12.x', + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + EVENTBUS_NAME: { + Ref: "testnewlambdaeventbridge1CustomEventBusA089F10A" + } + } + } + }); +}); \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunction.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunction.expected.json new file mode 100644 index 000000000..7cd1edb46 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunction.expected.json @@ -0,0 +1,214 @@ +{ + "Description": "Integration Test for aws-lambda-eventbridge", + "Resources": { + "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "testlambdaeventbridgeLambdaFunctionServiceRoleDefaultPolicy0F454786": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "events:PutEvents", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":events:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testlambdaeventbridgeLambdaFunctionServiceRoleDefaultPolicy0F454786", + "Roles": [ + { + "Ref": "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "testlambdaeventbridgeLambdaFunction361ABB82": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077", + "Arn" + ] + }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1", + "EVENTBUS_NAME": "default" + } + }, + "Handler": "index.handler", + "Runtime": "nodejs12.x", + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "testlambdaeventbridgeLambdaFunctionServiceRoleDefaultPolicy0F454786", + "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + } + }, + "Parameters": { + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687": { + "Type": "String", + "Description": "S3 bucket for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554": { + "Type": "String", + "Description": "S3 key for asset version \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15ArtifactHashEDF6AFC7": { + "Type": "String", + "Description": "Artifact hash for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunction.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunction.ts new file mode 100755 index 000000000..cb2ea5559 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunction.ts @@ -0,0 +1,37 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "@aws-cdk/core"; +import { LambdaToEventbridge, LambdaToEventbridgeProps } from "../lib"; +import * as lambda from '@aws-cdk/aws-lambda'; +import { generateIntegStackName } from '@aws-solutions-constructs/core'; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.templateOptions.description = 'Integration Test for aws-lambda-eventbridge'; + +// Definitions +const props: LambdaToEventbridgeProps = { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/lambda`) + } +}; + +new LambdaToEventbridge(stack, 'test-lambda-eventbridge', props); + +// Synth +app.synth(); \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithNewEventBus.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithNewEventBus.expected.json new file mode 100644 index 000000000..7737b1a2b --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithNewEventBus.expected.json @@ -0,0 +1,641 @@ +{ + "Description": "Integration Test for aws-lambda-eventbridge", + "Resources": { + "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "testlambdaeventbridgeLambdaFunctionServiceRoleDefaultPolicy0F454786": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:AssignPrivateIpAddresses", + "ec2:UnassignPrivateIpAddresses" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "events:PutEvents", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "testlambdaeventbridgeCustomEventBus5F110E81", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testlambdaeventbridgeLambdaFunctionServiceRoleDefaultPolicy0F454786", + "Roles": [ + { + "Ref": "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "testlambdaeventbridgeReplaceDefaultSecurityGroupsecuritygroup11BC24F6": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "deployFunctionWithNewEventBus/test-lambda-eventbridge/ReplaceDefaultSecurityGroup-security-group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W5", + "reason": "Egress of 0.0.0.0/0 is default and generally considered OK" + }, + { + "id": "W40", + "reason": "Egress IPProtocol of -1 is default and generally considered OK" + } + ] + } + } + }, + "testlambdaeventbridgeLambdaFunction361ABB82": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077", + "Arn" + ] + }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1", + "EVENTBUS_NAME": { + "Ref": "testlambdaeventbridgeCustomEventBus5F110E81" + } + } + }, + "Handler": "index.handler", + "Runtime": "nodejs12.x", + "TracingConfig": { + "Mode": "Active" + }, + "VpcConfig": { + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "testlambdaeventbridgeReplaceDefaultSecurityGroupsecuritygroup11BC24F6", + "GroupId" + ] + } + ], + "SubnetIds": [ + { + "Ref": "VpcisolatedSubnet1SubnetE62B1B9B" + }, + { + "Ref": "VpcisolatedSubnet2Subnet39217055" + }, + { + "Ref": "VpcisolatedSubnet3Subnet44F2537D" + } + ] + } + }, + "DependsOn": [ + "testlambdaeventbridgeLambdaFunctionServiceRoleDefaultPolicy0F454786", + "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + }, + "testlambdaeventbridgeCustomEventBus5F110E81": { + "Type": "AWS::Events::EventBus", + "Properties": { + "Name": "deployFunctionWithNewEventBustestlambdaeventbridgeCustomEventBusF8520BFF" + } + }, + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithNewEventBus/Vpc" + } + ] + } + }, + "VpcisolatedSubnet1SubnetE62B1B9B": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "isolated" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Isolated" + }, + { + "Key": "Name", + "Value": "deployFunctionWithNewEventBus/Vpc/isolatedSubnet1" + } + ] + } + }, + "VpcisolatedSubnet1RouteTableE442650B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithNewEventBus/Vpc/isolatedSubnet1" + } + ] + } + }, + "VpcisolatedSubnet1RouteTableAssociationD259E31A": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcisolatedSubnet1RouteTableE442650B" + }, + "SubnetId": { + "Ref": "VpcisolatedSubnet1SubnetE62B1B9B" + } + } + }, + "VpcisolatedSubnet2Subnet39217055": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "isolated" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Isolated" + }, + { + "Key": "Name", + "Value": "deployFunctionWithNewEventBus/Vpc/isolatedSubnet2" + } + ] + } + }, + "VpcisolatedSubnet2RouteTable334F9764": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithNewEventBus/Vpc/isolatedSubnet2" + } + ] + } + }, + "VpcisolatedSubnet2RouteTableAssociation25A4716F": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcisolatedSubnet2RouteTable334F9764" + }, + "SubnetId": { + "Ref": "VpcisolatedSubnet2Subnet39217055" + } + } + }, + "VpcisolatedSubnet3Subnet44F2537D": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "isolated" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Isolated" + }, + { + "Key": "Name", + "Value": "deployFunctionWithNewEventBus/Vpc/isolatedSubnet3" + } + ] + } + }, + "VpcisolatedSubnet3RouteTableA2F6BBC0": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithNewEventBus/Vpc/isolatedSubnet3" + } + ] + } + }, + "VpcisolatedSubnet3RouteTableAssociationDC010BEB": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcisolatedSubnet3RouteTableA2F6BBC0" + }, + "SubnetId": { + "Ref": "VpcisolatedSubnet3Subnet44F2537D" + } + } + }, + "VpcFlowLogIAMRole6A475D41": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "vpc-flow-logs.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithNewEventBus/Vpc" + } + ] + } + }, + "VpcFlowLogIAMRoleDefaultPolicy406FB995": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:DescribeLogStreams" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "VpcFlowLogLogGroup7B5C56B9", + "Arn" + ] + } + }, + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "VpcFlowLogIAMRole6A475D41", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "VpcFlowLogIAMRoleDefaultPolicy406FB995", + "Roles": [ + { + "Ref": "VpcFlowLogIAMRole6A475D41" + } + ] + } + }, + "VpcFlowLogLogGroup7B5C56B9": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": 731 + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W84", + "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)" + } + ] + } + } + }, + "VpcFlowLog8FF33A73": { + "Type": "AWS::EC2::FlowLog", + "Properties": { + "ResourceId": { + "Ref": "Vpc8378EB38" + }, + "ResourceType": "VPC", + "TrafficType": "ALL", + "DeliverLogsPermissionArn": { + "Fn::GetAtt": [ + "VpcFlowLogIAMRole6A475D41", + "Arn" + ] + }, + "LogDestinationType": "cloud-watch-logs", + "LogGroupName": { + "Ref": "VpcFlowLogLogGroup7B5C56B9" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithNewEventBus/Vpc" + } + ] + } + }, + "VpcCLOUDWATCHEVENTS1A0DAFBF": { + "Type": "AWS::EC2::VPCEndpoint", + "Properties": { + "ServiceName": { + "Fn::Join": [ + "", + [ + "com.amazonaws.", + { + "Ref": "AWS::Region" + }, + ".events" + ] + ] + }, + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "PrivateDnsEnabled": true, + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "deployFunctionWithNewEventBusCLOUDWATCHEVENTSsecuritygroupD8A263BF", + "GroupId" + ] + } + ], + "SubnetIds": [ + { + "Ref": "VpcisolatedSubnet1SubnetE62B1B9B" + }, + { + "Ref": "VpcisolatedSubnet2Subnet39217055" + }, + { + "Ref": "VpcisolatedSubnet3Subnet44F2537D" + } + ], + "VpcEndpointType": "Interface" + } + }, + "deployFunctionWithNewEventBusCLOUDWATCHEVENTSsecuritygroupD8A263BF": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "deployFunctionWithNewEventBus/deployFunctionWithNewEventBus-CLOUDWATCH_EVENTS-security-group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": { + "Fn::GetAtt": [ + "Vpc8378EB38", + "CidrBlock" + ] + }, + "Description": { + "Fn::Join": [ + "", + [ + "from ", + { + "Fn::GetAtt": [ + "Vpc8378EB38", + "CidrBlock" + ] + }, + ":443" + ] + ] + }, + "FromPort": 443, + "IpProtocol": "tcp", + "ToPort": 443 + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W5", + "reason": "Egress of 0.0.0.0/0 is default and generally considered OK" + }, + { + "id": "W40", + "reason": "Egress IPProtocol of -1 is default and generally considered OK" + } + ] + } + } + } + }, + "Parameters": { + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687": { + "Type": "String", + "Description": "S3 bucket for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554": { + "Type": "String", + "Description": "S3 key for asset version \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15ArtifactHashEDF6AFC7": { + "Type": "String", + "Description": "Artifact hash for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithNewEventBus.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithNewEventBus.ts new file mode 100644 index 000000000..670b11223 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithNewEventBus.ts @@ -0,0 +1,39 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "@aws-cdk/core"; +import { LambdaToEventbridge, LambdaToEventbridgeProps } from "../lib"; +import * as lambda from "@aws-cdk/aws-lambda"; +import { generateIntegStackName } from '@aws-solutions-constructs/core'; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.templateOptions.description = "Integration Test for aws-lambda-eventbridge"; + +// Definitions +const props: LambdaToEventbridgeProps = { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_12_X, + handler: "index.handler", + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + }, + deployVpc: true, + eventBusProps: {} +}; + +new LambdaToEventbridge(stack, "test-lambda-eventbridge", props); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithVpc.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithVpc.expected.json new file mode 100644 index 000000000..54fcc72c6 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithVpc.expected.json @@ -0,0 +1,647 @@ +{ + "Description": "Integration Test for aws-lambda-eventbridge", + "Resources": { + "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "testlambdaeventbridgeLambdaFunctionServiceRoleDefaultPolicy0F454786": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:AssignPrivateIpAddresses", + "ec2:UnassignPrivateIpAddresses" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "events:PutEvents", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":events:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testlambdaeventbridgeLambdaFunctionServiceRoleDefaultPolicy0F454786", + "Roles": [ + { + "Ref": "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "testlambdaeventbridgeReplaceDefaultSecurityGroupsecuritygroup11BC24F6": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "deployFunctionWithVpc/test-lambda-eventbridge/ReplaceDefaultSecurityGroup-security-group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W5", + "reason": "Egress of 0.0.0.0/0 is default and generally considered OK" + }, + { + "id": "W40", + "reason": "Egress IPProtocol of -1 is default and generally considered OK" + } + ] + } + } + }, + "testlambdaeventbridgeLambdaFunction361ABB82": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077", + "Arn" + ] + }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1", + "EVENTBUS_NAME": "default" + } + }, + "Handler": "index.handler", + "Runtime": "nodejs14.x", + "TracingConfig": { + "Mode": "Active" + }, + "VpcConfig": { + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "testlambdaeventbridgeReplaceDefaultSecurityGroupsecuritygroup11BC24F6", + "GroupId" + ] + } + ], + "SubnetIds": [ + { + "Ref": "VpcisolatedSubnet1SubnetE62B1B9B" + }, + { + "Ref": "VpcisolatedSubnet2Subnet39217055" + }, + { + "Ref": "VpcisolatedSubnet3Subnet44F2537D" + } + ] + } + }, + "DependsOn": [ + "testlambdaeventbridgeLambdaFunctionServiceRoleDefaultPolicy0F454786", + "testlambdaeventbridgeLambdaFunctionServiceRole0FD85077" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + }, + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithVpc/Vpc" + } + ] + } + }, + "VpcisolatedSubnet1SubnetE62B1B9B": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "isolated" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Isolated" + }, + { + "Key": "Name", + "Value": "deployFunctionWithVpc/Vpc/isolatedSubnet1" + } + ] + } + }, + "VpcisolatedSubnet1RouteTableE442650B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithVpc/Vpc/isolatedSubnet1" + } + ] + } + }, + "VpcisolatedSubnet1RouteTableAssociationD259E31A": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcisolatedSubnet1RouteTableE442650B" + }, + "SubnetId": { + "Ref": "VpcisolatedSubnet1SubnetE62B1B9B" + } + } + }, + "VpcisolatedSubnet2Subnet39217055": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "isolated" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Isolated" + }, + { + "Key": "Name", + "Value": "deployFunctionWithVpc/Vpc/isolatedSubnet2" + } + ] + } + }, + "VpcisolatedSubnet2RouteTable334F9764": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithVpc/Vpc/isolatedSubnet2" + } + ] + } + }, + "VpcisolatedSubnet2RouteTableAssociation25A4716F": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcisolatedSubnet2RouteTable334F9764" + }, + "SubnetId": { + "Ref": "VpcisolatedSubnet2Subnet39217055" + } + } + }, + "VpcisolatedSubnet3Subnet44F2537D": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "isolated" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Isolated" + }, + { + "Key": "Name", + "Value": "deployFunctionWithVpc/Vpc/isolatedSubnet3" + } + ] + } + }, + "VpcisolatedSubnet3RouteTableA2F6BBC0": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithVpc/Vpc/isolatedSubnet3" + } + ] + } + }, + "VpcisolatedSubnet3RouteTableAssociationDC010BEB": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcisolatedSubnet3RouteTableA2F6BBC0" + }, + "SubnetId": { + "Ref": "VpcisolatedSubnet3Subnet44F2537D" + } + } + }, + "VpcFlowLogIAMRole6A475D41": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "vpc-flow-logs.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithVpc/Vpc" + } + ] + } + }, + "VpcFlowLogIAMRoleDefaultPolicy406FB995": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:DescribeLogStreams" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "VpcFlowLogLogGroup7B5C56B9", + "Arn" + ] + } + }, + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "VpcFlowLogIAMRole6A475D41", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "VpcFlowLogIAMRoleDefaultPolicy406FB995", + "Roles": [ + { + "Ref": "VpcFlowLogIAMRole6A475D41" + } + ] + } + }, + "VpcFlowLogLogGroup7B5C56B9": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": 731 + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W84", + "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)" + } + ] + } + } + }, + "VpcFlowLog8FF33A73": { + "Type": "AWS::EC2::FlowLog", + "Properties": { + "ResourceId": { + "Ref": "Vpc8378EB38" + }, + "ResourceType": "VPC", + "TrafficType": "ALL", + "DeliverLogsPermissionArn": { + "Fn::GetAtt": [ + "VpcFlowLogIAMRole6A475D41", + "Arn" + ] + }, + "LogDestinationType": "cloud-watch-logs", + "LogGroupName": { + "Ref": "VpcFlowLogLogGroup7B5C56B9" + }, + "Tags": [ + { + "Key": "Name", + "Value": "deployFunctionWithVpc/Vpc" + } + ] + } + }, + "VpcCLOUDWATCHEVENTS1A0DAFBF": { + "Type": "AWS::EC2::VPCEndpoint", + "Properties": { + "ServiceName": { + "Fn::Join": [ + "", + [ + "com.amazonaws.", + { + "Ref": "AWS::Region" + }, + ".events" + ] + ] + }, + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "PrivateDnsEnabled": true, + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "deployFunctionWithVpcCLOUDWATCHEVENTSsecuritygroupB8797C76", + "GroupId" + ] + } + ], + "SubnetIds": [ + { + "Ref": "VpcisolatedSubnet1SubnetE62B1B9B" + }, + { + "Ref": "VpcisolatedSubnet2Subnet39217055" + }, + { + "Ref": "VpcisolatedSubnet3Subnet44F2537D" + } + ], + "VpcEndpointType": "Interface" + } + }, + "deployFunctionWithVpcCLOUDWATCHEVENTSsecuritygroupB8797C76": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "deployFunctionWithVpc/deployFunctionWithVpc-CLOUDWATCH_EVENTS-security-group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": { + "Fn::GetAtt": [ + "Vpc8378EB38", + "CidrBlock" + ] + }, + "Description": { + "Fn::Join": [ + "", + [ + "from ", + { + "Fn::GetAtt": [ + "Vpc8378EB38", + "CidrBlock" + ] + }, + ":443" + ] + ] + }, + "FromPort": 443, + "IpProtocol": "tcp", + "ToPort": 443 + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W5", + "reason": "Egress of 0.0.0.0/0 is default and generally considered OK" + }, + { + "id": "W40", + "reason": "Egress IPProtocol of -1 is default and generally considered OK" + } + ] + } + } + } + }, + "Parameters": { + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687": { + "Type": "String", + "Description": "S3 bucket for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554": { + "Type": "String", + "Description": "S3 key for asset version \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15ArtifactHashEDF6AFC7": { + "Type": "String", + "Description": "Artifact hash for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithVpc.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithVpc.ts new file mode 100644 index 000000000..dcbcb39b7 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.deployFunctionWithVpc.ts @@ -0,0 +1,38 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "@aws-cdk/core"; +import { LambdaToEventbridge, LambdaToEventbridgeProps } from "../lib"; +import * as lambda from "@aws-cdk/aws-lambda"; +import { generateIntegStackName } from '@aws-solutions-constructs/core'; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.templateOptions.description = "Integration Test for aws-lambda-eventbridge"; + +// Definitions +const props: LambdaToEventbridgeProps = { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_14_X, + handler: "index.handler", + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + }, + deployVpc: true, +}; + +new LambdaToEventbridge(stack, "test-lambda-eventbridge", props); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingEventBus.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingEventBus.expected.json new file mode 100644 index 000000000..5068ee7cd --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingEventBus.expected.json @@ -0,0 +1,208 @@ +{ + "Description": "Integration Test for aws-lambda-eventbridge", + "Resources": { + "LambdaFunctionServiceRole0C4CDE0B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "LambdaFunctionServiceRoleDefaultPolicy126C8897": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "events:PutEvents", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "existingeventbusA5B80487", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRoleDefaultPolicy126C8897", + "Roles": [ + { + "Ref": "LambdaFunctionServiceRole0C4CDE0B" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "LambdaFunctionBF21E41F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "LambdaFunctionServiceRole0C4CDE0B", + "Arn" + ] + }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1", + "EVENTBUS_NAME": { + "Ref": "existingeventbusA5B80487" + } + } + }, + "Handler": "index.handler", + "Runtime": "nodejs12.x", + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "LambdaFunctionServiceRoleDefaultPolicy126C8897", + "LambdaFunctionServiceRole0C4CDE0B" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + }, + "existingeventbusA5B80487": { + "Type": "AWS::Events::EventBus", + "Properties": { + "Name": "existingEventBusexistingeventbus3651CF44" + } + } + }, + "Parameters": { + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687": { + "Type": "String", + "Description": "S3 bucket for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554": { + "Type": "String", + "Description": "S3 key for asset version \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15ArtifactHashEDF6AFC7": { + "Type": "String", + "Description": "Artifact hash for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingEventBus.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingEventBus.ts new file mode 100755 index 000000000..5b5bec384 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingEventBus.ts @@ -0,0 +1,45 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "@aws-cdk/core"; +import { LambdaToEventbridge, LambdaToEventbridgeProps } from "../lib"; +import * as lambda from '@aws-cdk/aws-lambda'; +import * as events from '@aws-cdk/aws-events'; +import * as defaults from '@aws-solutions-constructs/core'; +import { generateIntegStackName } from '@aws-solutions-constructs/core'; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.templateOptions.description = 'Integration Test for aws-lambda-eventbridge'; + +// Definitions +const lambdaFunctionProps = { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/lambda`) +}; + +const existingFunction = defaults.deployLambdaFunction(stack, lambdaFunctionProps); +const existingEventBus = new events.EventBus(stack, 'existing-event-bus', {}); + +const props: LambdaToEventbridgeProps = { + existingLambdaObj: existingFunction, + existingEventBusInterface: existingEventBus +}; + +new LambdaToEventbridge(stack, 'test-lambda-eventbridge', props); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingFunction.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingFunction.expected.json new file mode 100644 index 000000000..1b8c82b6b --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingFunction.expected.json @@ -0,0 +1,214 @@ +{ + "Description": "Integration Test for aws-lambda-eventbridge", + "Resources": { + "LambdaFunctionServiceRole0C4CDE0B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "LambdaFunctionServiceRoleDefaultPolicy126C8897": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "events:PutEvents", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":events:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRoleDefaultPolicy126C8897", + "Roles": [ + { + "Ref": "LambdaFunctionServiceRole0C4CDE0B" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "LambdaFunctionBF21E41F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "LambdaFunctionServiceRole0C4CDE0B", + "Arn" + ] + }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1", + "EVENTBUS_NAME": "default" + } + }, + "Handler": "index.handler", + "Runtime": "nodejs12.x", + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "LambdaFunctionServiceRoleDefaultPolicy126C8897", + "LambdaFunctionServiceRole0C4CDE0B" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + } + }, + "Parameters": { + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3Bucket5888F687": { + "Type": "String", + "Description": "S3 bucket for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15S3VersionKey04CB1554": { + "Type": "String", + "Description": "S3 key for asset version \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + }, + "AssetParameters39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15ArtifactHashEDF6AFC7": { + "Type": "String", + "Description": "Artifact hash for asset \"39721fe57b2d03d62c85b7a0c1be09848bfc1e0a4dfcce824896ec4e3a7efb15\"" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingFunction.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingFunction.ts new file mode 100755 index 000000000..7fe43e0e3 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/integ.existingFunction.ts @@ -0,0 +1,42 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "@aws-cdk/core"; +import { LambdaToEventbridge, LambdaToEventbridgeProps } from "../lib"; +import * as lambda from '@aws-cdk/aws-lambda'; +import * as defaults from '@aws-solutions-constructs/core'; +import { generateIntegStackName } from '@aws-solutions-constructs/core'; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.templateOptions.description = 'Integration Test for aws-lambda-eventbridge'; + +// Definitions +const lambdaFunctionProps = { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/lambda`) +}; + +const existingFunction = defaults.deployLambdaFunction(stack, lambdaFunctionProps); + +const props: LambdaToEventbridgeProps = { + existingLambdaObj: existingFunction +}; + +new LambdaToEventbridge(stack, 'test-lambda-eventbridge', props); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/lambda/index.js b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/lambda/index.js new file mode 100755 index 000000000..e98b35290 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-eventbridge/test/lambda/index.js @@ -0,0 +1,34 @@ +/** + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +const aws = require('aws-sdk'); +const eventbridge = new aws.EventBridge(); +exports.handler = () => { + const params = { + Entries: [{ + EventBusName: process.env.EVENTBUS_NAME, + Source: 'solutionsconstructs', + DetailType: 'test', + Detail: JSON.stringify({ + Hello: 'World' + }) + }] + }; + eventbridge.putEvents(params, function (err, data) { + if (err) { + throw Error('An error while putting the event.'); + } else { + console.log('Event was successfully sent.'); + } + }); +}; \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/core/lib/input-validation.ts b/source/patterns/@aws-solutions-constructs/core/lib/input-validation.ts index eecc4e4cb..42116f220 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/input-validation.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/input-validation.ts @@ -158,8 +158,8 @@ export function CheckProps(propsObject: VerifiedProps | any) { errorFound = true; } - if (propsObject.existingEventBus && propsObject.eventBusProps) { - errorMessages += 'Error - Either provide existingEventBus or eventBusProps, but not both.\n'; + if (propsObject.existingEventBusInterface && propsObject.eventBusProps) { + errorMessages += 'Error - Either provide existingEventBusInterface or eventBusProps, but not both.\n'; errorFound = true; } diff --git a/source/patterns/@aws-solutions-constructs/core/lib/vpc-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/vpc-helper.ts index 63cc52777..3256c4980 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/vpc-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/vpc-helper.ts @@ -89,6 +89,7 @@ export enum ServiceEndpointTypes { SAGEMAKER_RUNTIME = "SAGEMAKER_RUNTIME", SECRETS_MANAGER = "SECRETS_MANAGER", SSM = "SSM", + EVENTS = "CLOUDWATCH_EVENTS" } enum EndpointTypes { @@ -144,6 +145,11 @@ const endpointSettings: EndpointDefinition[] = [ endpointType: EndpointTypes.INTERFACE, endpointInterfaceService: ec2.InterfaceVpcEndpointAwsService.SSM, }, + { + endpointName: ServiceEndpointTypes.EVENTS, + endpointType: EndpointTypes.INTERFACE, + endpointInterfaceService: ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_EVENTS + } ]; export function AddAwsServiceEndpoint( diff --git a/source/patterns/@aws-solutions-constructs/core/test/vpc-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/vpc-helper.test.ts index 559b91e1d..ae6497b17 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/vpc-helper.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/vpc-helper.test.ts @@ -266,3 +266,22 @@ test('Test adding bad Endpoint', () => { // Assertion expect(app).toThrowError(); }); + +// -------------------------------------------------------------- +// Test adding Events Interface Endpoint +// -------------------------------------------------------------- +test('Test adding Events Interface Endpoint', () => { + // Stack + const stack = new Stack(); + // Build VPC + const testVpc = defaults.buildVpc(stack, { + defaultVpcProps: DefaultPublicPrivateVpcProps(), + }); + + AddAwsServiceEndpoint(stack, testVpc, ServiceEndpointTypes.EVENTS); + + // Assertion + expect(stack).toHaveResource('AWS::EC2::VPCEndpoint', { + VpcEndpointType: 'Interface', + }); +}); \ No newline at end of file