Question: How do I use the custom email sender Lambda trigger in Amazon Cognito user pools?

I must invoke the Amazon Cognito custom email sender AWS Lambda trigger to process and deliver email messages.

ResolutionUse an Amazon Cognito custom email sender trigger to allow third-party providers to send email notifications to your users from your Lambda function code. Amazon Cognito sends email message requests to a Lambda function. Then, the Lambda function processes and delivers the email messages.Create a Lambda function to be your custom email sender trigger1.    Create a file with the name index.js, add the following code to the file, and then save your changes.const AWS = require('aws-sdk');const b64 = require('base64-js');const encryptionSdk = require('@aws-crypto/client-node');// Configure the encryption SDK client with the KMS key from the environment variables.const { encrypt, decrypt } = encryptionSdk.buildClient(encryptionSdk.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT);const generatorKeyId = process.env.KEY_ALIAS;const keyIds = [ process.env.KEY_ARN ];const keyring = new encryptionSdk.KmsKeyringNode({ generatorKeyId, keyIds })exports.handler = async (event) => {// Decrypt the secret code using encryption SDK.let plainTextCode;if(event.request.code){const { plaintext, messageHeader } = await decrypt(keyring, b64.toByteArray(event.request.code));plainTextCode = plaintext}// PlainTextCode now has the decrypted secret.if(event.triggerSource == 'CustomEmailSender_SignUp'){// Send email to end-user using custom or 3rd party provider.// Include temporary password in the email.console.log("CustomEmailSender_SignUp: " + plainTextCode);}else if(event.triggerSource == 'CustomEmailSender_ResendCode'){console.log("CustomEmailSender_ResendCode: " + plainTextCode);}else if(event.triggerSource == 'CustomEmailSender_ForgotPassword'){console.log("CustomEmailSender_ForgotPassword: " + plainTextCode);}else if(event.triggerSource == 'CustomEmailSender_UpdateUserAttribute'){console.log("CustomEmailSender_UpdateUserAttribute: " + plainTextCode);}else if(event.triggerSource == 'CustomEmailSender_VerifyUserAttribute'){console.log("CustomEmailSender_VerifyUserAttribute: " + plainTextCode);}else if(event.triggerSource == 'CustomEmailSender_AdminCreateUser'){console.log("CustomEmailSender_AdminCreateUser: " + plainTextCode);}else if(event.triggerSource == 'CustomEmailSender_AccountTakeOverNotification'){console.log("CustomEmailSender_AccountTakeOverNotification: " + plainTextCode);}return;};Warning: This code is scripted to decrypt encrypted secrets. The secrets are temporary passwords and authorization codes sent by Amazon Cognito. The script also prints the plaintext code in logs for demonstration purposes. However, logging secrets in plaintext is a security issue. When your application is in production, NEVER print decrypted secrets in logs.Add the relevant code in the index.js file for sending email messages to users from your custom email provider.2.    Create a .zip file archive, also known as a compressed index.js file:$ zip function.zip index.js3.    Open the Lambda console.4.    Create a Lambda function with a Node.js runtime.The following is an example AWS Command Line Interface (AWS CLI) command to create a Lambda function with Node.js 14.x runtime.Note: If you receive errors when running AWS CLI commands, make sure that you're using the most recent version of the AWS CLI.$ aws lambda create-function --function-name CustomEmailSender --runtime nodejs14.x --handler index.handler --zip-file fileb://function.zip --role <ExecutionRoleARN> --region <specify a region>Note: Replace <ExecutionRoleARN> with the AWS Identity and Access Management (IAM) role Amazon Resource Name (ARN) that you want to associate with the Lambda function. For more details, see Lambda execution role. You can replace CustomEmailSender with the function name of your choice. Replace <specify a region> with your AWS Region code.5.    Install the @aws-crypto/client-node module to use the AWS Encryption SDK.If you don't have the @aws-crypto/client-node module installed, then you must complete the following high-level tasks:Install the module.Package the latest version of the module.Create a Lambda layer that contains the dependency that you can use with the Lambda function.Note: Run the installation commands in a local development environment that's compatible with Lambda.Create a working directory:$ mkdir -p aws-crypto-layer/nodejsChange the working directory:$ cd aws-crypto-layer/nodejsInstall the latest version of the aws-crypto module with the npm package manager:$ npm install @aws-crypto/client-nodeNote: It's a best practice to use an Amazon Linux 2 environment when developing Lambda resources. Use Docker to install the aws-crypto module when you're developing with a Windows or macOS operating system:$ docker run --entrypoint "" -v "$PWD":/var/task "public.ecr.aws/lambda/nodejs:14" /bin/sh -c "npm install @aws-crypto/client-node; exit"Create a .zip file to upload to your Lambda layer:$ zip -r ../package.zip ../Create a Lambda layer that includes the latest version of the @aws-crypto/client-node module:$ aws lambda publish-layer-version --layer-name node_crypto --description "My layer" --license-info "MIT" --compatible-runtimes nodejs14.x --zip-file fileb://../package.zip --region <specify a region>Add the Lambda layer to your function:$ aws lambda update-function-configuration --function-name CustomEmailSender --layers arn:aws:lambda:us-east-2:123456789012:layer:node_crypto:1 --region <specify a region>Note: Replace CustomEmailSender with the name of your function. Replace arn:aws:lambda:us-east-2:123456789012:layer:node_crypto:1 with the LayerVersionArn value from the publish-layer-version command output. Replace <specify a region> with your Region code.Create an encryption key in AWS KMSAmazon Cognito uses an AWS Key Management Service (AWS KMS) symmetric encryption key to encrypt temporary passwords and authorization codes generated by Amazon Cognito.1.    Create an AWS KMS key:$ aws kms create-key --description "KMS Key for CustomEmailSender" --region <specify a region>Note: Replace <specify a region> with your Region code.Because this command doesn't specify a key policy, the default key policy is assigned. To apply a customized key policy to your AWS KMS key, add the --policy parameter to the command with your JSON policy. Make sure that you grant kms:CreateGrant permission to the IAM entity that performs the UpdateUserPool operation to add a custom email trigger.You can't specify an alias when you run the create-key command. To create an alias for the new KMS key, use the create-alias command:$ aws kms create-alias --alias-name alias/custom-email-key --target-key-id <KeyId> --region <specify a region>Note: Replace <KeyId> with the key ID value from the create-key command output. Replace <specify a region> with your Region code.The code example relies on the environment variables KEY_ALIAS and KEY_ARN for encryption key information. You must configure these environment variables in the CustomEmailSender Lambda trigger.$ aws lambda update-function-configuration --function-name CustomEmailSender --environment 'Variables={KEY_ALIAS=alias/custom-email-key,KEY_ARN=<key_ARN>}' --region <specify a region>Note: Replace CustomEmailSender with your Lambda function name. Replace alias/custom-email-key with the key alias for your AWS KMS key. Replace <key_ARN> with the ARN value from the create-key command output. Replace <specify a region> with your Region code.Grant the Amazon Cognito service principal the cognito-idp.amazonaws.com permission to invoke the Lambda functionUse the following command to grant permission to the Amazon Cognito service principal for invoking the Lambda function:$ aws lambda add-permission --function-name CustomEmailSender --statement-id "CognitoLambdaInvokeAccess" --action lambda:InvokeFunction --principal cognito-idp.amazonaws.com --source-arn <UserPoolArn> --region <specify a region>Note: Replace CustomEmailSender with your Lambda function name. Replace <UserPoolArn> with your user pool ARN. To find the user pool ARN, open the Amazon Cognito console User pools page. Then, select the user pool from the list. Or, run the describe-user-pool command. Replace <specify a region> with your Region code.Update the Amazon Cognito user pool so that it uses a custom email sender Lambda triggerUpdate the user pool by setting the CustomEmailSender parameter in the UpdateUserPool API. UpdateUserPool requires all the existing parameters of your user pool and the parameters you want to change. Amazon Cognito sets the values of any missing parameters to their defaults. For more information, see Updating user pool configuration.In the following example, only the --lambda-config parameter is used with CustomEmailSender Lambda function details. So, the command adds the custom email sender trigger to your user pool and sets the remaining user pool parameters to default. If you configure your user pool with non-default values, then pass the values in the update-user-pool command to avoid setting them to default.$ aws cognito-idp update-user-pool --lambda-config "CustomEmailSender={LambdaVersion=V1_0,LambdaArn= <LambdaArn>},KMSKeyID=<KMSKeyArn>" --user-pool-id <UserPoolId> --region <specify a region>Note: Replace <LambdaArn> with the function's ARN value from the create-function command output. Replace <KMSKeyArn> with the ARN value provided in the create-key command output. Replace <UserPoolId> with your Amazon Cognito user pool ID. Replace <specify a region> with your Region code.Test the functionalityTo test the Lambda function's integration, simulate an email sender operation for a user from your user pool, such as email verification or password recovery. The following example shows a ForgotPassword event sent to the Lambda function by Amazon Cognito.{  version: '1',  triggerSource: 'CustomEmailSender_ForgotPassword',  region: 'us-east-1',  userPoolId: 'us-east-1_xxxxxxxx',  userName: 'example_user',  callerContext: {    awsSdkVersion: 'aws-sdk-unknown-unknown',    clientId: '12a3b4example-clientid'  },  request: {    type: 'customEmailSenderRequestV1',    code: 'XXXXeBlI7XP3RQmipedVF+7OGa4AgQACABVhdXXXXXXXvLXB1YmxpYy1rZXkAREF6Zk9NR2lBR0FUeDRITStmRHl4RDJyNlpqa2wvWktBbG45ckRmTEpMZ1A3THp4ME9RaVVjVHl3MVFOSEZjS3piZz09AAt1c2VycG9vbC1pZAATdXMtZWFzdC0xX29DOUhnUHVKWgABAAdhd3Mta21zAEthcm46XXdzOmttczp1cy1lYXN0LTE6XXX3XXc0NDA5OXXzOmtleS8yNmQ0ZjVmMy00YmZhLTQ0OXXtODUxZS01ZTM2ZWIwYjhmYjMAuAECAQB42Am0o+Rx0MgG+wLLyKtm1/vTm03JK3jQBZxqABAkreYBMOoAUtm3mLS7+kb2VL0SHgAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHjPPHlSWPt+UKrQOQIBEIA7oFUeGe2NPX2cuEVi+Qxwp8/IH1bgyYQut+QbbkElc1rXXXXXbeEVMjMkFQmQufmJyag9v1f+PUalvXwXAAAAAAwAABAAAAAAAAAAAAAAAAAAscZHg8dY/cKTskGQc065mv////8AAAABAAAAAAAAAAAAAAABAAAABpofXaVnP4pmf+yMoCElrOGy7Gn8pIUAZzBlAjEAn/7tuTNko8/HCwXXXXlCOJDeU0SSyB7o9y0TXHM7GptdvmB1JL9OzLxmUg6zChIhAjAFFbH4NrSblvwh/m0inDc11BpeOSKghtg8Pg5Nkf8eY6vmXX6GxjaCuyhBSO7IDcM=',    clientMetadata: null,    userAttributes: {      sub: '1a2b3cde-33cd-402f-5g67-examplesub',      'cognito:user_status': 'CONFIRMED',      email_verified: 'true',      email: 'user@example.com'    }  }}The following example is a response in plaintext code:CustomEmailSender_ForgotPassword: 12345You can see the complete logs in the Amazon CloudWatch log group created for your Lambda function. For more details on how to see these logs, see Accessing Amazon CloudWatch logs for AWS Lambda.