Public/Get-NSPNpsAuthAttempt.ps1

function Get-NSPNpsAuthAttempt {
    <#
    .SYNOPSIS
        One row per RADIUS authentication attempt in the NPS log: who, from which NAS, which policies,
        and the outcome - Accepted, Rejected, or NoDecision (the request was logged but no
        Accept/Reject followed, e.g. discarded before Network Policy evaluation).
    .DESCRIPTION
        Groups log entries the way NPS Manager's paired view does (by AcctSessionId, else server +
        time + user) and summarises each group: the first Access-Request and the last Accept/Reject.
        Takes entries from the pipeline (Get-NSPNpsLog) or reads the log itself.
    .PARAMETER Path
        Log file(s). Default: the newest IN*.log in the configured log folder.
    .PARAMETER UserName
        Only attempts by this user (wildcards allowed).
    .PARAMETER Outcome
        Only attempts with this outcome.
    .EXAMPLE
        Get-NSPNpsAuthAttempt -Outcome Rejected | Format-Table Time, UserName, Reason, NetworkPolicy
    .EXAMPLE
        Get-NSPNpsLog -Path .\IN2610.log | Get-NSPNpsAuthAttempt -UserName '*jdoe*'
    #>

    [CmdletBinding(DefaultParameterSetName = 'File')]
    [OutputType([pscustomobject])]
    param(
        [Parameter(ParameterSetName = 'File')][string[]]$Path,
        [Parameter(ParameterSetName = 'Entries', ValueFromPipeline)]$InputObject,
        [string]$UserName,
        [ValidateSet('Accepted', 'Rejected', 'NoDecision')][string[]]$Outcome
    )
    begin { $buffer = New-Object Collections.Generic.List[object] }
    process { if ($PSCmdlet.ParameterSetName -eq 'Entries') { foreach ($e in @($InputObject)) { $buffer.Add($e) } } }
    end {
        $entries = if ($PSCmdlet.ParameterSetName -eq 'Entries') { $buffer.ToArray() } else {
            $logArgs = @{}; if ($Path) { $logArgs.Path = $Path }; if ($UserName) { $logArgs.UserName = $UserName }
            @(Get-NSPNpsLog @logArgs)
        }
        foreach ($grp in @($entries | Group-Object -Property { Get-NPSRadiusLogGroupKey -Entry $_ })) {
            $rows = @($grp.Group)
            $req = $rows | Where-Object { $_.PacketType -eq 'Access-Request' } | Select-Object -First 1
            if (-not $req) { continue }
            $decision = $rows | Where-Object { $_.PacketType -in 'Access-Accept', 'Access-Reject' } | Select-Object -Last 1
            $result = if (-not $decision) { 'NoDecision' } elseif ($decision.PacketType -eq 'Access-Accept') { 'Accepted' } else { 'Rejected' }
            if ($UserName -and -not ($req.UserName -like $UserName -or $req.UserFQDN -like $UserName)) { continue }
            if ($Outcome -and $Outcome -notcontains $result) { continue }
            $last = if ($decision) { $decision } else { $req }
            [pscustomobject]@{
                PSTypeName      = 'NSP.NPS.AuthAttempt'
                Time            = "$($req.RecordDate) $($req.RecordTime)"
                UserName        = $req.UserName
                UserFQDN        = $(if ($decision -and $decision.UserFQDN) { $decision.UserFQDN } else { $req.UserFQDN })
                Outcome         = $result
                Reason          = $last.ReasonCode
                ReasonCodeRaw   = $last.ReasonCodeRaw
                NetworkPolicy   = $last.PolicyName
                ConnectionPolicy = $(if ($last.ProxyPolicyName) { $last.ProxyPolicyName } else { $req.ProxyPolicyName })
                Nas             = $(if ($req.ClientFriendlyName) { $req.ClientFriendlyName } else { $req.ClientIPAddress })
                NasIPAddress    = $req.NASIPAddress
                CallingStation  = $req.CallingStationID
                AuthType        = $last.AuthenticationType
                EapType         = $last.EAPFriendlyName
                ChallengeRounds = @($rows | Where-Object { $_.PacketType -eq 'Access-Challenge' }).Count
                FramedIPAddress = $(if ($decision) { $decision.FramedIPAddress })
                RadiusServer    = $req.RadiusServer
            }
        }
    }
}