diff --git a/Tools.ps1 b/Tools.ps1 index 7c56529..7b920fb 100644 --- a/Tools.ps1 +++ b/Tools.ps1 @@ -887,99 +887,108 @@ Function Get-GUIDFromMSIUninstallString ([string]$str) { Return $retval } -#Function Uninstall-MSI { -# param( -# [Parameter(ValueFromPipeline=$true,Mandatory=$true)][string[]]$APP_NAMES, -# [Parameter(ValueFromPipeline=$false,Mandatory=$false)][switch]$Match, -# [Parameter(ValueFromPipeline=$false,Mandatory=$false)][switch]$IncludeNonMSI -# ) -# BEGIN { -# # Get all of the uninstall registry keys -# $UNINSTALL_KEYS = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall -ErrorAction SilentlyContinue -# $UNINSTALL_KEYS += Get-ChildItem -Path HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall -ErrorAction SilentlyContinue -# -# # Exit if we can't find any software at all -# If ( $UNINSTALL_KEYS.Length -eq 0 ) { Write-Error "Could not find any installed apps in the Windows Registry" ; Return } -# -# # Array to store custom objects about each installed MSI app -# $INSTALLED_APPS = @() -# -# Foreach ( $reg in $UNINSTALL_KEYS ) { -# # Get the uninstall key -# $REG_PROPERTY = Get-ItemProperty -Path $reg.PSPath -# -# # Skip apps missing both the UninstallString and ModifyPath -# If ( [string]::IsNullOrEmpty($REG_PROPERTY.UninstallString) -and [string]::IsNullOrEmpty($REG_PROPERTY.ModifyPath) ) { Continue } -# -# # Determine if the app is installed via MSI -# If ( $REG_PROPERTY.WindowsInstaller -eq 1 ) { -# -# $MSI = $true -# -# # Get the product code from the UninstallString or ModifyPath if the app is installed via MSI -# If ( ![string]::IsNullOrEmpty($REG_PROPERTY.UninstallString) ) { $PRODUCT_CODE = $(Get-GUIDFromMSIUninstallString -str $REG_PROPERTY.UninstallString) } -# -# # Get the product code from the ModifyPath -# ElseIf ( ![string]::IsNullOrEmpty($REG_PROPERTY.ModifyPath) ) { $PRODUCT_CODE = $(Get-GUIDFromMSIUninstallString -str $REG_PROPERTY.ModifyPath) } -# -# # Don't add an app with an unknown product code -# If ( [string]::IsNullOrEmpty($PRODUCT_CODE) ) { Continue } -# -# } Else { $MSI = $false } -# -# # Get a usable name for the entry -# If ( ![string]::IsNullOrEmpty($REG_PROPERTY.DisplayName) ) { -# $AppName = $REG_PROPERTY.DisplayName -# } ElseIf ( ![string]::IsNullOrEmpty($REG_PROPERTY.Name) ) { -# $AppName = $REG_PROPERTY.DisplayName -# } Else { -# $AppName = '[unknown]' -# } -# -# $customObject = [PSCustomObject]@{ -# MSI = $MSI -# DisplayName = $AppName -# UninstallString = $REG_PROPERTY.UninstallString -# ModifyPath = $REG_PROPERTY.ModifyPath -# ProductCode = $PRODUCT_CODE -# } -# -# # Cleanup object properties -# #If ( $customObject.) -# -# $INSTALLED_APPS += $customObject -# -# } -# } -# PROCESS { -# Foreach ( $app in $APP_NAMES ) { -# Foreach ( $installed_app in $INSTALLED_APPS ) { -# -# # Do not add apps which name does not match the one we're looking for -# Switch ( $Match ) { -# $true { If ( !($installed_app.DisplayName -match $app) ) { Continue } } -# $false { If ( !($installed_app.DisplayName -ilike $app) ) { Continue } } -# } -# -# $DisplayName = $installed_app.DisplayName -# $UninstallString = $installed_app.UninstallString -# $ProductCode = $installed_app.ProductCode -# -# # Print out uninstall string for unsupported apps -# If ( $installed_app.UninstallSupported -eq $false ) { -# Write-Output "Cannot uninstall ""${DisplayName}""`n UninstallString: ${UninstallString}" -# Continue -# } -# -# # Uninstall the app -# Write-Output "Uninstalling ""${DisplayName}"" with product code: ${ProductCode}" -# $arguments = '/X{' + $ProductCode + '} /qn /norestart' -# Try { Start-Process -FilePath "msiexec.exe" -ArgumentList $arguments -Wait } -# Catch { Write-Error $_.Exception.Message } -# } -# } -# } -#} +Function Get-GUIDFromMSIUninstallString ([string]$str) { + $start = $str.IndexOf('{') + 1 + $end = $str.IndexOf('}') + $retval = $str.Substring($start, $end - $start) + If ( $retval.Length -ne 36 ) { $retval = $null } + Return $retval +} +Function Uninstall-MSI { + param( + [Parameter(ValueFromPipeline=$true,Mandatory=$true)][string[]]$APP_NAMES, + [Parameter(ValueFromPipeline=$false,Mandatory=$false)][switch]$Match, + [Parameter(ValueFromPipeline=$false,Mandatory=$false)][switch]$IncludeNonMSI + ) + BEGIN { + # Get all of the uninstall registry keys + $UNINSTALL_KEYS = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall -ErrorAction SilentlyContinue + $UNINSTALL_KEYS += Get-ChildItem -Path HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall -ErrorAction SilentlyContinue + + # Exit if we can't find any software at all + If ( $UNINSTALL_KEYS.Length -eq 0 ) { Write-Error "Could not find any installed apps in the Windows Registry" ; Return } + + # Array to store custom objects about each installed MSI app + $INSTALLED_APPS = @() + + Foreach ( $reg in $UNINSTALL_KEYS ) { + # Get the uninstall key + $REG_PROPERTY = Get-ItemProperty -Path $reg.PSPath + + # Skip apps missing both the UninstallString and ModifyPath + If ( [string]::IsNullOrEmpty($REG_PROPERTY.UninstallString) -and [string]::IsNullOrEmpty($REG_PROPERTY.ModifyPath) ) { Continue } + + # Determine if the app is installed via MSI + If ( $REG_PROPERTY.WindowsInstaller -eq 1 ) { + + $MSI = $true + + # Get the product code from the UninstallString or ModifyPath if the app is installed via MSI + If ( ![string]::IsNullOrEmpty($REG_PROPERTY.UninstallString) ) { $PRODUCT_CODE = $(Get-GUIDFromMSIUninstallString -str $REG_PROPERTY.UninstallString) } + + # Get the product code from the ModifyPath + ElseIf ( ![string]::IsNullOrEmpty($REG_PROPERTY.ModifyPath) ) { $PRODUCT_CODE = $(Get-GUIDFromMSIUninstallString -str $REG_PROPERTY.ModifyPath) } + + # Don't add an app with an unknown product code + If ( [string]::IsNullOrEmpty($PRODUCT_CODE) ) { Continue } + + } Else { $MSI = $false } + + # Get a usable name for the entry + If ( ![string]::IsNullOrEmpty($REG_PROPERTY.DisplayName) ) { + $AppName = $REG_PROPERTY.DisplayName + } ElseIf ( ![string]::IsNullOrEmpty($REG_PROPERTY.Name) ) { + $AppName = $REG_PROPERTY.DisplayName + } Else { + $AppName = '[unknown]' + } + + $customObject = [PSCustomObject]@{ + MSI = $MSI + DisplayName = $AppName + UninstallString = $REG_PROPERTY.UninstallString + ModifyPath = $REG_PROPERTY.ModifyPath + ProductCode = $PRODUCT_CODE + } + + # TODO: Cleanup object properties + + $INSTALLED_APPS += $customObject + + } + } + PROCESS { + Foreach ( $app in $APP_NAMES ) { + Foreach ( $installed_app in $INSTALLED_APPS ) { + $uninstall = $false + + # Do not add apps whose name does not match the one we're looking for + Switch ( $Match ) { + $true { If ( $installed_app.DisplayName -match $app ) { $uninstall = $true } } + $false { If ( $installed_app.DisplayName -ilike $app ) { $uninstall = $true } } + } + + # Print out uninstall string for unsupported apps + If ( ($uninstall) -and ($installed_app.UninstallSupported -eq $false) ) { + Write-Output "Cannot uninstall ""${DisplayName}""`n UninstallString: ${UninstallString}" + $uninstall = $false + } + + If ( $uninstall ) { + $DisplayName = $installed_app.DisplayName + $UninstallString = $installed_app.UninstallString + $ProductCode = $installed_app.ProductCode + + # Uninstall the app + Write-Output "Uninstalling ""${DisplayName}"" with product code: ${ProductCode}" + $arguments = '/X{' + $ProductCode + '} /qn /norestart' + Try { Start-Process -FilePath "msiexec.exe" -ArgumentList $arguments -Wait } + Catch { Write-Error $_.Exception.Message } + } + } + } + } +} # Reboot an endpoint based on the amount of time it has been running without a reboot Function Restart-EndpointOnUptime ([timespan]$MaxUptime) {