Remove SCCM Distribution Point Role

If you work in a large SCCM/MECM environment you may run into a time when all of your remote Distribution Points need to be replaced with new servers for a hardware refresh. Servers are built with the latest OS, boxed up and shipped and then installed on-site at their new locations.

Once they’re there, you don’t need the old server as a Distribution Point anymore but the old server may co-exist with the new one for a period of time for business purposes before being decommissioned which means you still need to manage it as a client, but no longer need it as a DP.

If you’re doing a massive rollout of a few thousand DPs, you may need to decommission 50 or 100 old DPs per week for the better part of a year. Who wants to do that manually every week clicking in the console? No thanks.

You can use the below script to remove the Distribution Point role from a list of servers. Note that this only removes the DP role and triggers Distribution Manager to do the uninstall for the DP. It doesn’t delete the Site System from SCCM. I’ll talk more about that in a minute below the script.

In my experience, this process still leaves many files and the content library behind on the servers although the servers are no longer Distribution Points within SCCM. You need to clean these files up from your SCCM drive manually or via script, or leave it behind as the servers are going to be decommissioned anyway (my case).

##############################################################################################
#
#  Script Name:      RemoveDPRole_v1.0.ps1  
#
#  Description:      Removes SCCM DP Role from Server(s)
#
#  Authors:          Jeff LeBlanc
#
#  Version History:  1.0 - Initial Script
#
#  Command Line:     
#        .\RemoveDPRole.ps1 -CASSiteCode <SiteCode> -CASSiteServer <ServerFQDN> -SiteSystemSiteCode <SiteCode> -SiteSystemNamesFile .\Servers.txt
#
#  Note: This script requires the Site Code for the system(s) in the specified text file to be specified
#        as a script parameter -SiteSystemSiteCode. Each text file should only include systems from the 
#        same site and should be specified in FQDN format in the txt file.
#  
##############################################################################################

# Verify Command Line Parameters
[CmdletBinding()]
param(
    #Specify the CAS Site Code
    [parameter(Mandatory=$true)]
    $CASSiteCode,

    #Specify the CAS Site Server
    [parameter(Mandatory=$true)]
    $CASSiteServer,

    #Specify the Site System SiteCode
    [parameter(Mandatory=$true)]
    $SiteSystemSiteCode,

    #Specify the Site System Name File
    [parameter(Mandatory=$true)]
    $SiteSystemNamesFile
)


# Setup Log File Path
$SMSLogPath = $PSScriptRoot
$global:LogFile = "$SMSLogPath\RemoveDPRole.log"


##############################################################################################
#
#   Functions
#
##############################################################################################

Function New-CMSite {

    #Load the Configuration Manager Module
    Import-Module $env:SMS_ADMIN_UI_PATH.Replace("\bin\i386","\bin\ConfigurationManager.psd1")    # only Use if console is installed locally

    $SiteCode = $CASSiteCode
    $SiteServer = $CASSiteServer

    $CMDrive = Get-PSDrive -Name $SiteCode -ErrorAction Ignore

    # Connect to CMDrive
    if ($CMDrive)
    {
        Write-Host "PSDrive: $SiteCode already exists, changing current drive"
        Set-Location $SiteCode":"
    }
    else
    {
        Write-Host "PSDrive: $SiteCode does not exist, adding..."
        $i = 1

        # Sometimes the initial connection attempt(s) fail. Try up to 10 times
        Do {
            Write-Host "Loop $i"
            $NewDrive = New-PSDrive -Name $SiteCode -PSProvider "CMSite" -Root $SiteServer -Scope Global
            $i++
        }
        Until ($NewDrive -ne $null -or $i -gt 10)
        Set-Location $SiteCode":"
    }
}


Function Log-Write {

#########################################################################################
<#
.SYNOPSIS
   Log to a file in a format that can be read by Trace32.exe / CMTrace.exe 

.DESCRIPTION
   Write a line of data to a script log file in a format that can be parsed by Trace32.exe / CMTrace.exe

   The severity of the logged line can be set as:

        1 - Information
        2 - Warning
        3 - Error

   Warnings will be highlighted in yellow. Errors are highlighted in red.

   The tools to view the log:

   SMS Trace - http://www.microsoft.com/en-us/download/details.aspx?id=18153
   CM Trace - Installation directory on Configuration Manager Site Server - <Install Directory>\Tools\

.EXAMPLE
   Log-ScriptEvent c:\output\update.log "Application of MS15-031 failed" Apply_Patch 3

   This will write a line to the update.log file in c:\output stating that "Application of MS15-031 failed".
   The source component will be Apply_Patch and the line will be highlighted in red as it is an error 
   (severity - 3).

#>
#########################################################################################

#Define and validate parameters
[CmdletBinding()]
Param(
      #Path to the log file
      [parameter(Mandatory=$True)]
      [String]$NewLog,

      #The information to log
      [parameter(Mandatory=$True)]
      [String]$Value,

      #The source of the error
      [parameter(Mandatory=$True)]
      [String]$Component,

      #The severity (1 - Information, 2- Warning, 3 - Error)
      [parameter(Mandatory=$True)]
      [ValidateRange(1,3)]
      [Single]$Severity
      )


#Obtain UTC offset
$DateTime = New-Object -ComObject WbemScripting.SWbemDateTime 
$DateTime.SetVarDate($(Get-Date))
$UtcValue = $DateTime.Value
$UtcOffset = $UtcValue.Substring(21, $UtcValue.Length - 21)


#Create the line to be logged
$LogLine =  "<![LOG[$Value]LOG]!>" +`
            "<time=`"$(Get-Date -Format HH:mm:ss.fff)$($UtcOffset)`" " +`
            "date=`"$(Get-Date -Format M-d-yyyy)`" " +`
            "component=`"$Component`" " +`
            "context=`""" " +`
            "type=`"$Severity`" " +`
            "thread=`" " +`
            "file=`"`">"

#Write the line to the passed log file
Add-Content -Path $NewLog -Value $LogLine

}


#########################################################################################
#
#   MAIN SCRIPT
#
#########################################################################################

# Connect to SCCM Provider
New-CMSite

# Get Server List from File
$SiteSystems = Get-Content -Path $PSScriptRoot\$SiteSystemNamesFile

# Process Servers
ForEach ($SiteSystem in $SiteSystems) {
    Write-Host "Processing $SiteSystem for site $SiteSystemSiteCode" -ForegroundColor Cyan
    Log-Write $LogFile "Processing $SiteSystem for site $SiteSystemSiteCode" RemoveDPRole 1

    # Get DP Object and Remove it from SCCM
    Get-CMDistributionPoint -SiteSystemServerName $SiteSystem -SiteCode $SiteSystemSiteCode | Remove-CMDistributionPoint -Force

    #Check to see if Site System was removed
    $dp = Get-CMDistributionPoint -SiteSystemServerName $SiteSystem -SiteCode $SiteSystemSiteCode

    if ($dp) {
        Write-Host "Distribution Point on Server $SiteSystem was NOT removed." -ForegroundColor Red
        Log-Write $LogFile "Distribution Point on Server $SiteSystem was NOT removed." RemoveDPRole 3
    }
    else {
        Write-Host "DP Role successfully removed from $SiteSystem." -ForegroundColor Green
        Log-Write $LogFile "DP Role successfully removed from $SiteSystem." RemoveDPRole 1
    }
}

Set-Location $PSScriptRoot

As I mentioned above, this script only removes the DP role from the target servers but does NOT delete them as Site Systems. To delete them as Site Systems you need to slightly modify the script and replace a few lines and then re-run it.

REPLACE THIS:
# Get DP Object and Remove it from SCCM
    Get-CMDistributionPoint -SiteSystemServerName $SiteSystem -SiteCode $SiteSystemSiteCode | Remove-CMDistributionPoint -Force

WITH THIS:
# Get Site System Object and Remove it from SCCM
    Get-CMSiteSystemServer -SiteSystemServerName $SiteSystem -SiteCode $SiteSystemSiteCode | Remove-CMSiteSystemServer -Force

and then...

REPLACE THIS:
#Check to see if Site System was removed
    $dp = Get-CMDistributionPoint -SiteSystemServerName $SiteSystem -SiteCode $SiteSystemSiteCode

    if ($dp) {

WITH THIS:
#Check to see if Site System was removed
    $ss = Get-CMSiteSystemServer -SiteSystemServerName $SiteSystem -SiteCode $SiteSystemSiteCode

    if ($ss) {

Save them as two separate scripts and run the DP Removal one first, wait a few hours to allow Distribution Manager to process the uninstalls and then run the second script to delete the systems as Site Systems from SCCM.

Why not just run the second script to delete them as Site Systems to begin with? Won’t that uninstall the DP role from the server as well? The answer is unfortunately NO and the DP binaries will remain installed and running on the server even though you’ve removed it as a Site System from SCCM. Only do this if the old server no longer exists and you just need to remove it from SCCM. I believe this is a bug, MS support agrees but I haven’t officially filed the bug with them yet.

Happy computing!

Leave a Comment