Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

amplify add trigger #1609

Closed
rawadrifai opened this issue Jun 7, 2019 · 7 comments
Closed

amplify add trigger #1609

rawadrifai opened this issue Jun 7, 2019 · 7 comments
Assignees
Labels
feature-request Request a new feature platform Issues tied to the general CLI platform

Comments

@rawadrifai
Copy link

Is your feature request related to a problem? Please describe.
No. Just a convenience and a step towards no console at all.

Describe the solution you'd like
add a command amplify add trigger
It takes a source and a destination. The destination is a lambda.
The source can be cognito triggers, dynamodb table

Describe alternatives you've considered
I do it manually in the console. Not sure if there's a way to include triggers in an amplify project.

Additional context
I love Amplify and would like to see it completely console-less.

@eelzinaty
Copy link

That would be a great addition 👍

For now, to achieve this I create a function to handle the trigger, PostConfirm. In the generated stack for the function I add four things:
1- Permission for Cognito to invoke Lambda:

"UserPoolPostConfirmLambdaInvokePermission": {
			"Type": "AWS::Lambda::Permission",
			"Properties": {
				"Action": "lambda:invokeFunction",
				"Principal": "cognito-idp.amazonaws.com",
				"FunctionName": {
					"Fn::GetAtt": [
						"LambdaFunction",
						"Arn"
					]
				},
				"SourceArn": {
					"Fn::Sub": [
						"arn:aws:cognito-idp:${region}:${account}:userpool/${userpool}",
						{
							"region": {
								"Ref": "AWS::Region"
							},
							"account": {
								"Ref": "AWS::AccountId"
							},
							"userpool": {
								"Ref": "cognitoUserPoolId"
							}
						}
					]
				}
			}
		}

2- I create inline function that is repsonsible for connecting the Lambda function with cognito trigger.

