Workloads/Get-ConditionalAccessData.ps1

# Get-ConditionalAccessData.ps1
# Collects Conditional Access policies, named locations, and legacy auth signal.
# Part of the M365-QuickAssess module -- not exported.

function Get-ConditionalAccessData
{
    param
    (
        $Assessment
    )

    Write-Log "Collecting Conditional Access data"

    # -------------------------------------------------------------------
    # Policies and Named Locations
    # -------------------------------------------------------------------
    try
    {
        $policies  = Get-MgIdentityConditionalAccessPolicy -All -ErrorAction Stop
        $locations = Get-MgIdentityConditionalAccessNamedLocation -All -ErrorAction Stop

        # -------------------------------------------------------------------
        # Policy Table -- flattened for the viewer
        # -------------------------------------------------------------------
        $policyTable = @()

        foreach ( $p in $policies )
        {
            $grantControls = if ( $p.GrantControls -and $p.GrantControls.BuiltInControls )
            {
                $p.GrantControls.BuiltInControls -join ", "
            }
            else { "" }

            $clientApps = if ( $p.Conditions -and $p.Conditions.ClientAppTypes )
            {
                $p.Conditions.ClientAppTypes -join ", "
            }
            else { "" }

            $users = if ( $p.Conditions -and $p.Conditions.Users -and $p.Conditions.Users.IncludeUsers )
            {
                $p.Conditions.Users.IncludeUsers -join ", "
            }
            else { "" }

            $apps = if ( $p.Conditions -and $p.Conditions.Applications -and $p.Conditions.Applications.IncludeApplications )
            {
                $p.Conditions.Applications.IncludeApplications -join ", "
            }
            else { "" }

            $policyTable += [ordered]@{
                Name       = $p.DisplayName
                State      = $p.State
                Grant      = $grantControls
                ClientApps = $clientApps
                Users      = $users
                Apps       = $apps
            }
        }

        # -------------------------------------------------------------------
        # Summary Signals
        # -------------------------------------------------------------------
        $hasMFA = ( $policies | Where-Object {
            $_.GrantControls -and $_.GrantControls.BuiltInControls -contains "mfa"
        } ).Count -gt 0

        $hasTrustedLocations = ( $locations | Where-Object { $_.IsTrusted -eq $true } ).Count -gt 0

        $Assessment.ConditionalAccess.HasConditionalAccessPolicies = ( $policies.Count -gt 0 )
        $Assessment.ConditionalAccess.ConditionalAccessPolicyCount = $policies.Count
        $Assessment.ConditionalAccess.HasMFAEnforcement            = $hasMFA
        $Assessment.ConditionalAccess.HasTrustedLocations          = $hasTrustedLocations
        $Assessment.ConditionalAccess.Policies                     = $policyTable

        Write-Log "Conditional Access: Policies=$( $policies.Count ) MFA=$hasMFA TrustedLocations=$hasTrustedLocations"

        # -------------------------------------------------------------------
        # Finding: No Conditional Access policies
        # -------------------------------------------------------------------
        if ( $policies.Count -eq 0 )
        {
            $Assessment.Findings += New-Finding `
                -Type           "NoConditionalAccessPolicies" `
                -Summary        "No Conditional Access policies detected" `
                -Category       "Security" `
                -Severity       "Medium" `
                -Impact         "No Entra Conditional Access policies are configured. This may be expected if the tenant uses a third-party identity provider such as Duo, Okta, or Ping for MFA and access control." `
                -Recommendation "Confirm whether a third-party IdP is in use. If not, implement Conditional Access policies in the target tenant starting with MFA enforcement."
        }

        # -------------------------------------------------------------------
        # Finding: No MFA enforcement via CA
        # -------------------------------------------------------------------
        if ( $policies.Count -gt 0 -and -not $hasMFA )
        {
            $Assessment.Findings += New-Finding `
                -Type           "NoMFAEnforcement" `
                -Summary        "No Conditional Access policy enforces MFA" `
                -Category       "Security" `
                -Severity       "Medium" `
                -Impact         "No Entra CA policy requiring MFA was detected. If MFA is enforced via a third-party IdP such as Duo or Okta this may be expected." `
                -Recommendation "Confirm MFA enforcement method. If relying solely on Entra CA, create a policy requiring MFA for all users."
        }

        # -------------------------------------------------------------------
        # Finding: Policies in report-only mode
        # -------------------------------------------------------------------
        $reportOnlyCount = ( $policies | Where-Object { $_.State -eq "enabledForReportingButNotEnforced" } ).Count

        if ( $reportOnlyCount -gt 0 )
        {
            $Assessment.Findings += New-Finding `
                -Type           "ReportOnlyCAPolices" `
                -Summary        "$reportOnlyCount Conditional Access policies are in report-only mode" `
                -Category       "Security" `
                -Severity       "Medium" `
                -Impact         "Report-only policies are not enforced and provide no protection." `
                -Recommendation "Review report-only policies and enable them if appropriate."
        }
    }
    catch
    {
        Write-Log "Conditional Access collection failed: $( $_.Exception.Message )" "ERROR"
    }

    # -------------------------------------------------------------------
    # Legacy Auth Signal
    # -------------------------------------------------------------------
    try
    {
        Write-Log "Checking legacy authentication exposure"

        $legacyBlockPolicies = $policies | Where-Object {
            $_.Conditions -and
            $_.Conditions.ClientAppTypes -and
            (
                $_.Conditions.ClientAppTypes -contains "exchangeActiveSync" -or
                $_.Conditions.ClientAppTypes -contains "other"
            )
        }

        $hasLegacyBlock = ( $legacyBlockPolicies | Where-Object {
            $_.GrantControls -and $_.GrantControls.BuiltInControls -contains "block"
        } ).Count -gt 0

        $Assessment.Summary.LegacyAuthEnabled = ( -not $hasLegacyBlock )

        Write-Log "Legacy Auth Blocked: $hasLegacyBlock"

        # -------------------------------------------------------------------
        # Finding: Legacy auth not blocked
        # -------------------------------------------------------------------
        if ( -not $hasLegacyBlock )
        {
            $Assessment.Findings += New-Finding `
                -Type           "LegacyAuthNotBlocked" `
                -Summary        "Legacy authentication protocols are not blocked" `
                -Category       "Security" `
                -Severity       "High" `
                -Impact         "Legacy auth protocols bypass MFA and are a common attack vector." `
                -Recommendation "Create a Conditional Access policy to block legacy authentication for all users."
        }
    }
    catch
    {
        Write-Log "Legacy auth evaluation failed: $( $_.Exception.Message )" "WARN"
    }
}