Overblog
Editer l'article Suivre ce blog Administration + Créer mon blog

[CMCB] [Powershell] Clean DPs auto

Publié le par damcuvelier

Sometimes your DPs needs cleaning, because orphean content stay in and decrease free space on the CMCB partition.

A solution is to rebuild the DP.

Another solution is to use "ContentLibraryCleanup.exe" that is in the CMCB tools into the CMCB source.

But if some content are distributing on the DP, this tool do not want to run.

The solution is so to stop the distribution, clean the dp and rerun the distribution. Again there is 2 problem: First if the DP is in DP Group, the distribution rerun immediatly and second if there is more than 10 content to stop in more than 10 DP... i let you counting....

So THE solution is:

1. Remove the DP groups

2. Stop the distributions

3. run the tool

4. ReAdd the DP groups.

This is what i give you:

1. First a general prerequire script:

#prerequire for general
param([Parameter(mandatory = $false)]$CMCBSRVMP)

If(!($CMCBSRVMP)){$CMCBSRVMP = $env:computername}
$Global:CMCBMPSRV = $CMCBSRVMP
$Global:MPCMCB = (([System.Net.Dns]::GetHostByName(("$CMCBMPSRV"))).Hostname)

$here = (Get-Location).path

$PrcFldr = "$here\Process"
If(!(test-path $PrcFldr)){New-Item -ItemType Directory -Force -Path $PrcFldr | Out-Null}

$Distb = "$PrcFldr\Distrib"
If(!(test-path $Distb)){New-Item -ItemType Directory -Force -Path $Distb | Out-Null}

$LogFldr = "$PrcFldr\logs"
If(!(test-path $LogFldr)){New-Item -ItemType Directory -Force -Path $LogFldr}

#Get list of DP
$listDP = get-content "$here\dplist.txt"
$total = ($listDP | Measure-Object -Line).lines


$LogTime = Get-Date -Format "dd-MM-yyyy"

remove-item "$PrcFldr\grps.txt" -Force -EA SilentlyContinue | out-null

function connectCMCB {
#connect to CMCB
    # Import the ConfigurationManager.psd1 module 
    $CMCBModule = (get-childitem -path $($ENV:SMS_ADMIN_UI_PATH) -Recurse | where-object {$_.Name -match "ConfigurationManager.psd1"}).FullName
    Import-Module $CMCBModule

    $CMCBSite = (Get-PSDrive -PSProvider CMSITE).Name
    If(!($CMCBSite)){$CMCBSite = (Get-PSDrive -PSProvider CMSITE | Where {$_.Root -eq $MPCMCB}).Name}
    If(!($CMCBSite)){$CMCBSite = (Get-CMSITE | where-object {$_.type -eq 2}).SiteCode}

    $Global:Site = $CMCBSite
    $Global:SiteCode = $CMCBSite
    $Global:CMCBlocation = "$($SiteCode):"
    $Global:namespace = "ROOT\SMS\site_$Site"

}


Function getstate {
param($state)
    if($state -eq 0    ){return "DISTRIBUTED"}
    if($state -eq 1    ){return "DISTRIBUTION_PENDING"}
    if($state -eq 2    ){return "DISTRIBUTION_RETRYING"}
    if($state -eq 3    ){return "DISTRIBUTION_FAILED"}
    if($state -eq 4    ){return "REMOVAL_PENDING"}
    if($state -eq 5    ){return "REMOVAL_RETRYING"}
    if($state -eq 6    ){return "REMOVAL_FAILED"}
    if($state -eq 7    ){return "CONTENT_UPDATING"}
    if($state -eq 8    ){return "CONTENT_MONITORING"}
}

Function ConvertTo-PackageIDCIID {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$PackageID,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$SiteServer,
        
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$SiteCode
    )
    begin {
        $Namespace = "ROOT/SMS/Site_{0}" -f $SiteCode
    }
    process {
          $Query = "SELECT SMS_ApplicationLatest.CI_ID
        FROM SMS_ApplicationLatest
        WHERE SMS_ApplicationLatest.ModelName in (
            SELECT SMS_PackageStatusDistPointsSummarizer.SecureObjectID
            FROM SMS_PackageStatusDistPointsSummarizer
            WHERE PackageID = '{0}'
        )" -f $PackageID

        (Get-CimInstance -ComputerName $SiteServer -Namespace $Namespace -Query $Query).CI_ID
    }
    end {

    }
}

