Configuration Manager (SCCM) | Application and Package Clean-up

Is it that time of year, when you are getting asked to make room on the Distribution Points and perform some housing keeping in the Configuration Manager enviorment

Then this script could be the tool for you…..helping to identify potential unused applications and packages that could be removed

The original script author was Matt Bobke Finding Unused SCCM Applications and Packages – Scripting Secures Success (mattbobke.com), but have made a few enhancements

<#
.SYNOPSIS
    This script will export a .csv file showing all applications and packages that are not in use within Configuration Manage

.DESCRIPTION
   Connect to your Configuration Manger PowerShell Console and execute. A .csv file of all applications and packages that are not in use
   will be written to C:\Windows\Temp\CM_Apps.csv and C:\Windows\Temp\CM_Packages.csv

.NOTES
    The original author was Matt Bobke : https://mattbobke.com/2018/05/06/finding-unused-sccm-applications-and-packages/ and enhancements by Skatterbrainz
    and has since been updated by S.P.Drake

    Full functionality requires Configuration Manager Release 2010 or later 

    File Name      : CM - Cleanup.ps1
    Author         : S.P.Drake
    Version        : 1.1  : Added content distribution count, package type translation and ConfigurationManager compatibility
    Version        : 1.0  : Enhanced package filter, exclude predefined packages, Configuration Manager Client Piloting Package and DefaultImages and dependant programs

#>

# Assign report folder
$ReportFolder = "$env:systemdrive\Windows\Temp"

# Suppress Fast check not in use warning message
$CMPSSuppressFastNotUsedCheck = $true

Write-Verbose "Get - distribution points and package content" -Verbose

# Initialise $packageContentCount array
$packageContentCount = @()

# Get all distribution points servers
$allDPs = Get-CMDistributionPointInfo | Select-Object -ExpandProperty ServerName

# Get the content on each distribution pint
foreach ($dp in $allDPs){

    $packageContentCount += Get-CMDeploymentPackage -DistributionPointName $dp | Select-Object -ExpandProperty PackageID }

# Group the content found on each distribution point to get a package distributed total count
$groupedPackageContentCount  = $packageContentCount | Sort-Object $_.Name | Group-Object $_.Name

Write-Verbose "Get - applications" -Verbose

# Get all Applications that are not deployed, have no dependant task sequences and no deployment types that depend on this application - (can't filter packages like applications)
$AllApplications = Get-CMApplication | Where-Object {($_.IsDeployed -eq $False) -and ($_.NumberofDependentTS -eq 0) -and ($_.NumberofDependentDTs -eq 0)}

Write-Verbose "Get - packages" -Verbose

# If running ConfigurationManager version 2010 or later
If ((Get-Module -Name ConfigurationManager).Version.Major -ge 5 -and (Get-Module -Name ConfigurationManager).Version.Minor -ge 2010){

    # Get all Regular Packages that are not predefined packages and package name not 'Configuration Manager Client Piloting Package'
    $RegularPackage = Get-CMPackage -Fast -PackageType RegularPackage | Where-Object {($_.IsPredefinedPackage -eq $false) -or ($_.Name -ne 'Configuration Manager Client Piloting Package')}

    # Get all Driver Packages
    $DriverPackage = Get-CMPackage -Fast -PackageType Driver

    # Get all Operating System Image packages
    $ImageDeploymentPackage = Get-CMPackage -Fast -PackageType ImageDeployment

    # Get all Operating System Upgrade packages
    $OSInstallPackagePackage = Get-CMPackage -Fast -PackageType OSInstallPackage

    # Get all Boot Image packages, DefaultImage=False
    $BootImagePackage = Get-CMPackage -Fast -PackageType BootImage | Where-Object {$_.DefaultImage -eq $false}

    # Combine all packages lists together
    $AllPackages = ($RegularPackage + $DriverPackage + $ImageDeploymentPackage + $OSInstallPackagePackage + $BootImagePackage)}

    else{

    # Get all Regular Packages that are not predefined packages and package name not 'Configuration Manager Client Piloting Package'
    $AllPackages = Get-CMPackage -Fast | Where-Object {($_.IsPredefinedPackage -eq $false) -or ($_.Name -ne 'Configuration Manager Client Piloting Package')}

    }

Write-Verbose "Get - deployments" -Verbose

# Get all deployments, filter to just a list of their package IDs
$DeploymentPackageIDs = Get-CMDeployment | Select-Object PackageID | Sort-Object | Get-Unique -AsString

Write-Verbose "Get - task sequences" -Verbose

# Get all task sequences that have references and not disabled (cannot use -Fast)
$FilteredTaskSequences = Get-CMTaskSequence | Where-Object { ($_.References -ne $null) -and ($_.TsEnabled -ne $false) }

# If filtered task Sequence found
if ($FilteredTaskSequences.Count -ne 0) {

    Write-Verbose "Filter - task sequence references only" -Verbose

    # Filter task sequences to just a list of their references (cannot use -Fast)
    $TSReferences = ( $FilteredTaskSequences | Select-Object References).References.Package | Sort-Object | Get-Unique -AsString

    Write-Verbose "Filter - task sequence dependant programs only " -Verbose

    # Filter task Sequence’s dependant programs, filter to just a list of their references (cannot use -Fast)
    $TSDependentProgram = $FilteredTaskSequences | Select-Object DependentProgram | Foreach-Object {$_.DependentProgram.Split(';;')[0]} | Sort-Object | Get-Unique -AsString
}

Write-Verbose "Filter - applications and packages that are not active" -Verbose

# Initialise FinalApplications
$FinalApplications = New-Object -TypeName 'System.Collections.ArrayList'

# Initialise FinalPackage
$FinalPackages = New-Object -TypeName 'System.Collections.ArrayList'

# Append content distribution count to the application list
foreach ($App in $AllApplications) {
        $App | Add-Member -MemberType NoteProperty DPCount -Value ($groupedPackageContentCount | Where-Object {$_.Name -eq $App.PackageID} | Select-Object -ExpandProperty count)
        $FinalApplications.Add($App) | Out-Null
}

# Filter packages to only those that do not have their PackageID in the list of references and append content distribution count to the package list
foreach ($Package in $AllPackages) {
    if (($Package.PackageID -notin $TSReferences) -and ($Package.PackageID -notin $DeploymentPackageIDs.PackageID) -and ($Package.PackageID -notin $TSDependentProgram)) {
        $Package | Add-Member -MemberType NoteProperty DPCount -Value ($groupedPackageContentCount | Where-Object {$_.Name -eq $Package.PackageID} | Select-Object -ExpandProperty count)
        $FinalPackages.Add($Package) | Out-Null
    }
}

Write-Verbose "Export - applications and packages that are not active" -Verbose

# Export application list to .csv
$FinalApplications `
    | Select-Object -Property LocalizedDisplayName, PackageID, DateCreated, DateLastModified, IsDeployable, IsEnabled, IsExpired, IsHidden, IsSuperseded, DPCount  `
    | Sort-Object -Property LocalizedDisplayName `
    | Export-Csv -Path "$ReportFolder\CM_Apps.csv" -NoTypeInformation

# Export package list to .csv
$FinalPackages `
    | Select-Object Name, @{Name = "PackageType";Expression = {$_.PackageType -replace '258','BootImage' -replace '3','Driver' -replace '257','ImageDeployment' -replace '259','OSInstallPackage' -replace '0','RegularPackage'}},PackageID, SourceDate, LastRefreshTime, PkgSourcePath, DPCount `
    | Sort-Object -Property PackageType, Name `
    | Export-Csv -Path "$ReportFolder\CM_Packages.csv" -NoTypeInformation

Write-Verbose "Done - CSVs stored in $ReportFolder" -Verbose

# Future releases will have the option for Report Only, Prompt on Delete, or Auto Delete

Connect to CM Environment and launch – Connect via Windows PowerShell ISE

Run default CM Environment script that is loaded when open Windows PowerShell ISE

Open the attached script – (CM-Cleanup.ps1) in a new tab or copy the script in a new tab and run

When the script is complete you will find two .csv in C:\Windows\Temp , SCCM-Apps.csv and SCCM-Packages.csv , this will be a list of inactive applications and packages

NOTE: A task sequence that has been disabled, will be treated as it does not exist.

Therefore, if an application or package is ‘only’ associated with this task sequence and the application or package is ‘not’ independently deployed it will be counted as unused.

This functionality can be changed by changing line 70 to below syntak

$FilteredTaskSequences = Get-CMTaskSequence | Where-Object { $_.References -ne $null }

*** The next release will have the option to ‘ReportOnly’, ‘DeleteWithPrompt’ or ‘AutoDelete’ Packages and Applications ***

*** Keep us updated with your feedback ***

Git Script : https://github.com/Drakey2000/CommunityHelper/blob/master/CM-Cleanup/CM-Cleanup.ps1

Excellent….you now have a reporting tool to identify potential unused applications and packages in Configuration Manager, which can be removed to free up disk space on your Distribution Points, but I hear you…..

How do you remove the source contents on our file severs that are now redundant ?

Check this guy out Adam Cook….not going to say any more, but he has the answers

Git Link : GitHub – codaamok/Get-CMUnusedSources: A PowerShell script that will tell you what folders are not used by Microsoft Endpoint Manager Configuration Manager.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: