How to get working set memory from a SYSTEM process - powershell

I am fairly unexperienced in powershell and I need to pull the memory usage from a process that falls under the SYSTEM user while logged in under my user in Windows Server 2012.
I can pull the information from my user easily but there is a large discrepancy from that and what it shows in task manager under the SYSTEM user.
The goal is to stop the java.exe process if it is using a certain amount of memory as shown below.
$java = Get-Process “java” -ErrorAction SilentlyContinue
if (!$java.WS/1GB -gt 20) {
$java | Stop-Process -Force}
However, when I run
(Get-Process "java").WS/1GB
I get ~1.364GB when the process under the system user in task manager is using ~173GB.

Related

Powershell - CloseMainWindow work for all?

I'm looking for a way to gracefully close/quit the GoogleDrive app which runs under the process GoogleDriveFS.
get-process GoogleDriveFS
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
219 16 10796 5732 0.05 4392 1 GoogleDriveFS
333 22 11820 32364 0.17 8424 1 GoogleDriveFS
297 19 16528 34860 0.06 12036 1 GoogleDriveFS
245 17 10472 23992 0.03 14296 1 GoogleDriveFS
572 26 52256 82728 0.84 17788 1 GoogleDriveFS
518 21 28668 68208 0.44 18460 1 GoogleDriveFS
1024 59 47016 89396 27.95 19452 1 GoogleDriveFS
is something like Process.CloseMainWindow Method suitable for this ? or is there a better way to ensure the app isn't running?
tl;dr
System.Diagnostics.Process.CloseMainWindow() will not work, for the reasons explained in the bottom section.
Note:
If the target processes weren't started from your own user account, you'll need to run the following from an elevated (run as admin) session.
You can try the following to achieve graceful termination, but there's no guarantee it will work:
# Asks all GoogleDriveFS processes to terminate, which they may or may not do.
# A status line is output to stdout for each targeted process,
# indicating whether the termination request was successfully *sent*.
# Note: ".exe" must be used, whereas it mustn't be
# with PowerShell's *-Process cmdlets.
taskkill.exe /im GoogleDriveFS.exe
If it doesn't, forceful termination is your only option, which is most easily accomplished with:
# !! Forcefully terminates all GoogleDriveFS, without cleanup.
Stop-Process -Force -Name GoogleDriveFS
Note: As discussed below, Stop-Process always terminates forcefully. The only function of the -Force switch is to suppress a potential confirmation prompt that is presented when you attempt to terminate processes belonging to a different user (only works with elevation).
Here's a snippet that first tries graceful termination, then falls back to forceful termination after a specifiable timeout:
$processName = 'GoogleDriveFS'
$timeOutSecs = 2
# Get all existing processes of interest.
$processes = Get-Process -ErrorAction Ignore -Name $processName
if (-not $processes) {
Write-Verbose -Verbose "No $processName processes running."
} else {
# Ask the processes to terminate, which they may or may not do.
taskkill.exe /im "$processName.exe" *>$null
try {
# Wait for up to $timeOutSecs seconds for the processes to -
# potentially - terminate gracefully.
$processes | Wait-Process -ErrorAction Stop -Timeout $timeOutSecs
} catch {
Write-Warning "Forcefully terminating (remaining) $processName processes..."
# Note: This assumes that you don't care about any new
# processes that may have launched since Get-Process was called.
$processes | Stop-Process -Force
}
}
On Windows, graceful termination is fundamentally only an option for GUI-subsystem applications, i.e. processes that have a main window (whether visible or not) and therefore a message loop to which the WM_CLOSE message can be posted.
In other words: you cannot ask console applications on Windows to terminate gracefully (unless they implement some application-specific custom mechanism through which other processes can request termination).
For supported applications, there are important considerations:
Termination isn't guaranteed, and even if it does happen, its timing isn't guaranteed:
The target process may be in a state where it cannot process the WM_CLOSE message, such as when it happens to be displaying a modal dialog at the time or happens to be stuck.
The target process may quietly refuse to terminate.
The target process may put up a modal dialog to confirm the intent to terminate, notably when trying to close an editor-like application that has an unsaved document open.
Therefore, if you need to ensure termination, you'll have to monitor the process for actual termination afterwards, and possibly terminate it forcefully after a suitable timeout period.
taskkill.exe offers forceful termination via its /f option.
.NET offers forceful termination via System.Diagnostics.Process.Kill()
As an aside: As of PowerShell 7.2.x, the Stop-Process cmdlet invariably uses this method, i.e. invariably terminates processes forcefully - allowing requesting graceful termination on an opt-in basis is the subject of GitHub issue #13664.
At the Windows API level, it doesn't matter if the targeted main window is visible or not, so that even (GUI-subsystem) processes that by design run invisibly - as GoogleDriveFS.exe appears to be - can be targeted with a WM_CLOSE message.
While System.Diagnostics.Process.CloseMainWindow() is designed to request graceful termination of a given process by sending a WM_CLOSE message to its main window, it unfortunately doesn't find that window if it happens to be invisible (hidden) (still applies as of .NET 6.0)
By contrast, the taskkill.exe utility does not have this limitation.
A limitation that BOTH methods share is the inability to target processes that are UWP / Microsoft Store applications.
However, this applies only to "pure" UWP applications (e.g, Settings, Calculator), and not to desktop applications packaged as UWP apps (e.g., Windows Terminal, Microsoft Edge).
The reason is that both methods rely on the EnumWindows WinAPI method, which only supports desktop applications.
However, manually finding a UWP application's main window via FindWindowEx and posting WM_CLOSE to it, is possible.
You can do something like this:
do {
$running = try { Get-Process -Name GoogleDriveFS -ErrorAction Stop } catch { Write-Host "Error: $($PSItem.Exception.Message) " }
$running | ForEach-Object {
$_.CloseMainWindow()
Write-Debug -Message "Closing ($_).pm "
}
}
until ($running -eq $null)
However, this will prompt for the user to confirm the close.
You could also use the close() method to avoid prompting the user or kill() to immediately release all resources.
CloseMainWindow()
Closes a process that has a user interface by sending a close message to its main window.
Close() Frees all the resources that are associated with this component.
Kill() Immediately stops the associated process.
via https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process?view=net-6.0#methods
You could use the --quit argument of GoogleDriveFS.exe :
"C:\Program Files\Google\Drive File Stream\64.0.4.0\GoogleDriveFS.exe" --quit
But it will break after each software update so running this bat file should be better :
"%ProgramFiles%\Google\Drive File Stream\launch.bat" --quit
This bat file looks up the latest GoogleDriveFS.exe and runs it with the same arguments as the script.
And from Powershell :
Start-Process -FilePath "${env:ProgramFiles}\Google\Drive File Stream\launch.bat" -ArgumentList '--quit' -Wait -NoNewWindow

How to get the proper PID of a newly created Firefox window in Powershell?

Here is a very simple example of the problem I am experiencing:
$process = Start-Process 'C:\Program Files\Mozilla Firefox\firefox.exe' -argumentlist "-new-window https://google.com -foreground" -PassThru
Write-Host $process.Id
The firefox window will start and work as expected, it will return a process id, but when I actually check the running processes, I see no results for that PID.
I tried adding this just to see,
while (-not $process.HasExited){
Write-Host "..."
}
Write-Host $process.HasExited
And it looks like the process does run for maybe a couple milliseconds before it exits.
I'm thinking this may have something to do with how Firefox handles it's own processes. Because I tested a similar setup with some other random apps and they all worked as expected.
Any ideas on how to work around this when it comes to Firefox?
There are several challenges to overcome:
The firefox process that ends up presenting the actual browser window is different from the one that is initially launched. That is, as you've observed, the launched process spawns other processes and itself exits quickly.
As Olaf points out, modern browsers typically launch multiple non-transient processes, so the challenge is how to identify the one that represent the browser window.
Browsers may reuse existing processes, so a single process can present multiple windows / tabs, and closing one of them won't terminate the process as a whole.
If you need to ensure that a dedicated, new process is used, you have two options:
(a) Make sure that no preexisting Firefox instance is running, either by erroring out, or - if acceptable for your use case - by forcefully terminating all existing instances first (Stop-Process -Name firefox).
(b) With significantly more effort, create a dedicated, temporary Firefox profile that you can launch with the -new-instance option, which allows multiple independent Firefox instances to run concurrently and whose lifetime can be tracked separately.
The following - cumbersome - solution implements option (b):
If no firefox process is found, there is no concern about creating independent instances, and Firefox can be launched normally.
Otherwise, a temporary profile is created, and launched via the -new-instance and -profile options to ensure that a new process will be used to present the new browser window.
After launching the initial process, loop until a firefox process appears that was launched later and has a nonempty window title, which is then presumed to be the real process of interest.
You can then wait for the termination of this process to know when the dedicated browser window has been closed. If a temporary profile had to be created, it is cleaned up afterwards.
# Comment this statement out to silence the verbose messages below.
$VerbosePreference = 'Continue'
$now = Get-Date
$url = 'https://example.org' # URL to open.
# Launch a (new) Firefox instance.
if ($alreadyRunning = [bool] (Get-Process -ErrorAction Ignore firefox)) {
# Determine the path for a temporary profile with a unique name.
$tempProfilePath = Join-Path ([IO.Path]::GetTempPath()) ([datetime]::utcnow.tostring('o') -replace '\D')
Write-Verbose "Firefox is already running. Creating temp. profile $tempProfilePath..."
# Note: Creating an empty directory for the profile is seemingly enough.
$null = New-Item -Type Directory $tempProfilePath
Write-Verbose "and starting a new instance with it..."
Start-Process firefox "-new-instance -profile $tempProfilePath $url"
} else {
Write-Verbose 'Firefox isn''t running. Starting normally...'
Start-Process firefox $url
}
# Find the newly launched process that is the actual browser window.
Write-Verbose 'Waiting for a recently launched Firefox process with a nonempty window title to appear...'
while (-not (
$ps = Get-Process firefox |
Where-Object StartTime -gt $now |
Where-Object MainWindowTitle
)) {
Write-Host -NoNewLine .
Start-Sleep -MilliSeconds 500
}
Write-Host
Write-Verbose "Found. Waiting for process to exit..."
$ps.WaitForExit()
Write-Verbose 'Process has exited.'
if ($alreadyRunning) {
Write-Verbose "Cleaning up temporary profile $tempProfilePath..."
do {
# The profile dir. is typically held on to for a little while longer by associated processes that may not have terminated yet.
Start-Sleep -MilliSeconds 200
Remove-Item -ErrorAction Ignore -Literalpath $tempProfilePath -Recurse -Force
}
while (Test-Path -LiteralPath $tempProfilePath)
}
Thanks to #mklement0 work, in your case you can use the parent Process ID.
I use WMI to get the parent process, but it works for the very first launch.
$parentProcess = Start-Process 'C:\Program Files\Mozilla Firefox\firefox.exe' -argumentlist "-new-window https://google.com -foreground" -PassThru
$childProcess = get-process -id $(Get-CimInstance -Class Win32_Process -Filter "Name = 'firefox.exe'" | where {$_.ParentProcessId -eq $parentProcess.id}).ProcessId
# effectively stop the child
$childProcess | Stop-Process

