Fixing a Corrupt App-V Client

I picked up a handy script recently that saved me a lot of time when fixing a corrupt App-V client.  Kudos to Roy Essers with his post.

Essentially the issue occurs when a packager creates two packages with the same display name (and hence Package URI), but that have different version IDs.  The App-V client gets confused, and even fails to work if you run a simple Get-AppvClientPackage command with a resultant:

Get-AppvClientPackage : Application Virtualization Service failed to complete requested operation.
Operation attempted: Get Packages.

As an example, you can navigate to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AppV\Client\Streaming\Packages\

And you will no doubt find two subkeys (which are VersionIDs of packages) that contain a matching PackageURI registry value!

And the quick-fix is to run this script as an administrator – it will fix the corrupt App-V cache and initiate a publishing synchronisation as the logged in user:

cls
$logFile = "$env:TEMP\repair-app-v-client.log"

function Write-Log {
    [CmdletBinding()]
    param(
        [string]$message
    )
    try {
        Write-Host $message
        Add-Content -Value "$((Get-Date).tostring('dd/MM/yyyy HH:mm:ss')) - $message" -Path $logFile
    } catch {}
}

Write-Log "Repairing App-V Client.  Please Wait."

$corruptPackageFound = $false

$Catalog = "C:\Programdata\Microsoft\AppV\Client\Catalog\Packages"
Foreach($Package in (Get-ChildItem $Catalog)){ 
    Foreach($Version in (Get-ChildItem (Join-Path $Catalog $Package.Name))){

        $VersionID = $($Version.Name).Trim("{","}")
        $PackageID = $(($Package.Name).Trim("{","}"))

        $Manifest = "$Catalog\$($Package.Name)\$($Version.Name)\Manifest.xml"

        [xml]$Content = Get-Content $Manifest

        if((($Content.Package.Identity.VersionId).toUpper()) -ne $VersionID){
            Write-Log "Found Corrupt Package:"
            Write-Log "Name: $($Content.Package.Properties.DisplayName)"
            Write-Log "PackageID: $PackageID"
            Write-Log "VersionID: $VersionID"
           
            $corruptPackageFound = $true
        }       
    }

    if ($corruptPackageFound) {
        #remove all packages with same packageid
        #we iterate through folder structure and remove by packageid and versionid in case get-appvclientpackage still doesnt work
        #we also dont just remove the corrupt package because it often reappears on sync

        Write-Log "Cleaning package cache"

        Foreach($Version in (Get-ChildItem (Join-Path $Catalog $Package.Name))){

            $VersionID = $($Version.Name).Trim("{","}")
            $PackageID = $(($Package.Name).Trim("{","}"))
     
            Remove-AppvClientPackage -PackageId $PackageID -VersionId $VersionID | Out-Null
        }
    }

    $corruptPackageFound = $false
}


Write-Log "Re-synchronising App-V Applications."

#sync apps impersonating the logged in user

#create the action we want to perform
$command = 'powershell.exe'
$args = '-executionpolicy bypass -windowstyle hidden -command "Get-AppvPublishingServer | Sync-AppvPublishingServer"'
$action = New-ScheduledTaskAction -Execute $command -Argument $args

#create the scheduled task
$trigger = New-ScheduledTaskTrigger -AtLogOn
$principal = New-ScheduledTaskPrincipal -UserId (Get-CimInstance –ClassName Win32_ComputerSystem | Select-Object -expand UserName)
$task = New-ScheduledTask -Action $action -Trigger $trigger -Principal $principal
$taskName = "AppvSync"
Register-ScheduledTask $taskName -InputObject $task | Out-Null

#start the scheduled task
Start-ScheduledTask -TaskName $taskName

#wait for the task to finish - check every 2 seconds or max 60 seconds
$timeout = 60 #seconds
$timer =  [Diagnostics.Stopwatch]::StartNew()
while (((Get-ScheduledTask -TaskName $taskName).State -ne  'Ready') -and  ($timer.Elapsed.TotalSeconds -lt $timeout)) {    
  Write-Verbose  -Message "Waiting on scheduled task..."
  Start-Sleep -Seconds 2
}
$timer.Stop()

Unregister-ScheduledTask -TaskName $taskName -Confirm:$false

Write-Log "Finished Repairing App-V Client."
Start-Sleep -Seconds 5