Getting most accurate last logon time for Computer Objects via powershell - powershell

I want to get most accurate last logon time for Computer Objects via powershell.
why is returning lastlogon attribute like below ? is it normal ?
Name : SRV01
samAccountName : SRV01$
DistinguishedName : CN=SRV01,DC=contoso,DC=local
lastLogon : 133000120204176004
OperatingSystem : Windows Server 2016 Standard
LastLogonDate : 6/18/2022 10:47:00 AM
Script :
$AllDCs = Get-ADDomainController -Filter * # Get all DCs in the Domain
$logons = [System.Collections.Generic.Dictionary[string,object]]::new()
$params = #{
LDAPFilter = '(LastLogon=*)' # Only find computers that have this Property
SearchBase = 'DC=contoso,DC=local'
Properties = #(
'Name'
'samAccountName'
'DistinguishedName'
'lastLogon'
'OperatingSystem'
)
}
foreach($DC in $AllDCs)
{
$params.Server = $DC
# Find all computers using this target DC
$computerList = Get-ADComputer #params |
Select-Object #(
$params.Properties
#{
Name = 'LastLogonDate'
Expression = { [datetime]::FromFileTime($_.LastLogon) }
}
)
foreach($computer in $computerList)
{
if($logons[$computer.samAccountName].lastLogonDate -lt $computer.lastLogonDate)
{
$logons[$computer.samAccountName] = $computer
}
}
}
$logons.Values

As NiMux explained in his helpful comment and as stated in the Active Directory Schema (AD Schema) MS Docs for this attribute:
The last time the user logged on. This value is stored as a large integer that represents the number of 100-nanosecond intervals since January 1, 1601 (UTC). A value of zero means that the last logon time is unknown.
Below code shows you how you can exclude this property from the output and only keep it's friendly / human readable representation - [datetime]::FromFileTime($_.LastLogon) - as well as some performance improvements.
Dictionary<TKey,TValue> can be replaced with a hashtable, in this case, it is not a performance improvement but also using the generic class will likely not provide a performance gain.
Constructing new objects of all queried computers per Domain Controller is inefficient and should be removed, we're only interested in reconstructing the objects once (once we have the results from the query - bottom part of the code).
$AllDCs = Get-ADDomainController -Filter *
$logons = #{}
$params = #{
LDAPFilter = '(LastLogon=*)'
SearchBase = 'DC=contoso,DC=local'
Properties = 'lastLogon', 'OperatingSystem'
}
foreach($DC in $AllDCs) {
$params['Server'] = $DC
# Find all computers using this target DC
foreach($computer in Get-ADComputer #params) {
# if the reference `lastLogon` in the hashtable is lower than this `lastLogon`
if($logons[$computer.samAccountName].lastLogon -lt $computer.lastLogon) {
# replace the value for this key with this object
$logons[$computer.samAccountName] = $computer
}
}
}
# construct the output
& {
foreach($key in $logons.PSBase.Keys) {
$value = $logons[$key]
[pscustomobject]#{
Name = $value.Name
samAccountName = $value.samAccountName
DistinguishedName = $value.DistinguishedName
LastLogonDate = [datetime]::FromFileTime($value.LastLogon)
OperatingSystem = $value.OperatingSystem
}
}
} | Export-Csv path/to/output.csv -NoTypeInformation

Related

Exporting last logon date for inactive users via PowerShell