"UserPoolPostConfirmLambdaFunction": {
			"Type": "AWS::Lambda::Function",
			"Properties": {
				"Code": {
					"ZipFile": {
						"Fn::Join": [
							"\n",
							[
								"const aws = require('aws-sdk');",
								"exports.handler = async (event, context) => {",
								"    try {",
								"        console.log(event.RequestType);",
								"        console.log(event.ResourceProperties);",
								"        if (event.RequestType == 'Delete') {",
								"            await sendResponse(event, context, 'SUCCESS', {});",
								"            context.done();",
								"        }",
								"       if (event.RequestType == 'Update' || event.RequestType == 'Create') {",
								"           const identity = new aws.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18', region: event.ResourceProperties.region });",
								"           const params = {",
								"               UserPoolId: event.ResourceProperties.userpoolId,",
								"               LambdaConfig: {",
								"                   PostConfirmation: event.ResourceProperties.lambdaname",
								"               }",
								"           };",
								"           console.log(params);",
								"           await identity.updateUserPool(params).promise()",
								"           await sendResponse(event, context, 'SUCCESS', {});",
								"           context.done();",
								"       }",
								"    }",
								"    catch (err) {",
								"        console.log(err);",
								"        const responseData = { Error: err };",
								"        await sendResponse(event, context, 'FAILED', responseData);",
								"        context.done();",
								"    }",
								"};",
								"async function sendResponse(event, context, responseStatus, responseData, physicalResourceId, noEcho) {",
								"    var responseBody = JSON.stringify({",
								"        Status: responseStatus,",
								"        Reason: 'See the details in CloudWatch Log Stream: ' + context.logStreamName,",
								"        PhysicalResourceId: physicalResourceId || context.logStreamName,",
								"        StackId: event.StackId,",
								"        RequestId: event.RequestId,",
								"        LogicalResourceId: event.LogicalResourceId,",
								"        NoEcho: noEcho || false,",
								"        Data: responseData",
								"    });",
								"    console.log('Response body:', responseBody);",
								"    var https = require('https');",
								"    var url = require('url');",
								"    let parsedUrl;",
								"    try {",
								"        parsedUrl = url.parse(event.ResponseURL);",
								"    } catch (error) {",
								"        throw new Error(`Invalid url ${event.ResponseURL}`);",
								"    }",
								"    let options = {",
								"        hostname: parsedUrl.hostname,",
								"        port: 443,",
								"        path: parsedUrl.path,",
								"        method: 'PUT',",
								"        headers: {",
								"            'content-type': '',",
								"            'content-length': responseBody.length",
								"        }",
								"    };",
								"    return new Promise((resolve, reject) => {",
								"        const clientRequest = https.request(options, incomingMessage => {   ",
								"            let response = {",
								"                statusCode: incomingMessage.statusCode,",
								"                statusMessage: incomingMessage.statusMessage,",
								"                headers: incomingMessage.headers,",
								"                body: []",
								"            };",
								"            incomingMessage.on('data', chunk => {",
								"                response.body.push(chunk);",
								"            });",
								"            incomingMessage.on('end', () => {",
								"                console.log('Status code: ' + response.statusCode);",
								"                console.log('Status message: ' + response.statusMessage);",
								"                try {response.body = response.body.join(); console.log('Response Body: ' + JSON.parse(response.body));} catch(e) {}",
								"                return resolve(response);",
								"            });",
								"        });",
								"        clientRequest.on('error', error => {",
								"            return reject(error);",
								"        });",
								"        if (responseBody) {",
								"            clientRequest.write(responseBody);",
								"        }",
								"        clientRequest.end();",
								"    });",
								"}"
							]
						]
					}
				},
				"Handler": "index.handler",
				"FunctionName": {
					"Fn::If": [
						"ShouldNotCreateEnvResources",
						{
							"Fn::Join": [
								"",
								[
									{
										"Ref": "AppNamePrefix"
									},
									"-",
									"PostConfirmSetup"
								]
							]
						},
						{
							"Fn::Join": [
								"",
								[
									{
										"Ref": "AppNamePrefix"
									},
									"-",
									"PostConfirmSetup",
									"-",
									{
										"Ref": "env"
									}
								]
							]
						}
					]
				},
				"Role": {
					"Fn::GetAtt": [
						"UserPoolPostConfirmLambdaExecutionRole",
						"Arn"
					]
				},
				"Runtime": "nodejs8.10",
				"Timeout": "300"
			}
		}

3- Then I create and IAM role to give the above function the needed permissions to run

"UserPoolPostConfirmLambdaExecutionRole": {
			"Type": "AWS::IAM::Role",
			"Properties": {
				"RoleName": {
					"Fn::If": [
						"ShouldNotCreateEnvResources",
						{
							"Fn::Join": [
								"",
								[
									{
										"Ref": "AppNamePrefix"
									},
									"-",
									"userpool-postconfirm-lambda-iam"
								]
							]
						},
						{
							"Fn::Join": [
								"",
								[
									{
										"Ref": "AppNamePrefix"
									},
									"-",
									"userpool-postconfirm-lambda-iam",
									"-",
									{
										"Ref": "env"
									}
								]
							]
						}
					]
				},
				"AssumeRolePolicyDocument": {
					"Version": "2012-10-17",
					"Statement": [
						{
							"Effect": "Allow",
							"Principal": {
								"Service": [
									"lambda.amazonaws.com"
								]
							},
							"Action": [
								"sts:AssumeRole"
							]
						}
					]
				},
				"Policies": [
					{
						"PolicyName": "cloudWatchPolicy",
						"PolicyDocument": {
							"Version": "2012-10-17",
							"Statement": [
								{
									"Effect": "Allow",
									"Action": [
										"logs:CreateLogGroup",
										"logs:CreateLogStream",
										"logs:PutLogEvents"
									],
									"Resource": {
										"Fn::Sub": [
											"arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*",
											{
												"region": {
													"Ref": "AWS::Region"
												},
												"account": {
													"Ref": "AWS::AccountId"
												},
												"lambda": {
													"Fn::If": [
														"ShouldNotCreateEnvResources",
														{
															"Fn::Join": [
																"",
																[
																	{
																		"Ref": "AppNamePrefix"
																	},
																	"-",
																	"PostConfirmSetup"
																]
															]
														},
														{
															"Fn::Join": [
																"",
																[
																	{
																		"Ref": "AppNamePrefix"
																	},
																	"-",
																	"PostConfirmSetup",
																	"-",
																	{
																		"Ref": "env"
																	}
																]
															]
														}
													]
												}
											}
										]
									}
								}
							]
						}
					},
					{
						"PolicyName": "cognitoPolicy",
						"PolicyDocument": {
							"Version": "2012-10-17",
							"Statement": [
								{
									"Effect": "Allow",
									"Action": [
										"cognito-idp:UpdateUserPool"
									],
									"Resource": {
										"Fn::Sub": [
											"arn:aws:cognito-idp:${region}:${account}:userpool/${userpoolid}",
											{
												"region": {
													"Ref": "AWS::Region"
												},
												"account": {
													"Ref": "AWS::AccountId"
												},
												"userpoolid": {
													"Ref": "cognitoUserPoolId"
												}
											}
										]
									}
								}
							]
						}
					}
				]
			}
		}

