Powershell - Getting Last Log Off Date and Time of a computer - powershell

I'm not very good with powershell so here goes.
I'm having trouble with displaying the last logoff Date and time of a computer. So far I have:
$Logoff = GWMI -Comp $strComputer -Cl Win32_NetworkLoginProfile |
foreach-object {Write-Host "Last Logoff: "$_.Lastlogoff}
This gives me a list of I guess logoff dates and time. This seemed ok so I tried to convert the output using ConvertToDateTime to get a readable date/time but I don't now how to get it to work when a selection of datetimes are sent back. I've tried:
$Logoff = GWMI -Comp $strComputer -Cl Win32_NetworkLoginProfile |
foreach-object {Write-Host "Last Logoff: "ConvertToDateTime($_)}
but as you can guess this didn't work. Can someone point me in the right direction? Maybe I'm going about this wrong and I should be looking at a different way of getting last logoff/logoff details

Another way of achieving the same result as Ansgar's suggested command:
Get-EventLog -ComputerName $Computer -LogName 'Security' -InstanceId 4634 -newest 1 | Select-object TimeGenerated
On my computer, there was a big difference in time taken to retrieve the result.

You could read the most recent logoff event from the computers' eventlogs:
Get-EventLog -Computer $strComputer "Security" `
| ? { $_.EventId -eq 4634 } `
| sort -Desc TimeGenerated `
| select -First 1 TimeGenerated
Note that reading the Security eventlog requires admin privileges. Also, reading the entire eventlog may require significant amounts of time, so you may want to restrict the processed events by date (-After (Get-Date).AddDays(-1)) or by number (-Newest 500).

This is a better working script over here.
Note that it shows full User Logon/Logoff, but does not show "idle" times.
User Logon/Logoff Information using Powershell

Related

get-eventlog returns different results each run

I have a function that runs the directory sync tool (for azure AD) on a remote server.
the problem starts with the cmdlet that suppose to return the time the sync started.
It first runs Start-ADSyncSyncCycle -PolicyType Delta on the remote server (using Invoke-Command). After that it runs this code to get the newest event (using specific parameters):
$Event = Get-EventLog -LogName Application -ComputerName $ComputerName -Newest 1000 | Where-Object {$_.Source -eq "Directory Synchronization" -and $_.Message -match "Scheduler::StartSyncCycle : Started sync cycle."} |Sort-Object Time |Select-Object -First 1
The problem starts with the if statement that follows
if ($Event) {Write-Output "Started sync cycle at" $Event.TimeGenerated} else {Write-Output "Sync did not start"}
The result of that if is "Started sync cycle at " (with an empty space after the "at") meaning it cant grab the $Event.TimeGenerated. When doing Write-Host $Event it shows System.Diagnostics.EventLogEntry and the weird thing is that other times the result of the if statement shows the correct info like "Started sync cycle at Wednesday, February 17, 2021 3:27:16 PM".
Can anyone please help me figure this out? What can cause it to show a different result on each run?
Or better yet, what is this System.Diagnostics.EventLogEntry object it sometimes returns?
I have a feeling Im missing something dumb... :(
Thanks in advance.
I see a couple bugs. The core one is here:
Sort-Object Time | Select-Object -First 1
First, while the output for Get-EvenLog shows a column named Time, there is no property actually named Time. You can see this with Get-EventLog -LogName Application -Newest 1 | Format-List -Property *. There are two properties you might want: TimeGenerated and TimeWritten. Offhand, I'm not sure which Time represents since the events I see have the same value for each.
Second, the default order for sorting a datetime is ascending. That means this code gets the oldest event in the newest 1000 events. That's not what you described the code doing. You should use the -Descending switch to get the newest log record.
Next, you should specify the source when you call Get-EventLog with the -Source parameter since you know the exact source. That will improve performance by making the command do the filtering for you.
Finally, your pattern matching here contains special regex characters:
$_.Message -match "Scheduler::StartSyncCycle : Started sync cycle."
If this is a literal string you want to match, you should instead match against:
$_.Message -match "Scheduler::StartSyncCycle : Started sync cycle\."
So I would write your code like so:
$Event = Get-EventLog -LogName Application -ComputerName $ComputerName -Source "Directory Synchronization" -Newest 1000 |
Where-Object Message -match "Scheduler::StartSyncCycle : Started sync cycle\." |
Sort-Object -Property TimeGenerated -Descending |
Select-Object -First 1
I'm not quite sure what the issue is with the Write-Output statement because I can't recreate it here. However, I would try your code like so:
if ($null -ne $Event) {
"Started sync at $($Event.TimeGenerated)"
}
else {
"Sync did not start."
}

Using Powershell to build a list of computer model numbers in our Domain?

As the title states, I am trying to determine every computer model used in our domain. I am new to the company, and have been placed in charge of producing a new encryption solution for all end point devices. By knowing the computer models in our domain, I will be able to determine which machines have a TPM 1.2 chip, and which ones don't (almost 15k devices). I do not need anything to look pretty, but I'm open for ideas. I more or less want a list (text or csv for sorting purposes) so I can quantify models and research.
Here's what I have so far:
Get-ADComputer -Filter {Name -like 'ML*'} | select -expand name |
ForEach {
If (Test-Connection $_ -count 1 -quiet)
{Get-WmiObject -Class Win32_ComputerSystem -ComputerName $_} Select-Object -Property model | Export-Csv "c:\scripts\Models.csv"}
Else { Add-Content -value $_ c:\scripts\not.responding.txt}
I know there are problems with this. Right now I'm having trouble querying AD and passing the computer name variable only. Because of this, the ping test fails, and everything exports to the failed text file. The failed text file indicates that the variable includes a lot more than just the computer name. If I can pull the variable correctly, I'm not sure if the rest would work, but I think it should. Any help would be greatly appreciated.
$ComputerNames = Get-ADComputer -Filter {Name -like 'ML*'} | select -expand name
foreach ($computername in $ComputerNames){
If (Test-Connection $computername -count 1 -quiet){
Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername | Select-Object -Property model | Export-Csv "c:\scripts\Models.csv"
}
else{
Add-Content -value $computername c:\scripts\not.responding.txt
}
}
there you go.

Working with Windows Event Logs in PowerShell

I have a script to read the last 30 entries for the Application and System event logs, currently the scripts only works on my machine and only outputs a partial message (see below for example).
411905 May 05 15:05 Information Microsoft-Windows... 1501 The Group Policy settings for the user were processed successfully. There were no changes detected since the last succ...
Can anyone tell me how the following can be done
use this for remote computers - I have tried entering the computer name in the format of \domain\computername but is doesn't work
How I can display the full message and not just a section
How I can save the file with the computer name as part of the file name e.g. "mycomputer application log.txt"
My script so far is like this
Get-EventLog -LogName Application -Newest 30 -ComputerName MYCOMPUTER | Out-File -FilePath "D:\Test\Application Event Logs.txt"
Get-EventLog -LogName System -Newest 30 -ComputerName MYCOMPUTER | Out-File -FilePath "D:\Test\System Event Logs.txt"
I am new to scripting and thought this could be a useful script to have but I can't get it to work.
Remote Computers
If you mean computers in another domain, then I don't think you can using the cmdlet alone.
Instead, you can use PowerShell remoting to run any powershell commands that exist on the remote computer. But you have to set up remotin gon the remote machine, and use SSL or trusted hosts, with explicit credentials to ensure the connection will be allowed:
$credential = Get-Credential # enter credentials for remote machine
$session = New-PSSession -ComputerName REMOTECOMPUTER -Credential $credential
Invoke-Command -Session $session -ScriptBlock {
Get-EventLog # parameters
}
The Full Text
It's important to note that what is returned by Get-WinEvent is a complex object. What you see when it's displayed on the screen is just a view. Writing it out to a file directly will also be just a view. Instead, explicitly figure out what you want, build a string, and then write it to a file.
Start by assigning the result of the cmdlet to a variable so that you can inspect it:
$events = Get-WinEvent #params
Now you can look at the results:
$events | Get-Member # see what properties are available
So then you can see that Message is a property.
To get just the message, you can use Select-Object and since you want it as a string and not a property, you -ExpandProperty:
$events | Select-Object -ExpandProperty Message | Out-File #etc
That would write out all the messages (but no other info).
In practice, you might want to operate on each log entry returned and build your string to write to the file:
$events | ForEach-Object {
# $_ represents the current object
$msg = $_.Message
$id = $_.Id
$timeCreated = $_.TimeCreated
"A log entry with ID $id was created at $timeCreated, and it says:`r`n`r`n$msg`r`n---------`r`n"
} | Out-File #params
Using the Computer Name
Assuming you know the computer name you're checking in advance, put it in a variable, then embed it in the file name:
$computer = 'MYCOMPUTER'
Get-WinEvent -ComputerName $computer | ForEach-Object {
# do stuff like above
} | Out-File -Path "D:\Whatever\$computer Application Log"

Powershell Get-EventLog hangs on RemoteComputer

The following runs fine on local computer, but when I put -ComputerName "myRemoteName", it hangs and doesn't return anything even after about 5 minutes; but program still seems to be running.
Is it trying to return a large packet of data across the "wire"?
In theory, I should have under 10 errors on the remote computer in the last 2 hours.
$getEventLog = Get-EventLog -log application -ComputerName "myRemoteName" -after ((get-date).addMinutes($minutes*-1)) -EntryType Error
Write-Host Get-Eventlog completed
# list of events to exclude (based on text found in the message)
$getEventLogFiltered = $getEventLog | Where-Object {$_.Message -notlike 'Monitis*' -and $_.Message -notlike '*MQQueueDepthMonitor.exe*' -and $_.Message -notlike '*The local computer may not have the necessary registry*' }
#to only select certain columns, use Select-Object -Property and list the property/columns
$getEventLogColumns = $getEventLogFiltered | Select-Object -Property TimeGenerated,Source,Message,EntryType,MachineName,EventID
$tableFragment = $getEventLogColumns | ConvertTo-Html -fragment
Write-Host "HTML-Table Built"
Code after that builds an email and sends it...
I've seen other posts that suggest switching to Get-WinEvents, but I think that would take me an hour or two to rewrite (due to my lack of experience with Powershell); what I have above is working fine on local computers.
Updates 03/04/2014 13:40 CT:
Running with $minutes = 120 ran 14.5 minutes.
Running with $minutes = 1 ran 12.5 minutes.
Conclusion, changing the range of $minutes doesn't really seem to effect the response time; both are slow.
After parameter is not very well designed, it prints all records it suppose to, but when it reaches set date it stills scans till the end of even log file despite the fact, that there is nothing to print (at least it looks so). I've used Where-object filter and .CompareTo() method to prints logs which are after set date (in my case a day before current date).
#Sets yesterday date (script will get all records after that date)
$YDate = (Get-Date).AddDays(-1)
#Gets all event logs from Security log where event id represents successful logon and records where generated after prepared date (current date - 24 hours)
$YestardayLogons = Get-EventLog -ComputerName $ServerName -LogName Security |
WHERE { ($_.EventId -eq '528') -and ($_.TimeGenerated.CompareTo($YDate) -eq '1') }
It seems that I was wrong, even with Where-Object filter it still scans like in case of -after parametr (I was just testing on different, freshly build machine and this is why it finished so quickly).
Additional research showed however that break function could be useful, so what I did was:
Get-EventLog -ComputerName $ServerName -LogName Security | WHERE { ($_.EventID -eq '528')} | ForEach-Object {
$_
if ($_.TimeGenerated.CompareTo($YDate) -lt 1) { Break}
}
It prints all event logs and when it hits event log older than (in my case 24 hours) break kicks in and stops get-eventlog cmdlet.
It is not the most pretty solution but it seems to be working fine so far.

Security Log filter for multiple remote servers using PowerShell

I would like assistance with getting security event logs from multiple remote servers. I've had success with the Application and System logs, but the Security logs are too large to work practically in the same manner.
Here is what I'm using for a successful Application log:
$StartTime = (get-date).adddays(-1)
$Credential = Get-Credential
Get-Content C:\Users\user\Documents\server_list.txt | Foreach-Object{
Get-WinEvent -ComputerName $_ -Credential $Credential -FilterHashTable #{LogName='Application';StartTime=$StartTime}
| ?{$_.LevelDisplayName -eq "Error" -or $_.LevelDisplayName -eq "Warning"}
| select machinename,timecreated,id,level,message
} | Export-Csv "C:\Users\user\Documents\App_logs.csv"
I can create something similar for the Security logs, but it took 10 hours to pull all the Security logs, so that isn't practical for me. Now I am trying to filter the fields it pulls so that I only get Audit Failures, Errors and Warnings.
I couldn't find a way to filter for those properties with Get-WinEvent and numerous other posts suggested using Get-EventLog for the Security log.
Here is what I have so far. This first part appears to work correctly:
$StartTime = (get-date).adddays(-1)
Get-Content C:\Users\user\Documents\server_list.txt | Foreach-Object{
Get-EventLog Security -ComputerName $_ -After $StartTime -EntryType Error,FailureAudit,Warning
} | Export-Clixml "C:\Users\user\Documents\Test_Sec_logs.xml"
The problem with this output is the output doesn't appear organized in a human-readable fashion. For instance, the first event it pulls will have roughly 15 lines and 15 columns and data all over. Then it repeats for the next event. I created a pivot table for it and still couldn't easily interrupt it.
I next attempted to filter this further and this is where it isn't working how I hoped. I run this as a separate file right now because when it's all in one script it errors.
$Seclog = Import-Clixml "C:\Users\user\Documents\Test_Sec_logs.xml"
$Seclog | fl -property EventID, MachineName, Category, EntryType, Message, Source, TimeGenerated, TimeWritten, Username
| Export-Clixml "C:\Users\user\Documents\Test_Sec_logs_filtered.xml"
The filtered XML is created but only shows the EventID property. I would like to capture the data in all the fields listed after "property".
I appriciate any help and advice. Thanks in advance.