Configure and run B2C to Entra External ID user migration. USE FOR: setup migration, configure B2C migration, run export import, run harvest workers, configure JIT password migration, create app registrations, generate RSA keys, set up devtunnel, test JIT flow, manage migration flags, validate readiness, deploy workers to Azure, analyze telemetry, phone registration, bulk migration, export B2C apps, transform API connectors to CAE. DO NOT USE FOR: general Azure questions, Entra ID concepts unrelated to migration.
Instrucciones de origen · Vista previa de solo lectura
name
b2c-migration
description
Configure and run B2C to Entra External ID user migration. USE FOR: setup migration, configure B2C migration, run export import, run harvest workers, configure JIT password migration, create app registrations, generate RSA keys, set up devtunnel, test JIT flow, manage migration flags, validate readiness, deploy workers to Azure, analyze telemetry, phone registration, bulk migration, export B2C apps, transform API connectors to CAE. DO NOT USE FOR: general Azure questions, Entra ID concepts unrelated to migration.
argument-hint
Describe what migration step you need help with
B2C to Entra External ID Migration Kit
This skill guides an agent through configuring, running, and troubleshooting the B2C Migration Kit — a tool for migrating users from Azure AD B2C to Microsoft Entra External ID.
Architecture Overview
B2CMigrationKit.Core/ # Business logic, models, abstractions
B2CMigrationKit.Console/ # CLI for bulk operations (export, import, harvest, worker-migrate, phone-registration, validate)
B2CMigrationKit.Function/ # Azure Function for JIT password migration
Two migration modes:
Mode
Best For
Steps
Simple (Export/Import)
< 1M users, no MFA phone migration
export → import
Advanced (Workers)
Large tenants, MFA phone, parallel scaling
harvest → worker-migrate → phone-registration
Both modes use for seamless first-login password transfer.
JIT password migration
Decision Flow
When the user asks for help, determine which phase they need:
Admin consent required on all permissions. Each parallel worker instance needs a dedicated app registration on a dedicated IP for independent throttle quotas.
ExtensionAppId
The ExtensionAppId is the Application ID of the b2c-extensions-app (32 hex characters, no hyphens). Custom extension attributes use format: extension_{ExtensionAppId}_{attributeName}.
Configuration Files
Simple Mode:
cd src/B2CMigrationKit.Console
Copy-Item appsettings.export-import.example.json appsettings.export-import.json
# Edit with tenant credentials
Advanced Mode:
cd src/B2CMigrationKit.Console
Copy-Item appsettings.master.example.json appsettings.master.json
Copy-Item appsettings.user-worker.example.json appsettings.user-worker.json
Copy-Item appsettings.phone-worker.example.json appsettings.phone-worker.json
# Edit each file with tenant credentials
Config patterns:
Local → use ClientSecret with actual value
Production → use ClientSecretName with Key Vault secret name
Configures Application ID URI matching the function URL domain
Creates the Custom Authentication Extension resource (onPasswordSubmitCustomExtension)
Creates a test client app with redirect to https://jwt.ms
Creates the event listener (onPasswordSubmitListener)
Manual step required: Grant admin consent for the Extension App in Azure Portal after the script completes.
Step 4: Switch Environments
Toggle JIT between local devtunnel and Azure Function endpoints:
.\scripts\Switch-JitEnvironment.ps1 -Environment Local # devtunnel
.\scripts\Switch-JitEnvironment.ps1 -Environment Azure # production
Local JIT Testing
Start the Function
cd src\B2CMigrationKit.Function
.\start-local.ps1
Expose with VS Code Port Forwarding
Ctrl+Shift+P → Ports: Forward a Port → enter 7071
Right-click the forwarded port in Ports panel → Port Visibility → Public
Copy the Forwarded Address (e.g., https://abc123-7071.brs.devtunnels.ms)
The JIT endpoint is: <forwarded-url>/api/JitAuthentication
The devtunnel URL is stable for the VS Code session. If it changes, update the Custom Authentication Extension with Configure-ExternalIdJit.ps1 or Switch-JitEnvironment.ps1.
Important: Application ID URI Must Match
The Application ID URI of the Custom Extension app registration must match the devtunnel domain. Format: api://<devtunnel-host>/<app-client-id>. The Configure-ExternalIdJit.ps1 script sets this automatically, but if you change the tunnel URL, update it:
Application ID URI domain must match the targetUrl domain in the CAE
Migration Flag Management
Query and update the RequireMigration flag on External ID users:
# List users pending migration
.\scripts\Manage-MigrationFlag.ps1
# List all users
.\scripts\Manage-MigrationFlag.ps1 -Filter all
# Clear flag for migrated users
.\scripts\Manage-MigrationFlag.ps1 -Filter true -SetFlag false
# Set flag for a specific user by ID
.\scripts\Manage-MigrationFlag.ps1 -UserId "<user-object-id>" -SetFlag true
# Set flag for a specific user by UPN
.\scripts\Manage-MigrationFlag.ps1 -UserUpn "user@tenant.onmicrosoft.com" -SetFlag true
# Discover extension attribute names in the tenant
.\scripts\Manage-MigrationFlag.ps1 -Discover
# Preview changes without applying
.\scripts\Manage-MigrationFlag.ps1 -Filter true -SetFlag false -WhatIf
After deployment, connect via Bastion and configure each VM:
# Open tunnel
.\scripts\Connect-Worker.ps1 -WorkerIndex 1
# SSH through tunnel (separate terminal)
ssh -p 2201 -i .\scripts\b2c-mig-deploy azureuser@localhost
# On the VM: configure worker
bash /opt/b2c-migration/repo/scripts/Configure-Worker.sh
App and Connector Migration
Migrate B2C app registrations and their API connectors to External ID as onTokenIssuanceStart Custom Authentication Extensions (CAE).
This procedure is app-centric. Each app is migrated as a unit. The agent asks only what's needed and handles the technical details automatically.
Agent Procedure — Per-App Migration
When the user asks to migrate an app (or a set of apps), follow this guided flow:
1. Gather required information
Ask the user (only what you don't already know):
B2C tenant domain or ID (e.g. contoso.onmicrosoft.com)
External ID tenant domain or ID
App name(s) to migrate (exact or wildcard)
2. Identify connectors for the app
Explain: "B2C Graph API doesn't expose which user flows belong to which app, so I can't automatically determine which API connectors are used by your app."
Ask: "Does your app use any B2C API connectors? If yes, what are their names? (You can find them in Azure Portal → B2C → API connectors)"
If the user doesn't know or wants to migrate all connectors: proceed without -ConnectorNames (all connectors will be migrated).
3. Identify claims (optional but recommended)
If the app has API connectors, ask: "What custom claims does your connector API return? (e.g. role, department, subscriptionTier) — These will be declared in the CAE so External ID includes them in tokens."
4. Run dry-run first
.\scripts\Migrate-B2CApp.ps1 `
-B2CTenantId "<b2c-tenant>" `
-EeidTenantId "<eeid-tenant>" `
-AppName "<AppName>" `
-ConnectorNames "<ConnectorName*>" ` # omit if user doesn't know / wants all
-ClaimsForToken "claim1","claim2" ` # omit if no connectors
-DryRun
Show the user the dry-run output and ask for confirmation before proceeding.
5. Run the actual migration
.\scripts\Migrate-B2CApp.ps1 `
-B2CTenantId "<b2c-tenant>" `
-EeidTenantId "<eeid-tenant>" `
-AppName "<AppName>" `
-ConnectorNames "<ConnectorName*>" `
-ClaimsForToken "claim1","claim2" `
-SkipExport # re-use the export from the dry-run
6. Explain the migration report to the user
After running, the script prints a report with three sections. Explain each:
Section
Meaning
✅ AUTOMATED
Done — no action needed
⚠️ MANUAL ACTIONS REQUIRED
User must do these steps in Azure Portal
❌ NOT MIGRATED
These features don't exist in External ID — user must redesign them
The one required manual step is always admin consent:
Azure Portal → App registrations → [CAE - <connector name>] → API permissions
→ Grant admin consent for <tenant>
Without this, tokens will be issued without the custom claims.
What the Script Does Automatically per App
Step
What happens
App registration
Re-created in External ID with matching redirect URIs, app roles, and API scopes
B2C identifier URIs
Filtered — b2clogin.com URIs are removed (invalid in EEID)
Per connector: CAE app
Created with CustomAuthenticationExtension.Receive.Payload permission
Per connector: CAE extension
onTokenIssuanceStartCustomExtension pointing to same target URL
Per connector: claims config
Populated from -ClaimsForToken parameter
Per connector: event listener
Created and linked to all apps in the EEID tenant automatically
What Cannot Be Migrated Automatically
B2C Feature
External ID Alternative
Action Required
User flow connector bindings
Event listeners (created automatically)
Verify in Portal
Client secrets / certificates
Must be regenerated
Portal → Certificates & secrets
B2C custom policies (IEF)
External ID user flows / custom auth extensions
Redesign required
Phone MFA configured on flows
External ID MFA settings
Re-configure in EEID
B2C identifier URIs (b2clogin.com)
Filtered out automatically
Update app code if needed
Basic Auth / API Key on connector
Azure AD bearer token
Update API endpoint
Auth Model Change (Required for All Connectors)
B2C API connectors authenticate with Basic Auth, API Key, or Client Certificate.
External ID CAEs authenticate with Azure AD bearer tokens.
After migration your API endpoint must:
Accept Authorization: Bearer <token> and validate it against Azure AD
Use the resourceId (audience) printed in the migration report