Get Task Manager list of Apps with PowerShell

Over the past couple of years I’ve been impressed by a series of small improvements to the Task Manager which have made it pretty great to use.

I recently noticed that you if you right click the column titles in the Processes tab and tick all the boxes, you rarely have to venture to the Details tab. (The most valuable column to add, in my opinion, is Command line.)

The Processes tab also attempts to lump items into a few categories: Apps, Background processes, Windows processes.

How does it do this?

Luckily, Raymond Chen briefly explains what’s going on in a blog post from 2017.

To take Apps as an example: If the process has a visible window, then Task Manager calls it an “App”

Can we do something similar with PowerShell?

Probably. Kinda.

Here’s my attempt:

Get-Process | Where-Object {$_.MainWindowTitle} | Select-Object Description

And the result:

You can see that I get processes that have a MainWindowTitle and display the process Description.

The results are similar but not identical: PowerShell shows some bits of Windows internals that are displayed elsewhere in Task Manager.

Can you get any closer?

Tip: avoid Wait-Debugger gotcha on Azure Functions

My biggest gotcha with Azure Functions is that you need to put Wait-Debugger in your script for local debugging, and it’s easy to forget to remove it when you deploy to Azure.

My last post talked about exploring Azure Functions’ environment, and I mentioned that you could compare the cloud version with its locally-run approximation.

Well, when you’re running your function locally, the variable $env:AZURE_FUNCTIONS_ENVIRONMENT is set to Development.

This means you can ensure that you never leave a function hanging at Wait-Debugger in the cloud by wrapping it like this:

if ($env:AZURE_FUNCTIONS_ENVIRONMENT -eq 'Development') {
    Wait-Debugger
}

Another solution is to wrap your debug command like this:

if ($Request.Query.Debug -eq 'True') {
    Wait-Debugger
}

And then invoke your function with &Debug=True when you want to debug it.

How do you handle this problem?

Explore Azure Function’s PowerShell environment

Want to know more about the environment that your Azure Functions script is running in?

Here’s a little script I put together for this month’s Minneapolis PowerShell User Group:

# d_ExploreAzureEnvironment

# Script to explore the Azure Functions PowerShell environment
# by running commands from a menu and returning their output
# as a string.

using namespace System.Net

param($Request, $TriggerMetadata)

$itemToRun = $Request.Query.Run

$runResult = switch ($itemToRun) {
    0 { Get-ChildItem env: }
    1 { $PSVersionTable }
    2 { Get-Variable }
    3 { Get-Process }
}
if ($runResult) {
    $status = [HttpStatusCode]::OK
    $body   = $runResult | Out-String
}
else {
    $status = [HttpStatusCode]::BadRequest
    $body   = 'Supply a parameter of Run with a value between 0 and 3'
}

Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = $status
    Body       = $body
})

(code is also on GitHub)

As you can see, the script contains a menu of commands that you can select by supplying a parameter of Run with a value between 0 and 3.

The selected command is executed, its output is stored in $runResult, and $runResult is converted to a string, then returned in the HTTP response body.

You can control the whole thing in your web browser like so:

I’ve included a few commands to examine system and PowerShell variables and processes. Adding a command is as simple as this:

You can also use this script to compare the local debug environment provided by Azure Functions Core Tools with Microsoft’s hosted version (a bit more on this soon…)

It would be trivial to extend this concept and make a script that executes arbitrary commands. But it feels like a bad idea, so I restrained myself.

What other commands would you like to try out? How’s your exploration of Azure Functions going? The race to be first commenter continues!

Resize browser window with PowerShell

I like to make little videos to go with my blog posts, and today I’ve been looking for a way to resize my browser window to a consistent size for recording.

Google immediately turned up this little beauty in the TechNet Script Center:

But when I tried it out:

Set-Window -ProcessName msedge -Width 1024 -Height 768

…I got a scary error:

Cannot convert argument "hWnd", with value: "System.Object[]", for "GetWindowRect" to type "System.IntPtr": "Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.IntPtr"."
At C:\Git\PowerShell\Set-Window.ps1:91 char:9
+         $Return = [Window]::GetWindowRect($Handle,[ref]$Rectangle)
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Here are the relevant lines from the function:

$Handle = (Get-Process -Name $ProcessName).MainWindowHandle
$Return = [Window]::GetWindowRect($Handle,[ref]$Rectangle)

A bit of exploring shows that one of the msedge processes is very different from the others:

$msedge = Get-Process msedge

$msedge

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     10     1.77       2.64       0.03     904   1 msedge
     19    17.69      30.85       0.34    4044   1 msedge
     57   128.71     150.07      57.81    5000   1 msedge
     83   185.82     182.69      38.77    8840   1 msedge
     29    47.08      75.16       9.78    9548   1 msedge
     83    82.12     128.55     171.92   11304   1 msedge
     22    27.96      41.45       0.41   11596   1 msedge
     20    21.31      38.14       2.47   12664   1 msedge
     49   303.89     156.82     116.14   12828   1 msedge
     24    29.71      48.62       1.52   14788   1 msedge
     26    21.57      31.14      25.20   15904   1 msedge
     56   132.80     157.91      76.39   15976   1 msedge
     20    19.50      33.82       0.27   16360   1 msedge
     16    11.88      19.46       0.05   16416   1 msedge
     34    59.82      89.91       3.55   17300   1 msedge
     29    48.37      76.49       3.34   17884   1 msedge
     22    25.07      42.07       0.41   18408   1 msedge
     27    51.70      78.73       5.69   18492   1 msedge
     26    39.59      60.88       0.81   18520   1 msedge
     28    54.87      73.89      18.56   19080   1 msedge
     47   108.27     135.33      26.95   19888   1 msedge

$msedge.MainWindowHandle

0
0
0
0
0
4326398
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

This was my first solution, which works fine:

# Only get non-zero handles
$Handle = (Get-Process -Name $ProcessName).MainWindowHandle | Where-Object {$_.ToInt32() -gt 0}
# The Handles have type IntPtr. Without .ToInt32() you get this error:
# Cannot compare "0" because it is not IComparable

But after a bit of poking around, I think this is a little better:

# Only get process whose parent is explorer
$Handle = (Get-Process -Name $ProcessName | Where-Object {$_.Parent.ProcessName -eq 'explorer'}).MainWindowHandle 

PowerShell Azure Functions dependency on PowerShell Gallery

I started writing a blog post today about hooking PowerShell Azure Functions into PowerApps.

But I couldn’t get even the simplest function to work – every query resulted in 500 (Internal Server Error).

Of course, I spent ages looking for a stupid mistake that I’d made. (And I found one: my code containted Wait-Debugger – this is still my biggest Azure Functions gotcha.)

Then I put two and two together. The PowerShell Gallery is down today, and this has broken a bunch of people’s CI/CD pipelines.

This is the error that I see in the Azure Functions portal:

Result: Failure
Exception: Fail to install function app dependencies.
Restarting the app may resolve the error.
Error: 'A connection attempt failed because the 
connected party did not properly respond after a period 
of time, or established connection failed because 
connected host has failed to respond A connection attempt
failed because the connected party did not properly respond 
after a period of time, or established connection failed 
because connected host has failed to respond'
Stack:
at Microsoft.Azure.Functions.PowerShellWorker.
DependencyManagement.DependencyManager.Initialize
(FunctionLoadRequest request) 
in C:\projects\azure-functions-powershell-worker\src\
DependencyManagement\DependencyManager.cs:line 179
at Microsoft.Azure.Functions.PowerShellWorker.
RequestProcessor.ProcessFunctionLoadRequest
(StreamingMessage request)
in C:\projects\azure-functions-powershell-worker\src\
RequestProcessor.cs:line 169

And I was able to fix my Function by commenting out the Az line in my local project’s requirements.psd1 and pushing the changes to Azure:

# This file enables modules to be automatically managed by the Functions service.
# Only the Azure Az module is supported in preview.
# See https://aka.ms/functionsmanageddependency for additional information.
#
@{
    #'Az' = '1.*'
}

Needless to say, PowerShell Azure Functions is in Public Preview, and it’s expected that things will sometimes not work.

But I expect dependencies on PowerShell Gallery in the wider ecosystem will get a lot of attention after today!