Oh, hey! I didn’t see you there. Come see what I’m doing.
I had a client request to create prestage content for every production package, application, OS image, etc. They are in the midst of a project in which they are replacing most of their Distribution Points, but the pipe into the new DPs will be limited. Therefore, standard distribution methods will not cut it. So, we elected to enable the DPs for prestage content and load it all once onsite.
Naturally, we could have selected each individual package, application, etc. and used the console to create the .pkgx files, but where’s the fun in that? This has to be easily scriptable, especially with the Publish-CMPrestageContent cmdlet, right? Wellllllllllllllll…
I scoured the web for any scripts that may already exist. After all, there’s no need to recreate the wheel. I came across this post by the well-known David O’Brien. However, I need wanted something a little more simplistic, with fewer parameters; I’m just trying to grab everything available and create content files. Also, the script was a little outdated (required you to run in an x86 PS session), so I figured I should take a stab at writing my own to fit my client’s needs.
The script (below) does the following:
- Accepts two parameters, “DestinationPath” and “SourceDP” (I plan on adding functionality later to allow for single Prestage Content file creation)
- Uses WMI and the namespace root\SMS\site_<site code> and various classes to gather all packages, driver packages, OS images (did not add OS installers yet), boot images, etc.
- To gather applications, I instead use the Get-CMApplication cmdlet
- Creates a couple directories based on your input
- Root directory based on the parameter
- Content directory underneath defined path in which the .pkgx files are written
- Copies extractcontent.exe (needed to unpack on the destination DPs) to the defined destination directory
- Creates a batch file with the necessary command parameters to run the extractcontent.exe and places it on the defined destination directory
- Gathers all available content for Prestage Content creation and creates files based on availability on the parameter-defined source DP
- Packages
- Applications
- Software Update Deployment Packages
- Driver Packages
- OS Images
- Boot Images
- Writes to a log file titled “PrestageContent_<short date and time>.log”, which exists in c:\windows\temp
- Cooks you breakfast in the morning
NOTE: This script must be run on a machine with the console installed (i.e. full access to the CM module). Also, be sure that the content of which you would like to create prestage files exists on the source DP (script will not fail, but no file will be created unless content has been distributed). Also, pardon the blog formatting… still getting used to this plugin.
[CmdletBinding()] param( [Parameter(Mandatory=$TRUE)] [string]$DestinationPath, [Parameter(Mandatory=$TRUE)] [string]$SourceDP ) ## Gets date and formates it to short date and time $Date = Get-Date -Format G ## Gets current working directory for reverting at end of script $CurrentPath = Convert-Path . ## Creates logfile $LogSuffix = $Date -replace " ", "" -replace "/", "" -replace ":", "" | % {$_.Substring(0,$_.length - 2)} $LogFile = "$env:windir\temp\PrestageContent_$LogSuffix.log" ## Creates a logfile that will be written to during the process Function Write-Log ($LogText) { Add-Content $LogFile "$(Get-Date) - $LogText" Write-Output "$Date - $LogText" } Write-Log "Log file located at $LogFile" ## Tests required connection to source DP Write-Log "Testing connection to $SourceDP" if (!(Test-Connection $SourceDP -Count 1 -ErrorAction SilentlyContinue)) { Write-Log "Cannot connect to $SourceDP. Please check name and try again." exit 1 } ## Ensures source DP is always FQDN Write-Log "Qualifying $SourceDP" $SourceDP = ([System.Net.Dns]::GetHostByName($SourceDP)).HostName Write-Log "The FQDN of the source DP is $SourceDP" ## Imports the configuration manager module Function Import-CMModule { Write-Log "Imorting configuration manager module" Try { Import-Module -Name "$(split-path $Env:SMS_ADMIN_UI_PATH)\ConfigurationManager.psd1" $global:Site = Get-PSDrive -PSProvider CMSite CD "$($Site):" Write-Log "Site PSDrive now $Site" Set-Variable -Name Site -Value $Site.Name } Catch { Write-Log "Cannot import the Configuration Manager module. $_" exit 1 } } Import-CMModule ## Defines error action, location of extractcontent.exe, and arrays for item type variables $ErrorActionPreference = "SilentlyContinue" $ExtractContentApp = (Split-Path $env:SMS_LOG_PATH) + "\bin\x64\extractcontent.exe" $Packages = @() $DriverPackages = @() $SoftwareUpdatesPackages = @() $Applications = @() $OSImages = @() $BootImages = @() ## Verifies site variable exists if (($Site -eq "") -or ($site -eq $null)) { Write-Log "Failed to write site variable; exiting script" exit 1 } ## Sets variables for each item type we are importing based on data from WMI $Packages = gwmi -class SMS_Package -Namespace root\sms\site_$Site $DriverPackages = gwmi -Class SMS_DriverPackage -Namespace root\sms\site_$Site $SoftwareUpdatesPackages = gwmi -Class SMS_SoftwareUpdatesPackage -Namespace root\sms\site_$Site $Applications = Get-CMApplication $OSImages = gwmi -class SMS_ImagePackage -Namespace root\sms\site_$Site $BootImages = gwmi -Class SMS_BootImagePackage -Namespace root\sms\site_$Site ## Create Destination Path if does not exist if (!(Test-Path $DestinationPath)) { Write-Log "Destination path does not exist. Creating $DestinationPath" New-Item -ItemType directory -Path $DestinationPath | Out-Null } ##Create content directory $ContentDir = if (!(Test-Path "$DestinationPath\Content")){New-Item -ItemType directory -Path "$DestinationPath\Content" | Out-Null; "$DestinationPath\Content"} else {"$DestinationPath\Content"; Write-Log "Content directory exists"} ## Copy extractcontent.exe to destination path for unpacking later Write-Log "Copying $ExtractContentApp to $DestinationPath" Copy-Item -Path $ExtractContentApp -Destination $DestinationPath ## Create batch file for unpacking and place in destination folder Write-Log "Creating batch file for unpacking content on destination server" $batch = "%~dp0extractcontent.exe /P:%~dp0Content /F" | Out-File -FilePath "$DestinationPath\UnpackContent.bat" -Append -Encoding ascii ## Create prestaged content for software packages Write-Log "Discovered $(@($Packages).count) packages. Begin processing packages..." ForEach ($Package in $Packages) { Write-Log "Creating file for $($Package.name)" Try { Publish-CMPrestageContent -PackageId $($Package.PackageID) -FileName $(Join-Path $ContentDir "$($Package.name).pkgx") -DistributionPointName $SourceDP | Out-Null if (!(Test-Path $(Join-Path $ContentDir "$($Package.name).pkgx"))) { Write-Log "Warning: issue creating $($Package.name). Please try again later or create manually." } else { Write-Log "Successfully created $($Package.name)" } } Catch { Write-Log "Failed to copy $($Package.name); $_" } } ## Create prestaged content for driver packages Write-Log "Discovered $(@($DriverPackages).count) driver packages. Begin processing driver packages..." ForEach ($DriverPackage in $DriverPackages) { Write-Log "Creating file for $($DriverPackage.name)" Try { Publish-CMPrestageContent -DriverPackageId $($DriverPackage.PackageID) -FileName $(Join-Path $ContentDir "$($DriverPackage.name).pkgx") -DistributionPointName $SourceDP | Out-Null if (!(Test-Path $(Join-Path $ContentDir "$($DriverPackage.name).pkgx"))) { Write-Log "Warning: issue creating $($DriverPackage.name). Please try again later or create manually." } else { Write-Log "Successfully created $($DriverPackage.name)" } } Catch { Write-Log "Failed to copy $($DriverPackage.name); $_" } } ## Create prestaged content for software updates packages Write-Log "Discovered $(@($SoftwareUpdatesPackages).count) software updates packages packages. Begin processing software updates packages..." ForEach ($SoftwareUpdatesPackage in $SoftwareUpdatesPackages) { Write-Log "Creating file for $($SoftwareUpdatesPackage.name)" Try { Publish-CMPrestageContent -DeploymentPackageId $($SoftwareUpdatesPackage.PackageID) -FileName $(Join-Path $ContentDir "$($SoftwareUpdatesPackage.name).pkgx") -DistributionPointName $SourceDP | Out-Null if (!(Test-Path $(Join-Path $ContentDir "$($SoftwareUpdatesPackage.name).pkgx"))) { Write-Log "Warning: issue creating $($SoftwareUpdatesPackage.name). Please try again later or create manually." } else { Write-Log "Successfully created $($SoftwareUpdatesPackage.name)" } } Catch { Write-Log "Failed to copy $($SoftwareUpdatesPackage.name); $_" } } ## Create prestaged content for applications Write-Log "Discovered $(@($Applications).count) applications. Begin processing applications..." ForEach ($Application in $Applications) { Write-Log "Creating file for $($Application.LocalizedDisplayName)" Try { Publish-CMPrestageContent -ApplicationName $($Application.LocalizedDisplayName) -FileName $(Join-Path $ContentDir "$($Application.LocalizedDisplayName).pkgx") -DistributionPointName $SourceDP | Out-Null if (!(Test-Path $(Join-Path $ContentDir "$($Application.LocalizedDisplayName).pkgx"))) { Write-Log "Warning: issue creating $($Application.LocalizedDisplayName). Please try again later or create manually." } else { Write-Log "Successfully created $($Application.LocalizedDisplayName)" } } Catch { Write-Log "Failed to copy $($Application.LocalizedDisplayname); $_" } } ## Create prestaged content for OS images Write-Log "Discovered $(@($OSImages).count) OS images. Begin processing OS Images..." ForEach ($OSImage in $OSImages) { Write-Log "Creating file for $($OSImage.name)" Try { Publish-CMPrestageContent -OperatingSystemImageId $($OSImage.PackageID) -FileName $(Join-Path $ContentDir "$($OSImage.name).pkgx") -DistributionPointName $SourceDP | Out-Null if (!(Test-Path $(Join-Path $ContentDir "$($OSImage.name).pkgx"))) { Write-Log "Warning: issue creating $($OSImage.name). Please try again later or create manually." } else { Write-Log "Successfully created $($OSImage.name)" } } Catch { Write-Log "Failed to copy $($OSImage.name); $_" } } ## Create prestaged content for boot images Write-Log "Discovered $(@($BootImages).count) boot images. Begin processing boot images..." ForEach ($BootImage in $BootImages) { Write-Log "Creating file for $($BootImage.name)" Try { Publish-CMPrestageContent -BootImageID $($BootImage.PackageID) -FileName $(Join-Path $ContentDir "$($BootImage.name).pkgx") -DistributionPointName $SourceDP | Out-Null if (!(Test-Path $(Join-Path $ContentDir "$($BootImage.name).pkgx"))) { Write-Log "Warning: issue creating $($BootImage.name). Please try again later or create manually." } else { Write-Log "Successfully created $($BootImage.name)" } } Catch { Write-Log "Failed to copy $($BootImage.name); $_" } } cd $CurrentPath Write-Log "Prestaged content creation complete"