I have a command that will export a list of users who have logged in for 12 months but I am struggling to export the last login date and time.
The command is as follows:
Search-ADAccount –AccountInActive -UsersOnly –TimeSpan 365:00:00:00 –ResultPageSize 2000 –ResultSetSize $null |?{$_.Enabled –eq $True} | Select-Object Name, SamAccountName, DistinguishedName, lastLogon| Export-CSV “C:\Users\Me\Desktop\InactiveUsers.CSV” –NoTypeInformation
But lastLogon is showing a blank in the CSV file.
I am new to PowerShell I understand the command can be made much smoother.
Any help on this is much appreciated.
Search-ADAccount doesn't have an option to pull other attributes from the AD Objects than the default ones, you can use Get-ADUser with an elaborate filter to query the users who haven't logged on for the past year. One option is to query the user's lastLogonTimeStamp attribute however by doing so you're risking not getting accurate results because this attribute is not replicated in real time. To get accurate one must query the user's lastLogon attribute but, since this attribute is not replicated across the Domain, one must query all Domain Controllers to get the latest logon from the user.
For more information on this topic, please check this excellent TechNet Article: Understanding the AD Account attributes - LastLogon, LastLogonTimeStamp and LastLogonDate.
$dateLimit = [datetime]::UtcNow.AddYears(-1).ToFileTimeUtc()
$AllDCs = Get-ADDomainController -Filter *
$logons = #{}
$params = #{
LDAPFilter = -join #(
"(&" # AND, all conditions must be met
"(!samAccountName=krbtgt)" # exclude krbtgt from this query
"(!samAccountName=Guest)" # exclude Guest from this query
"(userAccountControl:1.2.840.113556.1.4.803:=2)" # object is Disabled
"(lastLogon<=$dateLimit)" # lastLogon is below the limit
")" # close AND clause
)
Properties = 'lastLogon'
}
foreach($DC in $AllDCs) {
$params['Server'] = $DC
foreach($user in Get-ADUser #params) {
# this condition is always met on first loop iteration due to ldap filtering condition
if($logons[$user.samAccountName].LastLogon -lt $user.LastLogon) {
$logons[$user.samAccountName] = $user
}
}
}
$logons.Values | ForEach-Object {
[PSCustomObject]#{
Name = $_.Name
SamAccountName = $_.SamAccountName
DistinguishedName = $_.DistinguishedName
lastLogon = [datetime]::FromFileTimeUtc($_.lastLogon).ToString('u')
}
} | Export-CSV "C:\Users\Me\Desktop\InactiveUsers.CSV" -NoTypeInformation

The server has returned the following error: invalid enumeration context. Get-ADComputer

