Windows 7 Reference Task Sequence Creation With ConfigMgr and MDT Integration
A customer earlier in the week had implemented ConfigMgr for their builds and was getting good results with it. They hadn’t implemented MDT as they couldn’t see the benefit, so with this series of posts I’m going to highlight why we mostly do it this way, and what benefits using MDT Task Sequences brings.
Now that SP2 for ConfigMgr is in Release Candidate (and due for RTM at the end of October according to Mr Niehaus) we can use this stuff for Windows 7 deployment.
First up, install SQL, ConfigMgr, its dependencies, and MDT 2010 RTM.
Now, integrate MDT with ConfigMgr by clicking
Now open the ConfigMgr console. nothing much has changed, but you have a couple of new options when you right click in the OS Deployment node. You can create MDT Boot Media clicking in boot images and you can create an MDT Task Sequence clicking in Task Sequences, let’s do that now!
When we do this we are prompted to pick a template. So, here’s the first benefit with MDT. More pre-configured templates:
The standard ConfigMgr task sequence only gives three options:
Further to that, the ConfigMgr standard Task Sequence expects you to have set up all of the dependent packages, boot images, etc. yourself before running through the wizard. MDT will create them for you if required…
Ok, so we pick to deploy the MDT Client Task Sequence, I can already tell that this is going to be a rambling post, but the first thing of note is that you no longer have to provide a capture destination if you’re not going to be doing a capture. Hurray, a minor irritant squashed (it’s the little things…)!
That said, I need to capture this build, so I fill in the box.
With the standard ConfigMgr task sequence, I’d need to select one of the pre-built boot images, but I want the goodness of ADO and other brilliant, so I get MDT to make a special one just for me:
The MDT asks if you want other languages, a custom wallpaper (I always make mine in PowerPoint, some of the templates make for pretty wallpapers, when there’s all this technology around there’s still no getting away from the fact that the customers like their logos on things, and why not.). On the same screen as the wallpaper, language, ADO options etc. you can also provide an extra directory to add. I put my diag tools in here (Trace32.exe etc.) they make life easier if you have problems in PE).
I create a Deployment Toolkit File Package. This holds all the scripts and bits that the MDT task sequence needs. Those of you still with us may notice that I put everything in a sub-folder of a root folder called OSImaging. This keeps things nice and tidy as far as I’m concerned, and is something I recommend.
Now MDT wants to create our OS package for us. Again under the standard ConfigMgr task sequence you’d have to do this outside of the wizard.
It also creates the ConfigMgr client package for you. Again, it’s not hard to do yourself, but why bother when the wizard can sort you out…
and USMT package:
Last thing is the MDT Settings Package. This handles the unattend.xml and customsettings.ini files.
We don’t need Sysprep, so can skip the final screen and then we’re ready. The wizard goes off and creates all the objects listed above.
Once it’s finished we just need to add the packages created to distribution points (this includes the OS install, so it can take a little while). I’ve got a PXE Service Point, so I add my new boot image to that DP too.
Next we’ll deploy and capture this and then start to look at the clever stuff we can do with the MDT integration to streamline deployment and support advanced deployment scenarios.
DPM 2010 Beta 1
As I’m all Hyper-V R2 for my demo environment I figure I may as well have a look at DPM 2010 Beta 1 than deploy the excellent DPM 2007 SP1 (there’s no Hyper-V R2 support in the current version).
I’ve created a new Server 2008 R2 x64 VM and downloaded the DPM beta.
I have also created a new LUN on my excellent Qnap TS-119 called “Backup”.
The iSCSI initiator on the new DMP VM connects to this:
Disk administrator wants me to initialize the disk:
Now I’m ready to give this disk to DPM. I don’t need to format it, DPM will manage that.
DPM’s setup routine is very attractive!
Oops, I’m not logged on with domain credentials. Also the Single Instance Storage engine isn’t installed. DPM setup will handle this, but it’ll need a reboot.
I’ve been lazily using the built in Administrator account to date, Windows has a minor irritation of thinking you want to log on locally when you type in “Administrator” and switches the logon context as you type, consequently you have to enter DOMAIN\Administrator every time. Perhaps it’s best if I create a new account, this also gives me chance to try out Windows Server 2008 R2 “Active Directory Administrative Center”
I don’t do a lot of support, but I can see that this thing would be a boon for account management types. No more multiple-dialogue-tab hell, just everything in a nice neat expandable list. You can remove the sections that you don’t use and get everything relevant in a great, easy to use view:
What’s not to like, this is great. The search is really quick and doesn’t suffer from the terrible user interface horrors of AD U&C.
Anyway, back to the DPM install…
Logged on with my new domain credentials I’m good to go!
This being an R2 server I already have PowerShell and Installer 4.5, so the SIS goes in.
DPM comes with its own copy of SQL 2008, unlike the rest of the System Center Suite… Quite why this is I have no idea, but if you wish you can let it do its own thing with SQL…
And we’re done.
I then pushed out an agent to a test SQL box from the Management node. On this server I’d created a dummy database with a single table. I added two dummy records to this table, then created a DPM Protection Group to protect this database.
One of the new features is illustrated above, I can now protect a SQL Instance (HYPERV-VMM above) the bonus being that whenever I add a new database it’s automatically protected.
I left this running for a while and now I can recover back to any point in time:
I delete some records from my demo database, pick a time from just before I did that and as if by magic, the data reappears…
With DPM I can elect to recover my data in a few different ways. One cool feature with SQL restore is I can take a SQL 2005 backup and recover to SQL 2008.
Having selected my recovery option I see a summary of the items to be recovered, then we’re done.
When this completes, the restore is complete. The DPM interface is very polished, I reckon it’s hard to get excited about backup, but there’s some really good stuff here and with the integration of online services (DPM to cloud backup) things get very interesting…
A Portable Highly Available Hyper-V and System Center Demo Environment – Making Progress
I’ve had the various bits and pieces that I outlined in my earlier post for a few weeks and have had some time to play around with it.
I started out booting Hyper-V Server 2008 R2 from the USB sticks, this works brilliantly but caused me a few headaches… The primary problem is that I want to be able to administer the Hyper-V instance from a remote Windows 7 laptop. This proved to be pretty complicated without the two being in a domain. It’s possible to get most of the way clear outside of the domain using WinRM TrustedHosts and a few other little tips and tricks. This eventually allowed me to load Server Manager remotely, but I absolutely could not get Disk Administrator to load from the remote machine and didn’t have much better luck with Hyper-V Administrator either.
After getting tied in knots with this I eventually created a physical DC, added my Hyper-V Server machine (booted from USB) and my Windows 7 laptop to the domain and I could then do all the admin I liked. This is fine, but obviously I don’t want to have to carry around an additional physical DC, so I P-V’d this into Hyper-V (probably not a great idea). All was fine until I shut everything down. I’m now in a situation where my Hyper-V server is joined to a domain which is virtualised upon itself. This leads to some, err, inconsistencies in the stability of the environment.
After a few late nights and plenty of red wine one of our Active Directory consultants took pity on me. We decided the best approach was to scrap the Hyper-V Server approach for the first node and install Server 2008 R2 on the physical laptop, make this a Hyper-V machine, then make it a domain controller in a shiny new domain. This done, it’s been solid as a rock. Obviously in a live infrastructure these issues would never arise and we would always plan to maintain at least one physical DC.
I now have a stable, currently single node, Windows Server 2008 R2 Hyper-V laptop and have virtualised a few machines to provide Virtual Machine Manager and Configuration Manager. Next up is DPM 2010, so more on that shortly.
A last point worth making is that performance is very good. Taking the storage away from the laptop hard disk to a much higher performing iSCSI device makes a massive difference to the performance of the VMs. I have a virtualised Windows 7 machine running Office 2010 very happily within this environment which I just RDP into for email, presentations, etc.
USB Boot Disk
We’re building a load of laptop kit. Problems with the WAN make PXE booting impossible for now, and the laptops don’t have optical drives. Obviously a USB Flash boot disk is the answer. It’s worth a quick note as to how to create these:
Insert the USB Stick into a computer running the Vista or Windows 7, then use diskpart:
diskpart
select disk 2*
clean
create partition primary
select partition 1
active
format fs=fat32
assign
exit
Now create Task Sequence media boot ISO. Mount the ISO and copy the contents to the USB stick.
Easy as that!
*NB: The value of disk 2 is the USB Stick’s disk ID. You can find this in Disk Manager.
Package Mapping in the Replace Computer Scenario
The MDT Package Mapping approach is a great bit of OS deployment technology and can help to provide an excellent deployment rate when it comes to large-scale zero touch deployment projects. One limitation that currently exists is that package mapping only works in the refresh computer scenario, i.e. I’m moving from XP to Vista, when I install my Vista image, put back the applications which I used to have.
For the Replace Computer scenario, where we’re deploying new hardware, out of the box, package mapping does nothing for us.
In the ConfigMgr Replace Computer scenario we use ConfigMgr Computer Associations to provide the capability to recover the user state (via a state migration point) from the OLDCOMPUTER to the NEWCOMPUTER during the build process. We run the Replace Computer Scenario task sequence on the OLDCOMPUTER, this captures the user state, then the NEWCOMPUTER state restore phase magically recovers this. All good stuff, but it doesn’t help us with the apps…
But it can. A small change to the PackageMapping stored procedure can use the same Computer Association record to migrate the applications across machines in the same way as we migrate the user state. It’s a shame that this isn’t integrated in the ConfigMgr console as elegantly as the Computer Associations are, but it works…
First we need to import the NEWCOMPUTER into ConfigMgr using the Import Computer Information wizard. Select the OLDCOMPUTER as the Source for the NEWCOMPUTER (obviously this can be done on a large-scale by populating a CSV file with these entries).
Next we need to modify the PackageMapping process so that it runs against the MACAddress of the OLDCOMPUTER rather than the NEWCOMPUTER.
In SQL Management Studio, browse to the PackageMapping stored procedure and select to modify it. Replace the entry shown with this text (replace SMS_JON with the name of your SMS database)
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER PROCEDURE [dbo].[RetrievePackages] @MacAddress CHAR(17) AS SET NOCOUNT ON /* Select and return all the appropriate records based on OLDCOMPUTER inventory */ SELECT * FROM PackageMapping WHERE ARPName IN ( SELECT ProdID0 FROM SMS_JON.dbo.v_GS_ADD_REMOVE_PROGRAMS a, SMS_JON.dbo.v_GS_NETWORK_ADAPTER n WHERE a.ResourceID = n.ResourceID AND MACAddress0 = (select sourceMACAddresses from SMS_JON.dbo.v_statemigration where restoreMACAddresses=@MacAddress) AND n.ResourceID IN (Select ResourceID from SMS_JON.dbo.v_R_System_Valid))
A little explanation of what is happening here:

