Skip to content

Prospective case · Identity as code

Moving a regulated insurer from Okta to Microsoft Entra ID, as code

A large regulated insurer licensed Microsoft 365 and Entra ID, and paid separately for Okta to sign its people in. The migration is designed so that every application, group and access policy moves through a reviewed pipeline, and the Entra ID tenant that remains is managed as code rather than by console.

Situation

Two identity providers, one of them already paid for

Identity was mastered in on-premises Active Directory and synchronized to two cloud directories at once: Okta, which signed users in to several hundred SAML and OIDC applications and provisioned dozens more, and an Entra ID tenant that Microsoft 365 required in any case. The Microsoft 365 domain was federated to Okta, so every Microsoft sign-in made a round trip through the second provider.

The Entra ID tenant was not empty. Several years of Conditional Access policy had been built in it by hand, and nobody could say with confidence which policies were still doing work. Any migration plan that treated the target as a blank tenant would have overwritten controls the business depended on.

The insurer was regulated, so the migration had to produce evidence as it went: who changed each control, when, and under which change record. And it had to be reversible at every step, because the characteristic identity outages — administrators locked out, a policy enforced before it was observed, a change larger than intended — are the ones a migration is most likely to cause.

Cost and benefit

What moving off Okta is worth to a Microsoft estate

Two separate economic questions sit inside this decision, and they are worth keeping apart. The first is the license: what the organization stops paying when Okta is retired, net of anything Entra ID needs that is not already owned. The second is the operating model: what it costs to run the identity platform by console against running it as code. Either can justify the program on its own; the strongest cases have both.

The license

For an organization that already runs Microsoft 365, the identity license for Entra ID is usually paid for before the migration begins. Microsoft 365 E3 and Business Premium include Entra ID P1, which carries Conditional Access, group-based application assignment and hybrid identity. Microsoft 365 E5 includes Entra ID P2, which adds risk-based Conditional Access, Privileged Identity Management and access reviews. In either case, Okta is a second identity provider licensed on top of an entitlement the organization already holds.

The chart compares the annual identity license per user before and after the move, at an illustrative Okta rate of $14 per user per month. The one case in which Entra ID adds cost — an E3 estate that needs P2 features — still leaves a margin, and it is the case in which the cheaper answer is often an E5 security add-on that brings the Defender and Purview capabilities described below with it.

What the license line leaves out

  • The saving begins at Okta contract exit, not at the first wave. Okta stays live as the fallback for the length of the program, so the two providers run side by side for most of a year.
  • Okta features with no license-free equivalent must be costed: workflow automation, device assurance and any desktop MFA agent each need a named replacement (Intune compliance, Logic Apps or the existing governance platform) before the contract can end.
  • Use the contracted Okta rate and the real Microsoft 365 mix. The model is only as good as those two inputs.

Identity license beyond Microsoft 365, per user per year

  • Today: Okta alongside Microsoft 365$168

    Illustrative Okta rate; substitute the contracted one.

  • After: Microsoft 365 E5$0

    Entra ID P2 is already included.

  • After: Microsoft 365 E3, P1 sufficient$0

    Entra ID P1 is included: Conditional Access, group-based assignment, hybrid identity.

  • After: Microsoft 365 E3, P2 required$120

    Standalone Entra ID P2 at its $10 list price, for risk-based Conditional Access and Privileged Identity Management. Conservative: it pays again for the P1 already held.

Illustrative. Microsoft figures are Entra ID list prices as published in October 2026; the Okta figure is a placeholder at $14 per user per month.

The operating model

The second question is how the platform is run after the migration. The executive brief prices it with a total-cost-of-ownership model that names every variable: change volume, cost per change under each practice, environments that must hold the same configuration, audit preparation, incident cost, key-person risk, the up-front investment and pipeline maintenance.

Under the brief's base inputs, managing the platform as code costs more in the first year and roughly half as much over five, breaking even at about thirteen months. Under inputs built deliberately to disfavor it, it still breaks even inside five years. The inputs are illustrative and are replaced with the organization's own before any decision is made.