I have this portion of code that is throwing an invalid enumeration context error.
$ADComputers1 = (get-adcomputer -Filter {enabled -eq $true} -ResultPageSize 500 -ResultSetSize $null -Properties instanceType, IPv4Address, IPv6Address, isCriticalSystemObject, isDeleted, KerberosEncryptionType, LastBadPasswordAttempt, LastKnownParent, localPolicyFlags, Location, CannotChangePassword)
foreach ($computer in $ADComputers1){
$values += [PSCustomObject]#{
instanceType=$computer.instanceType #check
IPv4Address=$computer.IPv4Address #fail
IPv6Address=$computer.IPv6Address #fail
isCriticalSystemObject=$computer.isCriticalSystemObject
isDeleted=$computer.isDeleted
KerberosEncryptionType=$computer.KerberosEncryptionType
LastBadPasswordAttempt=$computer.LastBadPasswordAttempt
LastKnownParent=$computer.LastKnownParent #fail
localPolicyFlags=$computer.localPolicyFlags
Location=$computer.Location
CannotChangePassword=$computer.CannotChangePassword
}
}
I have looked online and found multiple errors similar to the one that is being thrown. In doing so, I have adjusted the -Filter, -ResultPageSize, and shortened the number of properties being accessed multiple times, yet this error is always thrown.
The rest of this code is checking about 70 properties fine, but this section, no matter how small or large I make it, is always throwing an error.
Any help would be appreciated.
This TechNet article provides information about this error, basically this happens when a single query has been running for more than 30 minutes. This answer also provide a few more details.
In this case, there doesn't seem to be a way to refine your LDAP query further because you're interested in gathering all Computer Objects, however something that may help is to query one Organizational Unit at a time. This would increment the number of queries but also reduce the time per query. In my personal experience, changing the values for -ResultPageSize and -ResultSetSize don't give me better results but I'll leave up to you for testing.
Get-ADOrganizationalUnit -Filter * | & {
begin {
$params = #{
Filter = "enabled -eq '$true'"
Properties = #(
'instanceType'
'IPv4Address'
'IPv6Address'
'isCriticalSystemObject'
'isDeleted'
'KerberosEncryptionType'
'LastBadPasswordAttempt'
'LastKnownParent'
'localPolicyFlags'
'Location'
'CannotChangePassword'
)
}
}
process {
$params['SearchBase'] = $_.DistinguishedName
Get-ADComputer #params | & {
process {
[PSCustomObject]#{
instanceType = $_.instanceType
IPv4Address = $_.IPv4Address
IPv6Address = $_.IPv6Address
isCriticalSystemObject = $_.isCriticalSystemObject
isDeleted = $_.isDeleted
KerberosEncryptionType = $_.KerberosEncryptionType
LastBadPasswordAttempt = $_.LastBadPasswordAttempt
LastKnownParent = $_.LastKnownParent
localPolicyFlags = $_.localPolicyFlags
Location = $_.Location
CannotChangePassword = $_.CannotChangePassword
}
}
}
}
} | Export-Csv path\to\export.csv -NoTypeInformation
If have Computers in Containers instead of Organizational Units, which would be pretty odd, you can change the initial query for:
Get-ADObject -LDAPFilter '(|(objectclass=container)(objectclass=organizationalUnit))' | & {

Check and Update multiple attributes of AD users

I am trying to do an update to Active Directory from a CSV.
I want to check each value to see if the AD and CSV values match.
If the AD value and CSV values don't match, then I want to update the AD value.
finally I want to create a log of the values changed, which would eventually be exported to a CSV report.
Now there is about 30 values I want to check.
I could do an if statement for each value, but that seems like the hard way to do it.
I am try to use a function, but I cant seem to get it working.
I am getting errors like:
set-ADUser : replace
At line:94 char:9
+ set-ADUser -identity $ADUser -replace #{$ADValue = $DIAccount ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (JDoe:ADUser) [Set-ADUser], ADInvalidOperationException
+ FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.SetADUser
set-ADUser : The specified directory service attribute or value does not exist
Parameter name: Surname
At line:94 char:9
+ set-ADUser -identity $ADUser -replace #{$ADValue = $DIAccount ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (JDoe:ADUser) [Set-ADUser], ArgumentException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.SetADUser
Any suggestions would be welcome
Code I am using:
Function AD-Check ($ADValue, $ADUser, $ADAccount, $UpdateAccount)
{
If ($ADAccount -ne $UpdateAccount)
{
set-ADUser -identity $ADUser -replace #{$ADValue = $UpdateAccount}
$Change = "Updated"
}
Else
{
$Change = "No Change"
}
Return $Change
}
$Import = get-content C:\temp\ADUpdates.csv
Foreach ($user in $Import)
{
$Account = get-aduser $User.Samaccountname -Properties *
#First Name Check
$Test = AD-Check "GivenName" $Account.samaccountname $Account.givenname $user.givenname
$ChangeGivenName = $Test
#Initials Check
$Test = AD-Check "Initials" $Account.samaccountname $Account.Initials $user.Initials
$ChangeInitials = $Test
#Last Name Check
$Test = AD-Check "Surname" $Account.samaccountname $Account.SurnameSurname $user.Surname
$ChangeSurname = $Test
}
Reply to Theo, cant seem to add this any other way...
Thanks Theo, it seems to make sense, but getting an error.
Select-Object : Cannot convert System.Collections.Specialized.OrderedDictionary+OrderedDictionaryKeyValueCollection to one of the following types {System.String,
System.Management.Automation.ScriptBlock}.
changed the following to get all properties for testing and it works.
$Account = Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue -Properties $propsToCheck
Left the following and it kicks the error
$oldProperties = $Account | Select-Object $propsToCheck
Using the following just for testing:
$propertiesMap = [ordered]#{
SamAccountName = 'sAMAccountName'
mail = 'mail'
GivenName = 'givenName'
Initials = 'initials'
Surname = 'sn'
Office = 'physicalDeliveryOfficeName'
MobilePhone = 'mobile'
DistinguishedName = 'DistinguishedName'
}
Starting of with a WARNING:
Replacing user attributes is not something to be taken lightly and you
need to check any code that does that on a set of testusers first.
Keep the -WhatIf switch to the Set-ADUser cmdlet so you
can first run this without causing any problems to the AD.
Only once you are satisfied all goes according to plan, remove the -WhatIf switch.
Please carefully read all inline comments in the code.
In your code you use an input CSV file, apparently with properties and values to be checked/updated, but instead of using Import-Csv, you do a Get-Content on it, so you'll end up with just lines of text, not an array of parsed properties and values..
Next, as Mathias already commented, you need to use the LDAP attribute names when using either the -Add, -Remove, -Replace, or -Clear parameters of the Set-ADUser cmdlet.
To do what you intend to do, I would first create a hashtable to map the PowerShell attribute names to their LDAP equivalents.
To see which property name maps to what LDAP name, you can use the table here
# create a Hashtable to map the properties you want checked/updated
# the Keys are the PowerShell property names as they should appear in the CSV
# the Values are the LDAP AD attribute names in correct casing.
$propertiesMap = [ordered]#{
SamAccountName = 'sAMAccountName'
GivenName = 'givenName'
Initials = 'initials'
Surname = 'sn'
Office = 'physicalDeliveryOfficeName'
Organization = 'o'
MobilePhone = 'mobile'
# etcetera
}
# for convenience, store the properties in a string array
$propsToCheck = $propertiesMap.Keys | ForEach-Object { $_.ToString() }
# import your CSV file that has all the properties you need checked/updated
$Import = Import-Csv -Path 'C:\temp\ADUpdates.csv'
# loop through all items in the CSV and collect the outputted old and new values in variable $result
$result = foreach ($user in $Import) {
$sam = $user.SamAccountName
# try and find the user by its SamAccountName and retrieve the properties you really want (not ALL)
$Account = Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue -Properties $propsToCheck
if (!$Account) {
Write-Warning "A user with SamAccountName '$sam' does not exist"
continue # skip this one and proceed with the next user from the CSV
}
# keep an object with the current account properties for later logging
$oldProperties = $Account | Select-Object $propsToCheck
# test all the properties and create a Hashtable for the ones that need changing
$replaceHash = #{}
foreach ($prop in $propsToCheck) {
if ($Account.$prop -ne $user.$prop) {
$ldapAttribute = $propertiesMap[$prop] # get the LDAP name from the $propertiesMap Hash
# If any of the properties have a null or empty value Set-ADUser will return an error.
if (![string]::IsNullOrWhiteSpace($($user.$prop))) {
$replaceHash[$ldapAttribute] = $user.$prop
}
else {
Write-Warning "Cannot use '-Replace' with empty value for property '$prop'"
}
}
}
if ($replaceHash.Count -eq 0) {
Write-Host "User '$sam' does not need updating"
continue # skip this one and proceed with the next user from the CSV
}
# try and do the replacements
try {
##########################################################################################################
# for safety, I have added a `-WhatIf` switch, so this wll only show what would happen if the cmdlet runs.
# No real action is performed when using '-WhatIf'
# Obviously, there won't be any difference between the 'OLD_' and 'NEW_' values then
##########################################################################################################
$Account | Set-ADUser -Replace $replaceHash -WhatIf
# refresh the account data
$Account = Get-ADUser -Identity $Account.DistinguishedName -Properties $propsToCheck
$newProperties = $Account | Select-Object $propsToCheck
# create a Hashtable with the old and new values for log output
$changes = [ordered]#{}
foreach ($prop in $propsToCheck) {
$changes["OLD_$property"] = $oldProperties.$prop
$changes["NEW_$property"] = $newProperties.$prop
}
# output this as object to be collected in variable $result
[PsCustomObject]$changes
}
catch {
Write-Warning "Error changing properties on user '$sam':`r`n$($_.Exception.Message)"
}
}
# save the result as CSV file so you can open with Excel
$result | Export-Csv -Path 'C:\temp\ADUpdates_Result.csv' -UseCulture -NoTypeInformation

