PowerShell Issue - powershell

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

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."
}

Get the size to a snapshot via Powershell in Hyper-V

Good Evening, I am developing a C# too, that is supposed to help users to control VMs on a server.
Now I have created the functions to create and erase Snapshots of these VMs and want to create a function to list all Snapshots of a VM including a view selected Snapshot parameter. With:
Invoke-Command -ComputerName ServerName -ScriptBlock { Get-VMSnapshot -VMName VMName |Select-Object}
I am able to get nearly all information that are requested but the total size of the Snapshot itself.
However I could get the size with this command:
Invoke-Command -ComputerName ServerName -ScriptBlock {Get-VM -VMName VMName | gci -r -Filter *.avhd*| select #{Name="MB";Expression={$_.Length / 1Mb}}, *}
Not to get the two results together I compared the creation time of these two with a tolerance of up to 10 seconds and took the closest one. (Primitive I know), but now it becomes even more complicated.
If I now use a Snapshot to set the VM to this state, the time of the File creation changes and so my previous method stops working completely.
Why I want to know is, is there a better way to compare these two results or even better, to get the Snapshot size without such primitive comparisons?
Thanks in advance for the help.
Below one-liners should suffice your need.
1)
Get-VM | Sort Name | Get-Snapshot | Where { $_.Name.Length -gt 0 } | Select VM,Name,Created,SizeMB
2)
Get-VM | Format-Table Name, #{Label="NumSnapshots";Expression={(Get-Snapshot -VM $_ | Measure-Object).Count}}, #{Label="TotalSnapShotSizeMB";Expression={(Get-Snapshot -VM $_ | Measure-Object -Sum SizeMB).Sum}}
Hope it helps.

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.

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

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

Kill process by filename

I have 3 instances of application running from different places. All processes have similar names.
How can I kill process that was launched from specific place?
You can get the application path:
Get-Process | Where-Object {$_.Path -like "*something*"} | Stop-Process -WhatIf
That will work for the local machine only. To terminate remote processes:
Get-WmiObject Win32_Process -Filter "ExecutablePath LIKE '%something%'" -ComputerName server1 | Invoke-WmiMethod -Name Terminate
I would like to slightly improve Shay Levy's answer, as it didn't work work well on my setup (version 4 of powershell)
Get-Process | Where-Object {$_.Path -like "*something*"} | Stop-Process -Force -processname {$_.ProcessName}
You can take a look at the MainModule property inside of the Process class (which can be invoked via powershell).
foreach (Process process in Process.GetProcesses())
{
if (process.MainModule.FileName == location)
{
process.Kill();
}
}
I'd also consider the possible exceptions that can occur while calling this code. This might occur if you're trying to access processes that are no longer present (killed since the last time GetProcess was called) or processes for while you do not have permissions.
Try this:
http://technet.microsoft.com/en-us/library/ee177004.aspx
Stop-Process -processname notepad
The below command kills processes wherein "something" is part of the path or is a command line parameter. It also proves useful for terminating powershell scripts such as powershell -command c:\my-place\something.ps1 running something.ps1 from place c:\my-place:
gwmi win32_process | Where-Object {$_.CommandLine -like "*something*"} | % { "$(Stop-Process $_.ProcessID)" }
The solution works locally on my 64bit Windows 10 machine.