Is there a way to find which user run what application on a server using Powershell

I am trying to find a way to find out who has ran an application (for example SQL) on a server, just to get some idea.
I tried Get-Process but this doesn't give me historic information, I want to get historical information
Get-Process -IncludeUserName *
what I want the return resule is "name of application", "user who ran it" and the last datetime it was ran by that user'
As for ...
I am trying to find a way to find out who has ran an application (for
example SQL) on a server, just to get some idea.
What you are asking for here is software metering.
SQL is a service that is always running once it is installed, so, no individual user is ever going to be running it. So, that is a bad example. MS Word for example would be a better example.
Yet there is nothing native in PowerShell that does this, software metering, but of course PowerShell can look at event logs. Yet if your auditing is not setup correctly then it's moot. This is better for a software metering tool, and there are several out there. So, why try and reinvent the wheel.
As for ...
I tried Get-Process but this doesn't give me historic information, I
want to get historical information
That is not what a process is nor what Get-Process is for. It, Get-Process only checks for and lists whatever process is currently running, regardless of what/who launched it.
As for...
what I want the return resule is "name of application", "user who ran
it" and the last datetime it was ran by that user'
As long as the process is running, you can get this, with that cmdlet.
However, what are you trying to accomplish by this?
Again, there are purpose built tools to meter software use.
https://learn.microsoft.com/en-us/sccm/apps/deploy-use/monitor-app-usage-with-software-metering
If you must go down this reinvent the wheel road, using scripting, then you need a task watcher on the target machines, which watches for the WinWord process to appear.
Get-Process -IncludeUserName |
Where ProcessName -EQ 'Winword'
... you then write those results to a file or database or your own event log each time you see that process.
Use PowerShell to Create and to Use a New Event Log
New-EventLog -LogName ScriptingGuys -Source scripts
When the command runs, no output appears to the Windows PowerShell console. To ensure the command actually created a new event log, I use
the Get-EventLog cmdlet with the –List parameter. Here is the command
and the associated output.
Write-EventLog -LogName ScriptingGuys -Source scripts -Message “Dude, it works … COOL!” -EventId 0 -EntryType information
Or just to a file
Get-Process -IncludeUserName |
Where ProcessName -EQ 'Winword' |
Select-Object -Property Name, StartTime, Username |
Export-Csv -Path 'F:\Temp\AppLaunchLog.csv' -Append
Import-Csv -Path 'F:\Temp\AppLaunchLog.csv'
# Results
Name StartTime UserName
---- --------- --------
WINWORD 5/23/2019 9:02:53 PM WS01\LabUser001