The full model, with every variable, the conservative and aggressive cases and a sensitivity table, is in Configuration as an Asset.

Cumulative cost of running the identity platform

  • By console
  • As code
$0$500k$1.0M$1.5M$2.0M$2.5MStartYear 1Year 2Year 3Year 4Year 5Break-even ≈ 13 months$2.45MBy console$1.16MAs code
Show the figures
YearBy consoleAs code
Start$0$350,000
1$490,000$512,500
2$980,000$675,000
3$1,470,000$837,500
4$1,960,000$1,000,000
5$2,450,000$1,162,500
Illustrative, from the executive brief's base case: 300 changes a year across two environments, $350k up front. Replace the inputs with the organization's own before deciding.

Approach

Four steps, one write path

The program runs in four steps. The first two write nothing through the pipeline: they establish what exists on both sides before anything moves. The third builds the only path by which Entra ID changes. The fourth moves applications in waves, with Okta kept as the fallback until each wave has proved itself.

OKTA · SOURCE, RETIRINGRead-only through Steps 1 to 3; kept so anyapp can be pointed back; assignments removedonly after validation, as a separate changeSTEP 1Read Okta, read-only, into codeSTEP 2Import Entra, then map every objectSTEP 3Build the only path that changes EntraSTEP 4Move in waves; keep Okta as fallbackTHE ONLY WRITE PATHNO PIPELINE WRITESMICROSOFT ENTRA ID · TARGETChanged by the pipeline and nothing else;rehearsal tenant first, then production;nightly drift check opens an issue
Redrawn from the program's Lucid strategy set. Steps 1 and 2 make no pipeline writes; every change to Entra ID, including each wave, passes through Step 3.
  1. Step 1

    Discovery: read Okta into code

    A read-only Okta service application, authenticating with a private key rather than an API token, exports the entire org nightly: applications, groups and group rules, sign-on and password policies, network zones, authorization servers and profile schemas. Terraform import generates the configuration, which is normalized into one file per object type. Per-user records go to restricted storage; only counts reach the repository. Each night's export is compared with the last, so any change made in Okta during the migration appears the next morning as a reviewable difference.

    What it leaves behind

    • A diffable specification of what must move
    • A change-freeze monitor for the whole program
    • Assignment counts that size each wave's blast radius
  2. Step 2

    The mapping layer: brownfield first, then translate

    The existing Entra ID configuration is imported into code as it stands, every resource protected from deletion, until a plan shows no changes — proof the import is faithful. A decision workbook then rationalizes the hand-built Conditional Access policies: keep, rebuild into a persona framework, or both. Only then is each Okta object mapped, in a versioned catalog, to an Entra ID definition, a disposition, a wave and an owner, and a translator emits draft definitions for engineers to review. The authentication cutover — password hash sync, seamless sign-on, Staged Rollout groups and the MFA registration campaign — is designed here and gates every wave.

    What it leaves behind

    • The existing tenant under code, with nothing changed
    • A mapping catalog with fidelity recorded per row
    • Unresolved gaps that block a wave rather than surprise it
  3. Step 3

    The controlled write path

    Three pipeline identities — read, plan and apply — authenticate with short-lived federated tokens and hold no stored secret. Every change is a pull request. Policy-as-code rules reject a plan that removes the break-glass exclusion, enforces a new Conditional Access policy without a report-only period, or destroys an imported object. A change gate verifies an approved change record whose requester is not the author. Changes apply to a rehearsal tenant first and to production only after approval. A nightly drift check opens an issue for any console change and never reverts one automatically.

    What it leaves behind

    • The answer to who changed a control, when, and why
    • The classic identity outages prevented by rule
    • Audit evidence generated as a by-product of the work
  4. Step 4

    Cutover waves and Okta decommission

    Applications are risk-scored from inventory data and grouped by business unit under sponsor sign-off, starting with a pilot of IT staff. Each wave is validated with synthetic sign-in tests, error rates and owner sign-off over a window sized to criticality — 30, 14 or 7 days by tier. The Okta assignment is removed in a separate, later change, and Okta configuration is kept so any application can be pointed back. The domain conversion from federated to managed runs last, by runbook, with re-federation as its rollback.

    What it leaves behind

    • A rollback for every step that is a configuration change
    • Okta retained until each validation window closes
    • A hard decommission milestone with an executive owner

