Behind the PowerShell Pipeline logo

Behind the PowerShell Pipeline

Archives
February 24, 2026

Resolving The Registry Challenge

In this issue:

  • Using Registry PSDrives
    • Get-LocalAutoRun
  • Formatting the Output
  • Using Win32_Registry
    • Get-AutoRun
  • Summary

At the end of last month, I gave you, what I thought would be, a relatively simple PowerShell scripting challenge. In the Windows registry, you can find a list of applications scheduled to run automatically. You can find computer-specific applications under HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run. There is a similar key for user-specific applications under HKCU:.

I wanted you to write a PowerShell function to query these locations and write a custom object to the pipeline with the following properties:

  • The application or program name
  • The application or program command line
  • The registry key path
  • Scope (User or Machine based on the registry hive)
  • UserName
  • ComputerName

I hope found some time to work on a solution. As with all of these challenges, the benefit is is the process, not necessarily the final solution.

Using Registry PSDrives

The simplest approach is to use the existing registry PSDrives with native commands. This approach is straightforward and leverages PowerShell's built-in cmdlets.

PS C:\> Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'


SecurityHealth              : C:\WINDOWS\system32\SecurityHealthSystray.exe
RtkAudUService              : "C:\WINDOWS\System32\DriverStore\FileRepository\realtekservice.inf_amd64_e80fb71
                              73daab733\RtkAudUService64.exe" -background
Logitech Download Assistant : C:\Windows\system32\rundll32.exe C:\Windows\System32\LogiLDA.dll,LogiFetch
LogiOptions                 : C:\Program Files\Logitech\LogiOptions\LogiOptions.exe /noui
DYMOWebApi                  : "C:\Program Files (x86)\DYMO\DYMO Connect\DYMO.WebApi.Win.Host.exe" /auto
DymoOfficeHelper            : "C:\Program Files (x86)\DYMO\DYMO Connect\DYMO.OfficeHelper.exe" /w
Logi Tune                   : "C:\Program Files\Logitech\LogiTune\LogiTune.exe" --tray
1Password                   : C:\Program Files\1Password\app\8\1Password.exe --auto-start
PSPath                      : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Window
                              s\CurrentVersion\Run
PSParentPath                : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Window
                              s\CurrentVersion
PSChildName                 : Run
PSDrive                     : HKLM
PSProvider                  : Microsoft.PowerShell.Core\Registry

Each application is a separate property. Here's a cleaner way to get just the application and its value.

$Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
$r = Get-Item $Path
$r.property | ForEach-Object {
    [PSCustomObject]@{
        RegistryPath = $Path
        Scope        = ($path -match '^HKLM:') ? 'Machine' : 'User'
        Name         = $_
        Command      = (Get-ItemPropertyValue -Path $Path -Name $_)
        Username     = "$($env:UserDomain)\$($env:UserName)"
        Computername = $env:COMPUTERNAME
    }
}

This gives output close to the desired format.

RegistryPath : HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Scope        : Machine
Name         : 1Password
Command      : C:\Program Files\1Password\app\8\1Password.exe --auto-start
Username     : PROSPERO\Jeff
Computername : PROSPERO

This would also work for user current user. This code uses a variation to create the custom object.

$Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
$r = Get-Item $Path

foreach ($property in $($r.property)) {
    [PSCustomObject]@{
        PSTypename   = 'RegRunInfo'
        RegistryPath = $Path
        Scope        = ($path -match '^HKLM:') ? 'Machine' : 'User'
        Name         = $property
        Command      = $r.GetValue($property)
        Username     = "$($env:UserDomain)\$($env:UserName)"
        Computername = $env:COMPUTERNAME
    }
}

I get the same type of output.

RegistryPath : HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Scope        : User
Name         : com.squirrel.slack.slack
Command      : "C:\Users\Jeff\AppData\Local\slack\slack.exe" --process-start-args --startup
Username     : PROSPERO\Jeff
Computername : PROSPERO

Get-LocalAutoRun

Now that I have proof-of-concept commands running in the console, I can wrap the code in a function. I always try to get the core logic and commands working in the console before I start writing a function. This way, I can focus on the logic and not worry about the function syntax. But now that I have this working, here's the function:

#requires -version 7.5

function Get-LocalAutoRun {
    [CmdletBinding()]
    [OutputType("RegRunInfo")]
    param()

    $Computername = $env:COMPUTERNAME

    #capture the date this information was gathered
    $Capture = Get-Date

    #query LocalMachine and CurrentUser using registry PSDrives
    $paths = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
    'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'

    foreach ($path in $paths) {
        Write-Verbose "Querying auto run entries under $Path on $computerName"
        $r = Get-Item $Path
        #filter out the empty (Default) property
        $properties = $r.property | where { $r.GetValue($_) }
        Write-Verbose "Found $($properties.Count) setting(s)"
        Write-Information $r -Tags data
        foreach ($property in $properties) {
            #Using the ScopeOwner property to indicate the "owner"
            [PSCustomObject]@{
                PSTypename   = 'RegRunInfo'
                RegistryPath = $Path
                Scope        = ($path -match '^HKLM:') ? 'Machine' : 'User'
                Name         = $property
                Command      = $r.GetValue($property)
                ScopeOwner   = ($path -match '^HKLM:') ? $env:COMPUTERNAME : "$($env:UserDomain)\$($env:UserName)"
                Computername = $env:COMPUTERNAME
                Date         = $Capture
            }
        } #foreach property
    } #foreach registry path
}

This code is designed to be run on the local computer. It is also written for PowerShell 7 since I am using the ternary operator.

PS C:\> $l = Get-localAutoRun
PS C:\> $l[0]

RegistryPath : HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Scope        : Machine
Name         : SecurityHealth
Command      : C:\WINDOWS\system32\SecurityHealthSystray.exe
ScopeOwner   : PROSPERO
Computername : PROSPERO
Date         : 2/24/2026 10:51:40 AM

PS C:\> $l[-1]

RegistryPath : HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Scope        : User
Name         : com.squirrel.slack.slack
Command      : "C:\Users\Jeff\AppData\Local\slack\slack.exe" --process-start-args --startup
ScopeOwner   : PROSPERO\Jeff
Computername : PROSPERO
Date         : 2/24/2026 10:51:40 AM

This function meets the requirements of the challenge. It queries both the machine and user auto run registry keys and outputs a custom object with the desired properties. I've enhanced the output a little to provide additional information and context.

Formatting the Output

What might be nicer, is to format the output.

PS C:\> $l | Format-Table -GroupBy RegistryPath -Property Scope,ScopeOwner,Name,Command

   RegistryPath: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

Scope   ScopeOwner Name                        Command
-----   ---------- ----                        -------
Machine PROSPERO   SecurityHealth              C:\WINDOWS\system32\SecurityHealthSystray.exe
Machine PROSPERO   RtkAudUService              "C:\WINDOWS\System32\DriverStore\FileRepository\realtekservice…
Machine PROSPERO   Logitech Download Assistant C:\Windows\system32\rundll32.exe C:\Windows\System32\LogiLDA.d…
Machine PROSPERO   LogiOptions                 C:\Program Files\Logitech\LogiOptions\LogiOptions.exe /noui
Machine PROSPERO   DYMOWebApi                  "C:\Program Files (x86)\DYMO\DYMO Connect\DYMO.WebApi.Win.Host…
Machine PROSPERO   DymoOfficeHelper            "C:\Program Files (x86)\DYMO\DYMO Connect\DYMO.OfficeHelper.ex…
Machine PROSPERO   Logi Tune                   "C:\Program Files\Logitech\LogiTune\LogiTune.exe" --tray
Machine PROSPERO   1Password                   C:\Program Files\1Password\app\8\1Password.exe --auto-start

   RegistryPath: HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

