Restore the Missing Windows Installer Cache Files

When I was patching a production SharePoint farm, the Configuration Wizard failed in three machines

When I was patching a production SharePoint farm, the Configuration Wizard failed in three machines due to missing packages. I had verified those packages and I could confirm that they are already installed. After quick investigation, I found that several of the installer cache files are missing from the Windows Installer cache files "C:\Windows\Installer".

For such issue, you have two options:

  1. Copy the missing files from another healthy server
    Such solution is a nightmare, because the files are located on other servers with different names, so, even if you copied all the contents of the “Installer” folder from different healthy machine, will not resolve the problem.
  2. Rebuild the server
    This is the optimum solution but would cost time and sometimes money.

All those files' information are restored on the windows registry, so, there must be away to identify the missing files.

I had searched online hopefully to find a script which can identify the missing files from the registry, but unfortunately, I didn’t find a decisive script which can do it. I could find some scripts with VBS language which can precisely identify the MSP (patch) files with the unique revision number which can be used to identify the missing files on other destination, but they were not precise to identify the MSI (Package) files.


I had found online many are complaining about the same issue when they upgrade/patch SharePointSQL or other enterprise software.

Hence, I had built my PowerShell script “Restore-InstallerFiles.ps1” which is capable to identify the missing files and/or restore them from different sources such as local folder, shared folder or another machine which can be accessed from the target machine.


  1. Click here to download the script.
  2. Copy the script to the target machine.
  3. Make sure that the current local identity has Read/Write permission on the "C:\Windows\Installer" folder of the target machine, and Read permission on the source folder (If the source was a machine name, make sure you have read permission on the local folder "C:\Windows\Installer" inside the source machine).
  4. Execute the script locally on the target machine with the following parameters and as illustrated with the following examples.

Minimum System Requirements:

PowerShell Version: 2.0
CLR (Microsoft .Net Framework) Version: 2.0

Script Parameters:

      .PARAMETER SourceMachine 
        Alias: M 
        Data Type: System.String[] 
        Mandatory: True 
        Description: The name of the source machine(s) where the script can find the missing files there, and restore them to the target machine with the correct names. 
        Example(s): "Machine1_Name""Machine2_Name""Machine3_Name""Machine4_Name" 
        Default Value: N/A 
        Notes: This parameter is a mandatory if the the "SourceFolder" not specified. 
      .PARAMETER SourceFolder 
        Alias: F 
        Data Type: System.String[] 
        Mandatory: True 
        Description: The source folder(s) where the script can find the missing files there, and restore them to the target machine with the correct names. 
        Example(s): "D\installer_bak""D\installer_bak2""E\installer_bak3" 
        Default Value: N/A 
        Notes: This parameter is a mandatory if the the "SourceMachine" not specified. 
      .PARAMETER ScanOnly 
        Alias: S 
        Data TypeSwitch 
        Mandatory: True 
        Description: Only scan for the missing files and then display them without attempting the fix. 
        Example(s): N/A 
        Default Value: N/A 
        Notes: This parameter is a mandatory and cannot be combined with the two parameters "SourceMachine" or "SourceFolder". 
      .PARAMETER LogFile 
        Alias: L 
        Data Type: System.String 
        Mandatory: False 
        Description: The location of the output transcript logging file. 
        Example(s): "D:\Log.txt" 
        Default Value: N/A 
        Notes: N/A


.\Restore-InstallerFiles -SourceMachine "Machine1", "Machine2", "Machine3";
.\Restore-InstallerFiles -SourceFolder "D:\InstallerFiles", "E:\InstallerFiles", "\\MachineX\D$\MSI Files";
.\Restore-InstallerFiles -SourceFolder "D:\InstallerFiles", "E:\InstallerFiles", "D:\InstallerFiles2" -LogFile "D:\Log.txt"; 
For further details, please run Get-Help .\Restore-InstallerFiles.ps1 -Detailed;.
  •  In the following screenshot, I'm using the script to scan for the missing files, which returned 0 missing files on that machine.


  • In the following screenshot, I'm using the script to scan for the missing files, which returned 7 missing files on that machine.

  • In the following screenshot, I'm using the script to restore the missing files from a local folder which copy of the installer cache files from different machine.



