Serverless, Cognito, and Custom Resources

Pei Deng
Pei Deng
Aug 26 · 3 min read

As a site reliability engineer at work, I hate to deal with servers maintenance in my leisure time for personal projects. So, serverless is the only option I have. What that said, considering the number of supported geolocations, AWS Lambda stack would be the clear winner.

However, after digging more and more into the AWS Lamdba, Cloudformation, and all other tools, I realized serverless is not perfect (at all!). Fortunately, there are some acceptable workarounds! (Believe me, it is not that easy for me to call something acceptable.)

This main purpose of this article is to illustrate how to set up an OAuth-supported AWS Cognito user pool using Serverless Framework, and how to get around the potential pitfalls down the road.

Issues Preview:

Cloudformation can NOT define everything!

In Cloudformation, I cannot add a new resource server in a Cognito user pool; I cannot create a custom domain; I cannot configure an app client to make use of a new resource server…Hey AWS, you called it Cloudformation and Infrastructure as Code, not joking right?

Writing AWS Lambda function for custom resources is not as straightforward as I thought!

Alright, after googling for a while, I think I can write my own in AWS Lambda. But wait, what is a Service Token? How can I pass arguments to the lambda function? Why is my custom resource taking one hour or more to fail?

Testing custom resources on Cloudformation could be time-consuming!

Right, if you don’t know how to send an update to Cloudformation, you’ll need to wait one hour for it to fail if there’s an error in your Lambda function. BTW, cfn-response is suggested for sending Cloudformation updates, but it is NOT available if you want to deploy the lambda function from S3. In other words, you need to write the whole lambda function into the Cloudformation script, which is NOT acceptable to me at all.


  • Install the Serverless Framework and set up your AWS credentials. Instruction
  • Create a barebone serverless.yml file
  • Create a simple Cognito definition
  • Create a role for allowing Lambda to manipulate Cognito

Note 1: the AssumeRolePolicyDocument is essential for almost all Lambda function, since it allows AWS Lambda to manipulate your AWS resources on your behalf.

Note 2: the PolicyDocument allows the Lambda function to use CloudWatch for logging and do anything to the user pool you just created.

  • Create a custom resource! (finally)

Node.js script to add a resource server and an app client to the Cognito user pool. All users authenticate via this OAuth app will be granted admin:full scope for testing purpose.

CF template for custom resource:

Putting everything together in serverless.yml!

  • serverless deploy , everything should be up & running now!

Moment of Truth

Send a request to the endpoint from Postman!

Awesome! Something came back! Let’s check the access_token in

The scope is admin:full. Great!


To be honest, set this whole thing up is a lot harder than I expected it to be. The custom resource thing looks a bit hacky and not perfect, but at least I can set up or destroy the whole Cognito deployment by just serverless deploy or serverless remove without any other commands now!

Pei Deng

Written by

Pei Deng

Site Reliability Engineer / Production Engineer, Serverless Enthusiast

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade