Distribution Point Migration Tool-Kit

, , , , , , , , , , ,
The Distribution Point Migration 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 Point, 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 Point, 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 of Distribution Point Migration Tool-kit

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.

SCCM Powershell Tools

, , , , ,

I’ll get this out of the way right now: I think SCCM’s PowerShell module is garbage.  It’s slow, it doesn’t work like you’d expect a lot of the time, and it’s got absolutely nothing on the ease of use and functionality of something like the ActiveDirectory module.  Thankfully, WMI is still a thing, and that gives us an excellent way to interact with SCCM via PowerShell. With that in mind, here’s some quick and dirty (but still useful) PowerShell functions I’ve put together. Most, if not all, PowerShell tools I’ve written for SCCM can also be done with SQL queries and commands, so if that’s more your thing, have at.

Find-ClientByMac
File can be downloaded from my technet gallery here
This is a simple enough tool. It will find any clients in the SCCM database that match a “like” query against the MAC address you provide.  This is handy for finding duplicate objects with the same MAC, which can royally screw with trying to PXE boot and image a computer.

Function Find-ClientByMAC ($inputMac){
$namespace = “root/SMS/Site_TST”
$siteServer = “testServer”

    $inputMac = $inputMac.Replace(” “,””)
$inputMac = $inputMac.replace(“-“,”:”)

if ($inputMac -notlike “*:*”)
{
$count = 0
while ($count -lt ($inputMac.Length – 2))
{
$inputMac = $inputMac.Insert(($count)+2,’:’)
$count += 3
}
$inputMac
}
Get-WmiObject -Namespace $namespace -ComputerName $siteServer -class SMS_R_System -filter “MACAddresses like ‘%$inputMac%'”
}


Something to keep in mind with SCCM is that it stores MAC addresses with “:” between each pair, but your computer’s IPConfig command will give it to you with “-“‘s.  This will replace a – with a :, and if you just feed it a straight set of text, it will insert the “:” between each pair.
This will find any computer that matches the MAC you provided and returns with a WMI object of the SMS_Device class.  If you want to delete the duplicate objects, you can simply pipe to the remove-wmiobject command to delete.

 

ALSO CHECK : N-2 WIM and Task Sequence Managment

Using Custom Objects for Fun and Profit

, , , , ,

Custom objects were one of those things I never really saw the point of when I read about them. It wasn’t until I actually messed around with them that I really understood their uses. Custom objects are especially good for generating reports that pull data from multiple sources.  For example, let’s say your manager has requested a report of all the computers in your Accounting department. He wants to know their hostname, their IP Address, their Make/Model, and their Serial Number.  Now, there’s no built in function or class (that I know of) that will return all of those pieces of information, so we’ll have to pull from multiple data sets.  For this example, we’re going to need Active Directory, the Win32_BIOS class, and the Win32_ComputerSystem class. For the sake of argument, let’s say we’ll also need the Get-CompOU script from the previous post and the hostname of a computer in the Accounting department’s OU to get started.
An example asset report script can be downloaded here:
https://goo.gl/5bB03A

Step 1: Generate a list of all the computers needed for this report
$allComps = Get-ADComputer -SearchBase (Get-CompOU AccountingPC1) -filter * -properties * 

This gives us an array of all the computer objects hosted in the same OU as the Accounting PC we started with. We can get the hostname and IP information just from these objects, but we need more.

Step 2: Enumerate all the computer objects and start data mining
ForEach ($comp in $allComps)
{
        $SWMI = Get-WmiObject -ComputerName $comp.Name -class Win32_ComputerSystem
$BWMI = Get-WmiObject -ComputerName $comp.name -class Win32_BIOS

We’re not quite done with the loop yet, but this shows how we’re invoking the WMI classes needed for each computer.  Step 3 takes place within the same loop.

Step 3: Create the custom object to hold our required data
$object = New-Object -TypeName PSObject
$object | Add-Member -MemberType NoteProperty -Name “Hostname” -Value $comp.Name
$object | Add-Member -MemberType NoteProperty -Name “DNSName” -Value $comp.DNSHostName
$object | Add-Member -MemberType NoteProperty -Name “Serial” -Value $BWMI.SerialNumber
$object | Add-Member -MemberType NoteProperty -Name “IPAddress” -Value $comp.IPV4Address 
 
$object | Add-Member -MemberType NoteProperty -Name “Make” -Value $SWMI.Manufacturer
$object | Add-Member -MemberType NoteProperty -Name “Model” -Value $SWMI.Model 

$allObj += $object
 }
 This one is pretty straight forward. It’s a lot of text just to say that you’re adding properties to an item you created, assigning those properties values based on multiple classes created in Step 2, and then adding that object to an array of objects.  You can type $allObj = @() at the beginning of your script, but it isn’t required.

Step 4: Profit
At this point, we can dump our report out to a CSV, or we can just output to the screen.  Typing $allObj will just output to the screen, but if we want to make a report our management will be proud of:
$allObj | export-csv -path C:usersAdminDocumentsAssetReport.csv -NoTypeInformation

And we’re done!  Now, if you’re working in an environment with SCCM or other management software, these reports might be more easily generated by querying that database. However, this will give you up to the minute accuracy as the WMI queries are done live.  If there’s anything you want to see, leave me a comment, and I’ll add that to my next post.

ALSO CHECK : A quick (and useful) PowerShell script