How to export users from Amazon Cognito without losing accounts
Profile data, attributes, account status and MFA settings can be listed through the Cognito API, but password hashes cannot be exported, so leaving a user pool forces a choice between password resets and sign-in-time migration. This matters most when a renewal, a pricing change, a data residency requirement or a lock-in concern makes you plan an exit from a pool that holds live customer accounts. The export itself is the easy part. The hard part is deciding how users will authenticate on the other side before you move anyone.
The ListUsers operation returns users in pages of 60, so a full export means following the pagination token until no more pages remain, and it should be run against a copy of the data you can check. For each user, plan to capture the username, the sub value, email and phone attributes, verification flags, custom attributes, account status and whether MFA is enrolled. The sub matters because applications often store it as the user's stable identifier, and a new identity provider that issues different values will break those links unless you map them deliberately. Keep a record of which fields your applications actually read.
Because the export carries no password material, your options narrow to two. You can force a password reset for every account, which is simple but puts a sign-in step in front of each user. Or you can migrate users as they sign in, using a user migration trigger on the old pool to verify credentials once and then store them in the new system. Confirm the exact fields, paging behavior and trigger options against the current AWS documentation for your pool before you commit to either path, and test the full flow on a copy.
What you can export from a Cognito user pool
This table lists each data type you may need to move out of a Cognito user pool, the method used to read it, the output format and the known limits. Read the export method column first, because the main route is the ListUsers API, which returns users in pages of 60 per request. The main takeaway is that the export carries profile, status and enrollment data but not password hashes, so plan your credential migration before you start.
| Data type | Export method | Format | Limits and what to confirm |
|---|---|---|---|
| User profile and standard attributes | ListUsers API, paginated at 60 users per request | JSON via API | Confirm exact attribute field names in the AWS reference before mapping to the target |
| Custom attributes | ListUsers API, same paging as standard attributes | JSON via API | Custom attribute names carry a custom prefix; confirm how each one appears in responses |
| Username and sign-in alias | ListUsers API user name and attribute fields | JSON via API | Confirm whether email or phone is the sign-in alias for each pool, since this drives identifier mapping |
| Account status (confirmed, unconfirmed, disabled) | ListUsers API user status field | JSON via API | Unconfirmed and disabled users need separate handling; verify the status values returned for your pool |
| Creation and last-modified dates | ListUsers API timestamp fields | JSON via API | Confirm timestamp format and time zone in the reference; useful for audit and stale-account checks |
| Group membership | Separate group listing call per user or per group, as documented by AWS | JSON via API | Groups are not always returned in the user list; confirm the call and its paging before planning |
| MFA preferences and enrollment status | ListUsers API MFA settings fields, plus any per-user MFA calls | JSON via API | Enrollment secrets such as TOTP seeds are not exportable; users must re-enroll on the new platform |
| Password hashes | Not available through ListUsers | Not exported | Plan a forced reset or sign-in-time migration instead; confirm with AWS support for any other route |
| Console or CSV export, if offered | Console or tooling outside the API | Verify before use | Test on a sample pool and compare record counts and fields against the API output before relying on it |
How ListUsers paging shapes the export job
The export job has to follow pagination rather than assume a result size. ListUsers returns at most 60 users per request, so a user pool of any size needs a loop. Each response includes a PaginationToken when more users remain. Your code passes that token into the next request and stops only when a response returns no token. A loop that stops after a fixed number of pages, or that treats a short page as the last one, drops users without raising an error. Before the full run, test against a small pool, log the count for each page, and reconcile the total against an independent count of the pool's users. Write each page to storage as it arrives instead of holding the full list in memory.
Four operational drivers shape the job. The total user count determines how many requests the run makes and how long it takes, so measure it before you schedule the export. API throttling and account quotas limit how fast those requests can go. Confirm the Cognito request rates for your Region in AWS Service Quotas, and build retries with exponential backoff for throttling responses. The role running the export needs read access to the specific user pool, granted through an IAM policy scoped to that pool's ARN and kept separate from day-to-day administrator roles so the export can be audited and revoked. Confirm the exact actions in the Cognito IAM reference before you write the policy, since any other calls your script makes will need permissions too.
The output is a file of personal data, so storing it carries cost and risk. Write it to an S3 bucket you control, enable default encryption with a customer managed KMS key, block public access, restrict read access to named roles, and set a lifecycle rule that deletes the export once migration is verified. KMS requests, administrator time and periodic access reviews recur for as long as the copy exists, so keep the retention window short and written down. Plan to repeat the export close to cutover. Users created, updated or deactivated after the first pass will be missing or stale, so run a final pass, compare it with the first, and migrate from the newest set. The timing of that final pass depends on your user volume and your change freeze window.
What Cognito will not export and what to do about each gap
A ListUsers export gives you profiles, attributes and status, but several security-relevant items stay behind in the pool. Each gap below changes how users move, and the workaround you pick decides whether people sign in again on day one or only when they choose to.
Password hashes
Cognito does not export password hashes, so the export gives you usernames and attributes but nothing you can verify a password against. That leaves two paths: force a password reset for every user, or migrate on sign-in by validating the password against Cognito at the moment the user logs in and storing the result in the target. Check how your target platform supports a sign-in-time migration hook before you commit to either path.
MFA secrets and authenticator seeds
TOTP seeds and registered MFA devices are bound to the pool and are not part of a user list export. Users who enrolled an authenticator app will not carry it across, so plan re-enrollment at first sign-in in the new platform. Confirm with AWS documentation which MFA factors are recoverable, and communicate the enrollment step to users before cutover so help desk volume does not spike.
Active sessions and refresh tokens
Sessions, refresh tokens and device remembering state live in Cognito and cannot be copied to another identity provider. Every application will require a fresh sign-in after cutover, which also means existing tokens issued by Cognito stop being a reliable source of truth. Set a cutover window, shorten token lifetimes beforehand if your security policy requires it, and confirm how each app handles a forced re-login.
App client secrets
The secret values of app clients are generated by Cognito and are not included in the export. You will issue new client IDs and secrets in the target platform, then update every relying application and any server-side configuration that stores them. Inventory every app client and the systems that read its secret, because a missed update fails silently until a user reports it.
Lambda trigger logic
Pre-authentication, post-confirmation, token generation and migration triggers are code running inside your AWS account, not data in the pool. Their logic does not move with the users, so each rule (claim enrichment, custom validation, external calls) must be reimplemented in the target. Review each trigger with its owner, record its inputs and outputs, and test the rebuilt logic against the same sample logins.
Pool configuration and hosted UI
Password policy, sign-in settings, hosted UI branding and domain configuration are pool settings, and the export does not capture them. Write down each value from the console or your infrastructure as code before you change anything, then recreate the equivalents by hand or through infrastructure as code in the new platform. Confirm the exact settings with the current AWS documentation, since the list depends on your pool's configuration.
Why the sub claim decides the migration plan
The `sub` claim is the field most likely to decide how the migration runs. Applications often store the Cognito sub value as the stable identifier for each user, in database foreign keys, audit logs, authorization rules and file ownership records. The target system must either issue the same sub values for those users, or every application that stores them must be remapped to a new identifier. Before choosing a migration method, the team should confirm which claims each application reads, which ones it writes back, and whether any code compares sub against a value from another system.
To capture the identifier, the export has to include sub for every user, not only the username and email fields that appear in the console. A ListUsers pass that records sub alongside the username, the attributes and the account status gives a mapping file that can be checked against application databases before any cutover. Store that mapping in a controlled location with the old sub, the new identifier in the target system, and the date of the export. If the target can accept a supplied identifier at creation, the old sub can be carried over directly. If it cannot, the mapping becomes the input for a remapping job in each application.
This is what separates a bulk import from sign-in-time migration. Bulk import works best when the target can take the existing identifiers, because accounts are created before anyone signs in and the applications see no change. When identifiers cannot carry over, a team may need a remapping step for every dependent system, which pushes the work toward a migration that creates or links each account at first sign-in, so that the mapping is written as users return. Ask the application owners for a list of every system that stores sub before deciding. Confirm the target's identifier behavior with the vendor's documentation, because the choice depends on it.
Choosing between forced password resets and sign-in-time migration
This option suits teams with a modest, well-understood user base, a short list of applications and a tolerance for a coordinated cutover. You import profiles, attributes and group memberships into the target system with users flagged as needing a new password, then send each user a reset link. Because Cognito's export does not include password hashes, nothing about the old credential survives the move, so every account must be reset before it can sign in again. The team builds the reset email flow, the message templates, a support path for bounced addresses and a plan for users who cannot reach their inbox.
- What users experience: a one-time reset request on their next visit, which can coincide with MFA re-enrollment if authenticator devices do not carry over.
- What drives effort: the number of users to contact, the volume of helpdesk tickets in the first week, and how many applications share the login.
- Dormant accounts: users who never return will never reset, so their records sit in the new system unused and their access must be reviewed under your own policy.
- Email dependency: stale or shared mailboxes turn the reset into a manual recovery task.
- Cutover timing: the old pool can be retired once the reset campaign is complete, but teams often keep it for a fallback period.
Before committing, confirm with your own user data how many accounts have a verified, current email address and which applications depend on Cognito-issued tokens.
This option suits organizations that cannot ask every user to reset at once, such as those with many occasional users, partner accounts or applications that cannot tolerate a login disruption. The target system accepts the username and password at sign-in, calls a migration endpoint or custom logic that verifies the credential against the old Cognito pool, and then stores a new password in the target system so the lookup happens only once per user. The team builds that endpoint or extension, tests it against real sign-in paths and keeps the old pool running until the migration window closes.
- What users experience: a normal sign-in, with no reset email, for anyone who returns during the migration window.
- What the team builds: a verification endpoint that calls Cognito's authentication API, error handling for locked or disabled accounts, and logging that shows how many users have migrated.
- What drives effort: the number of applications that authenticate against the pool, the lifetime of the old pool, and whether MFA factors are migrated or re-enrolled at first login.
- Risks: dormant accounts remain in the old pool, so the pool must keep running, and its cost and operating burden continue until you decide to retire it.
- Ongoing dependency: any application still sending traffic to the old pool keeps the dependency alive.
Verify the Cognito API and quota limits that apply to your authentication calls, and confirm with the target product how it stores migrated passwords and whether it supports just-in-time migration out of the box.
Many teams combine both options, using a bulk import for every profile and a sign-in-time check for passwords. The bulk import puts profiles, groups and attributes in place so applications can resolve users before anyone signs in, while the verification path handles credentials for users who return. Users who never sign in are then either left to a reset campaign at the end of the window or disabled, depending on your policy. This approach splits the build work across both paths and generally requires the most planning, because the two populations must be tracked separately.
- Clear cutover rule: define when the old pool stops answering authentication requests and what happens to accounts that have not migrated by then.
- Two states to track: imported profiles that still need a password, and migrated accounts that now have a local credential.
- Shared identifiers: the subject identifier that applications store must survive the move, so the import must preserve it.
- Reporting: dashboards that show migration progress and remaining dormant accounts for the application owners.
- Exit point: a decision on the retirement date that the security and compliance owners approve in writing.
Ask your target vendor or implementation partner to document how each path behaves for a user who has MFA enrolled, since factor migration differs from password migration.
Export sequence: a complete pass with verification
This sequence turns a Cognito user pool into a verified, encrypted export that you can reconcile against the pool's own counts before any migration step begins. Each step closes with a check, so the output is evidence you can hand to the migration team and, if needed, an auditor.
Inventory pools and app clients
Platform or identity engineering lists every user pool in the AWS accounts and Regions in scope, along with each app client, identity provider configuration, Lambda trigger and custom attribute. Record the pool ID, Region, status of each user, and which applications depend on each client. This inventory defines what the export must cover. It is complete when every application that signs users in appears against a pool and a client, and the owning team has signed off on the list.
Confirm read permissions
Identity engineering creates a dedicated, least-privilege IAM role or user that can call the ListUsers action on the target pool and nothing that changes users. Confirm the role can read the pool with a single list call before any bulk work starts. Note the principal, the policy version and the approver in the change record. The step is complete when a single test call returns a user page and the policy has been reviewed by someone other than its author.
Run a test export against a non-production pool
Run the full export script against a non-production pool that mirrors the production attribute schema, including custom attributes and users in each status. Check that the output parses, that attribute names and types match the schema, and that pagination continues until no pagination token remains. Fix the script with the test results, not with assumptions about production. This step is complete when the test output loads into a scratch file and every field matches the schema document.
Run the full ListUsers loop with pagination
Run the production export with a loop that calls ListUsers and passes each returned pagination token back into the next request. ListUsers returns up to 60 users per request, so a pool of any size needs many sequential calls. Log the number of pages, the start and end times, and any throttling or errors with their retry outcome. The step is complete when the final call returns no pagination token and the log shows no unhandled errors.
Store the output encrypted with access logs
Write the export to an encrypted location controlled by the organization, such as a restricted storage bucket with server-side encryption and a customer-managed key, and deny public access. Enable access logging on that location and limit read access to named roles. Export files contain personal data, so treat them under your data handling policy from the first write. The step is complete when the file is encrypted at rest, public access is blocked, and a test read by an unauthorized principal is denied and logged.
Reconcile counts and status totals
Compare the number of records in the export against the user count reported for the pool, and reconcile the totals for each user status, such as confirmed, unconfirmed, archived and disabled, against the figures in the console or the pool's describe output. Investigate every difference before continuing, including users created or changed during the run. The step is complete when the counts match or each difference has a documented explanation and a re-run result.
Verify every sub value is present and unique
Check that every exported record contains a sub attribute and that no two records share one. The sub is the stable identifier that applications and tokens reference, so it is the field most likely to break sign-in after migration. Report the count of records without a sub and the count of duplicates. The step is complete when both counts are zero, or the team has a documented decision for each exception.
Spot-check attributes across statuses
Select a sample of users from each status and from each group of applications, then compare their exported attributes with the live pool record, including email, phone, custom attributes, MFA preference and group membership where the API exposes it. Confirm that the export shows no password material and that the attributes you expected are present. The sample size and selection rule should be written down before the check starts. The step is complete when every sampled record matches the source and each mismatch is explained.
Run a delta export before cutover
Shortly before the planned cutover, run a second export and compare it with the first to list users created, changed or disabled since the baseline. Apply the same counts, sub and attribute checks to the delta. Plan the cutover around the delta, since a password reset or sign-in-time migration still needs to handle users whose records changed after the baseline. The step is complete when the delta is reconciled and the migration team has the list of changed users.
Keep a signed record of counts and checksums
Record the final counts, status totals, sub check result, checksums of each export file, script version, operator and timestamps in a document signed by the data owner and the operator. Store the record alongside the encrypted export under the same retention rules. Ask your compliance lead which control evidence the organization needs, since DeployOpen prepares evidence but independent auditors determine whether it meets a given requirement. The step is complete when the signed record references each file by checksum and the signatures are stored.
What to plan before the AWS contract or pool use ends
Exporting users is the easy part. The work that follows is deciding how credentials move, how long the old pool stays live, and who changes the applications that depend on it. The cards below cover those decisions in the order most teams meet them, and each one shapes the effort and cost of the others.
Inventory pools, app clients and triggers
List every user pool, app client, domain, identity provider connection and Lambda trigger before you export anything. Each app client has its own callback URLs, scopes and token settings, and each trigger holds logic that must be rebuilt or replaced on the new platform. The number of applications and custom integrations is the main driver of effort here. Confirm the inventory against the AWS console and the infrastructure code, because the two often disagree.
Date the reset or migration decision
Choose between forced password resets and sign-in-time migration in writing, with a date for each pool. Cognito does not export password hashes, so a reset campaign means every user must create a new credential, while sign-in-time migration keeps users working but requires the old pool to stay reachable until the last active user has signed in. Your user count and how often people sign in decide which path is practical.
Run a parallel period
Keep the old user pool live while the new realm or instance serves a set of test users and a limited group of applications. Both systems must accept sign-ins during this window, so application configuration, token claims and session behavior are checked against real flows. Agree the length of the window and the criteria for ending it before work starts, and plan for the cost of running two identity stacks during that time.
Retention and deletion of exported data
An export is a copy of personal data, and each copy needs an owner, a storage location and a deletion date. Attributes such as email addresses, phone numbers and custom fields may fall under data protection rules that apply to your organization, and the export files often end up in tickets, laptops and shared drives. Confirm the retention period with your legal and privacy team, and record when the export files and the Cognito pool itself will be deleted.
Name the owner of DNS and redirects
Assign one named person or team to own DNS records, redirect URIs, logout URLs and the configuration in each application that points at the identity provider. Changing an issuer URL or a callback address breaks sign-in for every app that was not updated. Check the list of applications against the app client inventory, and agree who approves each change and who can roll it back during the cutover.
Write the rollback plan
Define how you return to the Cognito pool if the new platform fails during cutover or in the first weeks after it. The plan should name the switch that routes traffic back, the state of user records changed on the new side, and the point after which a rollback is no longer clean. Test the rollback in a non-production environment, and confirm the old pool and its app clients are not deleted until that window has closed.
Questions buyers ask before exporting Cognito users
Can password hashes be exported from Amazon Cognito?
No. The ListUsers operation returns profile attributes, account status and metadata, but it does not return password hashes, so no export file can carry a working password to a new system. That leaves two paths. The first is a forced password reset: users receive a reset flow or temporary password at their next sign-in, and the new system starts with no stored credentials. The second is sign-in-time migration: the Cognito user pool stays available, and at each sign-in the new system checks the submitted password against it and stores the credential on success. The second path keeps existing passwords working, but the old pool must keep running until the last active user has moved. Confirm the exact mechanics with your engineers before you choose.
How long does exporting Cognito users take?
It depends mostly on user count, because ListUsers returns 60 users per request. Divide your user count by 60 to get the minimum number of requests, then add time for throttling, retries and writing each record to storage. As an illustrative example, a pool of 600,000 users would need at least 10,000 requests before any retries. Other drivers include the number of attributes per user and whether other systems share the same API quota at the same time. The reliable answer comes from a test: create a pool with a representative sample of users, run your export script against it, time the run, count throttling errors and extrapolate from there. Ask your engineers to record both timing and error counts so the production plan rests on measured numbers.
Can our own team export Cognito users without outside help?
Yes, if you have engineers who know AWS identity services and the target identity system. The export itself is a script that pages through ListUsers, writes records to encrypted storage and checks the final count against the pool. The team also needs IAM permissions that allow read access to the user pool and nothing broader, a mapping from Cognito attributes to the target's user model, and a plan for groups, app clients and any Lambda trigger logic that depends on user data. Exported files contain personal data, so someone must own their storage, access logging and deletion. The harder work is usually the import, sign-in testing for every app client and cutover, not the API calls. Teams without this experience should weigh the time these tasks take against the cost of an engagement.
What does outside help for a Cognito export cost?
DeployOpen quotes from a written, fixed scope. Scope, deliverables and timelines are agreed in writing before work starts, and DeployOpen does not publish a price list, so the quote follows from the scope. The main cost drivers are the user count, the number of app clients and environments, custom attributes, MFA configuration, Lambda triggers that need rebuilding, the target platform and the cutover approach, including whether sign-in-time migration runs alongside the pool. Testing, documentation and handover to your team add to the work. Costs split into a one-time migration engagement and any recurring items, such as a support or upgrade retainer for the platform afterward. Ask for every deliverable and exclusion to be listed in writing, so you can compare proposals on the same basis.
Can MFA settings carry over when we export Cognito users?
Only partly. The export gives you the MFA options recorded on each user, such as the preferred factor, but enrolled authenticator secrets and registered devices cannot be moved to another system. Plan for users to re-enroll their second factor after cutover, and allow support time for that step. Start by inventorying which MFA methods your user pool enables and which users rely on them. Then decide what the new system will require, whether that is TOTP, passkeys, SMS or another method, and communicate the enrollment steps before the switch. Confirm the exact fields your export returns for each factor type against current Cognito documentation, because the details depend on the factor.
What happens to users who never sign in again?
Many organizations migrate active accounts and leave the rest behind, but that is a policy decision rather than a technical default. The export includes account status and timestamps, which let you separate users who signed in recently from dormant accounts. Options include migrating all users in bulk, migrating only active users, creating dormant users as disabled accounts or leaving them in the Cognito pool with an archive plan. Sign-in-time migration handles the dormant group naturally, because accounts move only when someone signs in, but the old pool must stay available for as long as dormant users may return. Involve your privacy and records owners early, since keeping or deleting unused personal data carries its own retention obligations.
How do we keep the Cognito sub claim after migrating?
Applications usually depend on the sub value, which is Cognito's unique identifier for each user, so changing it can break authorization data, audit trails and stored references. Before import, record the sub for every user from the export. Then check whether the target lets you set the user identifier at creation. Keycloak's import format includes a user ID field, and you can test whether it preserves the original value using the Keycloak import and export guide. With sign-in-time migration, the new system can create the user with the existing sub when the first sign-in succeeds. If the target assigns new IDs, keep a mapping from the old sub to the new ID and update applications and stored references during cutover. Confirm the behavior in a test realm before you commit to a plan.
What should we ask a vendor or consultant before accepting a quote?
Ask for a written scope that names the source user pool, target platform, user count, attributes, app clients, MFA methods and Lambda triggers in scope. Ask how the proposal handles sign-in-time migration compared with a forced reset, how the sub claim is preserved and how exported files are protected, stored and deleted. Request the test and cutover plans, rollback steps and acceptance criteria for each deliverable. Ask which items are excluded, who holds administrator access after handover, what documentation you receive and which timeline milestones depend on your team. Finally, ask how changes after signing are priced and whether ongoing support or upgrades are a separate retainer. Any provider that cannot state these points in writing deserves caution.
How DeployOpen scopes an export and migration
DeployOpen starts from the written scope for your Cognito user pools, then plans the export, the password strategy and the target identity platform inside your own cloud account or data center. Engagements are fixed-scope, with deliverables and timelines agreed in writing before work starts.
How we scope
- We inventory every user pool, app client, attribute, group and MFA setting you need to carry across.
- We confirm with you which users can take a forced password reset and which need sign-in-time migration.
- We agree in writing which export outputs, test accounts and cutover checks are in and out of scope.
How we build
- We script paged ListUsers exports and validate attribute, status and MFA fields against the source pool.
- We build the import or just-in-time migration path on the self-hosted platform you select, including the stable subject identifier.
- We test sign-in, token claims and app client behavior in a staging environment before any user is moved.
How we hand over
- We document the export runbook, import procedure, password strategy and rollback steps for your team.
- We deliver configuration as code and record each mapping decision so auditors and operators can trace it.
- We walk your operations staff through the running platform, including backup and upgrade procedures.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers after handover.
- Retainers can cover patching of the self-hosted identity platform as new releases ship.
- Compliance evidence for identity controls can be prepared for your independent assessors, who issue the reports.

