en-US/about_CaOutcome.help.txt

TOPIC
    about_CaOutcome

SHORT DESCRIPTION
    Folds a Conditional Access What If response into the outcome a sign-in
    actually meets, and into the outcome it would meet if the tenant's
    report-only policies were promoted.

LONG DESCRIPTION
    The Conditional Access What If API answers, policy by policy, whether each
    policy matches a simulated sign-in. A tenant with thirteen policies returns
    thirteen verdicts. What an administrator wants is three answers:

        does the sign-in succeed
        what does the user have to do to make it succeed
        what changes if the policy being piloted goes live

    All three come out of a single response, because each policy in it carries
    its state alongside its verdict. CaOutcome folds that response twice:

        Current policies in state 'enabled' - what the tenant enforces now
        Projected 'enabled' plus 'enabledForReportingButNotEnforced' - what
                     it would enforce if every report-only policy were promoted

    and reports the difference between the two as a Delta.

    There is no way to ask Graph to evaluate a hypothetical policy. The request
    takes a sign-in to simulate, not a policy set. Staging a candidate policy as
    report-only and reading both worlds out of one response simulates the
    promotion without enforcing anything, and costs no extra API calls.

THE COMMANDS
        ConvertTo-CaOutcome One response -> the outcome in both worlds
        Expand-CaScenario Personas x resources x conditions -> sign-ins
        Invoke-CaScenarioMatrix Evaluates each sign-in, with retry, and folds it
        Export-CaBaseline Records a run as deterministic, committable JSON
        Compare-CaBaseline Diffs a fresh run against that baseline

    ConvertTo-CaOutcome needs no connection: it transforms a response someone
    else fetched, including the collection Maester's
    Test-MtConditionalAccessWhatIf returns. Only Invoke-CaScenarioMatrix sends
    requests, and only through its default request handler.

HOW THE FOLD WORKS
    Every matching policy is evaluated and the aggregate is the most restrictive
    combination, as Entra documents it: one applying block ends it, otherwise
    every applying policy's grant clause must be satisfied.

    OR clauses are not flattened. "MFA or a compliant device" and "MFA and a
    compliant device" are different policies, so multi-option clauses are kept
    whole in OptionalChoices rather than merged into RequiredControls.

    Authentication strengths count as requirements. A modern MFA policy has an
    empty builtInControls array and its whole requirement in
    authenticationStrength.

    Session control conflicts are always reported. Most-restrictive is applied
    where restrictiveness has a defensible ordering - sign-in frequency and
    persistent browser. Anything else is returned with Resolved set to $false
    rather than guessed at.

SATISFIABILITY
    Without -SignInCondition the module reports what a sign-in is required to
    do. With it, requirements the simulated sign-in demonstrably cannot meet are
    reported as UnsatisfiableRequirements, and an outcome that is granted
    subject to something impossible has IsEffectivelyBlocked set.

    Every such finding rests on a documented Microsoft constraint - a legacy
    client passes no device state, device code flow cannot carry device
    compliance, an approved client app is only supported on iOS and Android.
    Anything the request cannot settle, such as whether a user has registered a
    method, is left unknown. A missed lockout is a finding not made; an invented
    one teaches the reader to ignore the field.

BASELINES
    Configuration comparison watches the policy document. A Conditional Access
    outcome also depends on group membership, role assignment, named locations
    and device state, none of which changes the document. Someone joins a group
    and a policy that already existed now locks them out.

    Export-CaBaseline records what the tenant does for each persona, not how it
    is configured, and Compare-CaBaseline reports any scenario whose outcome has
    moved - in either world. A baseline carries no timestamp, so a run that
    changed nothing produces no diff.

    A scenario that failed to evaluate is never recorded as absent, because the
    next comparison would report it as removed: a change invented by a transient
    HTTP error. Export-CaBaseline refuses such a run unless -Force is given, and
    Compare-CaBaseline reports a scenario missing from a run as Missing, not
    removed.

    A baseline names users, groups, applications and policies from the tenant
    it was taken in. Commit it alongside that tenant's tests, not publicly.

PERMISSIONS
    Invoke-CaScenarioMatrix's default request handler calls
    Invoke-MgGraphRequest, from Microsoft.Graph.Authentication, and needs a
    connection with Policy.Read.ConditionalAccess. The module does not declare
    the Graph SDK as a dependency; supply -RequestHandler to use any other
    transport, or to replay recorded responses.

    The What If endpoint is in the Microsoft Graph beta, and its response shape
    may change.

EXAMPLES
    Fold one response:

        $response = Invoke-MgGraphRequest -Method POST -OutputType Json `
            -Uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/evaluate' `
            -Body ($body | ConvertTo-Json -Depth 10)
        ConvertTo-CaOutcome -WhatIfResult $response -SignInCondition $body.signInConditions

    Find who a promotion would lock out:

        Expand-CaScenario -Matrix (Import-PowerShellDataFile .\ca-matrix.psd1) |
            Invoke-CaScenarioMatrix -DelayMillisecond 100 |
            Where-Object { $_.Delta.BecomesEffectivelyBlocked }

    Record a baseline, and later compare against it:

        $outcomes | Export-CaBaseline -Path .\ca-baseline.json
        $outcomes | Compare-CaBaseline -Path .\ca-baseline.json |
            Where-Object HasChange

    The Examples folder in the installed module holds a sample matrix and a
    Maester custom test template.

SEE ALSO
    ConvertTo-CaOutcome
    Expand-CaScenario
    Invoke-CaScenarioMatrix
    Export-CaBaseline
    Compare-CaBaseline
    https://github.com/fadwen/CaOutcome
    https://learn.microsoft.com/en-us/graph/api/conditionalaccessroot-evaluate?view=graph-rest-beta