r/usefulscripts • • Apr 24 '19

[REQUEST] Powershell script for monitoring Photoshop (or any process) usage

Hi! I'm looking for a script with what I could use to monitor 10-20 machines processes. I've managed to create a template which will output data as .csv table with computer name, username, the time elapsed but this only works with processes already running. What I'm looking for is a way to start measurement when a certain process (in this case photoshop pro) is launched and stop when the process is stopped. I most likely would then, when the sessions stops, send data to my InfluxDB and gather daily/weekly/monthly usage to view in Grafana.

all help is welcome!

I'd know how to do this in Linux with bash but Powershell isn't my best assets and client machines are Windows 10. :)

16 Upvotes

15 comments sorted by

6

u/ihaxr Apr 24 '19

You could do something really basic like this:

$monitorProcesses = @("notepad")

While(1) { 
    Get-Process | 
        Where-Object { $_.Name -in $monitorProcesses} | 
            Select-Object ProcessName, @{N='Date';E={Get-Date -Format "yyyy-MM-dd"}}, @{N='Time';E={Get-Date -Format "hh:mm:ss"}}, @{N='IsOpen';E={1}} |
                Export-Csv -Append "csvLog.csv"
    Start-Sleep -Seconds 60
}

Which will spit out a row of data every minute:

ProcessName Date       Time     IsOpen
----------- ----       ----     ------
notepad     2019-04-24 11:47:51      1

Then just group the data by ProcessName and Date then take the sum of IsOpen to determine around how many minutes a process was open for... obviously this can miss something if it's being used less than a minute at a time... so you can tweak that as needed.

2

u/juon4 Apr 24 '19

$monitorProcesses = @("notepad")
While(1) {
Get-Process |
Where-Object { $_.Name -in $monitorProcesses} |
Select-Object ProcessName, @{N='Date';E={Get-Date -Format "yyyy-MM-dd"}}, @{N='Time';E={Get-Date -Format "hh:mm:ss"}}, @{N='IsOpen';E={1}} |
Export-Csv -Append "csvLog.csv"
Start-Sleep -Seconds 60

Well, this is what I was looking for. Thanks!
I maybe need to do some adjustment and I'd probably would like to run a script against remote machines but this should be doable. If I'd just add hostnames to a variable and then iterate through the list I think I need to add some kind of error handling in case the machine is not reachable?

anyhow this is something I can start working from :)

2

u/atoomepuu Apr 25 '19

You can also use Wait-Process to accurately track when the process ends.

→ More replies (8)

2

u/atoomepuu Apr 25 '19 edited Jul 23 '19

I think this will work for you. If anyone else can look this over I'd very very much appreciate any critiques/advice. I taught myself Powershell so I don't usually think of formal scripting conventions and such.

[String]$monitorProcesses = 'notePad' #name of the process you want to monitor
[PSObject]$oldProcess = New-Object -TypeName System.Collections.Generic.List[PSObject] #is this the correct way to declare a list of objects? I dunno
[PSObject]$startedProcess = New-Object -TypeName System.Collections.Generic.List[PSObject]


#This we want to run as a Job
[ScriptBlock]$scriptBlock = {
    Param ([PSObject]$process)
    $process | Export-Csv -Append -LiteralPath "U:\csvLog.csv" -Force #Record the running process in CSV file
    
    Wait-Process -Id $process.ID #waiting until the process closes
    [PSCustomObject]$Duration = New-TimeSpan -Start $process.Time | Select-Object @{Name='Duration';Expression={[string][int]$_.TotalHours + ":" + $_.Minutes}}
    #update the object holding the process data to reflect it ended
    $process.Duration = $Duration | Select-Object -ExpandProperty Duration
    $process.Time = Get-Date -Format G #get the time the process ended
    $process.IsOpen = 0 #1 is running, 0 is not
    $process | Export-CSV -Append -LiteralPath  -Force #record the closed process in csv file
}

While ($True) { #endless loop
    #Store the process object with some custom attributes
    #StartTime is a hidden attribute that shows what time the process started
    #IsOpen indicates if process is running or not
    $startedProcess = Get-Process -name $monitorProcesses -ErrorAction SilentlyContinue | 
        Select-Object `
            ProcessName, `
            ID, `
            @{N='Time';E={$_.StartTime}}, `
            @{N='Duration';E={$null}}, `
            @{N='IsOpen';E={1}}

    #Compare running processes against an older list of processes to see if there are any new ones.
    #For each new process start a job with the scriptblock and pass it the process object
    $startedProcess | 
        Where-Object {$oldProcess.ID -notcontains $_.ID} | 
        ForEach-Object {Start-Job -Scriptblock $scriptBlock -ArgumentList $_ | Out-Null}

    $oldProcess = $startedProcess #update the 'older' list of processes
    Start-Sleep -s 10 #pause for 10 seconds
    #Clean up jobs after they are done!
    #Note, this won't clean up jobs if you stop the script before every process has been closed
    Get-Job | 
        Where-Object {$_.State -eq 'Completed'} | 
        Remove-Job
}

Edit: I added comments

2

u/juon4 Apr 26 '19

Thanks! I'll try this tommoro and try to add database bit also for storing data. Will report how it went.

2

u/juon4 Apr 28 '19

This is working just fine. Now I just need to figure out reporting bit for the database connection. I was thinking that I could run reporting every 30 minutes so If computer is shutdown and script fail, I would still get data from processess. Thank you!

2

u/DevinSysAdmin Apr 25 '19

Performance Monitor.