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.
PowerShell Script: Fix-DGMSCCMUpdateStore.ps1
<#
.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}>" -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}>" -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 Reply
Want to join the discussion?Feel free to contribute!