Function Resolve-DP {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [String]$Name,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$SiteServer,
        
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$SiteCode
    )
    begin {
        $OriginalLocation = (Get-Location).Path

        if($null -eq (Get-PSDrive -Name $SiteCode -PSProvider "CMSite" -ErrorAction "SilentlyContinue")) {
            $null = New-PSDrive -Name $SiteCode -PSProvider "CMSite" -Root $SiteServer -ErrorAction "Stop"
        }

        Set-Location ("{0}:\" -f $SiteCode) -ErrorAction "Stop"
    }
    process {
        try {
            $Obj = Get-CMDistributionPoint -Name $Name -AllSite -ErrorAction "Stop"
            if (-not $Obj) {
                throw ("Distribution point '{0}' does not exist" -f $Name)
            }
        }
        catch {
            Set-Location $OriginalLocation 
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
    end {
        Set-Location $OriginalLocation   
    }
}

Function Get-DPDistributionStatus {
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param (
        [Alias("Name", "ServerName")]
        [ParameteR(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [String[]]$DistbutionPoint,

        [Parameter()]
        [Switch]$Distbuted,

        [Parameter()]
        [Switch]$DistbutionPending,

        [Parameter()]
        [Switch]$DistbutionRetrying,

        [Parameter()]
        [Switch]$DistbutionFailed,

        [Parameter()]
        [Switch]$RemovalPending,

        [Parameter()]
        [Switch]$RemovalRetrying,

        [Parameter()]
        [Switch]$RemovalFailed,

        [Parameter()]
        [Switch]$ContentUpdating,

        [Parameter()]
        [Switch]$ContentMonitoring,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String]$SiteServer = $CMSiteServer,
        
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String]$SiteCode
    )
    begin {
        switch ($null) {
            $SiteCode {
                Write-Error -Message "Please supply a site code using the -SiteCode parameter" -Category "InvalidArgument" -ErrorAction "Stop"
            }
            $SiteServer {
                Write-Error -Message "Please supply a site server FQDN address using the -SiteServer parameter" -Category "InvalidArgument" -ErrorAction "Stop"
            }
        }
    }
    process {
        foreach ($TargetDP in $DistbutionPoint) {
            switch ($true) {
                ($LastDP -ne $TargetDP) {
                    try {
                        Resolve-DP -Name $TargetDP -SiteServer $SiteServer -SiteCode $SiteCode
                    }
                    catch {
                        Write-Error -ErrorRecord $_
                        return
                    }
                    
                    $LastDP = $TargetDP
                }
                default {
                    $LastDP = $TargetDP
                }
            }

            $Namespace = "ROOT/SMS/Site_{0}" -f $SiteCode
            $Query = "SELECT PackageID,PackageType,State,SourceVersion FROM SMS_PackageStatusDistPointsSummarizer WHERE ServerNALPath like '%{0}%'" -f $TargetDP
    
            $conditions = switch ($true) {
                $Distbuted          { "State = '{0}'" -f [Int][SMS_PackageStatusDistPointsSummarizer_State]"DISTRIBUTED" }
                $DistbutionPending  { "State = '{0}'" -f [Int][SMS_PackageStatusDistPointsSummarizer_State]"DISTRIBUTION_PENDING" }
                $DistbutionRetrying { "State = '{0}'" -f [Int][SMS_PackageStatusDistPointsSummarizer_State]"DISTRIBUTION_RETRYING" }
                $DistbutionFailed   { "State = '{0}'" -f [Int][SMS_PackageStatusDistPointsSummarizer_State]"DISTRIBUTION_FAILED" }
                $RemovalPending       { "State = '{0}'" -f [Int][SMS_PackageStatusDistPointsSummarizer_State]"REMOVAL_PENDING" }
                $RemovalRetrying      { "State = '{0}'" -f [Int][SMS_PackageStatusDistPointsSummarizer_State]"REMOVAL_RETRYING" }
                $RemovalFailed        { "State = '{0}'" -f [Int][SMS_PackageStatusDistPointsSummarizer_State]"REMOVAL_FAILED" }
                $ContentUpdating      { "State = '{0}'" -f [Int][SMS_PackageStatusDistPointsSummarizer_State]"CONTENT_UPDATING" }
                $ContentMonitoring    { "State = '{0}'" -f [Int][SMS_PackageStatusDistPointsSummarizer_State]"CONTENT_MONITORING" }
            }
    
            if ($conditions) {
                $Query = "{0} AND ( {1} )" -f $Query, ([String]::Join(" OR ", $conditions)) 
            }
            

            $ObjectQuery = "select NamePackageID,PackageType,State from SMS_PackageStatusDistPointsSummarizer WHERE ServerNALPath like '%{0}%'" -f $TargetDP
            Get-CimInstance -ComputerName $SiteServer -Namespace $Namespace -Query $Query | ForEach-Object {
                [PSCustomObject]@{
                    PSTypeName  = "PSCMContentMgmt"
                    ObjectID    = $_.PackageID
                    ObjectType    = $_.PackageType
                    ObjectName    = $_.Name
#                    State    = getstate -state $_.State
                    State    = $_.State
            }} 
        }
    }
    end {
    }
}

Function gettype {
param($ObjType)

 if($ObjType -eq 0){return "Package"}; if($ObjType -eq 3){return "DriverPackage"}
 if($ObjType -eq 4){return "TaskSequence"}
 if($ObjType -eq 5){return "DeploymentPackage"}
 if($ObjType -eq 6){return "Content"}
 if($ObjType -eq 8){return "Application"}
 if($ObjType -eq 257){return "OperatingSystemImage"}
 if($ObjType -eq 258){return "BootImage"}
 if($ObjType -eq 259){return "OperatingSystemInstaller"}
}

Function RemoveContentDistrib {
param($PKGt,$PKGN,$PKGI,$FQDN)
    if($PKGt -match "Application"){return Remove-CMContentDistribution -Force -ApplicationName $PKGN -DistributionPointName $FQDN}
    if($PKGt -match "DeploymentPackage"){return Remove-CMContentDistribution -Force -DeploymentPackageId $PKGI -DistributionPointName $FQDN}
    if($PKGt -match "DriverPackage"){return Remove-CMContentDistribution -Force -DriverPackageId $PKGI -DistributionPointName $FQDN}
    if($PKGt -match "Package"){return Remove-CMContentDistribution -Force -PackageId $PKGI -DistributionPointName $FQDN}
}

function getpkginfos{
param($Pkg,[Parameter(Mandatory=$false)]$PkgSDDPSumm)

$PackageInfos = Get-CMPackage -fast -PackageID $Pkg
if(-not($PackageInfos)){$PackageInfos = Get-CMApplication -fast | where-object {$_.PackageID -eq $Pkg}}
if(-not($PackageInfos)){$PackageInfos = (GetAppName -base $PkgSDDPSumm -PkgID $Pkg)}
if(-not($PackageInfos)){$PackageInfos = Get-CMDriverPackage -PackageID $Pkg}
if(-not($PackageInfos)){$PackageInfos = Get-CMSoftwareUpdateDeploymentPackage -PackageID $Pkg}
if(-not($PackageInfos)){$PackageInfos = Get-CMSoftwareUpdateDeploymentPackage -Id $Pkg}
if(-not($PackageInfos)){$PackageInfos = Get-CMOperatingSystemImage -PackageID $Pkg}
if(-not($PackageInfos)){$PackageInfos = Get-CMBootImage -PackageID $Pkg}
if(-not($PackageInfos)){$PackageInfos = Get-WmiObject -Class "SMS_ObjectContentInfo" -ComputerName $CMCBMPSRV -Namespace $namespace | where-object {$_.ObjectID -eq $Pkg}}
if(-not($PackageInfos)){$PackageInfos = Get-WmiObject -Class "SMS_ContentPackage" -ComputerName $CMCBMPSRV -Namespace $namespace | where-object {$_.PackageID -eq $Pkg}}
return $PackageInfos
}

Function getdppkginfos{
param($TargetDP,$PkgStatusDDPSumm,$CMCBSRVMP,$RemoteFQDN,$output,$grps)
[System.Collections.ArrayList]$resultat = @()
    foreach($PackageStatusDistPoints in $PkgStatusDDPSumm){
        if($PackageStatusDistPoints.ServerNALPath -match $TargetDP)
        {
        $PkgID = $PackageStatusDistPoints.PackageID
        $Type = gettype -ObjType ($PackageStatusDistPoints.PackageType)
        if($Type -match "Application"){$Name = (GetAppName -base $PkgStatusDDPSumm -PkgID $PkgID).LocalizedDisplayName}
        else{$Name = (getpkginfos -PkgSDDPSumm $PkgStatusDDPSumm -Pkg $PkgID).Name}

        if(!(GetObjGrp -grpnames $grps -ObjId $PkgID)){"ObjectName=$Name;Objectid=$PkgID;ObjectType=$Type" | out-file $output -append}

            $val = [pscustomobject]@{'ObjectName'=$Name;'Objectid'=$PkgID;'ObjectType'=$Type}
                $resultat.add($val) | Out-Null
                $val=$null
        }
    }
return $resultat
}

Function HostnLogs{
param($info,[switch]$only,[switch]$cleanDP)
if(!($only)){write-host $info}
$dT = Get-Date -Format "dd-MM"
if($cleanDP){$info | out-file "$ProcessFldr\DPCleanUP$dT.log" -encoding ASCII -append}
else{$info | out-file "$ProcessFldr\CleanManuel$dT.log" -encoding ASCII -append}
}

Function GetObjGrp{
param($grpnames,$ObjId)
if($grpnames.Substring($grpnames.Length-1) -eq ";"){$grpnames = $grpnames.Substring(0,$grpnames.Length-1)}

foreach($grpname in $grpnames){
$id = (gwmi -n $namespace -query "select * from sms_distributionpointgroup where name='$grpname'" -comp $CMCBMPSRV).groupid
$PackIDs = gwmi -n $namespace -query "select * from sms_dpgroupcontentinfo where groupid='$id'" -comp $CMCBMPSRV | sort PackageID | select -expand PackageID
$retour = $PackIDs | select-string $ObjId
if($retour){return $true}else{return $false}
}
}

Function cleansource{
param($file)
$ErrorActionPreference = "silentlycontinue"
foreach($line in (get-content $file))
{
$line = "$line;" -replace ";;",";"
$newline = $line.Substring(0,$line.Length-1)
((Get-Content -path $file -Raw) -replace $line,$newline) | Set-Content -Path $file
}
}

Function GetAppName{
param($base,$PkgID)
    foreach($val in $base)
    {
    if($val.PackageID -eq $PkgID)
        {
        $SecureObjectID = $val.SecureObjectID
        $result = (Get-CMApplication -fast -ModelName $SecureObjectID)
        }
    }
return $result
}

connectCMCB
Set-location $CMCBlocation

2. And the principal script:

param([Parameter(mandatory = $false)]$MP)

if(!($MP)){$MPCMCBCMCBSRVMP=$env:computername}else{$MPCMCBCMCBSRVMP=$MP}
$ErrorActionPreference = "silentlycontinue"
$here = (Get-Location).path

#Get Functions and prerequires
. $here\Functions.ps1 -CMCBSRVMP $MPCMCBCMCBSRVMP

connectCMCB

############################################################### MAIN ###############################################################
HostnLogs -info "################ START $LogTime ##################"

#Recuperation de l'etat des contenus CMCB
HostnLogs -info "Recuperation de l'etat des contenus CMCB"
$PackageStatusDistPointsSummarizer = Get-WmiObject -Namespace $namespace -Query "SELECT * FROM SMS_PackageStatusDistPointsSummarizer where state <> 0"

foreach($DPName in $listDP)
{
HostnLogs -info "Traitement du DP $DPName"
HostnLogs -info "Supression des groupes du DP $DPName"

#1. Backup n Delete Groups
    $GrpNames = ""
    $DPFQDN = (([System.Net.Dns]::GetHostByName(("$DPName"))).Hostname)
    $RemoteFQDN = $DPFQDN

    #Get groups (by name)
    $GrpIDs = (Get-WmiObject -Class "SMS_DPGroupMembers" -Namespace $namespace | where-object {$_.DPNALPath -match $DPName} | Select-Object GroupID).GroupID
    foreach($GrpID in $GrpIDs)
    {
    $GrpName = (Get-WmiObject -Class "SMS_DPGroupInfo" -ComputerName $CMCBMPSRV -Namespace $namespace | where-object {$_.GroupID -match $GrpID}).Name
    $GrpNames += "$GrpName;"

HostnLogs -info "$DPName est membre du groupe de DP $GrpName"
    

    #Delete from Group
    Remove-CMDistributionPointFromGroup -DistributionPointName $RemoteFQDN -DistributionPointGroupName $GrpName -force
    }

    #Backup Groups
    "$DPName=$GrpNames" | out-file "$PrcFldr\grps_save.txt" -Append
    #Log Groups to process file
    "$DPName=$GrpNames" | out-file "$PrcFldr\grps.txt" -Append

    #Create 2 lists
    foreach($GrpName in $GrpNames){if($GrpName -match "20H2"){$DPName | out-file "$PrcFldr\DP20H2.txt" -append}}
    if(!(Select-String -Path "$here\DP20H2.txt" -Pattern $DPName -EA "silentlycontinue")){$DPName | out-file "$PrcFldr\koDP20H2.txt" -append}

    if(!(Select-String -Path "$here\koDP20H2.txt" -Pattern $DPName -EA "silentlycontinue")){$DPName | out-file "$PrcFldr\okDP20H2.txt" -append}


#2. Backup n Delete Distributions pendings
HostnLogs -info "Backup n Delete Distributions pendings du DP $DPName"
    $DPFQDN = (([System.Net.Dns]::GetHostByName(("$DPName"))).Hostname)
    $logfile = "$Distb\Distrib-$DPName.csv"
    "$DPName" | out-file $logfile -Append

    Set-location $CMCBlocation
    $PcksOnError = getdppkginfos -TargetDP $DPName -PkgStatusDDPSumm $PackageStatusDistPointsSummarizer -RemoteFQDN $DPFQDN -CMCBSRVMP $CMCBMPSRV -output $logfile -grps $GrpNames
        foreach($val in $PcksOnError)
        {
            $PkgID = $val.Objectid
            $PkgType = $val.ObjectType
            $PkgName = $val.ObjectName
HostnLogs -info "stop distribution of objects of the DP $DPName" -only
HostnLogs -info "stop distribution of object $PkgName ($PkgID) on DP $DPName" -only
            $RemContDistrib = RemoveContentDistrib -PKGt $PkgType -PKGN $PkgName -PKGI $PkgID -FQDN $DPFQDN
            $RemContDistrib
HostnLogs -info "$RemContDistrib" -only
HostnLogs -info "---------------------------" -only
        }


HostnLogs -info "################ Next Step: CleanDP ##################"

cd $here

$DPName = $DPName.Trim("")

$StepDP = ($listDP | select-string $DPName).LineNumber
$Step = [math]::Round(($StepDP*100/$total),1)
HostnLogs -info "CleanUp du DP $DPName : $StepDP/$total = $Step%"

#run cmd cleandp
Invoke-Expression "$here\ContentLibraryCleanup.exe /dp $DPName /ps $MPCMCB /sc $site /log $LogFldr /delete /q > $LogFldr\$DPName.log"

#Recup ok/ko
$reslog = "$LogFldr\$DPName.log"
if(Select-String -Path $reslog -Pattern "Approximately")
    {
    HostnLogs -info "CleanDP $DPName = OK"
    foreach($line in (get-content $reslog)){if($line -match "Approximately"){"$DPName;$line" | out-file "$LogFldr\resultok.txt" -Append}}
    }
else
    {
    HostnLogs -info "CleanDP $DPName = KO"
    "$DPName" | out-file "$LogFldr\resultko.txt" -Append
    }

HostnLogs -info "################ Next Step ReAdd to groups ##################"

#3. ReAdd to Groups
HostnLogs -info "$DPName est remis dans les groupes:"
set-location $here
Foreach($line in (get-content "$PrcFldr\grps.txt"))
{
    if($line -match $DPName)
    {
    $DPName = $line.split("=")[0]
    $DPGroups = $line.split("=")[1]
    $DPFQDN = (([System.Net.Dns]::GetHostByName(("$DPName"))).Hostname)
    foreach($DPGroup in $DPGroups.split(";"))
            {if($DPGroup){
            $ErrorActionPreference = "silentlycontinue"
            HostnLogs -info "$DPGroup"
            Set-location $CMCBlocation
            Add-CMDistributionPointToGroup –DistributionPointGroupName $DPGroup –DistributionPointName $DPFQDN
            set-location $here
            }}
    }
}
HostnLogs -info "Fin de traitement CleanManuel du DP $DPName"
HostnLogs -info "--------------"
}


HostnLogs -info "################ END ##################"

Put all that scripts and ContentLibraryCleanup.exe on the same folder in you Management Point Serveur. Put the list of your DPs in a file named dplist.txt.

And Run the principal script.

Under .\Process\CleanManuel.log you will be able to follow the progress of the actions

 

 

 

Commenter cet article