HOWTO: Run Process Monitor on a Remote Machine from the Command Line

I have a treat for you today.  I have finally solved something that has been a pain in my side for years now.  Have you ever been in the following situation?

You are reviewing log files and discover that a remote computer (perhaps a virtual machine running on shared storage) is running wild and hammering on the disk.  You need to figure out what exactly what processes and files are causing that disk IO.  However perhaps that computer is always in use and you simply can’t log in locally to launch resource monitor or process monitor.  You need ultimately to run Process Monitor remotely.  Unfortunately you google this and discover that it’s not possible due to the amount of data that process monitor generates and can’t pass it all over the wire.  So what do you do?

I found myself in this exact situation yet again today and finally decided to sit down and solve it once and for all.  My googling revealed a suggestion in some forum to use psexec to run procmon.exe on the remote machine and then copy over the PML file to your machine for analysis.  I’m afraid I couldn’t find that blog post so I can’t give credit to the original author of the idea.  But there is a world of difference between an idea and a practical implementation and that’s what I have to share with you today.

Below is a PowerShell script that includes a function called Get-ProcMonData.  It accepts just two parameters, a -ComputerName for the name of the remote computer you wish to connect to and -Duration for how long procmon will run for on the remote system.  Note that the script is hardcoded to limit you to a maximum of 100 seconds as I discovered the hard way that Procmon generates an enormous amount of data and you can easily fill the remote drive if you’re not careful.

To run this script, I recommend opening it in the PowerShell ISE and editing the variables around line 34 to point to the path where you keep your psexec and procmon executables.  Once that’s done, go to the last line of the script and change “remotecomputerhere” to the computer you wish to collect data from and specify the duration anywhere from 10 to 100 seconds.

Note: This script was only tested in my environment and assumes you have full permissions to the target system.  In my case, I ran the script under a domain admin account.

Run the script.  Here’s what it’ll do:

  • Test to verify that the remote system responds to ping and that PowerShell can see procmon.exe and psexec.exe
  • Verify that both the source and target system have at least 500MB free
  • Copies procmon.exe to the c:\windows\temp folder on the remote system
  • Launches procmon.exe on the remote system (uses a seperate process so we can stop it later in the script)
  • Displays a progress bar for the duration specified while we wait for the data collection to complete
  • Stops procmon.exe on the remote system properly such that the generated PML file is valid
  • Copies the PML file to your local machine for analysis
  • Removes procmon.exe and the PML file from the remote machine (always clean up after yourself)
  • Displays the size of the PML file and reminds you to delete it when you’re done (They can get very large!)
  • Launches process monitor on your machine and opens the PML file for your analysis

 

There is still a lot I can do to clean this up and make it a more robust advanced function but at this point it’s working for me reliably so I’m going to call it.  I hope you find this useful!

 

# Use psexec to Connect to a Remote Machine and run Process Monitor to get detailed analysis of system activity
# DMS; psexec; report; remoting; Process Monitor; procmon;

Function Wait-ProgressBar([int]$Duration, [string]$Message)
{
    $TimeToWait = 1
    while($TimeToWait -lt $Duration) 
    {
        $Remaining = $Duration - $TimeToWait
        Write-Progress $Message -Status "$Remaining seconds remaining" -PercentComplete (($TimeToWait*100)/$Duration)
        start-sleep -Seconds 1
        $TimeToWait++
    }
}

Function Get-DiskSpace([string] $Computer)
{
        try { $HD = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -Filter "DeviceID='C:'" -ErrorAction Stop } 
        catch { Write-Warning "Unable to connect to $Computer. $_"; continue}
        if(!$HD) { throw }
        $FreeSpace = [math]::round(($HD.FreeSpace/1gb),2)
        return $FreeSpace
}

Function Get-ProcMonData
{
    Param(
        [Parameter(Mandatory=$true)][string]$ComputerName, 
        [Parameter(Mandatory=$true,
            HelpMessage="Enter a duration from 10 to 100 seconds (limited due to disk space requriements")]
            [ValidateRange(10,100)][int]$Duration
    )

    $Procmon = 'procmon.exe'
    $AdminShare = 'c$'
    $LocalDrive = 'c:'
    $psexecPath = "c:\bin\pstools\psexec.exe"
    $procmonPath = "c:\bin\$Procmon"
    $TargetFolder = "windows\temp"
    $LocalTarget = "c:\temp"
    
    write-host "Verifing that the target computer responds to ping and that we have psexec and Process monitor executables" -ForegroundColor Green
    
    if(Test-Path("$LocalTarget\$ComputerName.pml")) { try { Remove-Item "$LocalTarget\$ComputerName.pml" -ErrorAction Stop} catch { Write-Warning $_.Exception; break }}
    
    if (!(Test-Connection $ComputerName -ErrorAction Stop)) { Write-Warning "Cannot ping $ComputerName"; break } 
    if (!(Test-Path $psexecPath -ErrorAction Stop)) { Write-Warning "Cannot find $psexecPath"; break }
    if (!(Test-Path $procmonPath -ErrorAction Stop)) { Write-Warning "Cannot find $procmonPath"; break }

    # Process monitor generates enormous amounts of data.  
    # To try and offer some protections, the script won't run if the source or target have less than 500MB free
    write-host "Verifying free diskspace on source and target." -ForegroundColor Green
    if((Get-DiskSpace $ComputerName) -lt 0.5) 
        { Write-Warning "$ComputerName has less than 500MB free and thus process monitor could fill the disk."; break }

    if((Get-DiskSpace $Env:ComputerName) -lt 0.5) 
        { Write-Warning "Local computer has less than 500MB free and thus process monitor could fill the disk."; break }

    Write-Host "Copying Process monitor to the target system temporarily so we can execute it locally with psexec." -ForegroundColor Green
    try { Copy-Item $procmonPath "\\$ComputerName\$AdminShare\$TargetFolder" -Force -Verbose -ErrorAction Stop } catch { Write-Warning $_.Exception; break }

    # Process monitor must be launched as a separate process otherwise the sleep and terminate commands below would never execute and fill the disk
    Write-Host "Starting process monitor on $ComputerName" -ForegroundColor Green
    Start-Process -FilePath "psexec.exe" -ArgumentList "-s -i 0 \\$ComputerName $LocalDrive\$TargetFolder\$Procmon /backingfile /accepteula $LocalDrive\$TargetFolder\$ComputerName /quiet" -PassThru | Out-null
    
    Wait-ProgressBar -Duration $Duration -Message "Waiting for procmon data collection to complete"

    Write-Host "Terminating process monitor process on $ComputerName" -ForegroundColor Green
    $Discard = & $psexecPath -s -i 0 \\$ComputerName "$LocalDrive\$TargetFolder\$Procmon" /terminate 2>&1; $Output | select -skip 1 | out-string

    Write-Host "Copy process monitor file to local machine for analysis" -ForegroundColor Green
    try { Copy-Item "\\$ComputerName\$AdminShare\$TargetFolder\$ComputerName.pml" $LocalTarget -Force -Verbose -ErrorAction Stop }
    catch { $_ ; }

    Write-Host "Remove temporary process monitor executable as well as log file from target system" -ForegroundColor Green
    Remove-Item "\\$ComputerName\$AdminShare\$TargetFolder\$ComputerName.pml" -Verbose -Force
    Remove-Item "\\$ComputerName\$AdminShare\$TargetFolder\$Procmon" -Verbose -Force

    Write-host "Launching Process Monitor and loading collected log data" -ForegroundColor Green
    if(Test-Path("$LocalTarget\$ComputerName.pml")) { & $procmonPath /openlog $LocalTarget\$ComputerName.pml }

    $FileSize = [math]::round(((Get-Item "$LocalTarget\$ComputerName.pml").Length/1mb),2)    
    Write-Warning "$LocalTarget\$ComputerName.pml is $FileSize MB. Remember to delete it when finished." 
}

Get-ProcmonData -ComputerName remotecomputerhere -Duration 20

 

2 comments

    • Ric Trujillo on January 3, 2018 at 9:35 pm
    • Reply

    Hello,

    I am trying to use your script (excellent) but am running to an issue: for $Procmon = ‘procmon.exe’, what is the “$” preceding Procmon?

    • Ninar on July 17, 2018 at 11:40 pm
    • Reply

    Great script Robbie! – Using some of the logic in your script I have written a tool that automates procmon captures on remote computers.
    Check it out here: https://ninaronline.com/2018/07/16/automated-process-monitor-captures-remote-machines-using-powershell/

    Features

    – The tool can run from anywhere – the tool will copy the supporting files to your local machine and to the remote machine.
    – Capture indefinitely (until the machine dies – relevant in some VDI troubleshooting scenarios) or for limited number of captures
    -Stop capturing when the capture file size hits a pre-defined limit that you set. The script will copy the capture from the remote machine to your local machine before starting a new capture.
    -You can stop capturing by pressing and holding on F12.
    – Capture only what you are looking for: per-configured capturing configs give you the option to either capture
    Read/Writes to disk
    Registry Keys
    Everything
    – Live monitoring of the remote machine disk space while capturing (optional).

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.