Share via


Deleting the SCCM Cache the right way

I’ve been struggling a bit finding a good solution for the cache cleanup problem as deleting the files directly is not supported and the SCCM SDK is quite limited. There are some scripts out there but none of them check if the Cache Item is actually needed before deleting it. This approach generates a lot of unnecessary network traffic.

We can do better with a PowerShell script that deletes only unused cache items and can be added to a CI and scheduled to run after Patch Tuesday.

A big thank you to my colleague (Ioan Popovici) for taking the time in developing this script.

Original Article Here: http://sccmzone.ro/deleting-the-sccm-cache-the-right-way

<#
**********************************************************************************************************
*                                                                                                        *
*** This Powershell Script is used to clean the CCM cache of all non persisted content                  **
*                                                                                                        *
**********************************************************************************************************
* Created by Ioan Popovici, 13/11/2015  | Requirements Powershell 3.0                                    *
* =======================================================================================================*
* Modified by   |    Date    | Revision |                            Comments                            *
*________________________________________________________________________________________________________*
* Ioan Popovici | 13/11/2015 | v1.0      | First version                                                 *
* Ioan Popovici | 16/11/2015 | v1.1      | Improved Loging                                               *
* Ioan Popovici | 16/11/2015 | v1.2      | Vastly Improved                                               *
*--------------------------------------------------------------------------------------------------------*
*                                                                                                        *
**********************************************************************************************************
 
    .SYNOPSIS
        This Powershell Script is used to clean the CCM cache of all non persisted content.
    .DESCRIPTION
        This Powershell Script is used to clean the CCM cache of all non persisted content that is not needed anymore.
        It only cleans packages, applications and  updates that have a installed status and are not persisted, other 
        chache items will NOT be cleaned.
#>
 
##Initialization
CLS
 
#Global variable
$Global:Result  =@() 
 
#Set log path
$ResultCSV = "C:\Temp\Clean-CCMCache.log"
 
#Remove previous log it it's more than 500 KB
If (Test-Path $ResultCSV) {
    If ((Get-Item $ResultCSV).Length -gt 500KB) {
        Remove-Item $ResultCSV  -Force | Out-Null
    }
}
 
#Get log parent path
$ResultPath =  Split-Path $ResultCSV -Parent
 
#Create path directory if  it does not exist
If ((Test-Path $ResultPath) -eq $False) {
    New-Item -Path $ResultPath  -Type Directory | Out-Null
}
 
#Get the current date
$Date = Get-Date
 
#Get list of all non persisted content in CCMcache, only this content will be removed
$CM_CacheItems = Get-WmiObject -Namespace root\ccm\SoftMgmtAgent -Query 'SELECT ContentID,Location FROM CacheInfoEx WHERE PersistInCache = 0'
 
#Get list of updates
$CM_Updates = Get-WmiObject -Namespace root\ccm\SoftwareUpdates\UpdatesStore -Query 'SELECT UniqueID,Title,Status FROM CCM_UpdateStatus'
 
#Get list of applications
$CM_Applications = Get-WmiObject -Namespace root\ccm\ClientSDK -Query  'SELECT * FROM CCM_Application'
 
#Get list of packages
$CM_Packages = Get-WmiObject -Namespace root\ccm\ClientSDK -Query  'SELECT PackageID,PackageName,LastRunStatus,RepeatRunBehavior FROM CCM_Program'
 
##Functions
 
#This function  is used to remove the cache item pushed trough it if it's not persisted
Function Remove-CacheItem {
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [Alias('CacheTD')]
        [string]$CacheItemToDelete,
        [Parameter(Mandatory = $true, Position = 0)]
        [Alias('CacheN')]
        [string]$CacheItemName
    )
 
    #Delete chache item if it's non persisted
    If ($CM_CacheItems.ContentID -contains $CacheItemToDelete) {
 
        #Get Cache item location and  size
        $CacheItemLocation = $CM_CacheItems | Where {$_.ContentID -Contains $CacheItemToDelete} | Select -ExpandProperty Location
        $CacheItemSize =  Get-ChildItem $CacheItemLocation -Recurse -Force | Measure-Object -Property Length -Sum | Select -ExpandProperty Sum
 
        #Build result object
        $ResultProps = [ordered]@{
            'DeletionDate'  = $Date
            'Name' = $CacheItemName
            'ID' = $CacheItemToDelete
            'Location' = $CacheItemLocation
            'Size(MB)' = "{0:N2}"  -f ($CacheItemSize  / 1MB)
            'TotalDeleted(MB)' = ''
        }
 
        #Add items to result object
        $Global:Result  += New-Object PSObject -Property $ResultProps
 
        #Connect to resource manager COM object
        $CMObject = New-Object -ComObject "UIResource.UIResourceMgr"
 
        #Use GetCacheInfo method to return  cache properties
        $CMCacheObjects = $CMObject.GetCacheInfo()
 
        #Delete Cache element
        $CMCacheObjects.GetCacheElements() | Where-Object {$_.ContentID -eq $CacheItemToDelete} | 
            ForEach-Object { 
                $CMCacheObjects.DeleteCacheElement($_.CacheElementID)
            }
    } 
}
 
