Skip to content

Conditional Access Migration: Okta Sign-on Policies to Entra ID

Status: Authored 2026-04-30 Audience: Identity Architects, Security Engineers, IAM Analysts Purpose: Detailed guidance for migrating Okta sign-on policies to Entra Conditional Access


Overview

Okta sign-on policies control how users authenticate -- where MFA is required, which network locations are trusted, what session lifetimes apply, and which device conditions must be met. Entra Conditional Access is the equivalent policy engine in Microsoft's identity platform, but with significantly broader scope and richer signal integration.

This guide provides a systematic approach to mapping every Okta sign-on policy to an Entra Conditional Access policy, ensuring no security posture is lost during migration.

Microsoft has published dedicated guidance: Migrate Okta sign-on policies to Conditional Access.


1. Policy model comparison

Okta sign-on policy model

Okta Policy Hierarchy:

    Global Sign-On Policy (org-wide default)
        └── Rule 1: If [condition], then [action]
        └── Rule 2: If [condition], then [action]
        └── Default Rule: Allow with MFA

    Per-App Sign-On Policy (per application)
        └── Rule 1: If [condition], then [action]
        └── Rule 2: If [condition], then [action]
        └── Default Rule: Allow

Entra Conditional Access policy model

Entra CA Policy Model:

    Policy 1: [Users] + [Apps] + [Conditions] -> [Grant/Block] + [Session]
    Policy 2: [Users] + [Apps] + [Conditions] -> [Grant/Block] + [Session]
    Policy 3: [Users] + [Apps] + [Conditions] -> [Grant/Block] + [Session]
    ...
    (All policies evaluated; most restrictive grant controls apply)

Key differences

Aspect Okta sign-on policies Entra Conditional Access
Evaluation model First-match (ordered rules within policy) All-match (all applicable policies evaluated; most restrictive wins)
Scope IdP-layer only (authentication event) Identity + apps + devices + locations + risk + workload identities
Policy count One global + one per app Unlimited policies targeting any combination of users/apps/conditions
Risk signals Okta ThreatInsight (limited) Identity Protection (user risk + sign-in risk from billions of signals)
Device signals Requires Workspace ONE/Jamf Native Intune compliance
Session controls Session lifetime only Sign-in frequency, persistent browser, app-enforced restrictions, MCAS proxy
Report-only mode Limited testing capability Full report-only mode for policy impact analysis
Named locations IP-based network zones only IP ranges + GPS-based + country/region + compliant network

2. Policy-by-policy mapping

Export Okta sign-on policies

# Export global sign-on policy
curl -s -H "Authorization: SSWS ${OKTA_API_TOKEN}" \
  "https://${OKTA_DOMAIN}/api/v1/policies?type=OKTA_SIGN_ON" \
  | jq '[.[] | {
    id: .id,
    name: .name,
    status: .status,
    type: .type,
    priority: .priority,
    conditions: .conditions
  }]' > okta-signon-policies.json

# Export rules for each policy
for policy_id in $(jq -r '.[].id' okta-signon-policies.json); do
  curl -s -H "Authorization: SSWS ${OKTA_API_TOKEN}" \
    "https://${OKTA_DOMAIN}/api/v1/policies/${policy_id}/rules" \
    > "okta-policy-rules-${policy_id}.json"
done

Mapping template

For each Okta sign-on policy rule, create an Entra Conditional Access policy:

Okta rule element Maps to Entra CA configuration
conditions.people.users.include Users and groups conditions.users.includeUsers / includeGroups
conditions.people.users.exclude Exclude users conditions.users.excludeUsers / excludeGroups
conditions.network.connection Named locations conditions.locations.includeLocations / excludeLocations
conditions.authContext.authType Client app types conditions.clientAppTypes
conditions.riskScore Sign-in risk conditions.signInRiskLevels
actions.signon.access (ALLOW/DENY) Grant/Block grantControls.builtInControls
actions.signon.requireFactor Require MFA grantControls.builtInControls: ["mfa"]
actions.signon.factorLifetime Sign-in frequency sessionControls.signInFrequency
conditions.platform.include Device platform conditions.platforms.includePlatforms

3. Common policy migrations

Policy 1: Require MFA for all users

Okta rule:

{
    "name": "Require MFA for all",
    "conditions": {
        "people": { "users": { "include": ["EVERYONE"] } },
        "network": { "connection": "ANYWHERE" }
    },
    "actions": {
        "signon": {
            "access": "ALLOW",
            "requireFactor": true,
            "factorLifetime": 720
        }
    }
}

Entra Conditional Access:

$policy = @{
    displayName = "Require MFA for all users"
    state = "enabledForReportingButNotEnforced"
    conditions = @{
        users = @{
            includeUsers = @("All")
            excludeGroups = @("break-glass-accounts-group-id")
        }
        applications = @{
            includeApplications = @("All")
        }
        clientAppTypes = @("browser", "mobileAppsAndDesktopClients")
    }
    grantControls = @{
        operator = "OR"
        builtInControls = @("mfa")
    }
    sessionControls = @{
        signInFrequency = @{
            value = 30
            type = "days"
            isEnabled = $true
        }
    }
}

New-MgIdentityConditionalAccessPolicy -BodyParameter $policy

Policy 2: Block access from untrusted networks

Okta rule:

{
    "name": "Block untrusted network",
    "conditions": {
        "network": { "connection": "ZONE", "exclude": ["trusted-zone-id"] }
    },
    "actions": {
        "signon": { "access": "DENY" }
    }
}

Entra Conditional Access:

# Step 1: Create named location for trusted network
$trustedLocation = @{
    "@odata.type" = "#microsoft.graph.ipNamedLocation"
    displayName = "Corporate Network"
    isTrusted = $true
    ipRanges = @(
        @{ "@odata.type" = "#microsoft.graph.iPv4CidrRange"; cidrAddress = "10.0.0.0/8" },
        @{ "@odata.type" = "#microsoft.graph.iPv4CidrRange"; cidrAddress = "203.0.113.0/24" }
    )
}
$location = New-MgIdentityConditionalAccessNamedLocation -BodyParameter $trustedLocation

# Step 2: Create policy blocking untrusted locations
$blockPolicy = @{
    displayName = "Block access from untrusted networks"
    state = "enabled"
    conditions = @{
        users = @{ includeUsers = @("All") }
        applications = @{ includeApplications = @("All") }
        locations = @{
            includeLocations = @("All")
            excludeLocations = @($location.Id)
        }
    }
    grantControls = @{
        operator = "OR"
        builtInControls = @("block")
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $blockPolicy

Policy 3: Device trust (Okta + Workspace ONE to Entra + Intune)

Okta rule:

{
    "name": "Require managed device",
    "conditions": {
        "device": { "registered": true, "managed": true }
    },
    "actions": {
        "signon": {
            "access": "ALLOW",
            "requireFactor": false
        }
    }
}

Entra Conditional Access:

$devicePolicy = @{
    displayName = "Require compliant device"
    state = "enabled"
    conditions = @{
        users = @{ includeUsers = @("All") }
        applications = @{ includeApplications = @("All") }
        clientAppTypes = @("browser", "mobileAppsAndDesktopClients")
    }
    grantControls = @{
        operator = "OR"
        builtInControls = @("compliantDevice")
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $devicePolicy

Policy 4: Risk-based MFA (Okta ThreatInsight to Identity Protection)

Okta: ThreatInsight blocks or challenges suspicious IPs at the pre-authentication stage.

Entra Conditional Access with Identity Protection:

# High-risk sign-in: block access
$highRiskPolicy = @{
    displayName = "Block high-risk sign-ins"
    state = "enabled"
    conditions = @{
        users = @{ includeUsers = @("All") }
        applications = @{ includeApplications = @("All") }
        signInRiskLevels = @("high")
    }
    grantControls = @{
        operator = "OR"
        builtInControls = @("block")
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $highRiskPolicy

# Medium-risk sign-in: require MFA
$mediumRiskPolicy = @{
    displayName = "Require MFA for medium-risk sign-ins"
    state = "enabled"
    conditions = @{
        users = @{ includeUsers = @("All") }
        applications = @{ includeApplications = @("All") }
        signInRiskLevels = @("medium")
    }
    grantControls = @{
        operator = "OR"
        builtInControls = @("mfa")
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $mediumRiskPolicy

# High-risk user: require password change + MFA
$riskyUserPolicy = @{
    displayName = "Require password change for high-risk users"
    state = "enabled"
    conditions = @{
        users = @{ includeUsers = @("All") }
        applications = @{ includeApplications = @("All") }
        userRiskLevels = @("high")
    }
    grantControls = @{
        operator = "AND"
        builtInControls = @("mfa", "passwordChange")
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $riskyUserPolicy

4. Entra-exclusive capabilities (no Okta equivalent)

These Conditional Access capabilities have no direct Okta sign-on policy equivalent. Enable them as part of the migration to improve security posture:

Authentication context

# Create authentication context for step-up MFA
$authContext = @{
    id = "c1"
    displayName = "Sensitive Data Access"
    description = "Step-up authentication for accessing sensitive data"
    isAvailable = $true
}
New-MgIdentityConditionalAccessAuthenticationContextClassReference -BodyParameter $authContext

# Create CA policy using authentication context
$stepUpPolicy = @{
    displayName = "Require phishing-resistant MFA for sensitive data"
    state = "enabled"
    conditions = @{
        users = @{ includeUsers = @("All") }
        applications = @{
            includeAuthenticationContextClassReferences = @("c1")
        }
    }
    grantControls = @{
        operator = "OR"
        authenticationStrength = @{
            id = "00000000-0000-0000-0000-000000000004"
        }
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $stepUpPolicy

Continuous Access Evaluation (CAE)

CAE enforces policy changes in near-real-time rather than waiting for token expiration. This is automatically enabled for supported applications (Exchange Online, SharePoint Online, Teams, Graph API).

Token protection

# Require token binding (prevents token theft and replay)
$tokenProtectionPolicy = @{
    displayName = "Require token binding for Windows devices"
    state = "enabled"
    conditions = @{
        users = @{ includeUsers = @("All") }
        applications = @{ includeApplications = @("All") }
        platforms = @{
            includePlatforms = @("windows")
        }
    }
    sessionControls = @{
        secureSignInSession = @{
            isEnabled = $true
        }
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $tokenProtectionPolicy

Workload identity Conditional Access

# Apply CA to service principals (no Okta equivalent)
$workloadPolicy = @{
    displayName = "Require compliant network for service principals"
    state = "enabled"
    conditions = @{
        clientApplications = @{
            includeServicePrincipals = @("service-principal-id-1", "service-principal-id-2")
        }
        locations = @{
            includeLocations = @("All")
            excludeLocations = @("trusted-location-id")
        }
    }
    grantControls = @{
        operator = "OR"
        builtInControls = @("block")
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $workloadPolicy

5. Session controls migration

Okta session setting Entra session control Configuration
Session lifetime (hours) Sign-in frequency sessionControls.signInFrequency.value + .type (hours/days)
Idle session timeout Persistent browser session sessionControls.persistentBrowser.mode (always/never)
Force re-authentication Sign-in frequency: every time sessionControls.signInFrequency.frequencyInterval = "everyTime"
No Okta equivalent Application enforced restrictions sessionControls.applicationEnforcedRestrictions.isEnabled
No Okta equivalent MCAS session proxy sessionControls.cloudAppSecurity.isEnabled

6. Report-only mode and validation

Deploy all policies in report-only mode first

# List all CA policies in report-only mode
$reportOnlyPolicies = Get-MgIdentityConditionalAccessPolicy -Filter "state eq 'enabledForReportingButNotEnforced'"

Write-Host "Policies in report-only mode: $($reportOnlyPolicies.Count)"
$reportOnlyPolicies | ForEach-Object {
    Write-Host "  - $($_.DisplayName)"
}

Analyze report-only impact

# Query sign-in logs for report-only policy impact
$signIns = Get-MgAuditLogSignIn -Filter "createdDateTime ge 2026-04-01T00:00:00Z" -Top 1000

$policyImpact = $signIns | ForEach-Object {
    $_.ConditionalAccessPolicies | Where-Object { $_.Result -eq "reportOnlyFailure" }
} | Group-Object DisplayName | Sort-Object Count -Descending

Write-Host "Report-only policy impact (would have blocked):"
$policyImpact | ForEach-Object {
    Write-Host "  $($_.Name): $($_.Count) sign-ins affected"
}

Promote from report-only to enabled

# After validation, enable policies one at a time
$policyId = "policy-id-to-enable"
Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $policyId -State "enabled"

7. CSA-in-a-Box-specific Conditional Access policies

Configure Conditional Access policies specifically for CSA-in-a-Box platform components:

# Require MFA + compliant device for Fabric workspace access
$fabricPolicy = @{
    displayName = "CSA-in-a-Box: Secure Fabric access"
    state = "enabled"
    conditions = @{
        users = @{ includeGroups = @("fabric-users-group-id") }
        applications = @{
            includeApplications = @(
                "00000009-0000-0000-c000-000000000000"  # Power BI / Fabric
            )
        }
    }
    grantControls = @{
        operator = "AND"
        builtInControls = @("mfa", "compliantDevice")
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $fabricPolicy

8. Validation checklist

  • All Okta sign-on policies exported and documented
  • Each Okta policy rule mapped to a Conditional Access policy
  • Named locations created (IP ranges, GPS, countries)
  • All policies deployed in report-only mode
  • Report-only impact analyzed for 14+ days
  • No unexpected blocks in report-only data
  • Break-glass accounts excluded from all blocking policies
  • Policies promoted from report-only to enabled (one at a time)
  • Sign-in logs monitored for 7 days post-enablement
  • Okta sign-on policies documented as deprecated

Key Microsoft Learn references


Maintainers: csa-inabox core team Last updated: 2026-04-30