4- Finally, you need to create a custom resource to call the setup function

"UserPoolPostConfirmFunctionOutputs": {
			"Type": "Custom::LambdaCallout",
			"Properties": {
				"ServiceToken": {
					"Fn::GetAtt": [
						"UserPoolPostConfirmLambdaFunction",
						"Arn"
					]
				},
				"region": {
					"Ref": "AWS::Region"
				},
				"userpoolId": {
					"Ref": "cognitoUserPoolId"
				},
				"lambdaname": {
					"Fn::GetAtt": [
						"LambdaFunction",
						"Arn"
					]
				}
			}
		}

NOTE: you need to provide values for these parameters:
AppNamePrefix : any value
cognitoUserPoolId: you can get this from your auth resource

@yuth yuth added feature-request Request a new feature platform Issues tied to the general CLI platform labels Jun 10, 2019
@yuth
Copy link
Contributor

yuth commented Jun 10, 2019

@rawadrifai I have added this feature-request to support generic lambda function as trigger to our backlog. Our product team will go through and prioritize this.

@eelzinaty Auth RFC talks about some of the triggers that we will support. Please feel free to comment in the RFC

@kaustavghosh06 kaustavghosh06 self-assigned this Jun 10, 2019
@kaustavghosh06
Copy link
Contributor

@rawadrifai We're actively working on DynamoDB, S3 and Cognito Lambda triggers and I'll update this thread once we release support for it.

@kaustavghosh06
Copy link
Contributor

We've added support for Lambda Trigger support for the dynamo tables managed/added by the CLI using the amplify add storage command. Let me know if you find that useful. Please take a look at https://aws.amazon.com/blogs/mobile/amplify-framework-adds-supports-for-aws-lambda-triggers-in-auth-and-storage-categories/ for more details.

@rawadrifai
Copy link
Author

@kaustavghosh06 thanks for the update. Is the Pre Token Generation trigger for Cognito supported?

@julescsv
Copy link

julescsv commented Jul 22, 2019

We've added support for Lambda Trigger support for the dynamo tables managed/added by the CLI using the amplify add storage command. Let me know if you find that useful. Please take a look at https://aws.amazon.com/blogs/mobile/amplify-framework-adds-supports-for-aws-lambda-triggers-in-auth-and-storage-categories/ for more details.

Awesome work " Amplify team". Is there a reason why the lambda triggers cannot be added to dynamo tables created by "amplify add api" ? that is likely the biggest use case.

@github-actions
Copy link

This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels for those types of questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 27, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature-request Request a new feature platform Issues tied to the general CLI platform
Projects
None yet
Development

No branches or pull requests

6 participants