Scripted Deployment of Distribution Points in SCCM 2012 including rate scheduling

Somewhat quick and dirty, but I’m sure it’ll be of use to anyone doing a larger scale rollout of distribution points.

The following script can interactively roll out distribution points for you, saving a huge amount of clicking around in the SCCM console. The code can easily be adapted to run everything from functions and bulk import a list of distribution points from CSV to SCCM.

#Add distribution point with State Migration and Pulse Rate limitation and scheduling
#Author: Jos Lieben, OGD
#Copyright: Free to Use and Distribute
#Credits: David O’Brien (david-obrien.net) for WMI class interaction
#notice: you must have the required console updates for some of these commands to work with SCCM 2012 SP1

####GENERAL CONFIGURATION####
#If you clear any config, the script will prompt you for it, config simply helps you simplify deployments if for example, you only have one site
#Path to your PSD files
Import-Module “C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1″
#Your site code
$site_code = “EU1″
#Name of your distribution point (Fully Qualified Name required!)
$server_name = “”
#Drive letter for your primary content library, use Automatic if you wish SCCM to manage this for you
$driveletter_plibrary = “”
#Drive letter for your primary content share, use Automatic if you wish SCCM to manage this for you
$driveletter_pshare = “”
#If you wish to add a state migration point to the DP, set this to $true
$add_statemigrationpoint = $false
#Path to data on the state migration point
$state_migrationpoint_localpath = “”
#Distribution Point Group Membership can be configured with this variable
$distributionpointgroup = “”
####END OF GENERAL CONFIGURATION####
####SCHEDULING CONFIGURATION####
#Set to False if you do not wish to configure transfer schedules for your DP ($false recommended)
$add_schedule = $true
#size of each block sent, leave at 0 to prompt, only used when add_schedule=$true
$block_size = 0
#delay between each block sent, leave at 0 to prompt, only used when add_schedule=$true
$block_delay = 0
#Array containing 24 elements, one for each hour of the day. This property specifies the type of usage for each hour.
# 1 means all Priorities, 2 means all but low, 3 is high only, 4 means none
#this example allows only high priority sendings from monday till friday between 8AM and 8PM and everything outside that timeframe
$HourUsageScheduleWeekdays = @(1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1)
$HourUsageScheduleWeekend = @(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)
####END OF SCHEDULING CONFIGURATION#####
#standard variables
$ja = new-Object System.Management.Automation.Host.ChoiceDescription “&Yes”,”help”
$nee = new-Object System.Management.Automation.Host.ChoiceDescription “&No”,”help”

Clear-Host
if($site_name -eq “”) {
$site_code = Read-Host “Enter your site code (for example: EU1)”
}
if($server_name -eq “”) {
$server_name = Read-Host “Enter the FQDN of your new distribution point (for example: dp01.ogd.local)”
}
if($driveletter_plibrary -eq “”) {
$driveletter_plibrary = Read-Host “Enter the driveletter for the primary content library (for example: E)”
}
if($driveletter_pshare -eq “”) {
$driveletter_pshare = Read-Host “Enter the driveletter for the primary content share (for example: E)”
}
if($add_statemigrationpoint -eq $true -And $state_migrationpoint_localpath -eq “”){
$state_migrationpoint_localpath = Read-Host “Enter the path your state migration point should use to store data (use quotes, for example: “C:\USMT”)”
}
if($distributionpointgroup -eq “”) {
$distributionpointgroup = Read-Host “Enter the name of the group you wish your distribution point to be a member of (for example OGD Production Site)”
}
if($add_schedule -eq $true){
if($block_size -eq 0){
$block_size = Read-Host(“Specify the maximum block size in KB for the Pulse Rate (min 1, max 256)”)
}
if($block_delay -eq 0){
$block_delay = Read-Host(“Specify the delay between blocks (min 1, max 30)”)
}
if([int]$block_delay -gt 30) {[int]$block_delay = 30}
if([int]$block_delay -lt 1) {[int]$block_delay = 1}
if([int]$block_size -gt 256) {[int]$block_size = 256}
if([int]$block_size -lt 1) {[int]$block_size = 1}
}

#bind to correct site
Set-Location $site_code”:”
#summarize
Write-Host “”
Write-Host “”
Write-Host “We have enough information to proceed, the following will take place:”
Write-Host ” * A distribution point will be added to site $site_code”
Write-Host ” * The FQDN is $server_name”
Write-Host ” * A Content Library will be placed on drive $driveletter_plibrary”
Write-Host ” * A Content Share will be placed on drive $driveletter_pshare”
Write-Host ” * The DP will be added to the $distributionpointgroup group”
if($add_statemigrationpoint -eq $true){
Write-Host ” * A state migration point will be added”
Write-Host ” * State Migration Point Storage Path: $state_migrationpoint_localpath”
$folders = New-CMStorageFolder -StorageFolderName $state_migrationpoint_localpath -MaximumClientNumber 100 -MinimumFreeSpace 100 -SpaceUnit Megabyte
}else{
Write-Host ” * A state migration point will NOT be added”
}
if($add_schedule -eq $true){
Write-Host ” * Rate Limitation will be set to $block_size KB per $block_delay seconds according to schedule in config”
}else{
Write-Host ” * Rate Limitation will NOT be configured”
}

Write-Host “”
$options = [System.Management.Automation.Host.ChoiceDescription[]]($ja,$nee)
$prompt = $host.ui.PromptForChoice(“”,”Do you wish to proceed?”,$options,0)
Write-Host “”
if($prompt -eq 1) {
Write-Host “Cancelled…”
Exit
}

new-CMSiteSystemServer -ServerName $server_name -SiteCode $site_code

add-CMdistributionpoint -SiteSystemServerName $server_name -SiteCode $site_code -CertificateExpirationTimeUtc “Monday, January 2, 2017 1:00:00 AM” -MinimumFreeSpaceMB 5000 -InstallInternetServer -PrimaryContentLibraryLocation $driveletter_plibrary -PrimaryPackageShareLocation $driveletter_pshare -EnablePxeSupport -AllowRespondIncomingPxeRequest -EnableUnknownComputerSupport

add-CMDistributionPointToGroup -DistributionPointName $server_name -DistributionPointGroupName $distributionpointgroup
if($add_statemigrationpoint -eq $true){
add-cmstatemigrationpoint -EnableRestoreOnlyMode $false -sitesystemservername $server_name -sitecode $site_code -storagefolders $folders -TimeDeleteAfter 7 -TimeUnit Days -AllowFallBackSourceLocationForContent $false
}

if($add_schedule -eq $true){

$Percent = 100
$UsageAsBackup = @($true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true,$true)

$RateLimitingSchedule = @($Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent,$Percent)

$SMS_SCI_ADDRESS = “SMS_SCI_ADDRESS”
$class_SMS_SCI_ADDRESS = [wmiclass]””
$class_SMS_SCI_ADDRESS.psbase.Path =”ROOT\SMS\Site_$($site_code):$($SMS_SCI_ADDRESS)”

$SMS_SCI_ADDRESS = $class_SMS_SCI_ADDRESS.CreateInstance()
# Set the UsageSchedule For Weekdays
$SMS_SiteControlDaySchedule = “SMS_SiteControlDaySchedule”
$SMS_SiteControlDaySchedule_class = [wmiclass]””
$SMS_SiteControlDaySchedule_class.psbase.Path = “ROOT\SMS\Site_$($site_code):$($SMS_SiteControlDaySchedule)”
$SMS_SiteControlDaySchedule = $SMS_SiteControlDaySchedule_class.createInstance()
$SMS_SiteControlDaySchedule.Backup = $UsageAsBackup
$SMS_SiteControlDaySchedule.HourUsage = $HourUsageScheduleWeekdays
$SMS_SiteControlDaySchedule.Update = $true

# Set the UsageSchedule For Weekend
$SMS_SiteControlDayScheduleWeekend = “SMS_SiteControlDaySchedule”
$SMS_SiteControlDayScheduleWeekend_class = [wmiclass]””
$SMS_SiteControlDayScheduleWeekend_class.psbase.Path = “ROOT\SMS\Site_$($site_code):$($SMS_SiteControlDayScheduleWeekend)”
$SMS_SiteControlDayScheduleWeekend = $SMS_SiteControlDayScheduleWeekend_class.createInstance()
$SMS_SiteControlDayScheduleWeekend.Backup = $UsageAsBackup
$SMS_SiteControlDayScheduleWeekend.HourUsage = $HourUsageScheduleWeekend
$SMS_SiteControlDayScheduleWeekend.Update = $true

$SMS_SCI_ADDRESS.UsageSchedule = @($SMS_SiteControlDayScheduleWeekend,$SMS_SiteControlDaySchedule,$SMS_SiteControlDaySchedule,$SMS_SiteControlDaySchedule,$SMS_SiteControlDaySchedule,$SMS_SiteControlDaySchedule,$SMS_SiteControlDayScheduleWeekend)

$SMS_SCI_ADDRESS.RateLimitingSchedule = $RateLimitingSchedule

$SMS_SCI_ADDRESS.AddressPriorityOrder = “0”
$SMS_SCI_ADDRESS.AddressType = “MS_LAN”
$SMS_SCI_ADDRESS.DesSiteCode = “$($server_name)”
$SMS_SCI_ADDRESS.DestinationType = “1”
$SMS_SCI_ADDRESS.SiteCode = “$($site_code)”
$SMS_SCI_ADDRESS.UnlimitedRateForAll = $false

# Set the embedded Properties
$embeddedpropertyList = $null
$embeddedproperty_class = [wmiclass]””
$embeddedproperty_class.psbase.Path = “ROOT\SMS\Site_$($site_code):SMS_EmbeddedPropertyList”
$embeddedpropertyList = $embeddedproperty_class.createInstance()
$embeddedpropertyList.PropertyListName = “Pulse Mode”
$embeddedpropertyList.Values = @(1,$block_size,$block_delay) #second value is size of data block in KB, third is delay between data blocks in seconds

$SMS_SCI_ADDRESS.PropLists += $embeddedpropertyList

$embeddedproperty = $null
$embeddedproperty_class = [wmiclass]””
$embeddedproperty_class.psbase.Path = “ROOT\SMS\Site_$($site_code):SMS_EmbeddedProperty”
$embeddedproperty = $embeddedproperty_class.createInstance()
$embeddedproperty.PropertyName = “Connection Point”
$embeddedproperty.Value = “0”
$embeddedproperty.Value1 = “$($server_name)”
$embeddedproperty.Value2 = “SMS_DP$”
$SMS_SCI_ADDRESS.Props += $embeddedproperty

$embeddedproperty = $null
$embeddedproperty_class = [wmiclass]””
$embeddedproperty_class.psbase.Path = “ROOT\SMS\Site_$($site_code):SMS_EmbeddedProperty”
$embeddedproperty = $embeddedproperty_class.createInstance()
$embeddedproperty.PropertyName = “LAN Login”
$embeddedproperty.Value = “0”
$embeddedproperty.Value1 = “”
$embeddedproperty.Value2 = “”
$SMS_SCI_ADDRESS.Props += $embeddedproperty

$SMS_SCI_ADDRESS.Put() | Out-Null
}
#In case your executionpolicy is restricted and you can’t modify it, paste these lines before calling the script
#Copyright Oisin Grehan
#START (remove #’s)
#function Disable-ExecutionPolicy {
#($ctx = $executioncontext.gettype().getfield(“_context”,”nonpublic,instance”).getvalue($executioncontext)).gettype().getfield(“_authorizationManager”,”nonpublic,instance”).setvalue($ctx, (new-object System.Management.Automation.AuthorizationManager “Microsoft.PowerShell”))
#}
#Disable-ExecutionPolicy
#END

0x800706ba

An annoying issue kept recurring in distributing data from our main SCCM server to a remote distribution point.

The moment a file was fully transferred, it would be deleted by the DP and the PkgXferMgr.log would show the following:

Failed to get object class
ExecStaticMethod failed (800706ba) SMS_DistributionPoint,AddFile
CsendFileAction::AddFile failed; 0x800706ba
Sending failed.

After crossreferencing the time of failure with our firewall logs, we found a rule blocking RPC traffic down from the SCCM server to the distribution point.

 

Unknown Computer Support and Multiple Boot Images

I’ve been working on an SCCM 2012 SP1 implementation for a global sized company. This company would like a large degree of seperation in SCCM between departments.

