Uninstall Software

Based on the previous post handling the removal of the Ask software (the beloved add-on that everyone joyfully installs along with Java) a more developed script took form to handle any type of software.

Its based on the following borrowed pieces of code,

Get-LHSInstInstalledApp has been extended to also output the installationdate. Apart from that everything is as is from the original function

Convert-DateString has been used to convert the InstallationDate string to a date that can be used for calculations

ExitWithCode is a function that is simply used to end the script with an accumulated Exit Code from all uninstallations.

The script will accept the following parameters;

ApplicationName – a wild card search for the applications we want to remove.

PublisherName – we can validate that the right publisher have installed the application

InstallDateOlder – amount of days since the application was installed for us to remove it. Standard is 30

IgnoreInstallDate – True / False – we can choose to completely ignore when the application was installed

If the application is something other than an MSI – it will just report that a productcode is missing and not attempt the installation.

A log-file will be created in %WINDIR%\TEMP\APP_(yourappname)_Removal.LOG

Each uninstall will have a log-file written in %WINDIR%\TEMP with AP_UNINSTALL as prefix.

 

 

Running the script requires admin permissions

 

#========================================================================
# Created with: PowerShell ISE
# Created on: 2015-02-21 23:32
# Created by: Nicke Källén
# Organization: Applepie.se
# Filename: SCCM_Uninstall_Unused_Application
# Comment: Uninstalls an application (msi support only) based
# on Display Name in ARP, Publisher and how long ago
# it was installed
# Convert-DateString function
# http://www.powershellmagazine.com/2013/07/08/pstip-
# converting-a-string-to-a-system-datetime-object/
# Get-LHSInstalledApp - appended InstallDate to output
# https://gallery.technet.microsoft.com/scriptcenter/
# Get-Installed-Application-615fa73a
# Exit function
# http://weblogs.asp.net/soever/returning-an-exit-
# code-from-a-powershell-script
#========================================================================
param (
 [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$True)]
 [string]$ApplicationName = "",
 [Parameter(Position=1,Mandatory=$false,ValueFromPipeline=$false)]
 [string]$PublisherName = "",
 [int]$InstallDateOlder = "30",
 [Parameter(mandatory=$false)]
 [bool]$IgnoreInstallDate=$false
 )

 function Convert-DateString ([String]$Date, [String[]]$Format)
{
 $result = New-Object DateTime

 $convertible = [DateTime]::TryParseExact(
 $Date,
 $Format,
 [System.Globalization.CultureInfo]::InvariantCulture,
 [System.Globalization.DateTimeStyles]::None,
 [ref]$result)

 if ($convertible) { $result }
}

