Public/Get-MDSADLockoutSource.ps1

Function Get-MDSADLockoutSource {
    <#
    .SYNOPSIS
    Query Active Directory for the lockout source for the most recently lockout event

    .DESCRIPTION
    Query Active Directory for the lockout source for the most recently lockout event. If a server is listed the query will be for the last lockout event on that specific server.

    .EXAMPLE
    Get-MDSADLockoutSource -SamAccountName $SamAccountName

    Query a single user's lockout source
    .EXAMPLE
    Get-MDSADLockoutSource -SamAccountName SamAccountName1,SamAccountName2

    Query multiple users at a time. Also accepts SamAccountNames via the pipeline
    .EXAMPLE
    Get-MDSADLockoutSource -SamAccountName $SamAccountName -Server Server.domain.com

    Accepts a specific server to query assuming the metadata time is the event time for that server
    .NOTES
    Written by Rick A., December 2017

    #>

    [CmdletBinding(DefaultParameterSetName='None')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingPlainTextForPassword','')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUsePSCredentialType','')]

    param(
        [Parameter(
            Mandatory                       = $true,
            Position                        = 0,
            ValueFromPipeline               = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Parameter(ParameterSetName="MDSCredential")]
        [Parameter(ParameterSetName="Credential")]
        [Parameter(ParameterSetName="None")]
        [ValidateNotNullOrEmpty()]
        [string[]]$SamAccountName,

        [Parameter(Position=1,ParameterSetName="MDSCredential")]
        [Parameter(Position=1,ParameterSetName="Credential")]
        [Parameter(Position=1,ParameterSetName="None")]
        [string]$Server,

        [parameter(Position=2,ParameterSetName="MDSCredential")]
        [ValidateNotNullOrEmpty()]
        [String]$MDSCredential,

        [parameter(Position=2,ParameterSetName="Credential")]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.CredentialAttribute()]
        $Credential
    )
    #requires -Module ActiveDirectory
    begin {}
    process    {
        Try {
            # MDSCredential
            If ($PSBoundParameters.MDSCredential) {
                $Credential = Get-MDSCredential -Name $MDSCredential -ErrorAction Stop
            }

            If ($null -eq $PSBoundParameters.Server) {
                $Server = Get-ADDomain -ErrorAction Stop | Select-Object -Expand PDCEmulator
                $VerboseString = 'No server specified. Using the PDCEmulator {0}.' -f $Server
                Write-Verbose $VerboseString
            }
        }
        Catch {
            $PsCmdlet.ThrowTerminatingError($PSItem)
        }

        ForEach ($Name in $SamAccountName) {
            Try {
                $Events = $ADUser = $null

                $VerboseString = 'Performing AD query for {0}.' -f $Name
                Write-Verbose $VerboseString
                $getADUserSplat = @{
                    Filter      = {SamAccountName -eq $Name}
                    Properties  = 'AccountLockoutTime'
                    Server      = $Server
                    ErrorAction = 'Stop'
                }
                $ADUser = Get-ADUser @getADUserSplat

                If ($null -eq $ADUser) {
                    $ErrorString = 'Cannot find object with samaccountname: {0}' -f $SamAccountName
                    Write-Error $ErrorString
                    Continue
                }

                # Lockout: 'Microsoft-Windows-Security-Auditing', ID 4740
                # Failure Event: 'Microsoft-Windows-Security-Auditing',ID 4771
                $FilterHashtable = @{
                    LogName         = 'Security'
                    ID              = 4740
                    ProviderName    = 'Microsoft-Windows-Security-Auditing'
                    Data            = $Name
                }
                $getWinEventSplat = @{
                    ComputerName    = $Server
                    FilterHashtable = $FilterHashtable
                    ErrorAction     = 'Stop'
                    Verbose         = $False
                }
                If ($Credential) {
                    [void]$getWinEventSplat.Add('Credential',$Credential)
                }
                Try {
                    [array]$Events = Get-WinEvent @getWinEventSplat
                }
                Catch {
                    Write-Error $PSItem
                    Continue
                }

                If ($null -eq $Events) {continue}

                Write-Verbose 'Parsing returned events'
                ForEach ($Event in $Events) {
                    If($Event | Where-Object {$_.Properties[2].Value -match $ADUser.SID.Value}) {
                        [PSCustomObject] @{
                            AccountName     = $Name
                            EventComputer   = $Event.Properties[4].Value
                            LockoutTime     = $Event.TimeCreated
                            LockoutSource   = $Event.Properties[1].Value
                        }
                    }
                }
            }
            Catch {
                Write-Error $PSItem
            }
        }
    }
    end {}
}