The mapping layer

Every Okta object, its Entra ID target, and what changes it

The mapping catalog is the center of the program. Every Okta object gets an Entra ID target and a record of who changes it afterwards. Fidelity is recorded per row — exact, approximate or manual — so nothing is lost in translation without being written down.

  • SAML and OIDC applications

    Enterprise applications and app registrations

    Terraform (azuread)
  • SAML attribute statements

    Custom claims policy per application

    Beta Graph API; the portal edit is otherwise invisible to code.

    Graph (msgraph)
  • Groups and group rules

    Existing AD-synced groups; dynamic groups

    Matched by lookup, not recreated.

    Terraform (azuread)
  • Sign-on and authentication policies

    Conditional Access policies, report-only first

    Terraform (azuread)
  • Network zones

    Named locations

    Terraform (azuread)
  • Authenticators and enrollment

    Authentication methods policy and strengths

    Strengths in azuread; the methods policy needs Graph.

    Graph (msgraph)
  • Admin roles

    Directory roles under Privileged Identity Management

    Eligible, never standing.

    Terraform (azuread)
  • Authorization server scopes

    Exposed API scopes on app registrations

    Terraform (azuread)
  • WS-Fed federation to Microsoft 365

    Managed domain via Staged Rollout

    Graph or PowerShell, under privileged activation and change approval.

    Runbook

Terraform versus Graph

Terraform is the declarative engine for both platforms: it reads Okta and writes Entra ID. On the Entra ID side there are two providers, and the choice between them is the single most consequential design decision in the translation layer.

The lab settled the claims question by experiment. Each application's claims were edited in the portal and then planned. Under azuread's claims mapping policy, the portal edit was blocked and the portal displayed the wrong claims. Under the msgraph custom claims policy, the edit was allowed and Terraform saw every field. With claims held only in the portal, Terraform saw nothing. The program puts every application's claims in code through the custom claims policy.

Terraform, through the azuread provider

The mature, typed provider. It covers the core of the migration — applications, service principals, groups, Conditional Access, named locations, authentication strengths, directory roles and PIM — with plan-time validation and a clean diff. Core objects stay here.

Microsoft Graph, through the msgraph provider

A thin layer over the Graph REST API, which Microsoft labels preview. It reaches four objects on the migration's critical path that azuread does not: domain federation, Staged Rollout, the authentication methods policy and SAML custom claims. It is used for that list only, pinned to an exact version, in its own state, with PowerShell as the fallback.

Cutover

Two switches per wave, and a rollback for each

Each wave turns on two switches, in order, from one wave file: Entra ID begins authenticating the wave's users, then Okta begins redirecting them. Forward is always the first switch then the second; rollback is the reverse. Okta is never asked to redirect a user Entra ID is not yet ready to authenticate.

  1. S0

    Okta native

    Applications trust Okta. Nothing has moved.

  2. S1

    Staged Rollout on

    The wave's users join a Staged Rollout group and authenticate to the cloud with password hash sync. Verified within 30 minutes.

  3. S2

    Okta redirects

    An Okta routing rule sends those users to the Microsoft sign-in. One flag returns every user to Okta at once.

  4. S3

    Applications repointed

    Owners switch each application's trust to Entra ID. The original Okta application is unassigned, not deleted.

  5. S4

    Validated

    Healthy for the window, failures under threshold. Okta assignment removal is scheduled as its own change.

  6. S5

    Domain converted

    After the final wave the domain becomes managed and the Okta fallback ends. Okta agents leave endpoints only now.

Endpoints move with the wave. Staged Rollout groups double as the Intune assignment target, so Authenticator, Company Portal, the platform SSO extension and compliance policy arrive on a wave's Windows, macOS and mobile devices as its users move. Okta agents are not removed until the domain is converted, so a rollback never needs a reinstall.

Beyond the license

