SCCM Script: Fix Software Update Store

Overview

I developed this tool, Fix-DGMSCCMUpdateStore.ps1, to assist in fixing Windows UpdateStore Corruption (Datastore.edb) on SCCM Client Computers. On the SCCM client machine, the Windows UpdateStore Datastore.edb in Windows\Software Distribution\.. contains scan results. Over time, this may become corrupted which can stop updating from occurring on the client machine. Additionally, an error might be seen on the client application log reporting wuaueng.dll (1668) SUS20ClientDataStore: Database C:\WINDOWS\SoftwareDistribution\DataStore\DataStore.edb requires logfiles xx-yy in order to recover successfully.

Fix-DGMSCCMUpdateStore Tool

The tool ill automatically attempt to fix the Windows Update Store on an array of SCCM client computers imported via a .CSV file. When run, the tool will perform the following tasks on each computer within the .CSV:

  • Stop the Windows Update Service
  • Move SoftwareDistribution to a backup location
  • Start Windows Update Service
  • Recreate SoftwareDistribution

The tool requires the –csvfile argument, which is the path to a .CSV file containing one column, Hostname, with the hostnames listed in the column and can be run as in the example below.

Fix-DGMSCCMUpdateStore Log File

The utility will create a log file that is compatible with the CMTrace tool, which includes the thread, time, state and component for each process.


<h1>PowerShell Script: Fix-DGMSCCMUpdateStore.ps1</h1>
<#
.Synopsis
   Fix the Windows UpdateStore on an array of SCCM clients.
.DESCRIPTION
   The Windows UpdateStore Datastore.edb in Windows\Software Distribution\.. contains scan results. This may become corrupted. This tool will fix it on an array of computers imported.
.EXAMPLE
   Fix-DMGSCCMUpdateStore -CSVFile .\Fix-DMGSCCMUpdateStore-Import.csv

#>

function New-DGMLogFile
{
  param (
  [Parameter(Mandatory=$true)]
  $message,
  [Parameter(Mandatory=$true)]
  $component,
  [Parameter(Mandatory=$true)]
  $type )

  switch ($type)
  {
    1 { $type = "Info" }
    2 { $type = "Warning" }
    3 { $type = "Error" }
    4 { $type = "Verbose" }
  }

  if (($type -eq "Verbose") -and ($Global:Verbose))
  {
    $toLog = "{0} `$$<{1}><{2} {3}><thread={4}>" -f ($type + ":" + $message), ($Global:ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
    $toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $Global:LogFile)
    Write-Host $message
  }
  elseif ($type -ne "Verbose")
  {
    $toLog = "{0} `$$<{1}><{2} {3}><thread={4}>" -f ($type + ":" + $message), ($Global:ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
    $toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $Global:LogFile)
    if ($type -eq 'Info') { Write-Host $message }
    if ($type -eq 'Warning') { Write-Host $message -ForegroundColor Yellow}
    if ($type -eq 'Error') { Write-Host $message -ForegroundColor Red}
    

  }
  if (($type -eq 'Warning') -and ($Global:ScriptStatus -ne 'Error')) { $Global:ScriptStatus = $type }
  if ($type -eq 'Error') { $Global:ScriptStatus = $type }

  if ((Get-Item $Global:LogFile).Length/1KB -gt $Global:MaxLogSizeInKB)
  {
    $log = $Global:LogFile
    Remove-Item ($log.Replace(".log", ".lo_"))
    Rename-Item $Global:LogFile ($log.Replace(".log", ".lo_")) -Force
  }
} 

function GetScriptDirectory
{
  $invocation = (Get-Variable MyInvocation -Scope 1).Value
  Split-Path $invocation.MyCommand.Path
} 

