Circular dependency between resources. Cognito - lambda trigger - amazon-web-services

I have a SAM template that was working fine until I added a trigger to my cognito user pool.
I searched about the error that is throwing me: Circular dependency between resources I can understand that the trigger is creating a reference to the user pool and then the circular dependency arises, but I can not find how to solve the problem. I only need to set the trigger of my cognito user pool to get custom messages/emails when a user is created.
This is my SAM code:
AdminCognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
AutoVerifiedAttributes:
- email
VerificationMessageTemplate:
DefaultEmailOption: CONFIRM_WITH_LINK
Policies:
PasswordPolicy:
MinimumLength: 8
UsernameAttributes:
- email
Schema:
- AttributeDataType: String
Name: email
Required: true
Mutable: true
- AttributeDataType: String
Name: id
# Required: false
Mutable: true
AdminCognitoChangePassword:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/config.customCognitoEvents
Role: !GetAtt lambdaRole.Arn
Events:
CognitoEvent:
Type: Cognito
Properties:
UserPool: !Ref AdminCognitoUserPool
Trigger: CustomMessage

The problem was in the globals function environment variables. I was calling AdminCognitoUserPool and thats why the circular dependency was rising.

Related

Can I set a property in CloudFormation only if the entity doesn't exist yet?

I have a CloudFormation via Amplify that defines a Cognito User Pool. I want to set future environments that are deployed from this template to have UsernameConfiguration.CaseSensitive: False. If I just add that property to my CloudFormation template, the update fails with the following error:
Reason: Updates are not allowed for property - UsernameConfiguration. (Service: AWSCognitoIdentityProvider; Status Code: 400; Error Code: InvalidParameterException; Request ID: null; Proxy: null)
Is there a way to condition this property on whether the pool already exists?
I'd like to have any new environments that are created use the new config, but still allow the old environments to deploy.
Here's the snippet of the CloudFormation template that Amplify uses to create the User Pool.
# BEGIN USER POOL RESOURCES
UserPool:
# Created upon user selection
# Depends on SNS Role for Arn if MFA is enabled
Type: AWS::Cognito::UserPool
UpdateReplacePolicy: Retain
Properties:
UserPoolName:
!If [
ShouldNotCreateEnvResources,
!Ref userPoolName,
!Join ["", [!Ref userPoolName, "-", !Ref env]],
]
Schema:
- Name: email
Required: true
Mutable: true
LambdaConfig:
PostAuthentication: !Ref functiontestcd9b6b5ePostAuthenticationArn
PostConfirmation: !Ref functiontestcd9b6b5ePostConfirmationArn
PreTokenGeneration: !Ref functiontestcd9b6b5ePreTokenGenerationArn
AutoVerifiedAttributes: !Ref autoVerifiedAttributes
EmailVerificationMessage: !Ref emailVerificationMessage
EmailVerificationSubject: !Ref emailVerificationSubject
Policies:
PasswordPolicy:
MinimumLength: !Ref passwordPolicyMinLength
RequireLowercase: false
RequireNumbers: false
RequireSymbols: false
RequireUppercase: false
UsernameAttributes: !Ref usernameAttributes
MfaConfiguration: !Ref mfaConfiguration
SmsVerificationMessage: !Ref smsVerificationMessage
SmsAuthenticationMessage: !Ref smsAuthenticationMessage
SmsConfiguration:
SnsCallerArn: !GetAtt SNSRole.Arn
ExternalId: testcd9b6b5e_role_external_id
UsernameConfiguration:
CaseSensitive: False
Is there a way to condition this property on whether the pool already exists?
Sadly its not possible because CFN does not have functions to check if a pool exists or not. If you require such functionality you have to implement it yourself using custom resource.

Serverless framework AWS ApiGateway v2 authorizers

