Distribution Point Migration Tool-Kit

, , , , , , , , , , ,
The toolkit can be downloaded from my Technet Gallery HERE
This post is a long time in coming, but creating something robust enough to work in most environments that’s still user friendly (with associated documentation) can take a little bit of time.  In the course of one contract I’ve worked, we realized that we needed a way to convert old Secondary SCCM sites into Distribution Points, but we wouldn’t be given any new servers to migrate to. We also knew that the WAN links connecting these remote sites back to our headquarters were severely lacking.  Our solution was to prestage all the content currently stored on the content libraries so we could strip off all the roles (which would clear the SCCM content library), remove unneeded programs and features, add the servers back as Distribution Points, and then reload the prestaged content so it wouldn’t have to transfer over our unspeakably slow WAN connection. We got a peek at this work with my last post of the SCCM Universal Prestage script, but this post will give you the other pieces of the puzzle.  

The Core Functions

Initialize-Toolkit
                This is the first function you call if you’re running the Migration Kit from a PowerShell window you didn’t summon up from inside the Configuration Manager console.  This function will verify that you have Administrator rights, will seek out and import the Configuration Manager module, and will map your CMSite PSDrive if you don’t already have it mapped. This function is also called within every other function after a quick check to make sure that the CMSite drive is mapped.  If it isn’t mapped, it calls the Initialize-Toolkit function and maps it. 
 Console run without admin rights
Console run without admin rights
After the drive has been created
Get-DPContent
                The second function in the toolkit will query our primary site server and return a list of all content that is assigned to the distribution point we provided.  There are multiple ways to get this information. I’ve seen it done with Get-CMDeploymentPackage cmdlet since that will also return package type information that we’ll need later.   However, I chose to do it via the SMS_DPContentInfo WMI class because I find that it returns the same level of information, but does so in roughly 1/3 the time.  It also means that you can run the command without needing to be connected to the CMSite drive if you don’t want to fully initialize everything. 
 A simple report of package ID’s and names
An example of the data stored by SMS_DPContentInfo
Prestage-Content
                This is one of the ‘heavy lifters’ of the toolkit.  This function requires a package ID number, the Distribution Point containing the package, and the folder you want it dumped to after creation. What this creates is a PKGX file named with the package ID of whatever you prestaged.  The way it decides what to prestage is based on the PackageType value that comes from WMI’s SMS_PackageBaseClass. Again, you can get a package type identifier from Get-CMDeploymentPackage if you’d rather go that way, but I like WMI.  Once it’s pulled the PackageType value, it runs it through a SWITCH command and runs the appropriate Publish-CMPrestageContent command.  I don’t do any special logging with this function since Publish-CMPrestageContent already does a good job of it.
 Prestaging a single file
Prestaging multiple packages with a For Loop
Restage-Content
                This function is one of the main reasons I like to save my prestage files with the PackageID as the name.  You input the folder containing the prestage files as well as the name of the Distribution Point they need to be assigned to, and this will get the package type information for that package, run the same switch as Prestage-Content, and issue the Start-CMContentDistribution command with the appropriate flags.  Just to save time, it will also query the Get-DPContent function to make sure that it isn’t trying to reassign packages that are already assigned.
Packages were already assigned in SCCM
 Package distribution in progress
Extract-Content
                This function calls upon Microsoft’s ExtractContent.exe tool to run, and is designed to be run locally from whatever DP you’re importing the package to.  The only flag you need to specify is the location of the prestaged content folder.  It takes the hostname of the computer it’s running from and makes a WMI query to see any packages assigned to it that aren’t in State 0.  If the package shows as state 0, then there’s no further work to be done, and we can just work on the others.  There are multiple ways you can run the extractcontent.exe tool, but I’ve found some to work better than others.   Whether you run it specifying a single package to extract or you run it with an entire folder targeted, I’ve found that when I check the Distribution Point Configuration Status in the SCCM console, there’s always some that still show “waiting for prestage content.”  In almost every case where that’s happened, just re-prestaging the content cleared it up. I don’t know if this is a limitation of the extractcontent.exe tool, my impatience, or what, but it works for me.  Because of that, I actually have my Extract-Content function run through the Prestage content folder one item at a time, so you can re-run the function, it will re-query for unsuccessful packages, and only attempt to extract the packages that didn’t make it the first time.
 
ExtractContent running

Example

Stage-LocalDPContent
                I put this together for our SCCM architect who wanted something that he could quickly and easily run while logged into our Secondary Site Server that was being migrated.  What this does is query the local DP for all assigned content, export it with the Prestage-Content function, and give you a progress bar to show you how far along you are. 

Universal SCCM Content Prestager

, , , ,

PS1 file can be found at my TechNet gallery: HERE

The Use Case
    Prestaging content is a fact of life in the SCCM world.  Whether you’re standing up a new site, cloning a DP, or sending packages to a site with really bad bandwidth, there are a variety of reasons you need to create prestage packages.  At multiple contracts I worked, these packages were created by finding the content in the Configuration Manager GUI, right clicking, selecting Create Prestage Content File, and going through the wizard.  While this is technically a correct way to do things, it’s cumbersome, requires you to remember where everything is in the menu structure, and ties up your console while you do packages one at a time.   I could see using this method for one or two packages every now and then, but you’re on a PowerShell blog. Here, we’re all about scale. 

Not pictured: efficiency

Making it happen
     The Configuration Manager module actually comes with a prestage cmdlet built right in, but this cmdlet is one of the most poorly written ones in all of PowerShell.  It has no real intelligence of its own, requiring you to spell out exactly what package type you want to back up. Since every package has a unique PackageID value, I never understood why they didn’t just make it use that number and get on with life, but they didn’t.  Feel free to download the script and follow along.

I actually couldn’t fit them all on one screen


The Script
     The first thing we need to do is declare our variables. This script needs to know what the package ID number is (which can be found literally everywhere the package is mentioned in the SCCM console or WMI interface),  what DP we’re pulling the content from, and where the file needs to be saved. All of these are mandatory, so you’ll be prompted for them if you don’t put them inline.  Also, depending on the prestage location, you’ll need admin rights to move files there, so we just check for those at the outset.  We’ll also make sure you’re connected to your CMSite PSDrive. I also have the script make sure the package isn’t already there.  Something else to keep in mind is that the DP name needs to be a FQDN when the command runs. If you enter it with just the hostname, the script will sort that out for you, so no worries.

     After all the pre-reqs have checked out, the real logic comes in. The first thing it does is try to run get-cmpackge with the package ID, if that comes back as $NULL, meaning there was no package with that ID, it starts down the list of available content types. It checks if it’s a software update package, application, boot image, OS image, or driver package.  Until it finds something that actually returns an object, it’ll keep checking. Then, it makes a mental note of what it found so we can use it later.  For troubleshooting and debugging reasons, I like to output the name of the package as well as its source path, but this isn’t necessary. It will also tell you where it’s going and what it will be named.  Then, we use a Switch  statement with the count variable (our mental note from earlier) to select the correct Publish-CMPrestageContent flags that actually do the prestaging work for us.

Figuring out what you wanted
Actually prestaging it

Actually Using It
     On its own, it’s a nice function to have loaded in my shell. Being able to generate a prestage file without tying up my GUI is always handy, and depending on the organizational skills of the previous SCCM admins, not having to dig around to find a package in their menus can be a real time saver.  Where this script comes into its own, however, is when it’s chained together with other commands, which is what we’ll discuss in our next post. 

Working with CSV Files

, , ,

Hey everyone, it’s been a little while since my last post, but work’s been busy.  A recurring issue I see on TechNet is that plenty of people have trouble working with importing/exporting CSV files.  Specifically, I see questions about how to modify CSV files. Personally, I don’t see any value in modifying the CSV file directly, but rather importing the data from said file and working with the dataset natively in PowerShell.  Using one person’s thread as an example, he had a CSV file full of IP addresses that corresponded to his VM’s.  He wanted to add data about the VM based on the IP address, but wanted to know how to “modify the line” in the CSV file.    He had already written part of the script to create the CSV by pinging each computer in his IP range and outputting that to CSV.  For our test, here’s what our CSV looks like:

Now, let’s say we want to find the hostnames for each of these computers and add that to a new column. There are a couple ways we could do this, but in an enterprise environment, a good way to do it is to just ask AD who it is.   First, we create the empty “column” for our Hostname value.

Once that’s created, we need to fill it with values:

Keep in mind that you will see a lot of red text for any computer that doesn’t have an entry in AD. This could be a printer, a switch, etc.  If you’re using Windows 8.1+ or Server 2012R2+, you can use the resolve-dnsname cmdlet. 
You can add more columns to the CSV by using the Add-Member cmdlet to your heart’s content, and when you’re done, you pipe your $dataset variable back to the export-csv cmdlet.   
Really, once you start looking at CSV files as arrays of custom objects, they’re pretty easy to work with.