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."
}
Related
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.
get-eventlog -LogName system -ComputerName servername | where {$_.source -eq "user32"} | select -first 1 | Format-List
The above command does not return back to the prompt after displaying the desired result.
This happens only in the case of remote computers. If I use "localhost" it works just fine.
I think what you are running into is that Select-Object can kill the pipeline locally in PSv3, but it can't do that remotely. So you continue to get results until it is finished. Since your conditions mean that you will filter out all but one result, it seems like it is hanging.
Try removing the Select-Object (and maybe the Where-Object) to see how long it takes to run normally. You should also do more filtering in the remote call itself rather than filter after receiving the data.
Get-EventLog -LogName system -Source "user32" | select -first 1 | Format-List
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.
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
I started putting this PowerShell Script together, the hope would be to replace some tasks that are currently carried out manually
I'm using the
get-Date.AddDays()
function
I'm using the ISE to build the script and in testing I get output if I single out the 'starttime' property, but this seems to be a catch all because the values all come up null, ideally I'd like to use the 'timesubmitted' property, but the date seems to output in an odd that I don't think is being read correctly because my queries with 'timesubmitted' always come up empty
it comes out in this format, if you do an open query
20120416030836.778000-420
here's what I have so far.
disregard the | 'format-table' function that's just so I can see if I'm getting the desired output
#Clears Old Print Jobs on Specified server
#Sets Execution Policy for Script to run
Set-ExecutionPolicy RemoteSigned -Force
#establishes variable for cutoff date
$d = Get-Date
$old = $d.AddDays(-4)
#Queries WMI and retrieves print jobs
Get-WmiObject -class win32_printjob -namespace "root\CIMV2" | where-object {$_.timesubmitted -lt
"$old"} | ft caption,document,jobid,jobstatus,owner,timesubmitted
In PowerShell every WMI instance has a ScriptMethod that you can use to convert the dates from WMI format to .NET format:
Get-WmiObject Win32_PrintJob |
Where-Object { $_.ConvertToDateTime($_.TimeSubmitted) -lt $old } |
Foreach-Object { $_.Delete() }
Just an update in case anyone is looking in 2021.
This command/syntax worked for me in 2008 R2 (PowerShell version 2.0) (I was able to piece this together from this page, as well as others).
Finds all jobs over 30 minutes and deletes them:
Get-wmiobject win32_printjob
| Where-Object {[System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeSubmitted) -lt (Get-Date).addminutes(-30)}
| ForEach-Object { $_.delete() }*