One directory under the whole Microsoft security estate

The license is the smaller part of the argument. When the identity provider is Entra ID, the sign-in, the device, the data label and the security alert share one directory and one policy engine. Okta integrates with several of the products below through connectors; what it cannot do is make them one system, because the decision about access is taken in a different tenant from the signals that should inform it.

  • Conditional Access

    The single policy engine every Microsoft service and every migrated application evaluates at sign-in, combining user, group, location, device compliance and application in one rule.

    Entra ID P1

  • Entra ID Protection

    Scores sign-in and user risk from Microsoft's threat intelligence and feeds that score straight into Conditional Access, so a risky sign-in can require reauthentication or a password change.

    Entra ID P2

  • Privileged Identity Management and ID Governance

    Eligible rather than standing administrator roles, activated with approval and time limits; access reviews, entitlement packages and lifecycle workflows on the same objects.

    Entra ID P2; ID Governance

  • Intune

    Device compliance becomes a Conditional Access condition without an integration to maintain, replacing Okta device assurance with the management platform already deployed.

    Microsoft 365 E3 / E5

  • Microsoft Defender XDR

    Defender for Identity watches Active Directory, Defender for Cloud Apps watches SaaS sessions, and Defender for Endpoint watches the device. Their incidents correlate against the same user object the sign-in used, and containment acts on that object directly.

    Microsoft 365 E5

  • Microsoft Sentinel

    Sign-in, audit and risk logs arrive through a native connector, alongside the pipeline's own audit trail, so an investigation can follow a change from pull request to sign-in.

    Consumption

  • Microsoft Purview

    Sensitivity labels, data loss prevention and insider risk act on the identity Entra ID issued, and insider risk can raise a user's risk level in Conditional Access.

    Microsoft 365 E5

  • Global Secure Access

    Entra Private Access and Internet Access extend the same Conditional Access policy to private applications and web traffic, the path away from VPN-based access.

    Entra Suite

License inclusions are Microsoft's published packaging as of October 2026. Packaging changes; confirm against the organization's agreement.

Proven in the lab

What the method has already done

The method was run end to end in a public lab before it was put in front of the client: a free Okta org seeded with fictional users, groups, applications and policies; an Entra ID tenant with a hand-built brownfield configuration; and the full pipeline. These results are observed, and each is recorded with its evidence in the lab's findings log. The repository is public.

5

Applications translated and signed in

Four SAML and one OIDC application rebuilt in Entra ID as code and tested through My Apps and an OIDC debugger.

2 of 2

Unsafe changes blocked

A rule-breaking pull request and one with no change ticket both failed the pipeline before reaching a reviewer.

1 → 1

Drift caught, one issue opened

An induced console change opened exactly one issue and was put back through the pipeline.

Lessons

What the lab changed in the plan

The lab surfaced the details a design document misses. Each of these is now a step in the client runbooks.

  1. Read every hand-built policy back before importing it

    Two of three hand-built Conditional Access policies in the lab were missing the break-glass exclusion. A brownfield import faithfully preserves that mistake, which is why the exclusion is a policy-as-code rule rather than a checklist item.

  2. Claims edited in the portal are invisible unless they are in code

    The portal can also display the wrong claims. Every application's claims belong in the repository, and any portal touch is followed by a review of the plan.

  3. The identifier an application receives changes on migration

    Okta login names that are not valid Entra ID user names change the NameID sent to the application. Application owners are told before their wave, not after it.

  4. Give each SAML application its own identifier

    Okta tolerates applications sharing one audience; Entra ID requires a unique identifier on a verified domain. The mapping catalog records the new value per application.

  5. Budget for the gaps, and for Entra ID's replication delay

    The azuread provider could not activate a SAML signing certificate, so Graph does it. Entra ID sometimes returns not-found on an object it has just created, so the pipeline retries. Neither is a defect in the method; both are work to plan for.

  6. Size the waves to Staged Rollout's limits

    Staged Rollout accepts at most ten groups per feature, security groups only, with no nesting. The wave design adds users to a fixed set of cloud groups rather than creating a group per wave.