Public/Get-MDSADLockoutSource.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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)]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingPlainTextForPassword','')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUsePSCredentialType','')]

    param(
        [Parameter(
            Mandatory                       = $true,
            Position                        = 0,
            ValueFromPipeline               = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [ValidateNotNullOrEmpty()]
        [string[]]$SamAccountName,

        [Parameter(Position=1)]
        [string]$Server,

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

        [Parameter(Position=2,ParameterSetName="Credential")]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.CredentialAttribute()]
        $Credential
    )

    begin {}
    process    {
        Try {
            # MDSCredential
            If ($PSBoundParameters.MDSCredential) {
                $Credential = Get-MDSCredential -Name $MDSCredential -ErrorAction Stop
            }

            If ($null -eq $PSBoundParameters.Server) {
                $Server = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().PdcRoleOwner.Name
                $VerboseString = 'No server specified. Using PDCEmulator: {0}.' -f $Server
                Write-Verbose $VerboseString
            }
        }
        Catch {
            $PsCmdlet.ThrowTerminatingError($PSItem)
        }

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

                Try {
                    $Account = New-Object Security.Principal.NTAccount $Name
                    $SID = $Account.Translate([Security.Principal.Securityidentifier]).Value
                }
                Catch {
                    $ThrowString = 'Cannot find object with samaccountname: {0}' -f $SamAccountName
                    Throw $ThrowString
                }

                # 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)
                }

                [array]$Events = Get-WinEvent @getWinEventSplat

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