You can verify if a package is installed and retrieve its installation time in PowerShell by using the
Get-Package cmdlet with specific filters or by querying the Windows Registry for MSI-based software. While
Get-Package returns an empty collection rather than throwing an error when no matches are found, relying on its
InstalledDate property can be unreliable because it often reflects the last modification time of the package file rather than the actual system installation timestamp. For accurate results, especially with traditional Windows applications, checking the registry keys under
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall is the most robust method to determine both presence and true install dates.
Using Get-Package for Modern Package Managers
The
Get-Package cmdlet is primarily designed for use with package providers like NuGet, Chocolatey, or Winget. It does not inherently scan the entire system for every possible application but instead queries the registered package sources. When you run this command without any arguments, it lists all packages available in your configured sources, which can be overwhelming. To check for a specific package, you must filter the results by name.
A common misconception is that
Get-Package throws an error if a package is missing. In reality, it simply returns an empty array. This behavior allows you to check the count of returned objects to determine existence. If the count is greater than zero, the package is present in the provider's database. However, the metadata provided by these providers varies significantly. The
InstalledDate field may contain a timestamp from when the package was downloaded or cached, not necessarily when it was deployed to the operating system. Therefore, this method is best suited for verifying the presence of packages managed by modern tools where version consistency is critical, but less reliable for determining historical installation times across the whole OS.
Querying the Registry for MSI Installations
For traditional Windows applications installed via MSI (Microsoft Installer) or older setup executables, the registry provides a more consistent source of truth. Windows stores uninstall information for installed programs in specific registry hives. You can query these keys to find if a program is listed and extract the
InstallDate value, which is typically formatted as
YYYYMMDD.
To begin, you need to target the correct registry path. Most 64-bit applications on 64-bit systems are stored under
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall. On 32-bit systems or 32-bit applications running on 64-bit Windows, they may appear under
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall. By iterating through these keys, you can match the
DisplayName property against your target application name.
Here is how you can construct a script to check for an application named "ExampleApp":
$appName = "ExampleApp"
$regPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
)
foreach ($path in $regPaths) {
$keys = Get-ChildItem -Path $path -ErrorAction SilentlyContinue
foreach ($key in $keys) {
$displayName = Get-ItemProperty -Path $key.PSPath -Name DisplayName -ErrorAction SilentlyContinue | Select-Object -ExpandProperty DisplayName
if ($displayName -eq $appName) {
$installDate = Get-ItemProperty -Path $key.PSPath -Name InstallDate -ErrorAction SilentlyContinue | Select-Object -ExpandProperty InstallDate
Write-Host "Found: $appName at $installDate"
break
}
}
}
This approach directly reads the values written by the installer during the setup process. Unlike package managers, MSI installers strictly adhere to writing the installation date into the registry. This makes it highly reliable for auditing purposes or compliance checks where knowing exactly when software was added to the system is required.
Handling Winget and Chocolatey Specifics
If your environment relies heavily on
winget or
choco, you should use their respective CLI tools for the most accurate data.
winget list provides a clean output of installed packages along with their versions. While it does not always expose the exact install date in the standard table view, you can parse the output or use the
--id flag to get detailed info for a single package.
Similarly, Chocolatey maintains a log directory that records installation events. Checking the logs in
%ProgramData%\chocolatey\logs can provide timestamps for when each package was installed. This is particularly useful for troubleshooting issues where the registry state might be out of sync with the actual package manager state.
For deeper integration, consider looking into related automation techniques. For instance, understanding how to manage automatic variables can help streamline your scripts further. You can read more about
Understanding Automatic Variables in PowerShell: $global:? to optimize your variable scope when storing these check results.
Best Practices for Verification Scripts
When writing verification scripts, always handle errors gracefully. Use
-ErrorAction SilentlyContinue when accessing registry keys or calling external commands to prevent non-terminating errors from halting your script. Additionally, normalize the application name comparison. Use
-like or
-match operators if exact string matching fails due to trailing spaces or version suffixes in the display name.
It is also important to consider permissions. Reading certain registry keys or querying global package providers may require elevated privileges. Ensure your script runs with appropriate access levels to avoid silent failures where data appears missing because the user context lacks read permissions.
For more advanced text processing needs within your scripts, such as parsing complex output formats, you might find it helpful to explore other PowerShell capabilities. See our guide on
PowerShell Command to Display Specific Text from a Website to learn how to extract precise data strings from larger blocks of text, a skill that translates well to parsing registry or package outputs.
Frequently Asked Questions
Why does Get-Package return null instead of an error?
Get-Package is designed to return an empty collection when no packages match the criteria, rather than throwing a terminating error. This design allows scripts to continue executing even if a specific package is not found. You should always check the length or count of the returned object to determine if the package exists.
Is InstalledDate from Get-Package accurate?
No, the
InstalledDate property from
Get-Package is often unreliable for determining the actual system installation time. It frequently reflects the date the package file was downloaded or modified in the cache. For true installation timestamps, especially for Windows applications, querying the registry is a superior method.
How do I find 32-bit apps on a 64-bit system?
On 64-bit Windows, 32-bit applications are redirected to the
WOW6432Node registry path. You must check both
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall and
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall to ensure you capture all installed software regardless of architecture.
Can I use this method for AppX/UWP applications?
Traditional registry keys under
Uninstall do not typically store metadata for AppX or UWP applications. These modern apps are managed differently and are better queried using
Get-AppxPackage or
Get-AppxProvisionedPackage. For these types, the registry method described will not yield results.
What if the DisplayName contains special characters?
Registry
DisplayName values can sometimes include trailing spaces or special formatting. Using the
-eq operator requires an exact match, which might fail due to whitespace. Using
-like "*AppName*" is safer as it performs a substring match, ignoring minor formatting discrepancies.