(PowerShell) How do I filter usernames with Get-EventLog

I'm working on a Powershell script to get all users who have logged in/out of a server in the past 7 days, where their name is not like "*-organization". The below works, but no matter what I try I'm not able to filter names
$logs = get-eventlog system -ComputerName $env:computername -source Microsoft-Windows-Winlogon -After (Get-Date).AddDays(-7)
$res = #()
ForEach ($log in $logs)
{
if($log.instanceid -eq 7001){
$type = "Logon"
}
Elseif ($log.instanceid -eq 7002){
$type = "Logoff"
}
Else { Continue }
$res += New-Object PSObject -Property #{Time = $log.TimeWritten; "Event" = $type; User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])}};
$res
I've tried adding this line in various places and ways, but no matter what I can't get it to filter. It either fails and tells me my operator must have a property and value, or it runs fine and ignores any username filtering.
| Where-Object $_.User -notlike "*-organization"
Is it even possible to filter the login username with this method? If so, what am I doing wrong? If it's not possible, is there another way I can get what I need?
There would have to be a property named 'user' for that to work. Get-eventlog is actually obsolete now, and replaced by get-winevent. Unfortunately, you have to get into the xml to filter by usersid. I've included a time filter.
$a = get-winevent #{logname='system';
providername='Microsoft-Windows-Winlogon'} -MaxEvents 1
$e = $a.ToXml() -as 'xml'
$e.event.EventData
Data
----
{TSId, UserSid}
get-winevent #{logname='system';providername='Microsoft-Windows-Winlogon';
data='S-2-6-31-1528843147-473324174-2919417754-2001';starttime=(Get-Date).AddDays(-7);
id=7001,7002}
In powershell 7 you can refer to the eventdata named data fields directly:
get-winevent #{logname='system';providername='Microsoft-Windows-Winlogon';
usersid='S-2-6-31-1528843147-473324174-2919417754-2001'}
The get-winevent docs say you can use "userid" in the filterhashtable, but I can't get that to work.
EDIT: Actually this works. But without limiting it too much, at least for me.
get-winevent #{logname='system';userid='js2010'}
get-winevent #{providername='Microsoft-Windows-Winlogon';userid='js2010'}
You can do this with the -FilterXPath parameter like below:
$filter = "(*[System/EventID=7001] or *[System/EventID=7002]) and *[System/Provider[#Name='Microsoft-Windows-Winlogon']]"
$result = Get-WinEvent -LogName System -FilterXPath $filter | ForEach-Object {
# convert the event to XML and grab the Event node
$eventXml = ([xml]$_.ToXml()).Event
$eventData = $eventXml.EventData.Data
$userSID = ($eventData | Where-Object { $_.Name -eq 'UserSid' }).'#text'
$userName = [System.Security.Principal.SecurityIdentifier]::new($userSID).Translate([System.Security.Principal.NTAccount])
# you can add username filtering here if you like.
# remember the $userName is in formal DOMAIN\LOGONNAME
# if ($username -notlike "*-organization") {
# output the properties you need
[PSCustomObject]#{
Time = [DateTime]$eventXml.System.TimeCreated.SystemTime
Event = if ($eventXml.System.EventID -eq 7001) { 'LogOn' } else { 'LogOff' }
UserName = $userName
UserSID = $userSID
Computer = $eventXml.System.Computer
}
# }
}
# output on screen
$result
# output to CSV file
$result | Export-Csv -Path 'X:\TheOutputFile.csv' -NoTypeInformation
Note, I have commented out the username filtering in the code. It is just there to give you an idea of where to put it. Of course, you can also filter the $result afterwards:
$result | Where-Object { $_.UserName -notlike "*-organization" }
Adding to #js2010's helpful answer, and with the assumption you're using PowerShell 5.1. I usually identify the property array index and use Select-Object to create a custom property as needed.
$WinEvents =
get-winevent #{logname='system'; providername='Microsoft-Windows-Winlogon'} |
Select-Object #{Name = 'Time'; Expression = {$_.TimeCreated}},
#{Name = 'Event'; Expression = { If($_.ID -eq 7001){'Logon'} ElseIf($_.ID -eq 7002){ 'Logoff' } } },
#{Name = 'User'; Expression = { [System.Security.Principal.SecurityIdentifier]::new( $_.Properties[1].Value ).Translate([System.Security.Principal.NTAccount]) } }
In your case this should add a property called User with a value like DomainName\UserName to the objects. I also added expressions to derive the other properties you were adding to your custom objects. Select-Object emits custom objects as well so this should give the result you're looking for.
Let me know if this helps.
Update
Respectfully, the other 2 answers make the assumption that you are looking for logon/off events for a specific user. That's not how I read the question; in particular:
"get all users who have logged in/out of a server"
While PowerShell 7+ does let you directly cite UserID in the FilterHashtable, it's not very useful here because we're not seeking events for a specific user. Furthermore, it seems unhelpful for the ultimate output as by default it echoes as a SID. It would still need to be translated, not only for display but for further filtering. I'm also not positive that UserID will always be the same as Properties[1], there's certainly some variance when looking at other event IDs.
The XML work is very cool, but I don't think it's called for here.
There were some issues with my answer as well. I overlooked filtering the event IDs & dates up front. I also realized we don't need to instantiate [System.Security.Principal.SecurityIdentifier] class because the property is already typed as such. Along with some readability improvements I corrected those issues below.
# Should be the 1st line!
using NameSpace System.Security.Principal
$ResolveEventType = #{ 7001 = 'Logon'; 7002 = 'Logoff' }
$FilterHashTable =
#{
LogName = 'system'
ProviderName = 'Microsoft-Windows-Winlogon'
ID = 7001,7002
StartTime = (Get-Date).AddDays(-7)
}
[Array]$WinEvents =
Get-WinEvent -FilterHashtable $FilterHashTable |
Select-Object #{ Name = 'Time'; Expression = { $_.TimeCreated } },
#{ Name = 'Event'; Expression = { $ResolveEventType[ $_.ID ] } },
#{ Name = 'User'; Expression = { $_.Properties[1].Value.Translate( [NTAccount] ) } }
$WinEvents |
Where-Object{ $_.UserName -notlike "*-organization" } |
Format-Table -AutoSize
This tested good in PowerShell 5.1 & 7.0. I added Format-Table to display the output, but you can just change that out for an Export-Csv command as needed
Note: The last 2 pipelines can be combined, but I thought this was a
little more readable.
Let me know if this helps.