function Fix-DMGSCCMUpdateStore
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0,
                   ParameterSetName='Parameter Set 1')]
                   [ValidateScript({(Test-Path $_)})]
                   $CSVFile,
        # Param2 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0,
                   ParameterSetName='Parameter Set 2')]
                   [ValidateScript({(Get-ADComputer -Identity $_).objectclass -eq 'computer' })]
                   [String]$Hostname
    )

    Begin
    {
        
        if ($CSVFile -ne $null){
            Write-Host Importing $CSVFile...
            $csv = import-csv "$CSVFile"
        }else{
            $csv = [PSCustomObject]@{
                Hostname = $Hostname}
        }
        $service1 = "wuauserv"
        $service2 = "bits"
        Write-Host =========================================
        Write-Host SCCM Fix Windows Update Store by dmaiolo
        Write-Host =========================================
        Write-Host "v0.2 (2017-12-11)"
        New-DGMLogFile -message ("Starting Logging for Fix-DMGSCCMUpdateStore") -component "Main()" -type 1 
    }
    Process
    {
        $computers = @();
        
        $csv | foreach-object {
            $h = $_.Hostname
            
            
            #Test if machine is online
            if(Test-Connection -ComputerName $h -count 2 -quiet){
                Write-Host "Online: $h" -ForegroundColor Green

                #Stop Windows Update service
                try{
                    (get-service -ComputerName $h -Name $service1 -ErrorAction Stop).Stop()
                    New-DGMLogFile -message ("Stopped $service1 service on $h.") -component "Main()" -type 1
                }
                catch{
                    New-DGMLogFile -message ("Could NOT Stop $service1 service on $h.") -component "Main()" -type 3
                }
                
                #Sleep 10 seconds to give service enough time to react
                Write-Host "Sleeping 5 Seconds..."
                Start-Sleep -s 5

                #Stop BITS service
                try{
                    (get-service -ComputerName $h -Name $service2 -ErrorAction Stop).Stop()
                    New-DGMLogFile -message ("Stopped $service2 service on $h.") -component "Main()" -type 1
                }
                catch{
                    New-DGMLogFile -message ("Could NOT Stop $service2 service on $h.") -component "Main()" -type 3
                }
                #Sleep 5 seconds to give service enough time to react
                Write-Host "Sleeping 5 Seconds..."
                Start-Sleep -s 5

                #Rename the software update store
                
                $sourcepath = "\\$($h)\c$\Windows\SoftwareDistribution"
                $destinationpath = "\\$($h)\c$\Windows\SoftwareDistribution_$(Get-Date -Format dd-MM-yyyy)"
                $destinationpath2 = "\\$($h)\c$\Windows\SoftwareDistribution_$(Get-Date -Format dd-MM-yyyy)"

                #Appending destination path if script already run today
                if (Test-Path $destinationpath){
                    $n=0
                    while ((Test-Path $destinationpath2) -eq $true){  
                        New-DGMLogFile -message ("Backup location $destinationpath2 already exists.") -component "Main()" -type 1
                        ++$n
                        $destinationpath2 = $destinationpath + "-" + $n
                    }
                    $destinationpath = $destinationpath2
                    
                }

                Write-Host "Renaming $sourcepath..."
                try{
                    Move-Item -Path $sourcepath -Destination $destinationpath -Force
                    New-DGMLogFile -message ("Renamed SoftwareDistribution to $destinationpath.") -component "Main()" -type 1
                }
                catch{
                    New-DGMLogFile -message ("Could NOT Rename SoftwareDistribution to $destinationpath.") -component "Main()" -type 3
                }
                
                #Start the Windows Update service
                try{
                    (get-service -ComputerName $h -Name $service1 -ErrorAction Stop).Start()
                    New-DGMLogFile -message ("Started $service1 service on $h.") -component "Main()" -type 1
                }
                catch{
                    New-DGMLogFile -message ("Could NOT Start $service1 service on $h.") -component "Main()" -type 3
                }

                #Start the BITS service
                try{
                    (get-service -ComputerName $h -Name $service2 -ErrorAction Stop).Start()
                    New-DGMLogFile -message ("Started $service2 service on $h.") -component "Main()" -type 1
                }
                catch{
                    New-DGMLogFile -message ("Could NOT Start $service2 service on $h.") -component "Main()" -type 3
                }

                #Give the services 5 seconds to wake up and create new folders
                Start-Sleep -s 5

                #Verify new folder was created
                Write-Host "Checking new folder recreation..."
                if(Test-Path("\\$($h)\c$\Windows\SoftwareDistribution")){
                    New-DGMLogFile -message ("\\$($h)\c$\Windows\SoftwareDistribution was recreated.") -component "Main()" -type 1
                }
                else{
                    New-DGMLogFile -message ("\\$($h)\c$\Windows\SoftwareDistribution could NOT be recreated.") -component "Main()" -type 3
                }

            }
            else{
                #Machine is offline
                New-DGMLogFile -message ("Offline: $h.") -component "Main()" -type 2
            }

        }
    }
    End
    {
       Write-Host ===============================================================
       Write-Host Log File of Results Generated at $path\Fix-DMGSCCMUpdateStore_$(Get-Date -Format dd-MM-yyyy).log VIEW WITH CMTRACE.EXE
       New-DGMLogFile -message ("Ending Logging for Fix-DMGSCCMUpdateStore") -component "Main()" -type 1
    }
}

$VerboseLogging = "true"
[bool]$Global:Verbose = [System.Convert]::ToBoolean($VerboseLogging)
$Global:LogFile = Join-Path (GetScriptDirectory) "Fix-DMGSCCMUpdateStore_$(Get-Date -Format dd-MM-yyyy).log"
$Global:MaxLogSizeInKB = 10240
$Global:ScriptName = 'Fix-DMGSCCMUpdateStore.ps1' 
$Global:ScriptStatus = 'Success'

Leave a Comment

Your email address will not be published.