PowerShell: Monitor a Program Wizard for Its Task Completion

Once upon a time in the realm of SysAdmin endless green pastures, there were rampant wizards that would appear and vanish from thin air without any traces. It was a dark time for Technicians as they make daily deliverable by summoning these wizards. Those Admins would cast various spells to invite those supernatural beings. Frequently, those magicians would emerge to help Techies perform essential rites as they were bound by the law of their creators (aka Devs). However, it was unpredictable when a magician would initiate his liturgy or when he would conclude his enchantments. Thus, many SysAdmins would sit and watch the grass grows while waiting for the right moment of ceremonial conclusion to proceed to attend other duties.

Then, out of the summoner’s bag of tricks, there was this little spell that would enable SysAdmins to monitor the wizard’s mana (aka CPU) consumption during each sacrament. This would enable Technicians to determine the when when and whereabouts a wizard would appear. The land of slow-growing grassland had been enlightened ever so slightly after this new discovery.

Shilling aside, this stuff is really basic. The idea is to check a Windows process for CPU utilization. Logically, when a program is active, it would continue to seize resources. As soon as it has completed its functions, such accumulation would be released by a cleanup routine, or garbage collection. The script below could use some improvement to become more generalized. Ideally, this program watcher should be expanded in scope to gather information about all processes on a system to trace spawns from the original executable. For instance, recent versions of Dynamics CRM would run within the framework of Microsoft Management Console. Upon triggering certain wizards (e.g. Create New Org), new service objects would be called. In which case, a new memory space would be allocated to a child thread. Thus, future iteration of this script should take into account the parent application and trace its offspring as a more comprehensive monitoring utility that would be applicable to manage a wider varieties of programs.

function programCompletionWatcher{
    param(
        $targetMachine=$env:computername,
        $exeName='Microsoft.Crm.MultiTenantPackageDeploymentExecutor', #$exeName='mmc'
        $credential
        )
    $wizardWatcherClock=[System.Diagnostics.Stopwatch]::StartNew()
    if($credential){
        $destinationAppServerSession=New-PSSession $targetMachine -Credential $credential -SessionOption $(new-pssessionoption -IncludePortInSPN)
    }else{
        $destinationAppServerSession=New-PSSession $targetMachine -SessionOption $(new-pssessionoption -IncludePortInSPN)
        }

    if($destinationAppServerSession){      
        $checkCPU="Invoke-Command -Session `$destinationAppServerSession {return (Get-Process '$exeName' -EA SilentlyContinue).CPU}"        
        write-host "Checking CPU Usage..."
        $sameMeasurementCount=0
        $completionMarkerCount=10
        $almostComplete=$false
        $completionMarker=$false
        $previousCpuConsumption=0
        While (!$completionMarker) {            
            $currentCpuConsumption=invoke-expression $checkCPU -ea SilentlyContinue
            write-host $currentCpuConsumption
            $cpuConsumptionChanged=$currentCpuConsumption -gt $previousCpuConsumption
            $exeStarted=$currentCpuConsumption -ne $null
            if(!$exeStarted){
                write-host "$exeName has not started." -NoNewline
                }
            elseif(!$cpuConsumptionChanged){                               
                if ($sameMeasurementCount++ -ge $completionMarkerCount){
                    $almostComplete=$true
                    Write-Host "Almost complete: " -ForegroundColor Yellow -NoNewline
                    }                                
            }elseif($cpuConsumptionChanged -and $almostComplete){
                write-host "Completion marker reached!" -ForegroundColor Green -NoNewline
                $completionMarker=$true
                break
                }
            sleep -Seconds 10
            $previousCpuConsumption=$currentCpuConsumption
            }
        Remove-PSSession $destinationAppServerSession
        $minutesElapsed=$wizardWatcherClock.Elapsed.TotalMinutes
        return $minutesElapsed
    }else{
        write-warning "Unable to open a WinRM session to $targetMachine.`r`nPlease monitor it's progress manually."
        return $false
        }
}

Sample Output:

Leave a Reply

Your email address will not be published. Required fields are marked *