##Main Script
 
#Reset progress Counter
$ProgressCounter = 0
 
#Check for  installed applications (adapted)
Foreach ($Application in $CM_Applications) {
 
    #Show progrss bar
    If ($CM_Applications.Count -ne $null) { 
        $ProgressCounter++
        Write-Progress -Activity 'Processing Applications'  -CurrentOperation $Application.FullName -PercentComplete (($ProgressCounter / $CM_Applications.Count) * 100)
        Start-Sleep -Milliseconds 400
    }
 
    $Application.Get()
 
    #Enumerate all deployment types for  an application
    Foreach ($DeploymentType in $Application.AppDTs) {
        If ($Application.InstallState -eq "Installed" -and $Application.IsMachineTarget) {
 
            #Get content ID for  specific application deployment type
            $AppType = "Install",$DeploymentType.Id,$DeploymentType.Revision
            $Content = Invoke-WmiMethod -Namespace root\ccm\cimodels -Class CCM_AppDeliveryType -Name GetContentInfo -ArgumentList $AppType
 
            #Call Remove-CacheItem function
            Remove-CacheItem -CacheTD $Content.ContentID -CacheN $Application.FullName
        }
    }
}
 
#Reset progress Counter
$ProgressCounter = 0
 
#Check for  installed packages
Foreach ($Package in $CM_Packages) {
 
    #Show progrss bar
    If ($CM_Packages.Count -ne $null) { 
        $ProgressCounter++
        Write-Progress -Activity 'Processing Packages'  -CurrentOperation $Package.PackageName -PercentComplete (($ProgressCounter / $CM_Packages.Count) * 100)
        Start-Sleep -Milliseconds 800
    }
 
    If ($Package.LastRunStatus -eq "Succeeded" -and $Package.RepeatRunBehavior -ne "RerunAlways"  -and  $Package.RepeatRunBehavior -ne "RerunIfSuccess") {
 
        #Call Remove-CacheItem function
        Remove-CacheItem -CacheTD $Package.PackageID -CacheN $Package.PackageName
    }   
}
 
#Reset progress Counter
$ProgressCounter = 0
 
#Check for  installed updates
Foreach ($Update in $CM_Updates) {
 
    #Show Progrss bar
    If ($CM_Updates.Count -ne $null) {  
        $ProgressCounter++
        Write-Progress -Activity 'Processing Updates'  -CurrentOperation $Update.Title -PercentComplete (($ProgressCounter / $CM_Updates.Count) * 100)
    }
 
    If ($Update.Status -eq "Installed") {
 
        #Call Remove-CacheItem function
        Remove-CacheItem -CacheTD $Update.UniqueID -CacheN $Update.Title
    }
}
 
#Sort by size descending
$Result =  $Global:Result | Sort-Object Size`(MB`) -Descending
 
#Calculate total deleted size
$TotalDeletedSize = $Result  | Measure-Object -Property Size`(MB`) -Sum | Select -ExpandProperty Sum
 
#Build result object
$ResultProps = [ordered]@{
    'DeletionDate' = $Date
    'Name' = 'Total Size of Items Deleted in MB:'
    'ID' = ''
    'Location' = ''
    'Size(MB)' = ''
    'TotalDeleted(MB)' = $TotalDeletedSize
}
 
#Add total items deleted to result object
$Result += New-Object PSObject -Property $ResultProps
 
#Write result object to csv file (append)
$Result | Export-Csv -Path $ResultCSV -Delimiter ";" -Encoding UTF8 -NoTypeInformation -Append -Force
 
#Write to console
$Result | Format-Table Name,TotalDeleted`(MB`)