How to Enable User Installed Software Tracking in Windows Using PowerShell

Tracking software installations across endpoints is a critical task for IT professionals and Managed Service Providers (MSPs). With the growing need for visibility into user-level software installs—often missed by traditional system-wide scans—IT teams must ensure they have the right tooling in place. This is where PowerShell scripting proves invaluable. Today’s post walks through a custom PowerShell script designed to enable or disable tracking of software installed in the user context by configuring specific Windows registry keys tied to the NinjaOne agent.

Background

Most software inventory tools only track applications installed system-wide, typically under the Program Files directories. However, in many environments—especially in BYOD or remote setups—users install software in their own profile context. This presents a blind spot for asset management and security monitoring. The provided PowerShell script addresses this issue by enabling a registry-based flag that prompts the NinjaOne agent to monitor user-context installations as well.

For MSPs and IT teams managing distributed fleets, this script offers a lightweight, automated, and scalable approach to enhance software visibility and compliance without intrusive methods.

The Script

<#
.SYNOPSIS
    Adds or removes the necessary registry entries to enable Ninja to track software installed under the user context.
.DESCRIPTION
    Adds or removes the necessary registry entries to enable Ninja to track software installed under the user context.
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
    Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
.EXAMPLE
    (No Parameters)
    Enabling the tracking of software installed in the user context.
    Set Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\NinjaRMM LLC\NinjaRMMAgent\Agent\EnableUserSpecificAppMonitor to 1
    Successfully enabled the tracking of software installed in the user context.

PARAMETER: -Action "ReplaceMeWithDesiredAction"
    Specify whether or not to enable or disable the tracking of software installed under the user context.
    Valid actions include "Enable" and "Disable".

.NOTES
    Minimum OS Architecture Supported: Windows 10, Windows Server 2012
    Release Notes: Initial Release
#>

[CmdletBinding()]
param (
    [Parameter()]
    [String]$Action = "Enable"
)