Create a PowerShell script that would get the last 30 days history logon of Domain Admin member

I would like to write a Power Shell script that would do the following:
- If the user is member of (Domain admins) get me the last 30 days history logon of this user in any Domain joined computer.
I created something now but it still lacks a lot as it reads the security events on the Domain controller and brings the users,time and matches them with the Domain admin group as in the attached screenshot
I would appreciate if someone can help me evolve this script into something useful
$Rusers = Get-WinEvent -Computer dc02 -FilterHashtable #{Logname='Security';ID=4672} -MaxEvents 50 |
` select #{N='User';E={$_.Properties[1].Value}},TimeCreated
$DAUsers = Get-ADGroupMember -Identity "Domain Admins"
Foreach ($DAUser in $DAUsers){
$DomainUser = $DAUser.SamAccountName
foreach ($Ruser in $Rusers){
$RAUser = $Ruser.User
If ($RAUser -match $DomainUser){
Write-Host $Ruser is domain admin }
}[![enter image description here][1]][1]
}
# Get domain admin user list
$DomainAdminList = Get-ADGroupMember -Identity 'Domain Admins'
# Get all Domain Controller names
$DomainControllers = Get-ADDomainController -Filter * | Sort-Object HostName
# EventID
$EventID = '4672'
#
# Get only last 24hrs
$Date = (Get-Date).AddDays(-1)
# Limit log event search for testing as this will take a LONG time on most domains
# For normal running, this will have to be set to zero
$MaxEvent = 50
# Loop through Dcs
$DALogEvents = $DomainControllers | ForEach-Object {
$CurDC = $_.HostName
Write-Host "`nSearching $CurDC logs..."
Get-WinEvent -Computer $CurDC -FilterHashtable #{Logname='Security';ID=$EventID;StartTime = $Date} -MaxEvents $MaxEvent |`
Where-Object { $_.Properties[1].Value -in $DomainAdminList.SamAccountName } |`
ForEach-Object {
[pscustomobject]#{SamAccountName = $_.Properties[1].Value;Time = $_.TimeCreated;LogonEventLocation = $CurDC}
}
}
All the Domain Admin logon events should now be in $DALogEvents
You'll need to group results by name, then export to a file
Thanks a lot for your help, I apologize I was not clear enough. The kind of information I am looking for is pertaining to users who have been utilized for services e.g. (SQL reporting Services, Or Sccm Service ..etc )
This script does what I want but it doesn't run only for domain admin users, it runs for everyone basically and not sure if there's a limit to the time/date.
Is it possible to adjust it to let it run against Domain Admin users for 30 days and print information like. Source IP, User, Target Dc, Date?
Get-EventLog -LogName Security -InstanceId 4624 |
ForEach-Object {
# translate the raw data into a new object
[PSCustomObject]#{
Time = $_.TimeGenerated
User = "{0}\{1}" -f $_.ReplacementStrings[5], $_.ReplacementStrings[6]
Type = $_.ReplacementStrings[10]
"Source Network Address" = $_.ReplacementStrings[18]
Target = $_.ReplacementStrings[19]
}
}
I've added couple more of custom objects to get the result that I needed. I think turning this into a function would be great tool to use for auditing.
Thanks a lot to you #Specialist
# Get domain admin user list
$DomainAdminList = Get-ADGroupMember -Identity 'Domain Admins'
# Get all Domain Controller names
$DomainControllers = Get-ADDomainController -Filter * | Sort-Object HostName
# EventID
$EventID = '4624'
#
# Get only last 24hrs
$Date = (Get-Date).AddDays(-3)
# Limit log event search for testing as this will take a LONG time on most domains
# For normal running, this will have to be set to zero
$MaxEvent = 100
# Loop through Dcs
$DALogEvents = $DomainControllers | ForEach-Object {
$CurDC = $_.HostName
Write-Host "`nSearching $CurDC logs..."
Get-WinEvent -ComputerName $CurDC -FilterHashtable #{Logname='Security';ID=$EventID;StartTime = $Date} -MaxEvents $MaxEvent |`
Where-Object { $_.Properties[5].Value -in $DomainAdminList.SamAccountName } |`
ForEach-Object {
[pscustomobject]#{SourceIP = $_.Properties[18].Value; SamAccountName = $_.Properties[5].Value;Time = $_.TimeCreated;LogonEventLocation = $CurDC}
}
}
$DALogEvents