Amazon Cognito is a strong service for user authentication and user management. However, as applications grow, user search requirements often go beyond what default APIs such as ListUsers can handle efficiently. For example, support teams may need to find users by partial email, filter by group, audit across custom attributes, or perform fuzzy search with sub-second response time.
The AWS blog proposes a clear approach: do not force Cognito to become a search engine. Instead, build a dedicated search layer on top of Cognito using AWS Lambda, Amazon DynamoDB, DynamoDB Streams, and Amazon OpenSearch Serverless. Cognito remains the identity provider, while OpenSearch handles complex search queries.
For simple use cases, ListUsers may be enough. In production environments with a large number of users, search requirements usually become more advanced:
The difficult part is keeping the search index synchronized with Cognito. Scheduled batch jobs can introduce stale data. Cognito Lambda triggers alone can miss some administrator actions from the console or CLI. The solution combines multiple event sources to reduce the chance of data drift.

Source: AWS Architecture Blog - Building a scalable user search layer on top of Amazon Cognito
The solution has two main flows: ingestion and search.
The ingestion flow captures user data from Cognito, stores it in DynamoDB, and updates the OpenSearch index.
Adding CloudTrail and EventBridge is important because not every user change happens during the authentication flow. If an administrator creates, updates, disables a user, or changes group membership, the architecture can still synchronize the search index.
The search flow is separate from Cognito’s default APIs.
This allows the search API to support fuzzy matching, complex filtering, and sub-second response times without forcing Cognito to handle search workloads it was not designed for.
| Service | Role in the solution |
|---|---|
| Amazon Cognito | Manages users, authentication, and JWT tokens |
| AWS Lambda | Handles Cognito triggers, CloudTrail events, DynamoDB Streams, and search API requests |
| Amazon DynamoDB | Stores normalized user profile data as an intermediate source |
| DynamoDB Streams | Detects data changes and starts the indexing process |
| Amazon OpenSearch Serverless | Stores the search index and handles advanced search queries |
| AWS CloudTrail | Records Cognito Admin API calls |
| Amazon EventBridge | Matches CloudTrail events and starts synchronization Lambda functions |
| Amazon API Gateway | Provides the REST API for frontend search |
The most important idea is the separation of responsibilities. Cognito handles identity commands such as sign-up, sign-in, user updates, and group membership. OpenSearch handles queries such as fuzzy search, prefix search, and filtering.
This is similar to a practical CQRS pattern: keep the identity system as the source of authority, but create a read model optimized for search.
If the system only uses a Post-confirmation trigger, it may miss administrator updates. If it only uses batch synchronization, data can become stale. The article combines Cognito triggers, CloudTrail, EventBridge, and DynamoDB Streams to keep OpenSearch closer to the real Cognito state.
The original post notes that Cognito Lambda triggers have a timeout limit. Trigger functions should avoid heavy processing. A better approach is to write the required data to DynamoDB quickly, then let DynamoDB Streams and another Lambda handle indexing asynchronously.
Before reading this article, I tended to think of Cognito as both the user management system and the user search system. After studying the solution, I think the better approach is to let Cognito focus on identity and move advanced search to a dedicated search layer.
I also learned that user data synchronization is more complex than listening to one trigger. In reality, user data can change through sign-up, sign-in, the admin console, CLI, or API. A production architecture needs to account for all of those paths.
This article shows how to extend Amazon Cognito with a dedicated search layer, using DynamoDB as an intermediate data store and OpenSearch Serverless as the search index. The architecture supports fuzzy search, complex filtering, pagination, and sub-second response time at scale.
For a real implementation, I would start by defining which user attributes need to be searchable, designing the DynamoDB data model, optimizing the OpenSearch index, handling all Cognito change sources, and applying least-privilege IAM permissions to each Lambda function.