begin {
    # If script form variables are used, replace the command line parameters with the form variable value.
    if ($env:action -and $env:action -notlike "null") { $Action = $env:action }

    # If $Action has been set, trim any leading or trailing whitespace.
    if ($Action) {
        $Action = $Action.Trim()
    }

    # If $Action is not defined after the previous steps, display an error message and exit with an error code.
    if (!$Action) {
        Write-Host -Object "[Error] You must specify an action (Enable or Disable)."
        exit 1
    }

    # Define a list of valid actions: "Enable" and "Disable".
    $ValidActions = "Enable", "Disable"

    # Check if the value of $Action is not in the list of valid actions.
    if ($ValidActions -notcontains $Action) {
        Write-Host -Object "[Error] The action '$Action' is invalid. 'Enable' or 'Disable' are the only valid actions."
        exit 1
    }

    function Set-RegKey {
        param (
            $Path,
            $Name,
            $Value,
            [ValidateSet("DWord", "QWord", "String", "ExpandedString", "Binary", "MultiString", "Unknown")]
            $PropertyType = "DWord"
        )
    
        # Check if the specified registry path exists
        if (!(Test-Path -Path $Path)) {
            try {
                # If the path does not exist, create it
                New-Item -Path $Path -Force -ErrorAction Stop | Out-Null
            }
            catch {
                # If there is an error creating the path, output an error message and exit
                Write-Host "[Error] Unable to create the registry path $Path for $Name. Please see the error below!"
                Write-Host "[Error] $($_.Exception.Message)"
                exit 1
            }
        }
    
        # Check if the registry key already exists at the specified path
        if (Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue) {
            # Retrieve the current value of the registry key
            $CurrentValue = (Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name
            try {
                # Update the registry key with the new value
                Set-ItemProperty -Path $Path -Name $Name -Value $Value -Force -Confirm:$false -ErrorAction Stop | Out-Null
            }
            catch {
                # If there is an error setting the key, output an error message and exit
                Write-Host "[Error] Unable to set registry key for $Name at $Path. Please see the error below!"
                Write-Host "[Error] $($_.Exception.Message)"
                exit 1
            }
            # Output the change made to the registry key
            Write-Host "$Path\$Name changed from $CurrentValue to $((Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name)"
        }
        else {
            try {
                # If the registry key does not exist, create it with the specified value and property type
                New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType $PropertyType -Force -Confirm:$false -ErrorAction Stop | Out-Null
            }
            catch {
                # If there is an error creating the key, output an error message and exit
                Write-Host "[Error] Unable to set registry key for $Name at $Path. Please see the error below!"
                Write-Host "[Error] $($_.Exception.Message)"
                exit 1
            }
            # Output the creation of the new registry key
            Write-Host "Set $Path\$Name to $((Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name)"
        }
    }

    function Test-IsElevated {
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        $p = New-Object System.Security.Principal.WindowsPrincipal($id)
        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    if (!$ExitCode) {
        $ExitCode = 0
    }
}
process {
    # Check if the script is being run with elevated (administrator) privileges.
    if (!(Test-IsElevated)) {
        Write-Host -Object "[Error] Access Denied. Please run with Administrator privileges."
        exit 1
    }

    # Check if the NinjaRMM registry paths exist.
    # If neither exists, display an error message and exit, as the registry paths are required.
    if (!(Test-Path -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\NinjaRMM LLC\NinjaRMMAgent\Agent" -ErrorAction SilentlyContinue) -and !(Test-Path -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\NinjaRMM LLC\NinjaRMMAgent\Agent" -ErrorAction SilentlyContinue)) {
        Write-Host -Object "[Error] The registry path 'HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\NinjaRMM LLC\NinjaRMMAgent\Agent' or 'HKEY_LOCAL_MACHINE\SOFTWARE\NinjaRMM LLC\NinjaRMMAgent\Agent' is expected to exist. You may need to reinstall the agent."
        exit 1
    }

    # Define the registry key name that controls the user-specific app monitoring.
    $AppMonitorRegistryKeyName = "EnableUserSpecificAppMonitor"

    # Define an array of registry paths where the NinjaRMM agent settings could be stored.
    # These paths include both the 64-bit and 32-bit registry paths.
    $NinjaAgentRegistryPaths = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\NinjaRMM LLC\NinjaRMMAgent\Agent", "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\NinjaRMM LLC\NinjaRMMAgent\Agent"

    # Determine the action (Enable or Disable) and output a message accordingly.
    switch ($Action) {
        "Enable" { 
            Write-Host -Object "Enabling the tracking of software installed in the user context." 
        }
        "Disable" {
            Write-Host -Object "Disabling the tracking of software installed in the user context."
        }
    }

    # Iterate over each possible registry path to configure the NinjaRMM agent settings.
    $NinjaAgentRegistryPaths | ForEach-Object {
        # Current registry path in the iteration.
        $NinjaAgentRegistryPath = $_

        # If the current registry path does not exist, skip it and move to the next one.
        if (!(Test-Path -Path $NinjaAgentRegistryPath -ErrorAction SilentlyContinue)) {
            return
        }

        # Retrieve the current value of the "EnableUserSpecificAppMonitor" registry key, if it exists.
        $UserSpecificMonitor = Get-ItemProperty -Path $NinjaAgentRegistryPath -Name $AppMonitorRegistryKeyName -ErrorAction SilentlyContinue | Select-Object -ExpandProperty $AppMonitorRegistryKeyName

        # Based on the action (Enable or Disable), perform the respective changes.
        switch ($Action) {
            "Enable" {
                # If the key is already set to "1" (enabled), inform the user and skip the rest.
                if ($UserSpecificMonitor -eq "1") {
                    Write-Host -Object "The user specific monitor is already enabled at path '$NinjaAgentRegistryPath\$AppMonitorRegistryKeyName'."
                    return
                }

                # Set the "EnableUserSpecificAppMonitor" registry key to "1" (enabled).
                Set-RegKey -Path $NinjaAgentRegistryPath -Name $AppMonitorRegistryKeyName -PropertyType "String" -Value "1"
                Write-Host -Object "Successfully enabled the tracking of software installed in the user context."
            }
            "Disable" {
                # If the key is not set or already disabled, inform the user and skip the rest.
                if (!$UserSpecificMonitor) {
                    Write-Host -Object "The user specific monitor is already disabled at path '$NinjaAgentRegistryPath\$AppMonitorRegistryKeyName'."
                    return
                }

                # Attempt to remove the "EnableUserSpecificAppMonitor" registry key.
                try {
                    Remove-ItemProperty -Path $NinjaAgentRegistryPath -Name $AppMonitorRegistryKeyName -ErrorAction Stop
                    Write-Host -Object "$NinjaAgentRegistryPath\$AppMonitorRegistryKeyName was successfully removed."
                }
                catch {
                    # If the removal fails, display an error message and exit with an error code.
                    Write-Host -Object "[Error] Failed to remove '$AppMonitorRegistryKeyName' from '$NinjaAgentRegistryPath'"
                    Write-Host -Object "[Error] $($_.Exception.Message)"
                    exit 1
                }
            }
        }
    }

    # Exit the script with the final exit code.
    exit $ExitCode
}
end {
    
    
    
}

 

Detailed Breakdown

At a high level, the script toggles a specific registry key under the NinjaOne agent configuration to enable or disable user-specific application monitoring. Below is a step-by-step breakdown:

1. Parameter Handling

The script accepts an optional -Action parameter, defaulting to "Enable" if not provided. It also reads from the environment variable action to support script form variables.

2. Input Validation

It verifies that the action is either "Enable" or "Disable". If not, it exits with a descriptive error.

3. Privilege Check

A function Test-IsElevated checks if the script is being run with administrative rights, which is required to edit registry keys under HKEY_LOCAL_MACHINE.

4. Registry Path Validation

The script checks for the presence of NinjaOne agent registry paths:

  • HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\NinjaRMM LLC\NinjaRMMAgent\Agent
  • HKEY_LOCAL_MACHINE\SOFTWARE\NinjaRMM LLC\NinjaRMMAgent\Agent

5. Action Execution

Based on the action:

  • Enable: Sets the EnableUserSpecificAppMonitor key to "1" if not already set.
  • Disable: Deletes the key if present.

6. Custom Function

A robust helper function Set-RegKey ensures registry keys are created or updated safely, with proper error handling and feedback.

The script concludes by exiting with a relevant code based on success or failure, ensuring integration compatibility with remote automation tools like NinjaOne.

Potential Use Cases

Case Study: Remote Work Compliance in a Hybrid Environment

An MSP managing 500 laptops across various client locations receives a request to audit all installed applications—especially those installed without admin privileges. By deploying this script via NinjaOne’s automation engine, the MSP enables user-specific software tracking across the board. Within hours, previously invisible applications like user-installed browsers or development tools are now part of the asset inventory, enabling informed security decisions and licensing compliance.

Comparisons

Manual Registry Editing

Manually navigating to the registry and editing keys is error-prone, lacks scale, and requires direct access to each machine.

Group Policy (GPO)

While powerful, GPO doesn’t easily allow setting custom keys like those used by third-party tools such as NinjaOne.

PowerShell + NinjaOne

Combining PowerShell with NinjaOne’s automation allows IT teams to apply targeted, validated changes across fleets with audit logs, rollback options, and conditional logic.

FAQs

Q: What happens if the NinjaOne agent is not installed?

The script will exit gracefully, informing the user that the required registry paths are missing—likely indicating a missing or misconfigured agent.

Q: Is a reboot required after enabling tracking?

No. Once the registry key is set, the NinjaOne agent should begin tracking user-installed applications on its next scan cycle.

Q: Can this script be scheduled?

Yes. It can be deployed as part of a scheduled task or pushed on-demand using NinjaOne scripting policies.

Q: Does this impact user performance?

The additional monitoring is lightweight and typically imperceptible, especially when used in environments already running endpoint agents.

Implications

Enabling user-context software tracking tightens an organization’s security posture by closing a common visibility gap. However, it also implies a greater responsibility on the IT team to interpret and manage the increased data flow. Unauthorized software—whether benign or risky—can now be identified, leading to better enforcement of policy and improved cybersecurity hygiene.

Recommendations

  • Test First: Always validate the script on a sample group of machines before mass deployment.
  • Use with Automation: Leverage NinjaOne’s scripting module to deploy this script across your entire environment.
  • Audit Regularly: Once enabled, schedule periodic reviews of user-installed software to catch anomalies early.
  • Log Outputs: Redirect output to log files for compliance and troubleshooting purposes.

Final Thoughts

For IT professionals looking to bolster their visibility into user-installed software, especially in modern hybrid or remote-first environments, this PowerShell script offers a powerful yet simple solution. When used in conjunction with NinjaOne’s automation capabilities, it bridges a critical gap in endpoint monitoring. Whether you manage ten devices or ten thousand, enabling this form of tracking ensures no application goes unnoticed—securing your environment one user at a time.

By embracing automation with precise, purpose-built scripts like this, IT teams not only improve their operational efficiency but also demonstrate proactive governance. If you’re already using NinjaOne, this script is a valuable addition to your toolkit. If not, it exemplifies the power of coupling endpoint visibility with seamless automation.

Next Steps

Building an efficient and effective IT team requires a centralized solution that acts as your core service delivery tool. NinjaOne enables IT teams to monitor, manage, secure, and support all their devices, wherever they are, without the need for complex on-premises infrastructure.

Learn more about NinjaOne Remote Script Deployment, check out a live tour, or start your free trial of the NinjaOne platform.

Categories:

You might also like

×

See NinjaOne in action!

By submitting this form, I accept NinjaOne's privacy policy.

NinjaOne Terms & Conditions

By clicking the “I Accept” button below, you indicate your acceptance of the following legal terms as well as our Terms of Use:

  • Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms.
  • Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party.
  • Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library belonging to or under the control of any other software provider.
  • Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations.
  • Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks.
  • Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script.
  • EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).