I'm trying to setup simple authorizer based on this doc. Also using serverless plugin serverless-pseudo-parameters.
My serverless configuration for authorizer:
provider:
...
logs:
httpApi: true
httpApi:
cors: true
authorizers:
simpleAuthorizer:
identitySource: $request.header.Authorization
issuerUrl:
- Fn::Join:
- '/'
- - https://cognito-idp.#{AWS::Region}.amazonaws.com
- "#{CognitoUserPool}"
audience:
- "#CognitoUserPoolClient"
My configuration for simple lambda:
functions:
ping:
name: ${self:provider.stage}-ping
handler: test.handler
events:
- httpApi:
method: GET
path: /test
authorizer:
name: simpleAuthorizer
My configuration of user pool and user pool client:
resources:
Resources:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: ${self:service}-${self:provider.stage}-user
UsernameAttributes:
- email
Policies:
PasswordPolicy:
MinimumLength: 6
RequireLowercase: False
RequireNumbers: True
RequireSymbols: False
RequireUppercase: True
Schema:
- Name: email
Required: false
DeveloperOnlyAttribute: false
Mutable: true
AttributeDataType: String
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: cognito-example-client
GenerateSecret: False
UserPoolId: "#{CognitoUserPool}"
User pool, user pool client, HTTP API, lambda successfully created, but I can't see a authorizer at the AWS console of API Gateway service.
So, the problem has simple solution: just update your serverless (I used 1.63.0 which gave me this problem).

AWS Lambda and Cognito error on deploy: Only one request to update this UserPool can be processed at a time

In my AWS project, I use the serverless framework to deploy lambda functions and a Cognito user pool.
I want 2 of my lambda functions to be triggered by Cognito user pool events, so here is what I did:
functions:
autoMoveToUserGroup:
handler: src/auto-move-to-user-group.handler
name: auto-move-to-user-group
events:
- cognitoUserPool:
pool: test-user-pool
trigger: PostAuthentication
existing: True
autoValidationUserEmailModification:
handler: src/auto-validation-user-email-modification.handler
name: auto-validation-user-email-modification
events:
- cognitoUserPool:
pool: test-user-pool
trigger: CustomMessage
existing: True
resources:
Resources:
MyCognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: test-user-pool
AutoVerifiedAttributes:
- email
UsernameAttributes:
- email
Schema:
- Name: email
Required: True
When I deploy using the serverless deploy command, I sometimes got the following error:
An error occurred: AutoDashvalidationDashuserDashemailDashmodificationCustomCognitoUserPool1 - Failed to create resource.
Only one request to update this UserPool can be processed at a time.
It looks like a random bug, since it doesn’t occurs everytime (happens very often, though). Also, when it occurs, it doesn’t always occurs on the same function.
Did I do something wrong? How can I fix that?
Thanks for your help.
OK so here is a workaround: without using the events part in the serverless syntax, we can do this only using raw CloudFormation syntax:
resources:
Resources:
MyCognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
...
LambdaConfig:
CustomMessage: arn:aws:lambda:eu-central-1:123456789012:function:auto-validation-user-email-modification
lambdaCognitoPermissionCustomMessage:
Type: AWS::Lambda::Permission
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !Ref 'AutoValidationUserEmailModificationLambdaFunction'
Principal: cognito-idp.amazonaws.com
SourceArn: !GetAtt
- MyCognitoUserPool
- Arn
Also, according to the serverless team, it's actually a bug that should be fixed and deployed on August, 14th 2019.
Hope it helps.

How to configure Serverless Cognito Lambda Triggers

