Add repair feature of ConfigMgr 1810

It would be nice to get the repair feature supported in PSADT.

https://www.imab.dk/repairing-broken-applications-using-software-center-and-sccm-1810-system-center-configuration-manager/

“Deploy-Application.exe -DeploymentType repair”

Thank you for this great Tool.

1 Like

I came here needing this exact same thing. It is possible others have ran into the same issues as me.

For us, sometimes the installer source path is no longer present on the system. In our imaging process our CCMCache gets removed when preparing the ConfigMgr client, thus orphaning the MSI source.

Wouldn’t be a big deal if ConfigMgr’s Windows Source Management worked properly with PSADT.
This update cycle sets the “HKLM\Software\Classes\Installer\Products[Package_GUID]\SourceList\URL\1” location to the base content source path and does not include the “Files” subfolder in the PSADT.

So when a repair is ran it does not know the correct fallback URL of source .MSI and prompts the user for the path.

This is really a ConfigMgr shortcoming when using script installer technology and kind of a problem generated by using the PSADT.

Others have eluded to this issue in these posts.

ConfigMgr will update the URL subkey if the MSI product code is added under Windows Source Management for the Application Deployment Type. But on the client machine this gets set to “http://DistributionPoint.domain.com/sms_dp_smspkg$/ContentId” which as I said before is the base content location. But in PSADT scripts the .MSI is located in the “Files” subfolder.

In my tests adding this sub folder to the end of the URL fixes the broken source and repairs can then be ran.

One solution would be to change the PSADT script to look for the MSI in the root folder. This would fix the issue with running repair and ConfigMgr could fix the source list. But I am sure this could present additional issues.

Another temp solution would be to copy the .MSI into the root subfolder as well. But this is really not desirable either.

If the PSADT had new deployment type of repair the issue would only be partially addressed.

In order to run a repair the following syntax is needed:
msiexec /af {ProductCode}

The following syntax will still prompt for missing source files if the Net and Url subkeys do not contain the Msi.
msiexec /af “FilePathToMsi”

So the missing installer source path still needs to be fixed. Fixing this path could be scripted in a new “repair” DeploymentType in the PSADT.
But the Product Code would have to be defined in the ConfigMgr Deployment type and the Windows Source List Update cycle would need a previous run on the client. Then the PSADT would need to find the URL subkey and add “Files” to it.

You effectively have a broken install source until a repair is executed from software center.
This means future installs and uninstalls of the MSI will fail until this has executed.

I would like other peoples input including the developers on this scenario.

Function AddSourceList {
<#
.SYNOPSIS
Ajoute un chemin au sourcelist d’un MSI
.DESCRIPTION
Ajoute un chemin au sourcelist d’un MSI
.Parameter
Str_MSIPathAndFile
Donner un chemin complet d’un MSI incluant le nom du fichier. Le
MSI doit se trouver dans le répertoire.
.EXAMPLE
AddSourcelist MSI
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Str_MSIPathAndFile
)
Begin {
## Get the name of this function and write header
[string]${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -CmdletBoundParameters $PSBoundParameters -Header
}
Process {

     Try {

         $MSIPath=split-path $Str_MSIPathAndFile
         $ProductCode= Get-MsiTableProperty -Path $Str_MSIPathAndFile
         #$productcode='{09AAAB09-6DBA-4DD9-9865-54597D3FBCA8}'
         #$MSIPath='E:\Temp\Logiciels\Antidote 8\msi\druide'

         $Com_WI = New-Object -ComObject WindowsInstaller.Installer

         $codeInvokeMethod = {
             $type = $this.gettype();
             $index = $args.count - 1;
             $methodargs = $args[1..$index]
             $type.invokeMember($args[0], [System.Reflection.BindingFlags]::InvokeMethod, $null, $this, $methodargs)
         }
         $Com_WI = $Com_WI | Add-Member -MemberType ScriptMethod -Value $codeInvokeMethod -Name InvokeMethod -PassThru

         Try {
            #2017-02-17 FR J'ai modifié cette section car dans certains cas le produit n'aura pas de product code ce qui empêche un repair et génère une erreur. Je teste donc la présence du product code avant quoi que ce soit
            #Et génère une inscription dans le log le cas échéant.
             If ($productcode.productcode -ne $null) {
                Write-log "Ajout du Sourcelist $($MSIPath) au MSI $($Str_MSIPathAndFile) Product code $($productcode.productcode)"
                $Com_WI.InvokeMethod('AddSource', "$($productcode.productcode)", '', "$($MSIPath)")
            }
            Else {Write-log "Le product code de $($Str_MSIPathAndFile) est absent. On ne peut pas ajouter un sourcelist.`
                                     Il faut valider si on peut faire une réparation." -Severity 2 -Source $deployAppScriptFriendlyName}
         }
         Catch {
             Write-Log -Message "Failed to resolve registry path [$regKey]. `n$(Resolve-Error)" -Severity 3 -Source ${CmdletName}
             Continue
         }
     }
     catch {
         Write-Log -Message "Failed to resolve registry path [$regKey]. `n$(Resolve-Error)" -Severity 3 -Source ${CmdletName}
         Continue
     }

 }
 End {
         Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -Footer
     }

}
#************************************************************************************************************
#endregion 2.0 AddSourceList
#************************************************************************************************************

Please do not mix the function of msi installation source management with the new 1810 feature.
The msi repair is very limited, no custom steps are possbile. With the new 1810 feature you can use msi and exe files and add your own custom steps to the repair step.

Adding a section for repair to Deploy-Application.ps1 would be easy. I was trying to point out that it would not work all of the time if the sources are orphaned.

Adding this un-tested ElseIf code to should do the trick. I will try to test it later today.

ElseIf ($deploymentType -ieq 'Repair')
{
	##*===============================================
	##* PRE-REPAIR
	##*===============================================
	[string]$installPhase = 'Pre-Repair'

	## Show Progress Message (with the default message)
	Show-InstallationProgress

	## <Perform Pre-Repair tasks here>

	##*===============================================
	##* REPAIR
	##*===============================================
	[string]$installPhase = 'Repair'

	## Handle Zero-Config MSI Repairs
	If ($useDefaultMsi) {
		[hashtable]$ExecuteDefaultMSISplat =  @{ Action = 'Repair'; Path = $defaultMsiFile; }; If ($defaultMstFile) { $ExecuteDefaultMSISplat.Add('Transform', $defaultMstFile) }
		Execute-MSI @ExecuteDefaultMSISplat
	}
	# <Perform Repair tasks here>

	##*===============================================
	##* POST-REPAIR
	##*===============================================
	[string]$installPhase = 'Post-Repair'
	
	## <Perform Post-Repair tasks here>


}

The code above did not work without some other minor changes.

Line 43 Needs to be changed to:

[ValidateSet('Install','Uninstall','Repair')]

And line 113 needs to be changed to:

If ($deploymentType -ine 'Uninstall' -and $deploymentType -ine 'Repair') {

With these changes -DeploymentType Repair will work with the PSADT.

Hi g4m3c4ck, thanks for the code. I thought something need to be changed in the code of the deploy-application.exe before the repair part will work. I will try it later and give you a feedback. thanks

I would like to see it officially added to the project so I submitted the change with a working Deploy-Application.ps1 to an open issue on github here.

Implemented