Function Get-LHSInstalledApp {
<#
.SYNOPSIS
 List installed applications for local or remote computers.

.DESCRIPTION
 List installed applications for local or remote computers.

 List both 32-bit and 64-bit applications. Note that
 dotNet 4.0 Support for Powershell 2.0 needed.

 Output looks like this:
 -------------------------
 ComputerName : N104100
 AppID : {90120000-001A-0407-0000-0000000FF1CE}
 AppName : Microsoft Office Outlook MUI (German) 2007
 Publisher : Microsoft Corporation
 Version : 12.0.6612.1000
 Architecture : 32bit
 UninstallString : MsiExec.exe /X{90120000-001A-0407-0000-0000000FF1CE} 

.PARAMETER ComputerName
 Outputs applications for the named computer(s).
 If you omit this parameter, the local computer is assumed.

.PARAMETER AppID
 Outputs applications with the specified application ID.
 An application's appID is equivalent to its subkey name underneath the Uninstall registry key.
 For Windows Installer-based applications, this is the application's product code GUID
 (e.g. {3248F0A8-6813-11D6-A77B-00B0D0160060}). Wildcards are permitted.

.PARAMETER AppName
 Outputs applications with the specified application name.
 The AppName is the application's name as it appears in the
 Add/Remove Programs list. Wildcards are permitted.

.PARAMETER Publisher
 Outputs applications with the specified publisher name.
 Wildcards are permitted

.PARAMETER Version
 Outputs applications with the specified version.
 Wildcards are permitted.

.EXAMPLE
 PS C:\> Get-LHSInstalledApp

 This command outputs installed applications on the current computer.

.EXAMPLE
 PS C:\> Get-LHSInstalledApp | Select-Object AppName,Version | Sort-Object AppName

 This command outputs a sorted list of applications on the current computer.

.EXAMPLE
 PS C:\> Get-LHSInstalledApp wks1,wks2 -Publisher "*microsoft*"

 This command outputs all installed Microsoft applications on the named computers.
 * regular expression to match any characters.

.EXAMPLE
 PS C:\> Get-LHSInstalledApp wks1,wks2 -AppName "*Office 97*" 

 This command outputs any Application Name that match "Office 97" on the named computers.
 * regular expression to match any characters.

.EXAMPLE
 PS C:\> Get-Content ComputerList.txt | Get-LHSInstalledApp -AppID "{1A97CF67-FEBB-436E-BD64-431FFEF72EB8}" | Select-Object ComputerName

 This command outputs the computer names named in ComputerList.txt that have the specified application installed.

.EXAMPLE
 Get-LHSInstalledApp | Where-Object {-not ( $_.AppID -like "KB*") } |
 ConvertTo-CSV -Delimiter ';' -NoTypeInformation | Out-File -FilePath C:\temp\AppsInfo.csv
 Invoke-Item C:\temp\AppsInfo.csv

 Outputs all installed application except KB fixes to an CSV file and opens in Excel

.INPUTS
 System.String, you can pipe ComputerNames to this Function

.OUTPUTS
 PSObjects containing the following properties:

 ComputerName - computer where the application is installed
 AppID - the application's AppID
 AppName - the application's name
 Publisher - the application's publisher
 Version - the application's version
 Architecture - the application's architecture (32-bit or 64-bit)
 UninstallString - the application uninstall String

.NOTES
 More Info:
 ==========
 Why not using Get-WmiObject
 ---------------------------
 * Win32_Product
 At first glance, Win32_Product would appear to be one of those best solutions.
 The Win32_product class is not query optimized.
 Queries such as “select * from Win32_Product where (name like 'Sniffer%')”
 require WMI to use the MSI provider to enumerate all of the installed
 products and then parse the full list sequentially to handle the “where” clause:,

 * This process initiates a consistency check of packages installed,
 and then verifying and repairing the installations.
 * If you have an application that makes use of the Win32_Product class,
 you should contact the vendor to get an updated version that does not use this class.

 On Windows Server 2003, Windows Vista, and newer operating systems, querying Win32_Product
 will trigger Windows Installer to perform a consistency check to verify the health of the
 application. This consistency check could cause a repair installation to occur. You can
 confirm this by checking the Windows Application Event log. You will see the following
 events each time the class is queried and for each product installed:

 Event ID: 1035
 Description: Windows Installer reconfigured the product. Product Name: <ProductName>.
 Product Version: <VersionNumber>. Product Language: <languageID>.
 Reconfiguration success or error status: 0.

 Event ID: 7035/7036
 Description: The Windows Installer service entered the running state.

 I would not recommend querying Win32_Product in your production environment unless you are in a maintenance window.

 * Win32Reg_AddRemovePrograms
 Win32Reg_AddRemovePrograms is not a standard Windows class.
 This WMI class is only loaded during the installation of an SMS/SCCM client.

 What is great about Win32Reg_AddRemovePrograms is that it contains similar properties and
 returns results noticeably quicker than Win32_Product.

 Using Registry:
 ----------------
 By default, if your process is running as a 32 bit process you will end up accessing the 32 bit "reflection" of
 the remote system. Therefore, registry keys like HKLM\Software will actually be mapped to HKLM\Software\Wow6432Node
 which gets very frustrating! You can access the 64 bit "reflection" via WMI, but personally I find that quite painful.

 Fortunately, in .NET 4, the registry class had some extra features added to it which allowed for a new
 overload "RegistryView". Therefore, you can now specify exactly which "reflection" of the registry
 you want to access and manipulate! No more headaches!

 In order to use this function, the Powershell instance must support .Net 4.0 or greater, which is fairly straightforward if you follow these instructions.
 1. Open notepad and copy the below text exactly as shown into the document.

<?xml version="1.0"?>
<configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0.30319"/> <supportedRuntime version="v2.0.50727"/> </startup>
</configuration>

 2. Save this document as c:\windows\System32\WindowsPowerhsell\v1.0\Powershell.exe.config
 (and/or c:\windows\System32\WindowsPowerhsell\v1.0\Powershell_ise.exe.config)
 (in addition for the 32bit Powershell on a 64bit Windows C:\Windows\SysWOW64\WindowsPowerShell\v1.0\*.config)
 3. Reload powershell and type the following command: $PsVersionTable.clrVersion (It should show Major version 4 if .Net 4 is supported.)

 NAME: Get-LHSInatalledApp.ps1
 AUTHOR: u104018
 LASTEDIT: 02/06/2012 16:01:40
 KEYWORDS: Registry Redirection, Installed software, Registry64, WOW6432Node,Accessing Remote x64 Registry From an x86/x32 OS Computer

.LINK

http://poshcode.org/3186


http://blogs.technet.com/b/heyscriptingguy/archive/2011/11/13/use-powershell-to-quickly-find-installed-software.aspx


http://msdn.microsoft.com/en-us/library/aa393067%28VS.85%29.aspx

#Requires -Version 2.0
#>

[cmdletbinding(DefaultParameterSetName = 'Default', ConfirmImpact = 'low')] 

Param(

 [Parameter(ParameterSetName='AppID', Position=0,Mandatory=$False,ValueFromPipeline=$True)]
 [Parameter(ParameterSetName='Default', Position=0,Mandatory=$False,ValueFromPipeline=$True)]
 [string[]] $ComputerName=$ENV:COMPUTERNAME,

 [Parameter(ParameterSetName='AppID', Position=1)]
 [String] $AppID = "*",

 [Parameter(ParameterSetName='Default', Position=1)]
 [String] $AppName = "*",

 [Parameter(ParameterSetName='Default', Position=2)]
 [String] $Publisher = "*",

 [Parameter(ParameterSetName='Default', Position=3)]
 [String] $Version = "*"

 )

BEGIN {
 ${CmdletName} = $Pscmdlet.MyInvocation.MyCommand.Name

 If (!($PsVersionTable.clrVersion.Major -ge 4)) {Write-Error "Requires .Net 4.0 support for Powershell 2.0"; Return} 

} # end BEGIN

PROCESS {
 #Write-Verbose -Message "${CmdletName}: Starting Process Block"
 ForEach ($Computer in $ComputerName) {
 Write-Verbose "`$Computer contains $Computer"
 IF (Test-Connection -ComputerName $Computer -Count 2 -Quiet) {
 try { 

 Write-Verbose "Get Architechture Type of the system"
 $OSArch = (Get-WMIObject -ComputerName $Computer win32_operatingSystem -ErrorAction Stop).OSArchitecture
 if ($OSArch -like "*64*") {$Architectures = @("32bit","64bit")}
 else {$Architectures = @("32bit")}
 #Create an array to capture program objects.
 $arApplications = @()
 foreach ($Architecture in $Architectures){
 #We have a 64bit machine, get the 32 bit software.
 if ($Architecture -like "*64*"){
 #Define the entry point to the registry.
 $strSubKey = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
 $SoftArchitecture = "32bit"
 $RegViewEnum = [Microsoft.Win32.RegistryView]::Registry64
 }
 #We have a 32bit machine, use the 32bit registry provider.
 elseif ($Architectures -notcontains "64bit"){
 #Define the entry point to the registry.
 $strSubKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
 $SoftArchitecture = "32bit"
 $RegViewEnum = [Microsoft.Win32.RegistryView]::Registry32
 }
 #We have "64bit" in our array, capture the 64bit software.
 else{
 #Define the entry point to the registry.
 $strSubKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
 $SoftArchitecture = "64bit"
 $RegViewEnum = [Microsoft.Win32.RegistryView]::Registry64
 }

 Write-Verbose "Create a remote registry connection to the Computer."
 $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $Computer, $RegViewEnum)
 $RegKey = $Reg.OpenSubKey($strSubKey)

 Write-Verbose "Get all subkeys that exist in the entry point."
 $RegSubKeys = $RegKey.GetSubKeyNames() 

 Write-Debug "Architecture : $Architecture"
 Write-Debug "SoftArchitecture : $SoftArchitecture"
 Write-Verbose "Enumerate the subkeys."
 foreach ($SubKey in $RegSubKeys)
 {
 Write-Debug "`$SubKey : $SubKey"
 $Program = $Reg.OpenSubKey("$strSubKey\\$SubKey")
 $strDisplayName = $Program.GetValue("DisplayName")
 if ($strDisplayName -eq $NULL) { continue } # skip entry if empty display name

 switch ($PsCmdlet.ParameterSetName)
 { 

 "AppID" { if ((split-path $SubKey -leaf) -like $AppID)
 {
 $RegKey = ("HKLM\$strSubKey\$SubKey").replace("\\","\")

 $output = new-object PSObject
 $output | add-member NoteProperty "ComputerName" -value $computer
 $output | add-member NoteProperty "RegKey" -value ($RegKey) # useful when debugging
 $output | add-member NoteProperty "AppID" -value (split-path $SubKey -leaf)
 $output | add-member NoteProperty "AppName" -value $strDisplayName
 $output | add-member NoteProperty "Publisher" -value $Program.GetValue("Publisher")
 $output | add-member NoteProperty "Version" -value $Program.GetValue("DisplayVersion")
 $output | add-member NoteProperty "Architecture" -value $SoftArchitecture
 $output | add-member NoteProperty "UninstallString" -value $Program.GetValue("UninstallString")
 $output | add-member NoteProperty "InstallDate" -value $Program.GetValue("InstallDate")

 $output
 } #end if
 } #end "AppID"

 "Default" { If (( $strDisplayName -like $AppName ) -and (
 $Program.GetValue("Publisher") -like $Publisher ) -and (
 $Program.GetValue("DisplayVersion") -like $Version ))
 {
 $RegKey = ("HKLM\$strSubKey\$SubKey").replace("\\","\")

 $output = new-object PSObject
 $output | add-member NoteProperty "ComputerName" -value $computer
 $output | add-member NoteProperty "RegKey" -value ($RegKey) # useful when debugging
 $output | add-member NoteProperty "AppID" -value (split-path $SubKey -leaf)
 $output | add-member NoteProperty "AppName" -value $strDisplayName
 $output | add-member NoteProperty "Publisher" -value $Program.GetValue("Publisher")
 $output | add-member NoteProperty "Version" -value $Program.GetValue("DisplayVersion")
 $output | add-member NoteProperty "Architecture" -value $SoftArchitecture
 $output | add-member NoteProperty "UninstallString" -value $Program.GetValue("UninstallString")
 $output | add-member NoteProperty "InstallDate" -value $Program.GetValue("InstallDate")

 $output
 } #end if
 } #end "Default"
 } #end switch

 } # end foreach ($SubKey in $RegSubKeys)
 } # end foreach ($Architecture in $Architectures)
 } Catch {
 write-error $_
 }
 } Else {
 Write-Warning "\\$Computer DO NOT reply to ping"
 } # end IF (Test-Connection -ComputerName $Computer -count 2 -quiet)
 } # end ForEach ($Computer in $computerName)

} # end PROCESS

END { Write-Verbose "Function ${CmdletName} finished." }

} # end Function Get-LHSInatalledApp

function Log
{
 [cmdletbinding()]
 param (
 [Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)]
 [string]$text,
 [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$False)]
 [string]$filename
 )
 begin
 {
 Write-Debug 'Logging starting'
 Write-Debug "Filename: $($filename)"
 }
 process
 {
 foreach ($txt in $text)
 {
 Out-File $filename -append -noclobber -inputobject $txt -encoding ASCII
 Write-Verbose $txt
 }

 }

 end
 {
 Write-Debug 'Logging ending'
 }

}

function ExitWithCode
{
 param
 ($exitcode)

 Write-Verbose "Ending with $($ExitCode)"
 $host.SetShouldExit($exitcode)
 exit
}

function Remove-InstalledMSI
{
 [cmdletbinding()]
 param (
 [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$True,ValueFromPipelineByPropertyName)]
 [ValidatePattern('\{.+\}')]
 [Alias('AppID')]
 [string]$ProductCode = $null,
 [Parameter(Position=1,Mandatory=$false,ValueFromPipeline=$False)]
 [string]$LogFilePath,
 [Parameter(Position=1,Mandatory=$false,ValueFromPipeline=$False)]
 [string]$Property
 )

 begin
 {
 Write-Verbose 'Start of Remove-InstallMSI'
 $exitcode = $null
 } 

 process
 {

 foreach ($pc in $ProductCode)
 {
 Write-Verbose "Uninstall ProductCode: $($pc)"
 $AppName = $((Get-LHSInstalledApp -AppID $pc).AppName)
 Write-Verbose "AppName: $($AppName)"
 if ($LogFilePath -ne $null)
 {
 $LogFilePath = "c:\windows\TEMP\AP_UNINSTALL_$($AppName).log"
 }
 else
 {
 Write-verbose "LogFilePath: $LogFilePath"
 }
 $argumentlist = "/x $pc /qn REBOOT=ReallySuppress /lv `"$($LogFilePath)`" "
 $argumentlist += $Property
 Write-Verbose "Argument List: $($argumentlist)" 

 $exitcode = (Start-Process -filepath "msiexec.exe" -ArgumentList $argumentlist -Wait -PassThru).ExitCode
 Write-Verbose "Exit Code: $($exitcode)"

 $output = new-object PSObject
 $output | add-member NoteProperty "ProductCode" -value $pc
 $output | add-member NoteProperty "AppName" -value $AppName
 $output | add-member NoteProperty "ExitCode" -value $($ExitCode)
 $output | add-member NoteProperty "LogFilePath" -value $LogFilePath

 $output
 }

 }

 end
 {
 Write-Verbose 'End of Remove-InstallMSI'
 }

}

$logfile = "$env:windir\temp\AP_$($ApplicationName)_Removal.log"
Write-Verbose "Logfile: $($logfile)"
try { Remove-Item $logfile -force -ErrorAction SilentlyContinue }
catch { Write-Warning $_ }

log $logfile '--------------------------------------------'
log $logfile "$(get-date) - $($ApplicationName) - Removal Started"
log $logfile "$(get-date) - $($ApplicationName) - Searching for $($PublisherName) $($ApplicationName)"
 if ($IgnoreInstallDate -eq $true)
 {
 Write-Verbose "IgnoreInstallDate set $($IgnoreInstallDate)"
 log $logfile "$(get-date) - $($ApplicationName) - Ignoring installation date"
 }
 else
 {
 Write-Verbose "IgnoreInstallDate set $($IgnoreInstallDate)"
 log $logfile "$(get-date) - $($ApplicationName) - Removal only if install date is more than $($InstallDateOlder) days ago"
 }

if ($PublisherName)
{

 $applist = Get-LHSInstalledApp -AppName "*$($ApplicationName)*" -Publisher "*$($PublisherName)*"
}
else
{
 $applist = Get-LHSInstalledApp -AppName "*$($ApplicationName)*"
}

log $logfile "$(get-date) - $($ApplicationName) - Found $($applist.Count) $($ApplicationName) installations"
log $logfile '--------------------------------------------'
$applist | foreach { write-verbose "AppName: $($_.AppName)"}
$ReturnValue = 0
Write-Verbose "Current Exit Code: $($ReturnValue)"

$applist | where {$_.appid -notmatch ('\{.+\}') } | foreach { log $logfile "$(get-date) - $($_.AppName) has no productcode" }
$applist = $applist | where {$_.appid -match ('\{.+\}') } 

if ($IgnoreInstallDate -eq $true)
{
 $uninstall = $applist | Remove-InstalledMSI
}
else
{
 $uninstall = $applist | where-object { ($_.InstallDate -notin ($null,'')) -and`
 ( ((get-date) - (Convert-DateString -Date $($_.InstallDate) -Format 'yyyyMMdd')).days -gt $InstallDateOlder ) }`
 | Remove-InstalledMSI
}

$uninstall | foreach { log $logfile "$(get-date) - $($_.AppName) - Exit Code: $($_.ExitCode)" ; $returnvalue += $_.exitcode }

log $logfile '--------------------------------------------'
log $logfile "$(get-date) - $($ApplicationName) - Removal Finished"

ExitWithCode($ReturnValue)

 

 

 

Computer cleanup – Ask removal

Got a lot of computers with the Ask-suite of toolbars installed? When users are allowed administrative permissions on their end-points this is a likely scenario – here is a minor script bit that will cleanup the computer from the hassle.

First – retrieve the Get-LHSInstInstalledApp function from the Technet Gallery. Its a very well written function to retrieve information about both 32-bit and 64-bit applications installed within a Windows environment – great for retrieving information about the installed applications on a computer.

I am not certain if I wrote the Log function on my own, or if this is just something that I grabbed from a script someone previously wrote. If you did write it – just give a shout!

 

Running the script requires admin permissions

 

function Log
{
param([string]$filename,[string]$text)
Out-File $filename -append -noclobber -inputobject $text -encoding ASCII
}
$logfile = "$env:windir\temp\SMS_Ask_Removal.log"
Remove-Item $logfile -force
log $logfile "$(get-date) - Ask Removal Started"

$ask = Get-LHSInstalledApp -AppName *Ask* -Publisher "APN, LLC"

log $logfile "$(get-date) - Found $($ask.Count) Ask installations"

Foreach ($install in $ask)
{
log $logfile "--------------------------------------------"
log $logfile "$(get-date) - $($install.AppName) found"
try
{
log $logfile "$(get-date) - $($install.AppName) removal"
$exitcode = (Start-Process -filepath "msiexec.exe" -ArgumentList "/x $($install.appid) /qn REBOOT=ReallySuppress /lv `"c:\windows\TEMP\UNINSTALL_$($install.AppName).log`"" -Wait -PassThru).ExitCode
log $logfile "$(get-date) - $($install.AppName) - return code: $exitcode"
}
catch
{
log $logfile "$(get-date) - ERROR: $($install.AppName) removal failed"
}

}

log $logfile "--------------------------------------------"
log $logfile "$(get-date) - Ask Removal Finished" 

 

 

 

MSI EXIT CODE 1935

If the Windows Installer engine fails with a generic exit code of 1935 – I usually spent a few hours troubleshooting the machine in pure nerdy interest. Here are a few tips gathered to save myself and hopefully someone else time.

Quite often the issue lies that you are chasing a red-herring. The error code you may get back is either a generic access denied or a file not found. Reviewing all the activity with Process Monitor quite seldom gives any direct hints as there are numerous red herrings that will lead one astray.

After 1h tracking this particular issue – gathering ones thoughts for the future seemed the easiest.

ERROR:Error 1935.An error occurred during the installation of assembly 'Microsoft.VC80.ATL,type="win32",version="8.0.50727.42",publicKeyToken="1fc8b3b9a1e18e3b",processorArchitecture="amd64"'

 

In order of preference – try the following

Ensure that the folder C:\WINDOWS\WINSXS\TEMP and C:\WINDOWS\WINSXS\INSTALLTEMP exist and that the administrative group aswell as the system account has full access to it.

Run SFC /SCANNOW

Similiar posts;

One of possible solutions to: Visual C++ 2008 Redistributable installation error 1935 with HRESULT 0x8007005

Error 1935 when you try to install Microsoft Office 2010 or 2007

Troubleshooting 1935 and 2908 errors during installation

Symantec Endpoint Protection 12.X on OSX

If you are using ConfigMgr 2012 (or one of the plugins hi-jacking the infrastructure – such as Parallels) to manage the Mac OSX devices there are some caveats to the ordinary guide of both from Symantec on howto install the Symantec Endpoint Protection aswell as the Microsoft guide “How to Create and Deploy Applications for Mac Computers in Configuration Manager”.

First of all you need access to the Symantec Endpoint Protection media and to actually start the installation. Once its started you can immediately headover to Symantecs guide on howto Deploy (keyword deploy) SEP with Apple Remote Desktop or Casper.

The guide states that once the installation is fired up (and you acknowledge that its OK if this requires you to restart the computer) you can access the Tools-menu

image

Clicking the “Create remote deployment package” will immediately fire off a new menu that will allow you to choose a file-name and a place where the new package can be saved.

image

Once the deployment package is created you you will receive a helpful note about only deploying this with a deployment system, and remembering to restart afterwards.

image

As per the ConfigMgr article on howto deploy applications for Mac there is a need to convert the generic PKG-format to the ConfigMgr compatible (and unique) CMMAC format.

This specific package does unfortunately not provide any detection mechanism, so the command-line to convert this package is.

./CMApputil –c SEPRemote.pkg –o /volumes/usbstick/ -s

-c points the utility to our original package

-o points to the where we want to place our final package (named SEPRemote.cmmac)

-s will omit the creation of the detection rules

ConfigMgr Client failure – CCMRegisterWinTask

If the SCCM 2012 client fails with  a generic 1603 error at the following phase;


MSI: Action 7:01:41: CcmSetServiceConfig. Sets WMI ServiceRootDir and configures CCMEXEC service.            ccmsetup            2015-01-26 07:01:41       1764 (0x06E4)

MSI: Action 7:01:41: CcmRegisterWinTaskRollback. Rolls back the changes made by CcmRegisterWinTask.        ccmsetup            2015-01-26 07:01:41       1764 (0x06E4)

MSI: Action 7:01:41: CcmRegisterWinTask. Registers tasks with Windows Task Scheduler.           ccmsetup          2015-01-26 07:01:41            1764 (0x06E4)

MSI: Action 7:01:42: Rollback. Rolling back action:         ccmsetup          2015-01-26 07:01:42       1764 (0x06E4)

File C:\WINDOWS\ccmsetup\{181D79D7-1115-4D96-8E9B-5833DF92FBB4}\client.msi installation failed. Error text: ExitCode: 1603

Action:
ErrorMessages: ccmsetup          2015-01-26 07:02:14       1764 (0x06E4) 

 

This is usually caused by failures within the Task Scheduler and those errors will also happen outside of ConfigMgr.

In order of preference, try the following suggested solutions;

1. Run the below command and restart the computer

CHKDSK C: /R 

2. Run the below command and restart the computer

fsutil resource setautoreset true c:\

3. Delete all files in c:\Windows\System32\config\TxR and restart the computer

App-V 5.0 SP3 and remote SQL

As App-V 5.0 SP3 has been released it is not only the client that gotten an update, but the App-V Server components has gotten a fresh new installer for the first time since App-V 5.0 SP1.

If one is working in a locked down environment where the roles of server and databases are seperated, and databases might be located on a shared SQL-hotel that no ordinary administrator is allowed access to – the question that rises is how does one get the database up 2 speed?

According to the notes the only supported scenario is to run from the installer provided by App-V 5.0 SP3, and what that is can be debated, which no SQL admin would dream about.

If executing the appv_server_setup.exe with the switch /layout there will be a folder extracted named database scripts.

Looking at the contents (in databasescripts) in App-V 5.0 SP3 and comparing that to the contents of App-V 5.0 SP1 – the files look fairly much the same;

image

When comparing the contents of the files the following differences emerge;

Createtables.sql


CREATE TABLE dbo.PackageGroupMembers (
Id                              int PRIMARY KEY IDENTITY,
PackageGroupId                  int NOT NULL,
PackageVersionId                int NOT NULL,
LoadOrder                       int NOT NULL,
<em>PackageOptional                    bit NOT NULL DEFAULT 0,
VersionOptional                    bit NOT NULL DEFAULT 0</em>
)

CREATE TABLE dbo.SchemaVersion (
Version                       int NOT NULL
)

InsertVersionInfo.sql


SELECT @minserviceversion = N'5.0.10107.0'
SELECT @dbversion  = N'5.0.10107.0'

And finally, looking at updates.sql – you can notice that there are some major changes. For example;

to begin with;


-- Replace SchemaChanges table with SchemaVersion table
RAISERROR('Removing SchemaChanges table', 0, 1) WITH NOWAIT
GO
IF (EXISTS (SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = 'SchemaChanges'))
BEGIN
DROP TABLE [SchemaChanges]
INSERT INTO [SchemaVersion] VALUES (1)
END
GO

and continue with;


-- Add PackageOptional and VersionOptional columns to PackageGroupMemebers table
ALTER TABLE PackageGroupMembers ADD PackageOptional bit NOT NULL DEFAULT 0, VersionOptional bit NOT NULL DEFAULT 0

and


-- Erase the current schema version from the SchemaVersion table (the new current version will be written during the install)
RAISERROR('Removing current schema version', 0, 1) WITH NOWAIT
GO
DELETE FROM [SchemaVersion]
GO

It seems that all the changes to the database are actually contained in updates.sql and createtables.sql.

It seems that the below commands are suffice. All of this is just gathered information from the Microsoft provided scripts on howto setup a clean database.


CREATE TABLE dbo.SchemaVersion (
Version                       int NOT NULL
)

INSERT INTO [SchemaVersion] VALUES (1)

-- Add PackageOptional and VersionOptional columns to PackageGroupMemebers table
ALTER TABLE PackageGroupMembers ADD PackageOptional bit NOT NULL DEFAULT 0, VersionOptional bit NOT NULL DEFAULT 0
-- Update SchemaVersion table to version 2
DELETE FROM [SchemaVersion]
INSERT INTO [SchemaVersion] VALUES (2)

DELETE FROM [SchemaVersion]

Once this is completed, head on over to your App-V Management Server. install the .NET Framework 4.5.1

Then install the new App-V 5.0 SP3 bits – first the Management Server. Then the Publishing server. After that you are all done!

You can verify that your servers are operational by simply accessing their respective website. if anything odd shows up – check the event logs under Microsoft\App-V

App-V 5, ConfigMgr compliance and fixes

App-V 5 has recently gotten hit by two odd behaviors relating to an update and .NET Framework 4.5.2.

KB2984972 was released as an update for RDC, and caused some havoc both for App-V 4 and App-V 5. The workaround is documented in the article and essentially allows anyone to remove the unfortunate end-user experience by adding some registry keys.

.NET Framework 4.5.2 was released and quite early on people started noticing that a freeze could be experienced when using certain App-V applications. The culprit seems to be the processes wisptis.exe, and the issue could temporarily be worked around by terminating the process.

App-V 5 got hit by two issues that both were resolved by adding registry keys under the registry key ObjExclusions. The Gladiator has written an article that details more about this registry key, the purpose of it and the effects of it. The article is focused on App-V 4, however the knowledge and concepts still apply to App-V 5.

Under (HKLM\Software\Microsoft\AppV\Subsystem\ObjExclusions) this registry key there are a lot of registry keys starting at 1 and going upwards. Each registry key contains a value (oh, really?) that is the name of an object that is not virtualized.  Anyone can append new values by using the next available number. Aaron Parker wrote a great article on howto leverage Group Policy to add the requested registry keys to resolve the issues for KB2984972.

Let’s detail the fun fact about this registry key;

There are in a default installation of App-V registry keys from 1-92. On any given default installation the next available number we can use is 93. We now have two issues and would therefore end up with two extra registry keys (93 and 94). My guess is that Microsoft might potentially include these two above recommended registry keys in a future installation of App-V when a new version comes out. Forcing these values to be added to a specific number in the series could potentially throw other valuable exclusions out the window…

Therefore I personally voted against Group Policy (Preferences) and decided to go the route of ConfigMgr Compliance Settings.

By creating a configuration item I can achieve the following;

Detect if the specific value is already in the list
Find the next available number to create a new registry key in
Append the value if it doesn’t already exist.

In the end, this is what I came up with;

appv_ci

Detect if the App-V client is installed;

appv_detection

Two checks for each specific registry key;

appv_check

Create a rule set that will allow for remediation;

appv_wsptis

Scripts part of the Configuration Item. This sample is from the fix for KB2984972.

Check:

$regKey = "HKLM:\SOFTWARE\Microsoft\AppV\Subsystem\ObjExclusions"
$p = Get-ItemProperty $regKey
$kb2984972 = $p.PSObject.Properties | where { $_.Name -match "[0-9]" -and $_.Value -eq "TermSrvReadyEvent" } | select-object -ExpandProperty Name -ErrorAction SilentlyContinue

if(($? -and ($kb2984972 -ne $null))) {
1
}
else {
-1
}

Remediation:

$regKey = "HKLM:\SOFTWARE\Microsoft\AppV\Subsystem\ObjExclusions"
$p = Get-ItemProperty $regKey
$topvalue = $p.PSObject.Properties | Where-Object { $_.Name -match "[0-9]" } | Sort-Object -Property Name -Descending | Select-Object -first 1 -ExpandProperty Name
$topvalue = 1 + $topvalue

Function New-RegistryKey([string]$key,[string]$Name,[string]$type,[string]$value)

{

#Split the registry path into its single keys and save

#them in an array, use \ as delimiter:

$subkeys = $key.split("\")

#Do this for all elements in the array:

foreach ($subkey in $subkeys)

{

#Extend $currentkey with the current element of

#the array:

$currentkey += ($subkey + '\')

#Check if $currentkey already exists in the registry

if (!(Test-Path $currentkey))

{

#If no, create it and send Powershell output

#to null (don't show it)

New-Item -Type String $currentkey | Out-Null

}

}

#Set (or change if alreday exists) the value for $currentkey

Set-ItemProperty $CurrentKey $Name -value $Value -type $type

}

New-RegistryKey $regkey $topvalue "String" "TermSrvReadyEvent"

As a final treat. Here is the Configuration Item – ready to be imported into ConfigMgr.

 

2014-12-07 – Sebastian Gern stated an additional registry key for WISPTIS. The Configuration Item is also updated with the new settings.

AVE: Scripting

This is just some details that showed themselves while using AVE to insert a script for an App-V 5 package.

You can choose to insert the script directly into the package, or to a configuration file.

image

When configuring the script you can easily choose the context of the script, the triggers and howto await the completion of the script.

image

Triggers for machine context are;

image

For use context

image

The “Do not use encoded executable paths” simple toggles the usage of App-V tokens on / off.

image

Regardless if you toggle this on or off – the title will always use App-V tokens.

image

What scripts you have added are easily seen from the contents of the virtual package

image

Thats it! Somethings that just makes life easier…

Application Virtualization Explorer

Kalle Saunamäki is a reallly deep dive kind of guy. He knows a lot about the App-V 4 filesystem, the App-V 4 client and now he is digesting the App-V 5 client.

image

How does one notice this? Well, he is the creator of the most amazing App-V 4 / 5 editing tool. Anyone doing any type of packaging for App-V needs this tool.

Convert

Have you previously tried converting packages from App-V 4 to App-V 5? Seems really as the process is the following;

  1. Install the sequencer on a dedicated VM
  2. Run PowerShell cmdlets to spit out a new package
  3. Open the package to, and save the package again to get all new virtual extensions.

AVE does this in a much faster and easier way. Simply choose to Import the 4.X package, and then save it as a brand new package. This can happen on any workstation you are using, even if you have the App-V client installed!

image

Whats in my package?

Have you wondered exactly what a package contains? As the sequencer only reveals the VFS and registry (and sometimes the shortcuts and FTAs), quite often you resort to reading the DynamicConfig-files that are produced in XML – after you save the package. And even when reading the XML-files trying to understand what will get published on a client – there are quite a few entries missing (shell extensions and browser plugins as a sample).image

AVE will immediately make this visible and more to that!

Services

How about this great feature! Did a service you captured get left out? You can import it and directly insert into the package;

image

App-v and wisptis.exe

Some applications that are virtualized with App-V 5 have caused issues with wisptis.exe –  a process to handle the input of pen-enabled devices. image

Symptoms

When an application that uses a .NET Framework(WPF, silverlight, SCOM / SCCM console, App-V UI etc) 4.5.2 is virtualized on any version of the App-V 5 client there is severe mouse-lag or hang.

Reproduce the issue

Connect a device which uses the Tablet Input service – most commonly Wacom-devices. Any WPF application based on .NET Framework 4.5.2 can cause the issue – for example using Chrome to access Outlook Web Access and then pressing “New message” (will use Silverlight) to generate a new email will spawn the wisptis.exe. The mouse freezes. If the process (wisptis.exe) is terminated mouse responsive will return to normal. The below is the process start of wisptis.exe

Process 5836 starting at 000000013F5AD9C8 C:\Windows\System32\wisptis.exe 11:24:08.939: [6388/6532] NtTerminateProcess( ProcessHandle=0x4f8, ExitStatus=0xc000042c ) => 0

The attempt to start this process fails with an STATUS_ELEVATION_REQUIRED code.

(credits to Paul Richards for the above info)

Public Workarounds

It seems that reverting back to .NET 4.5.1 has resolved the issue for quite a few people. If your application actually requires the .NET Framework 4.5.2 version that is of no use as a workaround. Installing the below hotfix may improve the issue aswell, however the successrate so far is low;

Mouse drawing is displayed incorrectly when the screen resolution is restored after a full-screen application stops on a Windows 7-based computer that has a multitouch screen installed

(while you are at it – apply this one aswell: Tablet PC Input Panel cannot be moved after you install update 2973201 in Windows 7 or Windows Vista )

Obvious workarounds

WISPTIS.exe is a process to handle the pen input, and therefore we can reduce our impact by completely disabling the the functionality. The policy can be find under;

User Configuration – Administrative Templates – Windows Components – Tablet PC – Cursor image

You can also disable it as a service (applicable on Windows 7 – named “Touch Keyboard and Handwriting Panel Service” for Windows 8): image

I haven’t personally confirmed this and so far this seems extremely intrusive as it completely disables the functionality for all applications.

Community discussed workarounds

WPF uses an interface, documented on MSDN, to determine if this is a tablet / touch enabled device. If you, inside the App-V 5 package while sequencing, edit the following registry key;

HKEY_CLASSES_ROOT\Interface\{C247F616-BBEB-406A-AED3-F75E656599AE}

Change the default value (to something else), and then set it to Override Local. image

This obviously breaks the input functionality, however only for the virtualized application.

(Thanks Paul Richards for the above suggestion – brilliant in so many ways)

Microsoft Support Solution – 2014-10-15

This is the Microsoft suggested solution. Apparently App-V makes an attempt to start a second process, and therefore the lag is experienced. Create the following registry key on the App-V client machine. Location:“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AppV\Subsystem\ObjExclusions” Type:       Reg_SZ
Name:     93
Value:     {773F1B9A-35B9-4E95-83A0-A210F2DE3B37}-running

The number 93 is used in this because 93 is the first available number in a default installation. This might be higher if your installation has more object exclusions.

Thanks Paul Richards for the update

Long-term resolution

Microsoft has released an informal article (Sebastian Gernert posted it), and as a far as we can see the recommended changes are available in App-V 5.0 SP3

Interesting topic

If you are a heavy user of Wacom hardware – see this guide of Vizibler that hopefully can improve your situation.