Scope ScopeOwner    Name                     Command
----- ----------    ----                     -------
User  PROSPERO\Jeff OneDrive                 "C:\Users\Jeff\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /b…
User  PROSPERO\Jeff GoogleDriveFS            "C:\Program Files\Google\Drive File Stream\121.0.1.0\GoogleDrive…
User  PROSPERO\Jeff BraveSoftware Update     "C:\Users\Jeff\AppData\Local\BraveSoftware\Update\1.3.361.151\Br…
User  PROSPERO\Jeff DYMOConnectLauncher      "C:\Program Files (x86)\DYMO\DYMO Connect\DYMOConnectLauncher.ex…
User  PROSPERO\Jeff WingetUI                 "C:\Users\Jeff\AppData\Local\Programs\UniGetUI\UniGetUI.exe" --d…
User  PROSPERO\Jeff com.squirrel.slack.slack "C:\Users\Jeff\AppData\Local\slack\slack.exe" --process-start-ar…

Naturally, I don't want to manually format this so I'll create a custom format file using my handy New-PSFormatXML function.

$l[0] | New-PSFormatXML -GroupBy RegistryPath -Properties Scope,ScopeOwner,Name,Command -Path c:\scripts\RegRunInfo.format.ps1xml

I'll even add an additional named table view.

$l[0] | New-PSFormatXML -GroupBy Scope -Properties ScopeOwner,Name,Command -Path c:\scripts\RegRunInfo.format.ps1xml -Append -ViewName scope

One change I made to the file is to convert the registry path in the default view.

<groupby>
<scriptblock>
<!-- Convert the RegistryPath to full name if not already in that format-->
    if ($_.RegistryPath -NotMatch 'HKEY') {
    $regPath = (Convert-Path $_.RegistryPath)
    }
    else {
    $regPath = $_.RegistryPath
    }
    "{0}\{1}" -f $_.Computername,$regPath
    </scriptblock>
<label>RegistryPath</label>
</groupby>

I'll load the format file by adding this line to the end of the script file.

Update-FormatData C:\scripts\RegRunInfo.format.ps1xml

Now I have a clean default display.`

PS C:\&gt; $l

   RegistryPath: PROSPERO\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

Scope   ScopeOwner Name                        Command
-----   ---------- ----                        -------
Machine PROSPERO   SecurityHealth              C:\WINDOWS\system32\SecurityHealthSystray.exe
Machine PROSPERO   RtkAudUService              "C:\WINDOWS\System32\DriverStore\FileRepository\realtekservice…
Machine PROSPERO   Logitech Download Assistant C:\Windows\system32\rundll32.exe C:\Windows\System32\LogiLDA.d…
Machine PROSPERO   LogiOptions                 C:\Program Files\Logitech\LogiOptions\LogiOptions.exe /noui
Machine PROSPERO   DYMOWebApi                  "C:\Program Files (x86)\DYMO\DYMO Connect\DYMO.WebApi.Win.Host…
Machine PROSPERO   DymoOfficeHelper            "C:\Program Files (x86)\DYMO\DYMO Connect\DYMO.OfficeHelper.ex…
Machine PROSPERO   Logi Tune                   "C:\Program Files\Logitech\LogiTune\LogiTune.exe" --tray
Machine PROSPERO   1Password                   C:\Program Files\1Password\app\8\1Password.exe --auto-start

   RegistryPath: PROSPERO\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

Scope ScopeOwner    Name                     Command
----- ----------    ----                     -------
User  PROSPERO\Jeff OneDrive                 "C:\Users\Jeff\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /b…
User  PROSPERO\Jeff GoogleDriveFS            "C:\Program Files\Google\Drive File Stream\121.0.1.0\GoogleDrive…
User  PROSPERO\Jeff BraveSoftware Update     "C:\Users\Jeff\AppData\Local\BraveSoftware\Update\1.3.361.151\Br…
User  PROSPERO\Jeff DYMOConnectLauncher      "C:\Program Files (x86)\DYMO\DYMO Connect\DYMOConnectLauncher.ex…
User  PROSPERO\Jeff WingetUI                 "C:\Users\Jeff\AppData\Local\Programs\UniGetUI\UniGetUI.exe" --d…
User  PROSPERO\Jeff com.squirrel.slack.slack "C:\Users\Jeff\AppData\Local\slack\slack.exe" --process-start-ar…
Want to read the full issue?
GitHub
Bluesky
LinkedIn
Mastodon
jdhitsolutions.github.io
Powered by Buttondown, the easiest way to start and grow your newsletter.