Skip to content

Instantly share code, notes, and snippets.

@jlawhon
Forked from jdhitsolutions/Get-ADUserAudit.ps1
Created February 17, 2024 20:15
Show Gist options
  • Select an option

  • Save jlawhon/704652bc60b1c0569b6c5e853292dc99 to your computer and use it in GitHub Desktop.

Select an option

Save jlawhon/704652bc60b1c0569b6c5e853292dc99 to your computer and use it in GitHub Desktop.
A PowerShell function and format file for getting Active Directory user management events from the security event logs on domain controllers.
<?xml version="1.0" encoding="UTF-8"?>
<!--
Format type data generated 02/04/2021 08:56:49 by COMPANY\ArtD
This file was created using the New-PSFormatXML command that is part
of the PSScriptTools module.
https://github.com/jdhitsolutions/PSScriptTools
-->
<Configuration>
<ViewDefinitions>
<View>
<!--Created 02/04/2021 08:56:49 by COMPANY\ArtD-->
<Name>default</Name>
<ViewSelectedBy>
<TypeName>ADAuditTrail</TypeName>
</ViewSelectedBy>
<GroupBy>
<PropertyName>DomainController</PropertyName>
<Label>DomainController</Label>
</GroupBy>
<ListControl>
<ListEntries>
<ListEntry>
<ListItems>
<ListItem>
<Label>EventType</Label>
<PropertyName>EventType</PropertyName>
</ListItem>
<ListItem>
<Label>Since</Label>
<PropertyName>Since</PropertyName>
</ListItem>
<ListItem>
<Label>TargetCount</Label>
<PropertyName>TargetCount</PropertyName>
</ListItem>
<ListItem>
<Label>Targets</Label>
<PropertyName>Targets</PropertyName>
</ListItem>
<ListItem>
<Label>Administrators</Label>
<PropertyName>Administrators</PropertyName>
</ListItem>
</ListItems>
</ListEntry>
</ListEntries>
</ListControl>
</View>
</ViewDefinitions>
</Configuration>
#you might need to increase the size of the Security eventlog
# limit-eventlog -LogName security -ComputerName dom2,dom1 -MaximumSize 1024MB
Function Get-ADUserAudit {
[cmdletbinding()]
Param(
[Parameter(Position=0,HelpMessage = "Specify one or more domain controllers to query.")]
[ValidateNotNullOrEmpty()]
[string[]]$DomainController = (Get-ADDomain).ReplicaDirectoryServers,
[Parameter(HelpMessage = "Find all matching user management events since what date and time?")]
[Datetime]$Since = (Get-Date).Addhours(-24),
[Parameter(HelpMessage = "Select one or more user account events")]
[ValidateNotNullOrEmpty()]
[ValidateSet("Created","Deleted","Enabled","Disabled","Changed")]
[string[]]$Events = "Created",
[Parameter(HelpMessage = "Specify an alterate credential")]
[PSCredential]$Credential
)
Function _getNames {
# a private helper function to parse out names from the eventlog message
[cmdletbinding()]
Param(
[Parameter(Mandatory, ValueFromPipeline)]
[System.Diagnostics.Eventing.Reader.EventLogRecord]$Data
)
Process {
#convert the record to XML which makes it easier to parse
[xml]$r = $data.toxml()
#Target is the user account
#Subject is the admin who performed the operation
$target = "{0}\{1}" -f ($r.Event.EventData.Data.Where({ $_.name -eq 'TargetDomainName'}).'#text'), ($r.Event.EventData.Data.Where({$_.name -eq 'TargetUserName'}).'#text')
$admin = "{0}\{1}" -f ($r.Event.EventData.Data.Where({ $_.name -eq 'SubjectDomainName'}).'#text'), ($r.Event.EventData.Data.Where({$_.name -eq 'SubjectUserName'}).'#text')
[pscustomobject]@{
Target = $target
Administrator = $admin
TimeCreated = $data.timeCreated
}
} #process
} # close _getNames
# a hashtable of user management event IDs for the Security event log
$ADEvent = @{
UserChanged = 4738
UserCreated = 4720
UserDeleted = 4726
UserEnabled = 4722
UserDisabled = 4725
}
$EventIDs = @()
Switch ($events) {
"Created" { $EventIDs += $adevent.getenumerator().Where({$_.name -match "created"}) }
"Deleted" { $eventIDs += $adevent.getenumerator().Where({$_.name -match "deleted"}) }
"Enabled" { $eventIDs += $adevent.getenumerator().Where({$_.name -match "enabled"}) }
"Disabled" { $eventIDs += $adevent.getenumerator().Where({$_.name -match "disabled"}) }
"Changed" { $eventIDs += $adevent.getenumerator().Where({$_.name -match "changed"}) }
}
#this hashtable filter will be used by Get-WinEvent
$filter = @{LogName = 'Security'; ID = 0 ; StartTime = $Since }
#parameters to eventually splat to Get-WinEvent
$getParams = @{
ErrorAction = "Stop"
FilterHashtable = $filter
Computername = ""
}
if ($Credential.UserName) {
$getParams.add("Credential",$Credential)
}
Write-Verbose "Searching for AD log entries since $since"
#Searching the Security event log on each domain controller
foreach ($dc in $DomainController) {
Write-Verbose "Processing $dc"
foreach ($evt in $eventIDs) {
$filter.ID = $evt.value
$getParams.FilterHashtable = $filter
$getParams.Computername = $DC
Write-Verbose "...Looking for $($evt.name) events"
Try {
$logs = Get-WinEvent @getParams
Write-Verbose "Found $($logs.count) log records"
}
Catch {
Write-Warning "No matching $($evt.name) events $since found on $dc."
}
if ($logs.count -gt 0) {
$names = $logs | _getnames
$targets = ($names | Select-Object -Property Target -Unique).target
$admins = ($names | Select-Object -Property Administrator -Unique).administrator
[pscustomobject]@{
PSTypeName = "ADAuditTrail"
DomainController = $dc
ID = $evt.Value
EventType = $evt.Name
LogCount = $logs.count
Since = $Since
Targets = $targets
Administrators = $admins
}
Remove-Variable -Name logs
}
}
}
} #close function
Update-TypeData -TypeName ADAuditTrail -MemberType ScriptProperty -MemberName TargetCount -Value { $($this.targets).count } -Force
#the format file should be in the same folder as this file.
Update-FormatData -AppendPath $psscriptroot\adaudittrail.format.ps1xml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment