serverless: Cannot reference existing Cognito User Pool resource in configuration

This is a Bug Report

(but could be a Feature Proposal if desired behaviour wasn’t supposed to be supported)

Description

I’m using serverless to manage my backend services on AWS.

I’ve divided my project into two stacks: one that defines all the lambdas, and another that defines only resources, such as DynamoDB tables, and User Pools.

I’m now stuck not knowing how to define my PreSignUp trigger in the service stack, so that it will reference a User Pool that’s already been created. I can always manually go into the User Pool console and select the correct lambda, but that just seems wrong…

I’ve tried using an ARN where pool is specified, but I get a validation error (no colons accepted for example):

- cognitoUserPool:
    pool: "arn:aws:cognito-idp:us-east-1:XXXX:userpool/us-east-1_XXXXX"
    trigger: PreSignUp

The above does not work, so I’d like to know how to do this correctly. It seems that this is designed to only work with logical names.

Additional Data

Using Serverless Framework 1.19.0 Mac OS X 10.12.6 No stack trace No error messages

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 30
  • Comments: 33 (5 by maintainers)

Most upvoted comments

@GreenVine, It should look like this.

functions:
  handler:
    handler: hello.default
    events:
    - http:
        authorizer:
          arn: arn:aws:cognito-idp:us-east-1:XXXXXXXXXXXX:userpool/us-east-1_XXXXXXXXX
        path: hello

I’ve managed to set up a lambda triggered by the CustomMessage event on a pre-defined UserPool without touching the AWS console or CLI.

The steps I followed:

  • Define your lambda in your serverless.yml as you would normally
  • On your UserPool definition underneath “Resources” add the property LambdaConfig, which itself has a property called CustomMessage. This must be set to the ARN of the above lambda, this can be achieved using the Cloudformation Fn::GetAtt function
    • Something of note here is that when referencing the lambda function you must use the normalised name which will be generated by serverless - see here
  • You now need to create a resource of type AWS::Lambda::Permission which will allow Cognito to invoke the CustomMessage lambda.

I know this is just one stack, but you should be able to achieve the same with multiple stacks by using exports and Fn::ImportValue: to retrieve the lambda Arn.

So the relevant parts of the serverless.yml:

functions:
  authCustomMessage:
    handler: path/to/authCustomMessage.handler

resources:
  Resources: 
    UserPool:
      Type: 'AWS::Cognito::UserPool'
      Properties:
        # ... other properties
        LambdaConfig:
          CustomMessage: { "Fn::GetAtt": ["AuthCustomMessageLambdaFunction", "Arn"] }

    AuthCustomMessageLambdaFunctionInvokePermission:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName:
          Fn::GetAtt:
            - AuthCustomMessageLambdaFunction
            - Arn
        Principal: cognito-idp.amazonaws.com
        SourceArn:
          Fn::GetAtt:
            - UserPool
            - Arn

Note: I have other Cognito resources linked to the UserPool but I’ve left them out because they’re not relevant to the solution.

Something else I ran into when doing this is a circular dependency, this was because I was referencing the UserPool as an environment variable on the top provider: level of the yml, which was applying it to the custom message lambda. The solution here was to move that environment variable to the specific lambda it was being used in.

From what I can tell this is not a limitation of the Serverless Framework itself, but a limitation in AWS CloudFormation support for Cognito which prevents Serverless from implementing this feature - correct me if i’m wrong.

I put in a feature request with aws to add a “AWS::Cognito::Trigger” which would allow the linkage between an existing lambda and existing user pool. This would be similar to various VPC CloudFormation resources which allow separate creation such as “AWS::EC2::SecurityGroup”

Can we please get this fixed? CloudFormation support for Cognito sucks so we need to create externally.

Ignoring that, it’s not proper design to couple stateful resources with your stateless resources. Even if CFN support was complete for Cognito we wouldn’t combine it with the stateless API resources.

It seems the PR has gone a bit stale so I wanted to throw out a use case: CloudFormation does not support all Cognito features, such as the ability to use email/phone number instead of a username; however, this is possible through the console.

I would like to create a Cognitor User Pool through the console with features which are not supported by CloudFormation, and associate that Pool with cognitoUserPool function event triggers.

Hm, i cant even attach function to user pool that is created through serverless. When i define a trigger it endsup serverless create second pool with same name

How would you go about designing the serverless specification for the AWS lambda functions and the resources that they work with/upon?

Ideally, stateful resources (user pools, dynamo tables, S3 buckets) are declared separately either via API calls, CloudFormation, Terraform, whatever.

The reason for this is to reduce blast radius. I should be able to update my API code with 100% certainty that my stateful resources (the important stuff) isn’t going to be affected. I should also be able to tear down and recreate my API stack without affecting stateful resources.

Unfortunately, it appears we cannot do this in serverless if we want to use Lambda triggers on Cognito events. I realize this is because serverless is adding the LambdaConfig property to the user pool declaration. So obviously this issue isn’t easily fixed.

@cazzer I just reviewed #3799 and it does not fix #4207 (this issue). @phillipsnick Yes, you’re just running into the same issue that this current issue reports on.

This current issue differs from #3773 in that I’m trying to reference a User Pool resource that’s already been created, either using AWS CLI scripts or through another serverless.yml.

@janoist1 Hmm, that’s very odd if it doesn’t error. I don’t have time to try this out myself at the moment so the best I can advise would be to look over the cloudformation logs to look for any clues.

I will likely be setting up a PostConfirmation hook in the near future though, when I do I’ll be sure to get back to you with any findings.

@aldarund

Hm, i cant even attach function to user pool that is created through serverless. When i define a trigger it endsup serverless create second pool with same name

You can override it with the following syntax so it doesn’t create two: https://serverless.com/framework/docs/providers/aws/events/cognito-user-pool/

Ah, got it. I wasn’t able to get that working either. In my case, I create a Lambda for the customMessage hook and configure manually via web console. 🤷‍♂️

functions:
  customMessage:
    handler: src/cognito.customMessage