Using the Serverless framework to create a Cognito User Pool as well as several lambdas to be used for cognito events during TOPT SMS Authorization. Everything is created however the lambda functions are not registered with Cognito.
Relatively new to Serverless jut can't seem to get them to connect. Have tried pool names as others have tried to mark as already present at the end of creation the pool is there and the lambdas are there but there is no connection.
Currently following another post tried changing user pool to CognitoUserPoolMyUserPool and then in lambda referencing it as MyUserPool. Have also tried just CognitoUserPool in both locations and neither work.
Example serverless.yaml file:
service: cognito-authentication
frameworkVersion: ">=1.1.0 <2.0.0"
package:
individually: false
plugins:
- serverless-bundle
custom:
stage: ${opt:stage, self:provider.stage}
poolName: ${self:custom.stage}-user-pool
provider:
name: aws
runtime: nodejs10.x
stage: dev
iamRoleStatements:
- Effect: Allow
Action:
- sns:*
Resource:
- "*"
functions:
preSignUp:
handler: functions/pre-signup.main
events:
- cognitoUserPool:
pool: MyUserPool
trigger: PreSignUp
defineAuthChallenge:
handler: functions/define-auth-challenge.main
events:
- cognitoUserPool:
pool: MyUserPool
trigger: DefineAuthChallenge
createAuthChallenge:
handler: functions/create-auth-challenge.main
events:
- cognitoUserPool:
pool: MyUserPool
trigger: CreateAuthChallenge
verifyAuthChallengeResponse:
handler: functions/verify-auth-challenge-response.main
events:
- cognitoUserPool:
pool: MyUserPool
trigger: VerifyAuthChallengeResponse
resources:
Resources:
CognitoUserPoolMyUserPool:
Type: "AWS::Cognito::UserPool"
Properties:
# Generate a name based on the stage
UserPoolName: ${self:custom.poolName}
# Set phone_number as an alias
UsernameAttributes:
- phone_number
Policies:
PasswordPolicy:
MinimumLength: 6
RequireLowercase: False
RequireNumbers: False
RequireSymbols: False
RequireUppercase: False
CognitoUserPoolClient:
Type: "AWS::Cognito::UserPoolClient"
Properties:
# Generate an app client name based on the stage
ClientName: ${self:custom.stage}-sms-auth-client
UserPoolId:
Ref: CognitoUserPoolMyUserPool
ExplicitAuthFlows:
- CUSTOM_AUTH_FLOW_ONLY
GenerateSecret: false
Expectation is the User Pool is correctly created and configured to use the lambdas for triggered workflow execution.
I've copied pasted your code (and added relevant Lambda functions) and it works for me.
I've tested the PreSignUp with the following command:
aws cognito-idp admin-create-user --region <region> --user-pool-id <user-pool-id> --username <phone>
While not showing in the AWS Console Lambda UI, the triggers do show up in the Cognito->User Pools->dev-user-pool->Triggers, which is confusing.
Example repo: https://github.com/erezrokah/serverless-cognito-triggers
I found the issue in your serverless.yml. You are missing an indentation under cognitoUserPool. I tried it both ways and it works with the additional indentation.
preSignUp:
handler: functions/pre-signup.main
events:
- cognitoUserPool:
pool: MyUserPool
trigger: PreSignUp

Serverless.yml custom stack with cloudformation output variable

I am newbie for serverless so pardon me if this is very basic. I am having an issue in which i am creating AMAZON COGNITO POOL and i want to use this userPoolId into my custom stack block to connect it with appsync. Below is my serverless.yml
custom:
accountId: 123xxxxxxxx
appSync:
apiId: 123xyzxxxxxxx # only required for update-appsync
authenticationType: AMAZON_COGNITO_USER_POOLS
userPoolConfig:
awsRegion: ap-southeast-1
defaultAction: ALLOW
userPoolId: (here it only takes string but i want to reference)
resources:
Resources:
# Cognito - User pool
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: abc_xyz_pool
# Cognito - Client
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: abc_xyz_pool
GenerateSecret: false
UserPoolId:
Ref: CognitoUserPool
# Cognito - Identity
CognitoIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: sc_identity_pool
AllowUnauthenticatedIdentities: false
CognitoIdentityProviders:
- ClientId:
Ref: CognitoUserPoolClient
ProviderName:
Fn::GetAtt: [CognitoUserPool, ProviderName]
I can reference inside the Resources block but i cannot reference it inside the custom block
The custom block in serverless.yml gets evaluated before resources are created and therefore cannot reference those outputs. Even within CFN there are limitations to where and how you reference them.
You can however reference outputs from other CloudFormation stacks.
You should split your serverless project into two projects, the first establishing the user pool, and the second consuming that infrastructure.
In your first project you have your user pool resources, and export the ID for future use in other stacks like so:
Resources:
Outputs:
MyUserPoolId:
Value:
Ref: CognitoUserPool # Key name of user pool resource
Export:
Name: MyUserPoolId
In your second project that needs the pool ID you would import it:
custom:
appSync:
userPoolConfig:
userPoolId:
Fn::ImportValue: MyUserPoolId
Your first project will need to be deployed for the second project to import the exported value.
You can also use ENV variables but that still requires you to establish your user pool first.