ProcDump to catch high cpu of IIS website, need site name

I am using ProcDump to catch high cpu on server. I suspect its cause by one of the iis websites.
$processes = Get-Process -Name w3wp
foreach ($process in $processes) { D:\Server_Tools\Procdump\procdump.exe -c 30 -s 15 -ma $process.ID }
From what I understated, the code I have will triggers a dump if cpu for any w3wp process hit 30%.
My issue is that from current dump file I can not know which IIs website casued the issue.
How can I add the application pool name or something that will help me pin point the issue?
Thanks

How to pull a range of failed services from a remote server after a reboot

Caveat: Without spiking the cpu while a Get-WmiObject call parses the whole event log to match my specified filter.
Situation: I am working on a script that remotely runs some checks, then reboots a pc. I want it to check the health once the server reboots (after sleeping for some time) to make sure services that were supposed to start did. I've been running into "Automatic" services that start and then shut down (as intended) but then my current version picks them up as failed if they've already run. It was suggested that I check the event log for "Service Control Manager" errors, and report on those, the only problem now is that with the below script, we have servers who's event log can range anywhere from 20K to several hundred thousand events, and on a 2k server with 20K, this takes roughly 20 seconds to complete, and the cpu pegs near 100% while it's running.
I'm still learning powershell/wmi, so any advice would be appreciated.
function Check_Startup_Events {
BEGIN {
$time = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime((Get-Date).AddMinutes(-15))
}
PROCESS {
$results = Get-WmiObject Win32_NTLogEvent -computername $_ -Filter "LogFile='System' and SourceName='Service Control Manager' and TimeGenerated>='$time' and EventType=1" |
Format-Table -Autosize EventCode, Message
$results
}
}
$results = Get-EventLog -ComputerName w2kserver -LogName System -After $time
foreach ($result in $results){
if ($result.Source -eq "Service Control Manager" -and $result.EntryType -eq "Error"){
Write-Host $_.Description}}
I ran this against a 60k big event log on a W2K server in our environment. It takes a while to run but runs locally and does not tax the server. Not sure how you would want to output the data but I think Get-EventLog will do what you want.