With a thorough PackageMapping database and thorough User State Migration profile and very good planning in terms of assigning the OLDCOMPUTER and NEWCOMPUTERs through Computer Associations we can achieve a near seamless and very high performance desktop replacement approach.
Package Mapping v2
The MDT Package Mapping functionality (Scenario 5: Dynamic Computer-Specific Application Installation to give it its MDT name)
provides for the automatic re-installation of ConfigMgr applications during OS re-imaging. This is done via interrogation of the ADD_REMOVE_PROGRAMS SQL view. The deployment database contains a PackageMapping database. This is used to pair an Add/Remove Program inventory entry (ARP) with a ConfigMgr package and program in this way:

The database supplied with the deployment database does not contain the Comment and ID tables shown above, but I’m finding these are pretty useful. Comments are handy as the database gets larger and the ID field is used as a primary key on the table so that we can update the database from a Microsoft Access front end.
Populating the database with new mapping entries is somewhat error-prone… The ARP entries are often GUIDs, and the Packages entry requires the package ID and exact program name. Any of these entered incorrectly results in a failed build which can be awkward to troubleshoot. The only minor complexity on this is that the ID table should be configured as IsIdentity (AutoNumber) in SQL:
To reduce the risk of incorrect entries being added I have knocked up a quick MS Access form to allow selection of the ARP name and Install Package entry. To build this all we need are a couple of ODBC entries one "PackageMapping" connected to the DEPLOYMENT database and another "ARPData" connected to the ConfigMgr database.
Now we just need some linked tables in Access.
|
|
The PackageMapping table is from the DEPLOYMENT database. The others are SQL views from ConfigMgr The field highlighted in red builds the MappingEntry field to |
|
This structure allows us to select the PackageID from a list of all Packages, but displays the friendly package name.
Some Minor Package Mapping Improvements
The MDT Package Mapping functionality is an excellent way of providing application migration during zero touch OS deployment. Coupled with a decent USMT configuration you can get some really good results to provide a seamless OS refresh.
We’re using Package Mapping extensively on a current Zero-Touch project and have come across some minor issues related to obsolete resource records.
For example, machine PC001 has ten applications which are in-scope for package mapping (i.e. the Add/Remove Program (ARP) entries they have are mapped to ConfigMgr packages in the MDT PackageMapping table). PC001 has a problem and is rebuilt using the legacy Ghost approach by IS support. Of the ten applications which were installed previously, only three are actually required by the user, so only these three are replaced.
A couple of months go by… We’re finally in a position to deploy our new ZTI image to PC001, the install goes fine, but ten apps are re-installed to the machine when the image is installed. Checking the ARP record in Resource Explorer for the machine previously revealed only three in-scope apps, so what’s happened?
Pretty straightforward stuff, the stored procedure used to populate the PackageMapping entries (PACKAGESxxx) in the image installation task sequence does a lookup for the ConfigMgr ResourceID using the host machine’s MAC Address; this ResourceID is then used to interrogate the ARP entries table for all apps at last hardware inventory cycle. The problem is that when the query for the ResourceID is executed, it gets two results, the one from before the machine was re-Ghosted and the new record created when the machine re-joined the infrastructure. The old record will ultimately be aged out of the database (after 90 days) but in the mean time it’s hanging around, even though the old resource has been removed from the Admin Console.
The consequence of this is that the obsolete ResourceID is used by the PackageMapping process and thus the wrong apps are re-installed to the client. The same problem occurs in the lab when re-imaging test machines; even when the test machine’s record is deleted from the admin console, its inventory data remains and will be used by the PackageMapping process.
The fix for this is to modify the MDT RetrievePackages stored procedure to validate the ResourceID against the new ConfigMgr R_System_Valid view.
To do this using SQL Server Management Studio browse to the stored procedure, right click – modify, then add the following to the end of the supplied code:
AND n.ResourceID IN (Select ResourceID from SMS_xxx.dbo.v_R_System_Valid )
Alternatively, replace the provided stored procedure by executing:
use [Deployment]
go
if exists (select * from dbo.sysobjects where id = object_id(N’[dbo].[RetrievePackages]‘) and OBJECTPROPERTY(id, N’IsProcedure’) = 1)
drop procedure [dbo].[RetrievePackages]
go
CREATE PROCEDURE [dbo].[RetrievePackages]
@MacAddress CHAR(17)
AS
SET NOCOUNT ON
/* Select and return all the appropriate records based on current inventory */
SELECT * FROM PackageMapping
WHERE ARPName IN
(
SELECT ProdID0 FROM SMS_xxx.dbo.v_GS_ADD_REMOVE_PROGRAMS a, SMS_xxx.dbo.v_GS_NETWORK_ADAPTER n
WHERE a.ResourceID = n.ResourceID AND
MACAddress0 = @MacAddress
AND n.ResourceID IN (Select ResourceID from SMS_xxx.dbo.v_R_System_Valid ))
go
Now when Package Mapping is called, only non-obsolete data will be used and everything will function as expected. This has the added advantage in a lab of allowing you to delete the resource record of a test PC from the console and have that machine return no results from Package Mapping.
Thanks to John Nelson for his help with this on MyITForum.
Modify Package Source
We’re using Kim Oppalfens’ rather wonderful approach for migrating to ConfigMgr from a large SMS 2003 implementation. We don’t want to take the SMS hardware, but we do want the investment we’ve made in the development in the console (hundreds of packages, collections and advertisements which would take us a long time to set up again).
Kim’s approach implements a new child Primary SMS 2003 server. The package, collection and advertisement content is replicated to the child. We copy the package source across to the new server, detach from the parent and the packages etc become ‘owned’ by the new SMS site. This site can then be upgraded to ConfigMgr. This is great, one slight fly in the ointment is that on the old server the packages are stored in G:\DIST_Source\Vendor\Appname and on the new server they’ll be in D:\packages\Vendor\Appname.
Once the child site has been detached, the package source must be modified. This can be done using Transact-SQL and the most efficient (in terms of code) way of doing this in SQL 2005 is using REPLACE command.
USE SMS_XXX
UPDATE SMSPackages
SET Source = REPLACE(CAST(Source AS NVARCHAR(MAX)),
‘G:\DIST_source’,
‘D:\Packages’)
Note it’s important to cast ‘Source’ as NAVCHAR as REPLACE won’t work on STRING types.
Setting the Local Administrator Password with ConfigMgr Collection Variables
ConfigMgr provides an elegant opportunity to manage the local administrator password using a task sequence, collection variable, a package and a little bit of VBScript.
At the most basic level, we want to be able to manage the password of the local administrator account. We want to be able to set the password to be the same across all machines (security issues aside…). To do this we create a collection variable on the “All Windows Workstation or Professional Systems” ADMINPASSWORD=ourpassword.
This is obfuscated in the console, so can’t be read.
We have a package that just contains a single VBScript:
sNewPassword = Wscript.Arguments(0)
Set oWshNet = CreateObject(“WScript.Network”)
sComputer = oWshNet.ComputerName
sAdminName = GetAdministratorName
On Error Resume Next
Set oUser = GetObject(“WinNT://” & sComputer & “/” & sAdminName & “,user”)
oUser.SetPassword sNewPassword
oUser.SetInfo
On Error Goto 0
Function GetAdministratorName()
Dim sUserSID, oWshNetwork, oUserAccount
Set oWshNetwork = CreateObject(“WScript.Network”)
Set oUserAccounts = GetObject( _
“winmgmts://” & oWshNetwork.ComputerName & “/root/cimv2″) _
.ExecQuery(“Select Name, SID from Win32_UserAccount” _
& ” WHERE Domain = ‘” & oWshNetwork.ComputerName & “‘”)
On Error Resume Next
For Each oUserAccount In oUserAccounts
If Left(oUserAccount.SID, 9) = “S-1-5-21-” And _
Right(oUserAccount.SID, 4) = “-500″ Then
GetAdministratorName = oUserAccount.Name
Exit For
End if
Next
End Function
Our task sequence has a single step to call this script from the package we’ve created. The script takes a single parameter of %ADMINPASSWORD%, read by the task sequencing engine from the collection variable created above.
Obviously, this can be advertised out on a recurring schedule to reset password regularly and the only administrative change required is to replace the collection variable value. It is also easily possible to create different collection variable values for different collections, or even on a per-machine basis if desired, simply set the precedence on the new entry to be higher than the default.
Creating Collections from the Command Line
I’ve not done much work with the ConfigMgr SDK before, but a project I’m working on at the moment called for a large number of collections to be created for application distribution purposes. This is a direct replacement for ZEN Works, so to try to minimize the impact on the helpdesk we’ve decided to populate AD security groups with computer accounts to drive the population of the ConfigMgr collections. It’s not an approach that I’m particularly in favour of as it introduces latency:
|
Using AD Security Groups for Collection Population |
Using Direct Membership in the ConfigMgr Console |
|
|
|
|
|
|
Assuming the worst-case scenario this approach could take: AD Replication – 15 mins System group Discovery – 60 mins Colleval – 60 mins Computer Client update – 60 mins Maximum possible latency=3hrs 15 minutes |
Assuming the worst case scenario this approach would take: Computer Client update – 60 mins Maximum possible latency=60 minutes |
|
These issues aside, I decided to have a go at creating my many application based collections using a bit of VBScript from the SDK.
I always like to create a clean collection structure from the outset with a small number of root collections with sub collections. I normally break these down as shown:

This approach keeps the collection structure tidy, but means that creating collections using COLLADD won’t work as this script doesn’t set the parent collection, creating everything in COLLROOT. If you’re going to have to move them all manually later (by linking and then deleting the original) then you may as well create them manually in the first place.
Fortunately the SDK provides great samples on creating objects in the console, and the CreateDynamicCollection function automatically expects the Collection ID of the parent collection.
The script I have used is copied verbatim from the SDK with one minor modification to allow for escaping of the quotes around the AD System group Name:
Script1
‘ Setup a connection to the local provider.
Set swbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set swbemconnection= swbemLocator.ConnectServer(".", "root\sms")
Set providerLoc = swbemconnection.InstancesOf("SMS_ProviderLocation")
For Each Location In providerLoc
If location.ProviderForLocalSite = True Then
Set swbemconnection = swbemLocator.ConnectServer(Location.Machine, "root\sms\site_" + Location.SiteCode)
Exit For
End If
Next
Call CreateDynamicCollection(swbemconnection, "XXX0001B", "App Blueprint", "Members will receive the application", true, "SELECT * from SMS_R_System where SMS_R_System.SystemGroupName = ""MYDOMAIN\App Blueprint""", "App Blueprint")
‘NB As the AD Group Names may have spaces in them it is necessary to escape the quotes. The above query line is wrapped for legibility.
Sub CreateDynamicCollection(connection, existingParentCollectionID, newCollectionName, newCollectionComment, ownedByThisSite, queryForRule, ruleName)
‘ Create the collection.
Set newCollection = connection.Get("SMS_Collection").SpawnInstance_
newCollection.Comment = newCollectionComment
newCollection.Name = newCollectionName
newCollection.OwnedByThisSite = ownedByThisSite
‘ Save the new collection and save the collection path for later.
Set collectionPath = newCollection.Put_
‘ Define to what collection the new collection is subordinate.
‘ IMPORTANT: If you do not specify the relationship, the new collection will not be visible in the console.
Set newSubCollectToSubCollect = connection.Get("SMS_CollectToSubCollect").SpawnInstance_
newSubCollectToSubCollect.parentCollectionID = existingParentCollectionID
newSubCollectToSubCollect.subCollectionID = CStr(collectionPath.Keys("CollectionID"))
‘ Save the subcollection information.
newSubCollectToSubCollect.Put_
‘ Create a new collection rule object for validation.
Set queryRule = connection.Get("SMS_CollectionRuleQuery")
‘ Validate the query (good practice before adding it to the collection).
validQuery = queryRule.ValidateQuery(queryForRule)
‘ Continue with processing, if the query is valid.
If validQuery Then
‘ Create the query rule.
Set newQueryRule = QueryRule.SpawnInstance_
newQueryRule.QueryExpression = queryForRule
newQueryRule.RuleName = ruleName
‘ Add the new query rule to a variable.
Set newCollectionRule = newQueryRule
‘ Get the collection.
Set newCollection = connection.Get(collectionPath.RelPath)
‘ Add the rules to the collection.
newCollection.AddMembershipRule newCollectionRule
‘ Call RequestRefresh to initiate the collection evaluator.
newCollection.RequestRefresh False
End If
End Sub
When executed the script automatically creates the required collection beneath my "Applications" parent collection (XXX0001B) with the query which enumerates all machines in the "APP Blueprint" AD group.
Leave a Comment