Each department has their own distribution points and their own boot images. I ran into an issue when I attempted to PXE boot an unknown computer:

<![LOG[Client lookup reply: <ClientIDReply><Identification Unknown=”0″ ItemKey=”0″ ServerName=”” ServerRemoteName=””><Machine><ClientID/><NetbiosName/></Machine></Identification></ClientIDReply>
]LOG]!><time=”11:08:07.818-120″ date=”05-01-2014″ component=”SMSPXE” context=”” type=”1″ thread=”4320″ file=”libsmsmessaging.cpp:6363″>
<![LOG[00:0F:FE:C5:F0:65, 69DE15B4-D710-11DE-BBDA-FEC5F065000F: device is not in the database.]LOG]!><time=”11:08:07.818-120″ date=”05-01-2014″ component=”SMSPXE” context=”” type=”1″ thread=”4320″ file=”database.cpp:483″>
<![LOG[Getting boot action for unknown machine: item key: 2046820352]LOG]!><time=”11:08:07.818-120″ date=”05-01-2014″ component=”SMSPXE” context=”” type=”1″ thread=”4320″ file=”pxehandler.cpp:231″>
<![LOG[Client boot action reply: <ClientIDReply><Identification Unknown=”0″ ItemKey=”2046820352″ ServerName=”” ServerRemoteName=””><Machine><ClientID/><NetbiosName/></Machine></Identification><PXEBootAction LastPXEAdvertisementID=”” LastPXEAdvertisementTime=”” OfferID=”” OfferIDTime=”” PkgID=”” PackageVersion=”” PackagePath=”” BootImageID=”” Mandatory=””/></ClientIDReply>
]LOG]!><time=”11:08:08.364-120″ date=”05-01-2014″ component=”SMSPXE” context=”” type=”1″ thread=”4320″ file=”libsmsmessaging.cpp:6561″>
<![LOG[00:0F:FE:C5:F0:65, 69DE15B4-D710-11DE-BBDA-FEC5F065000F: no advertisements found]LOG]!><time=”11:08:08.364-120″ date=”05-01-2014″ component=”SMSPXE” context=”” type=”1″ thread=”4320″ file=”database.cpp:483″>
<![LOG[00:0F:FE:C5:F0:65, 69DE15B4-D710-11DE-BBDA-FEC5F065000F: No boot action. Rejected.]LOG]!><time=”11:08:08.535-120″ date=”05-01-2014″ component=”SMSPXE” context=”” type=”1″ thread=”4320″ file=”database.cpp:483″>
<![LOG[00:0F:FE:C5:F0:65, 69DE15B4-D710-11DE-BBDA-FEC5F065000F: Not serviced.]LOG]!><time=”11:08:08.535-120″ date=”05-01-2014″ component=”SMSPXE” context=”” type=”1″ thread=”4320″ file=”database.cpp:483″>

No advertisements were found. I was sure I had everything set up correctly!

  • A task sequence was deployed to the Unknown Computers group
  • The task sequence was set to PXE / Media mode
  • The boot image was present on the DP and set for PXE
  • The PXE was set to allow unknown computers

When adding the computer account to SCCM manually and adding it to a normal collection, then advertising a task sequence, things ran fine.

After much troubleshooting, I discovered that the distribution point checks in advance which boot images are assigned to the advertised task sequences. If it does not find at least 1 task sequence for the unknown computer with a boot image that has been distributed to the DP, it will error out with the above message. But we didn’t want the boot images of the other department on our own distribution points.

And we also didn’t want our task sequence deployed to the unknown computers group since that group is global and not department specific. We wanted to use the pre-execution hook in our boot image to select our own task sequence without letting the tech interact with the PXE process.

To accomplish this, we deployed a hidden task sequence to the Unknown Computers collection with the boot image that WAS distributed to the DP. This allowed the DP to let the client download the boot image itself. The boot image then runs a script to select the task sequence it needs instead of forcing the engineer to select a task sequence.

Inception

Welcome to my very first ever blog.

I’m going to use this blog to share little bits of knowledge with the world. I often run into issues that are hard or impossible to find using Google and would like to share those solutions for free.

And on occasion I’ll write scripts or more expansive articles which could be of use to others.