Date: March, 11th, 2017
Update(s): Backward compatibility with PS version 2.0 and CLR (Microsoft .Net Framework) version 2.0.
Fixing the bug in the internal function "Get-FileRevisionNumber" which was related with PS version backward compatibility.



Comments (29) -

  • Good job Ahmad , I have a question , Do the source machine need to hv a file name same as in target machine installer cache ?
    • Hi Pramod,
      If the names are the same on other machines, then, there is no need for my script!
      The idea of the script is to scan/crawl for the missing files on other sources (folders/machines) and copy them to the target machine. The scan basically looks into the subject and revision number inside the source file's metadata properties to find the matching with the missing ones.
      • Thank you Ahmad for a wonderful script , this was what I was looking for .
        Keep up the good work .
    • Hi,

      I hope it resolves your problem. Although I had already tried this script successfully on real large SharePoint farms, but it would be glad if you could share your experience with us and rate the script. Furthermore, your comments/suggestions would be much appreciated which would help me to enhance my script.
  • Hi, I have problem, when I try to use your script.
    I had few errors:
    Failed to access the metadata of the file "D:\x64\Setup\sql_rsshp.msi"!
    Method invocation failed because [System.__ComObject] does not contain a method named 'SummaryInformation'.


    Failed to access the metadata of the file "D:\x64\Setup\sqlsqm.msi"!
    Copy-TheMissingFile : Cannot bind argument to parameter 'DestinationFile' because it is an empty string.
    • Hi,

      Thanks for your comment.
      I think it would be a permission issue.
      Please make sure that you've read permission on the source folder and files. And you get to have Read/Write permission on the “C:Windows\Installer” folder.

      If you still encounter further errors, please use the "-LogFile" parameter to get the transcript of the script's output and share it with me. It would be something like that -LogFile "D:\Test.log" (Make sure you've Read/Write permission on that file path.).

      Looking forward to hearing from you soon.

      • HI Ahmad ,

        The Error : Failed to access the metadata of the file "D:\x64\Setup\sql_rsshp.msi"!
        Method invocation failed because [System.__ComObject] does not contain a method named 'SummaryInformation'.

        This error occurred because of function :  Function Get-FileRevisionNumber

        I have modified this function as below , now working fine:
        Function Get-FileRevisionNumber
                    [Parameter(Mandatory=$True, Position=0)][Alias("F")][String]$File
                $Installer = New-Object -com WindowsInstaller.Installer
                    Write-Host -Red "Failed to access the metadata of the file ""$File""!";
                    Return $null;
                $Database = $Installer.GetType().InvokeMember(“OpenDatabase”, “InvokeMethod”, $Null, $Installer, $($File,0))
                $SummaryInfo = $Database.GetType().InvokeMember(“SummaryInformation”, “GetProperty”,$Null , $Database, $Null)
                $PropertyCount = $SummaryInfo.GetType().InvokeMember(“PropertyCount”, “GetProperty”, $Null, $SummaryInfo, $Null)
                (0..$PropertyCount) | ForEach {  $Ar +=$SummaryInfo.GetType().InvokeMember(“Property”, “GetProperty”, $Null, $SummaryInfo, $_) }
                 return $Null
                if($Ar.Count -gt 8)
                     Return $output ;

        • Thank you very much for your precious comment.
          I had already seen this approach online before and I'm afraid, the function you had provided doesn't retrieve the revision number of the Patch files (*.msp).

          After I had reviewed my code, I can confirm that this error occurs when it tries access a non-Windows Installer file. In your case, the file you have could be corrupted, because I had tested on the  file “sql_rsshp.msi” for SQL Server 2012 and the file “sqlsqm.msi” for SQL Server 2012 and 2008 R2, and I could retrieve the revision number without any exception. I believe it had worked with you cause, you had added exception handler which would return a null value when it tried to call the “SummaryInformation“.

          Nevertheless, with thanks to you, I had updated that function with the Try/Catch scopes to handle the exception, and I had saw a missing parameter for the “Write-Line” and fixed it.
          • Thanks for the update Ahmad. I realize now that earlier method I used was wrong.
            I want to Point out one more function which was not working in Powershell 2 , The ordered Dictionary declaring technic was not supported in Powershell 2.

            so have modified the function Get-ProductCodeGuid  as below:

            Function Get-ProductCodeGuid
                        [ValidatePattern('^[0-9a-fA-F]{32}$')] [string]$CompressedGuid
                     write-host "CompressedGuid = $CompressedGuid"
                     $Indexes=New-Object System.Collections.Specialized.OrderedDictionary
                     #$Indexes =@{
                     $Indexes.Add(16, 2);
                     $Guid = '{'
                     foreach ($index in $Indexes.GetEnumerator()) {
                     $part = $CompressedGuid.Substring($index.Key, $index.Value).ToCharArray();[array]::Reverse($part)
                     $Guid += $part -join ''
                     $Guid = $Guid.Insert(9,'-').Insert(14, '-').Insert(19, '-').Insert(24, '-')
                     $Guid += '}'

                     Return $Guid

            let me know if this is correct way.
            • Hi,

              Thanks again for your precious comments!
              I admit that I didn’t consider the backward compatibility for my script to version 2.0. It was compatible with version 5.0 as I was using the “Class” keyword, and then I had modified my script to be compatible with version 4.0.
              After I had tested my script with backward compatibility using the “-Version” parameter “%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Version 2”, I had realized that this was the only function was not compatible with version 2. Hence, again with thanks to you, I had updated the function and replaced the array by the “OrderedDictionary” class as you suggested, also, I got to add the “$true” value for the “Mandatory” property as version 2 doesn’t accept it.

              Please don’t hesitate to contact me back if you have any further comments. You are always welcome.

  • Hi Ahmad,

    Cracking script but I'm having an issue where every file is returning the error "Failed to access the metadata of the file".

    I've tried several things to get around the problem.  I've used the "SourceFolder" and "SourceMachine" switches with no success.  I've tried using a domain account that has admin rights to both machines and I've also tried copying the source machine's "Installer" folder to the target machine and running as Admin but I keep getting the same result.  Both folders have been given full rights to the accounts I've used.

    Any guidance would be greatly received right now.

    • Hi,
      Usually this error is referring to non-Windows Installer files or corrupted ones as the script is trying to use the Windows Installer COM object to access the MSI and MSP files.
      Try another source, also, make sure you’ve read permission on that source folder and read/write permission on the target Installer folder.

      If you still encounter further errors, please do the following:
      1.  use the “-LogFile” parameter to get the transcript of the script’s output and share it with me. It would be something like that -LogFile
      D:\Test.log” (Make sure you’ve Read/Write permission on that file path.).
      2.  If possible, please upload one or more of the MSI/MSP files which the script can access to any Google Drive, One Drive, etc, and share the links with me.
      Looking forward to hearing from you soon.

      • Thanks for the quick reply Ahmad.

        I have tried 3 different machine sources and 1 folder source, all with the same outcome.  Not 1 of the files can be copied due to these errors.

        Every file that is scanned returns this error:

        Failed to access the metadata of the file "N:\TEST\24568c.msp"!

        Where it detects a file that is required on the target machine I get the following error:

        Failed to access the metadata of the file "N:\TEST\2d4f70.msi"!
        Copy-TheMissingFile : Cannot bind argument to parameter 'DestinationFile' because it is an empty string.
        At C:\New folder\Restore-InstallerFiles.ps1:256 char:72
        +                             Copy-TheMissingFile -SF $file.FullName -DF $destinat ...
        +                                                                        ~~~~~~~~~
            + CategoryInfo          : InvalidData: (Smile [Copy-TheMissingFile], ParameterBindingValidationException
            + FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Copy-TheMissingFile

        I've attempted to output the results to a log file using the "-LogFile" switch but nothing is written to the file. It remains blank.

        I've added some sample MSI files and a couple of screenshots to the following OneDrive link.  Let me know what think:!Anw5YzgudhsUg-ZzVqNFK8Kts8jyZg
        • Hi,

          Thank you very much for your reply and the info you had provided.
          I had tested the files, and there are all fine.
          I'm afraid we are facing environment compatibility issue.

          I would ask you kindly to do the following:
          1.  Download the updated script and run it again with the following two parameters:
          2.  Run this command on your PowerShell host and share with me the results:
          3.  Finally, I would like to know your Windows version info by the following commands:
          $windows = (Get-WmiObject -class Win32_OperatingSystem).Caption;
          $winVer = [System.Environment]::OSVersion.Version.ToString();
          $is64 = [System.Environment]::Is64BitOperatingSystem;
          $sp = [System.Environment]::OSVersion.ServicePack;

          Write-Host $windows;
          Write-Host "Win Ver: $winVer";
          Write-Host "Is 64 Bit: $is64";
          Write-Host "SP: $sp";

          Best Regards,
          • Morning Ahmad,

            Here are my results:

            1)  The log file is being created but no info is being passed to it still.  Verbose seems to now be showing more info.  Here's the start of it:

            PS C:\New folder> .\Restore-InstallerFiles.ps1 -Verbose -LogFile "C:\New folder\Log.log" -SourceFolder "H:\TEST"
            VERBOSE: Log file specified!
            VERBOSE: Validating the log file location ...
            VERBOSE: OK!
            VERBOSE: Attempting to create the log file ...
            VERBOSE: OK!
            VERBOSE: Scanning for the missing Package/Patch file(s) ...
            VERBOSE: Failed to retrieve the RevisionNumber/PackageCode from registry for the file "C:\Windows\Installer\9dbff01a.msi"!
            VERBOSE: Failed to retrieve the RevisionNumber/PackageCode from registry for the file "C:\Windows\Installer\9154d.msi"!
            VERBOSE: Failed to retrieve the RevisionNumber/PackageCode from registry for the file "C:\Windows\Installer\5474e7eb.msi"!
            VERBOSE: "68" found!
            VERBOSE: Validating the source path "H:\TEST" ...
            VERBOSE: -------------------------------------------------
            VERBOSE: The source location is OK!
            VERBOSE: Proceeding with crawling the missing file(s) from the source ...
            VERBOSE: -------------------------------------------------------------
            VERBOSE: Found "298" package/patch file(s) in the source location!
            VERBOSE: Processing the file:  H:\TEST\11e556.msp
            VERBOSE: Enters "Get-FileRevisionNumber" ...
            VERBOSE: Creating a "WindowsInstaller.Installer" COM object ...
            VERBOSE: "WindowsInstaller.Installer" COM object has been created successfully!
            VERBOSE: Retrieve the "SummaryInfo" of the file "H:\TEST\11e556.msp" ...
            VERBOSE: Exception: Method invocation failed because [System.__ComObject] doesn't contain a method named 'SummaryInformation'.
            VERBOSE: Source Method(s): Get-FileRevisionNumber Copy-MissingFiles Global:Restore-InstallerFiles <ScriptBlock> <ScriptBlock>
            VERBOSE: Code Line:             $SummaryInfo = $WindowsInstaller.SummaryInformation($File, 0);

            VERBOSE: Position Message: At C:\New folder\Restore-InstallerFiles.ps1:421 char:13
            +             $SummaryInfo = $WindowsInstaller.SummaryInformation($File, 0);
            +             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            VERBOSE: Windows Installer is null? False
            VERBOSE: SummaryInfo is null? True
            VERBOSE: Exits "Get-FileRevisionNumber"!
            VERBOSE: Revision Number:  
            VERBOSE: No "Revision Number" has been detected for the file "H:\TEST\11e556.msp"!
            VERBOSE: Attempting to to verify with the "Subject" property for "H:\TEST\11e556.msp"...
            VERBOSE: Registry Subject:
            VERBOSE: File Subject: Patch

            2) Result of $PSVersionTable:

            Name                           Value                                                                                                                                      
            ----                           -----                                                                                                                                      
            PSVersion                      3.0                                                                                                                                        
            WSManStackVersion              3.0                                                                                                                                        
            CLRVersion                     4.0.30319.36366                                                                                                                            
            BuildVersion                   6.2.9200.17065                                                                                                                              
            PSCompatibleVersions           {1.0, 2.0, 3.0}                                                                                                                            
            PSRemotingProtocolVersion      2.2

            3) Windows Version:

            Microsoft Windows Server 2012 Standard
            Win Ver: 6.2.9200.0
            Is 64 Bit: True

            Thanks for your time in looking at this.

            • Hi,

              Thank you very much for your reply. And sorry for my late one.
              After several tests, I can confirm that this script is not 100% compatible with PS version 2.0. And from the info you had provided, I believe we have the same compatibility issue with PS version 3.0 as well.
              Hence, I can confirm that, the minimum PowerShell Version required in order to run the script is "4.0".
              Run "$PSVersionTable;" in your PowerShell host to verify the running PSVersion (PowerShell Version).
              You can upgrade to a higher PowerShell version by downloading and installing the "Windows Management Framework".
              Windows Management Framework 4.0:
              Windows Management Framework 5.0:
              Please review the 'System Requirements' section first before you start your download to verify the compatibility with your OS version.

              Are you able to install a higher “Windows Management Framework” version on the target server? If not, are you able to run VBS script there? If upgrading the PS version is not applicable, and you are allowed to run VBS on that server, please bear with me some days to finish my VBS version which is still incomplete and unpublished yet.

              Best Regards,
              • Hi Ahmad,

                No need to apologise, I'm extremely grateful for the support you're providing.

                I've installed Framework 5.0 only one of our boxes and can confirm that the script is now working a treat.  I'm just hunting down the last 13 missing packages and it'll be complete.

                Thanks again for this script and all your efforts in troubleshooting the problems I've had.

  • Hi Ahmad,

    thank you for your script. i was able to restore about 64 packages but also have the same errors as other users.
    I am running with framework 4 so powershell 4 host.

    have the following errors still:
    Failed to access the metadata of the file "\\servername\C$\Windows\Installer\a4b512.msp"!
    Failed to access the metadata of the file "\\servername\C$\Windows\Installer\a4b51c.msp"!
    Failed to access the metadata of the file "\\servername\C$\Windows\Installer\a4b59b.msp"!
    Failed to access the metadata of the file "\\servername\C$\Windows\Installer\aacb5.msi"!
    [021/539] - 33a83.msi : \\servername\C$\Windows\Installer\aacb5.msi  >>>>>  C:\Windows\Installer\33a83.msi
    Failed to access the metadata of the file "\\servername\C$\Windows\Installer\aacb9.msi"!
    [022/539] - 33a87.msi : \\servername\C$\Windows\Installer\aacb9.msi  >>>>>  C:\Windows\Installer\33a87.msi
    Failed to access the metadata of the file "\\servername\C$\Windows\Installer\aacbd.msi"!
    [023/539] - 33a8b.msi : \\servername\C$\Windows\Installer\aacbd.msi  >>>>>  C:\Windows\Installer\33a8b.msi
    Failed to access the metadata of the file "\\servername\C$\Windows\Installer\aacc1.msi"!
    [024/539] - 33a8f.msi : \\servername\C$\Windows\Installer\aacc1.msi  >>>>>  C:\Windows\Installer\33a8f.msi
    Failed to access the metadata of the file "\\servername\C$\Windows\Installer\aacc5.msi"!
    [025/539] - 33a93.msi : \\servername\C$\Windows\Installer\aacc5.msi  >>>>>  C:\Windows\Installer\33a93.msi
    Failed to access the metadata of the file "\\servername\C$\Windows\Installer\aacc9.msi"!

    kind regards
    • Hi Jack,

      Thank you very much for your feedback.
      I know all the pain comes from the internal function “Get-FileRevisionNumber” which is not compatible with any PS version before 4.0.
      Not sure in your case why it’s not working, cause I had run the script on 10s of life servers already without an issue with PS version 4.0 and CLR version 4.0.
      Would you kindly run “$PSVersionTable” and share the output with me?

      Nevertheless, I believe I had fixed the lack of the “Get-FileRevisionNumber” function and it should be working fine now with PS version 2.0 and higher, and CLR (.Net Framework) version 2.0 and higher as tested on my labs.
      Please download the beta version from the following link and give it a shot, then give me your feedback.

      If you still encounter further issues, please use the command again with the following two parameters and share the log file with me, and of course, you can replace the server name, and remove your user name from the log file as it’s generated automatically:
      -LogFile "D:\ Restore-InstallerFiles-PSV2.0.log "

      Thanks in advance.

      Best Regards,
      • Good Morning Ahmad,

        hereby the output of $psversiontable:
        Name                           Value
        ----                           -----
        PSVersion                      4.0
        WSManStackVersion              3.0
        CLRVersion                     4.0.30319.18449
        BuildVersion                   6.3.9600.16406
        PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
        PSRemotingProtocolVersion      2.2

        will start to run the script now.
        thank you for your quick response!

        Kind Regards
      • Dear Ahmad,

        The new scripts works flawlesly! i am able to update the sharepoint servers now. no more error messages! thank you VERY much for you help!

        Kind Regards
  • Running windows 10 and although my c:\windows\installer is completely empty (unfortunately was erased by mistake), I got "0"  Found.
    Any Ideas?.
    • Hi Yehia,

      Thank you very much for your comment.
      First let’s clear one point, this script doesn’t restore the missing/deleted files from nil.
      It restore/copy the missing files from other existing healthy source such as other machine in the same network, or a copy of the installer folder from another machine with the same condition of the target one. When I say the same condition, I mean almost the same applications installed on the source machine, should be installed on the target one in order to give the script a chance to determine the missing files.

      For your comment about the “0” found, do you mean this is the scan result? Cause if it is, it should give you a big number greater than “0” for sure in your condition.

      Anyway, we feel the pain of the missing files when we try to uninstall an existing application or upgrade/patch it, or install some application which has dependency on one of the installed such as the SharePoint for example which needs some other files related to SQL. Which scenario do you have?

      Looking forward to hearing from you soon.

      Thanks in advance.

      Best Regards,
      • Hi Ahmed,
        Many many thanks for your instant reply. It was the scan result. The script finally worked. What I did was running the get-help command and it just worked afterword. The scan gave me a huge number found . I'm embarrassed to write it down here Smile . It covered some of them and I will try to cover the rest using another source machine. Thank you for your great work and help.
        Best regards.
  • This a great script
    Is this script applicable to be run remotely or using SCCM OR PStools.
    I have tried many ways but it failed to recover missing files and it give me missing files found 0 and when I run it locally it worked.

    Please advise how I can using is script to be run remote in many computer.
    • Hi Mohammed,

      Thank you very much for your comment.

      The script is designed to work locally.
      But, you can tweak it to work remotely which I had done it before for specific project to get the list of the servers from some data source.

      I hope you find my reply useful.
      • Thank you so much for your support

        Can you explain to me how i can make it work remotely...
        I really appreciate your support and help
        • Hi Mohammad,

          This would require for me to create a simulation VMs to test the remote task.
          And I'm afraid, I cannot share the code I've done for my ex employer cause it should belong to them and also it was customized to work with their environments with many validations.
          You can search online on how to execute a script remotely with consideration that you cannot perform a remote action/session from a remote session. I mention that because my script is copying files from remote machine already which cannot be added into a remote session. My code must be revised to use parts in remote action and the other parts as local. I know it sounds a complicated task, but I had achieved it before.

          Good luck.

          Best Regards,

Add comment