BitLocker in SCCM with 2nd HDD – NEW and REFRESH Scenarios

There are a couple of challenges when using BitLocker in ConfigMgr 2012. Using Pre-Provisioning and locking a 2nd HDD in REFRESH Scenarios is one such challenge.

Here is how i handle it.

Scenario 1: NEW – Single Disk

Background and overview: New PC or Laptop, single hard drive.

1. Create a PreInstall partition on the disk if there is no available partitions

2. Configure BIOS and TPM (see previous post)

3. Format and Partition Disk0 for use with BitLocker. Create a BDE partition with fixed size 500Mb, NTFS and store the drive letter as a variable BOOTPART. Create a System partition of 100% remaining space, NTFS, store drive letter as OSPART.

4. Pre-Provision BitLocker to “Logical drive letter stored in a a variable” – OSPART

5. At the end of the Task Sequence Enable BitLocker on “Current operating system drive”. Choose to wait for BitLocker to complete before continuing.

Scenario 2: REFRESH – Single Disk

Background and overview: Refresh PC or Laptop, single hard drive.

1. When started from Software Center, disable BitLocker on current operating system drive and reboot to WinPE.

2. If started from USB or PXE, use a script to unlock the operating system drive.

3. continue from step 2, Scenario 1.

Scenario 3: NEW – Additional Disk

Background and overview: New PC or Laptop, multiple hard drives.

1. Out of the box means started from USB or PXE, use a script to unlock OS and data drives.

2. continue from step 2, Scenario 1.

3. When finalizing BitLocker on the OS disk choose to continue on error. This because the attributes will be inconsistent after C, D and E drive letters are reassigned but the BitLocker process will finalize ok.

4. Enable BitLocker on the additional drive, choose either to wait for BitLocker to finish or continue and allow the drive to encrypt in the background. The machine will be usable but the 2nd disk will have limited availability until the process is finished which could be 20 – 50 minutes.

Scenario 4: REFRESH – Additional Disk

Background and overview: Refresh PC or Laptop, multiple hard drives.

1. When started from Software Center, disable BitLocker on current operating system drive and data drives and reboot to WinPE.

2. If started from USB or PXE, use a script to unlock the operating system drive and data drives.

3. continue from step 2, Scenario 1.

3. continue from step 2, Scenario 3.

Here are some screenshots of the TS:



SCCM Add Computer Associations With Powershell – Code

Below is the source code for this tool. I would like to compile it a bit better so it is more dynamic: it uses an ini file called acatsv4t.ini which should really be in a proper ini format with tags to allow config of the available options, for example available language and OS. Once I have the ini config sorted i’ll compile as an exe, with an install/self-extract routine.

Code is provided as is. No official support provided and i accept no responsibility for misuse. Other than that, hope it comes in handy.


MDT 2010 – Using The Database And Powershell To Enhance Administration

It is possible to build some automation into your MDT environment firstly by using the Advanced Configuration available and configuring a database (SQL Express for instance to keep costs down), and secondly start manipulating this database with powershell to create bulk-actions, populations, record changes etc etc…

First place to start is with Michael Niehaus. Back in 2009 Michael published this powershell module for use with MDT: Read about the cmdlets and download the module here:

Once you’ve downloaded and saved the psm1, open powerhell and set the execution policy to unrestricted: “set-executionpolicy unrestricted” and accept the warning:

Now import the psm1 module by typing “import-module %pathtoscript%\MDTDB.psm1 -verbose”

First task is to connect to the MDT database, using the Connect-MDTDatabase cmdlet. Use “get-help connect-mdtdatabase” to check the syntax but it’s basically:

“Connect-MDTDatabase -SQLServer ServerName -Instance InstanceName -Database DatabaseName

Then you’re ready to go.

Useful cmdlets: Get-MDTComputer, New-MDTComputer, Set-MDTComputer

Try using a csv, and an import-csv cmdlet to bulk populate your database with computers:

New-MDTComputer –macAddress MACAddress –settings @{OSInstall=’YES’}

The first thing I wanted to do after populating was control which computers could be installed via F12. I set the OSINSTALL attribute in the Default Section of CustomSettings.ini to OSINSTALL=NO and set the priority of Default to last.

Then I used Get-MDTComputer | Set-MDTComputer -Settings @{OSINSTALL=’YES’} to allow all machines to build.

Once they were built, it was easy to use Get-MDTComputer | Set-MDTComputer -Settings @{OSINSTALL=”} to stop them from being able to rebuild should someone decide they wanted to do so without authorisation.

MDT 2010 Update 1 Quick Start

My latest customer does not have requirements at the moment for SCCM, or funds or resources because it is a school with a tight IT budget.

So to roll-out new operating system images i’ve established a server and installed MDT 2010 Update 1.

Here is a quick start guide for MDT with a SQL database.

1. Build servers, or domain infrastructure. I am using a SQL Server 2008 R2 server for the MDT database.

2. Build a server operating system for MDT

3. Install WDS

3.1 Configure WDS – Plenty of resources on the internet about how to do this, i am concentrating here on the final steps of MDT setup, but don’t worry too much about images at this stage as we will replace them later; install the default boot image and configure the PXE response and Boot settings. Configure DHCP according to your environment.

4. Install WAIK It is a huge download

5. Install MDT

6. Configure MDT

6.1 Create Deployment Share

6.2 Add Operating System Image(s) – from source DVD or pre-captured WIM image

6.3 Create New Database in Advanced Configuration

6.3.1 Give the SQL server name, instance and database name

6.3.2 Define the SQL DeploymentShare, this allows the LiteTouch account to authenticate against the SQL Server. When you define this path, make sure the path exists on the SQL Server, this is important.

6.3.3 You can configure the database rules at this stage, it modifies the INI file on the MDT server rather than the database itself.

6.3.4 Configure the bootstrap.ini to include account credentials for connecting to the database.

6.4 Create Simple Task Sequence

That’s it, not a lot of detail i know but i will post next time with pictures and all that kind of stuff. The most important things to remember are 6.3.2 and 6.3.4 – without these settings configured you will get ZTI errors trying to authenticate to SQL in the OSD log.

Now go ahead and add a computer account to the database, and you open up much more flexibillity in MDT, allowing you to deploy to numerous machines with customisable settings.

Get ConfigMgr Machine Variables With Powershell

Machine Variables can be retrieved from computer objects in SCCM with the Powershell script at the bottom of the page.

Variables in SCCM can be very useful: Task Sequence Variables, Collection Variables, Machine Variables. I use Machine Variables to identify specific settings such as which Language to install the OS with – this one is pretty useful when you consider that UK, France and German-speaking countries, for a start, all have slight differences in keyboard layout.

I’m a big fan of complexity. I like massively over-engineered solutions for small tasks. The point is that even a motorised egg-cooker and alarm clock system such as Doc Brown’s in Back to the Future, offsets the overhead of required initial engineering against the savings made over time – it is just a matter of time before an automated task like this wins the efficiency battle. Yes, it takes 10 minutes for you to crack eggs in a pan and cook them and slap them onto a piece of bread, but it is ten minutes every time, of your time. If like me you could happily eat an egg sandwich every morning, that equates to 70 minutes per week, 60 hours in one year. I reckon Doc Brown would take less than 60 hours to conceive and build an egg-sandwich machine, lets say 30 hours, that leaves him 30 hours more free time in the first year and 60 every year thereafter. And the egg sandwich is always the same.

So building a complex solution can save time and increase efficiency. That is all.

What has this to do with Machine Variables? Not a lot except that building scripts which can automate tasks is a bit like building an egg-sandwich machine. When you then build the scripts to work against variables, or to generate variables and then consequently re-reference themselves against the generated variables, then you start to look at a pretty complex solution for clicking the screen that asks you which language to install an OS in. But this screen doesn`t appear straight away in OSD, so you would either wait for it, or go away and come back. Eventually a scripted, complex solution wins the efficiency argument, trust me it`s just a matter of time.

So, I have a script which writes machine variables to a computer object in SCCM. I want the script to reference these variables later, how do I get the variables and values?

In the Windows.SMS.Environment you can retrieve the SCCM variables on the local machine.

You could use remoting to get the variables in the same way.

But I want to reset a LastPXEAdvertisement according to flag set in a variable and the machine isn’t switched on. So I could open the SCCM console and find the machine, right-click and check the flag, then reset the PXEAdvert, for example. Then write another variable to say it has been reset.

I think I have gone on long enough about why automate, and why build complexity…. Here is a script to pull back the Machine Variables for a computer object from SCCM using Powershell:

objComputer = gwmi -computername “$smsProvider” -namespace “root\sms\site_$siteCode” -class “sms_r_system” | where{$_.Name -eq $computerName}

$objMachineSettings = gwmi -computername “$smsProvider” -namespace “root\sms\site_$siteCode” -class “sms_machinesettings” | where{$_.ResourceID -eq $objComputer.ResourceID}


$variable = ($objMachineSettings.MachineVariables | where{$_.Name -eq “$desiredVariable”}).Value

And that, aside from script variables which is something else entirely, is a script which will return the value of a specific variable. Use the SMS_MachineSettings Class and then populate the fields with the GET() command and there you are. Simple. But then complex machines are built of many, many simple parts. The trick is how to put them together.

Hope it helps someone, I spent a bit of time wondering why the MachineVariables field was empty until I realized I needed the second Get expression.

Time now for a nice, uncomplicated weekend….

SCCM Add Computer Associations With Powershell

I can’t wait to get ConfigMgr 2012 up and running. Beta 2 is going into my lab as i type…

Until then…. Yes, ConfigMgr 2007 is fantastic, i love it. But i deploy it at customer sites and as a rule the technicals who are to run and operate it once i have gone have not taken any form of SCCM training. With 2007’s MMC Console it can be a bit daunting for them, so much information, and the access security is not what it should be. So until the launch of 2012 with it’s lovely interfaces and user-friendly-centric operations how can i enable my customers to carry out day-to-day tasks in SCCM?

First of all identify the key stakeholders: Who wants SCCM? Who needs SCCM? Why do they want it? What role in the business do they expect it perform?

FD: Paying for this new technology, wants return on investment.

IT Manager or similar: Probably wants deployment processes streamlined for their staff, to enable them to work more efficiently. Wants to know what IT equipment and licensing they have. Doesn’t care about the technical side.

IT Technician: More interested in the technical aspects. May want to get under the hood, but without training not advisable. Has (hopefully) documented the deployment and operational processes they want to streamline. Knows the applications, the estate, the environment.

For FDs and IT Management, the built-in Reports may well be enough to get information on the estate and inventory, with R2’s Reporting Services Point enhancing this functionality.

For Technicians, those who will be running and maintaining the SCCM environment and carrying out the deployment and administrative tasks with SCCM the MMC console can be a maze of nodes and components.

I identified a need for a customer to carry out basic tasks, such as Import Computer Information. They wanted to quickly add in new hardware to receive OSD Task Sequence Advertisements via PXE. I created a simple tool in Powershell which they could run from their desktop and easily add a new laptop or computer for OS deployment.

First of all, and i won’t go into too much detail here, i set up an OSD Task Sequence, a Collection and advertised the TS. 

In normal circumstances, the technician would open the SCCM console, find the collection with the TS advert and drill-down through the OSD Node, to import the computer using the wizard.

All these steps are possible with WMI.

 The basic powershell to add a computer account to SCCM:

#create Computer Account in SCCM





$inParamsAddEntry.MACAddress =$mACAddress.Text

$inParamsAddEntry.OverwriteExistingRecord =$false

$inParamsAddEntry.NetbiosName =$serverName.Text

$objComputer=$siteClass.psbase.InvokeMethod($methodAddEntry, $inParamsAddEntry, $null)

Next add the direct collection membership rule, this always applies to the collection and not the computer:



$objCollectionRuleDirect.psbase.Properties[“ResourceClassName”].Value = “SMS_R_System”

$objCollectionRuleDirect.psbase.Properties[“ResourceID”].value = $objComputer.ResourceID


And add the computer to the collection using this rule:

$targetCollection=Get-WmiObject -ComputerName “$siteProviderName” -Namespace “root\sms\site_$siteCode” -Class “SMS_Collection” | where{$_.Name -eq $determinedCollection}




$addToCollectionResult=$targetCollection.psbase.InvokeMethod($methodAddToCollection, $inParamsAddToCollection, $null)

I also used Computer Variables to designate if the computer is to be installed with german, french or italian regional settings and keyboard layouts, this is useful when working in Switzerland. First you create an empty array of machine variables:

#create machine task sequence variables



$computerInstance = $machineClass.CreateInstance()









Then you can add variables, i use a Windows Form object to take all this information in from input:



I know i have a left a lot out here, and i will skip quickly to the end, but the important thing i have found is that once you harness WMI and powershell, with windows form objects, you can create all manner of dazzling(?) tools to help make SCCM life a bit easier.

Here is a couple of screengrabs of my tool:

For client management, Roger Zander’s Client Center is still freely available from sourceforge:

And for another method to set regional settings in a task sequence, i found this post very interesting:

Editing SMS_DEF.MOF in SCCM 2007

Editing SMS_DEF.MOF and Configuration.MOF to Report Registry Values

Ok, so vNext is on the horizon and changes how hardware information is inventoried… but for those who still want to customize the SMS_DEF.MOF here is a quick guide to setting up a simple query/report in Configuration Manager using the MOF files.

A couple of things to mention first:

1. Hardware inventorying in SCCM 2012 has changed, no more need for SMS_DEF.MOF. Instead, a wizard will alllow you to directly access WMI and choose the elements you want to inventory. It will be possible to import data from customized MOF files.

2. Upgrading ConfigMgr 2007 with service packs or releases will overwrite any customised MOF files:

If you have customized the default SMS_def.mof hardware inventory reporting file, you must create a backup of this file before upgrading the site. When upgrading a site, customizations made to the existing SMS_def.mof file will be overwritten.

With this in mind off we go…

For example try adding a registry key to a machine during OSD process; i have used a powershell script to write this in as part of the Task Sequence. The script adds a date/time stamp in the registry for reporting purposes. It writes the following key value:

Looking in Resource Explorer from ConfigMgr console we don’t see this information.

To collect the registry key information we have to modify SMS_DEF.MOF and Configuration.MOF. These two files are located in:

%Program Files%\Microsoft Configuration Manager\inboxes\clifiles.src\hinv

Edit both files as follows:

At the end of SMS_DEF.MOF:


//* Class: Custom Registry Key

//* Derived from: (nothing)


//* Created by; Andrew Craig


//* This Registry class queries the registry on clients for the Build Date



[ SMS_Report   (TRUE),

  SMS_Group_Name(“Build Date”),


Class BuildDate : SMS_Class_Template


    [SMS_Report(TRUE),key] string KeyName;

    [SMS_Report(TRUE)]     string BuildDateTime;


And at the end of Configuration.MOF:


Class BuildDate


    [key] string KeyName;

          string BuildDateTime;



instance of BuildDate


    KeyName = “BuildDate”;

    [PropertyContext(“Local|HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\BuildDate|BuildDateTime”), Dynamic, Provider(“RegPropProv”)] BuildDateTime;


From the client, to test, initiate the Machine Policy Retrieval & Evaluation Cycle

Monitor the PolicyAgent.log for successful policy update:


Then initiate the Hardware Inventory Cycle and monitor the InventoryAgent.log for success:


These policies will run according to schedule anyway but for testing purposes we can manually initiate them.

Back to Resource Explorer for the machine and we now see the BuildDate information has been harvested:

Excellent, but we don’t want our reports users having to go into the ConfigMgr console for this information so we will create a Custom Report, linked to Computer Details, and with the following SQL statement:

SELECT SYS.Netbios_Name0, SYS.Resource_Domain_OR_Workgr0,

  OPSYS.Caption0 as C054, OPSYS.Version0,

 MEM.TotalPhysicalMemory0,  IPAddr.IP_Addresses0, Processor.Manufacturer0,

 CSYS.Model0, Processor.Name0, BUILD.BuildDateTime0 

FROM v_R_System SYS

LEFT JOIN  v_RA_System_IPAddresses IPAddr on SYS.ResourceID = IPAddr.ResourceID

LEFT JOIN  v_GS_X86_PC_MEMORY MEM on SYS.ResourceID = MEM.ResourceID


LEFT JOIN  v_GS_PROCESSOR Processor  on Processor.ResourceID = SYS.ResourceID


LEFT JOIN v_GS_Build_Date0 BUILD on SYS.ResourceID=BUILD.ResourceID

WHERE SYS.Netbios_Name0 = @variable

ORDER BY SYS.Netbios_Name0, SYS.Resource_Domain_OR_Workgr0

Our reports users can then browse to the reports URL and run the custom report:

And they can then drill down through the report to see other computer details:

This process can be used to retrieve any registry information from clients for reporting purposes.

You can also, modify these files to return about any inventory information from clients.

Further reading:

Advertised Programs Do Not Download

I was deploying the HP WebCam software to Notebooks and it just would not work.

I even went back to the start and cleared the advertisement, re-created the package and program, cleared the cache on the target client and then readvertised.

It took a bit of log digging but i found it in the end.

1st Log: PolicyAgent on Client – Client receives policy update from triggered schedule (see previous thread)

2nd Log: Execmgr on Client – Client receives advertised program and sends to download

3rd Log: ContentTransferMananger on Client – Client starts downloading to cache. (Enters phase DOWNLOADING_DATA)

4th Log: CAS on Client – Client starts to download but NotifyDownloadProgressEx shows downloaded size 0

5th Log: DataTransferService on Client – the final entry is cdtsjob::joberror follwed by job GUID

At this point nothing else happens. I checked BITS throttling, all ok on server and client.

Thanks for reading this far, here is the good bit…

BITS goes through IIS on the distribution point. So check the logs on the DP. (C:\inetpub\logs\LogFiles\W3SVC1). Check the newest log and the latest entries. Here is mine:

2011-02-18 16:22:42 HEAD /SMS_DP_SMSPKGD$/00100028/WEBCAM_X64_10/BIN/AS_Storage_w32.dll – 80 – Microsoft+BITS/7.5 404 8 0 199
The key here is the code at the end. I have a 404 error. Another common scenario is a 401 error. The number directly after is the subcode, i.e. 404.8 in my case.

Here is the key to the http codes:

401.7 means the applicationHost.config needs to be correctly edited for webDav, 404.8 means part of the path is designated a ‘hidden segment’ in applicationHost.config – you should remember making similar changes when you installed SCCM.

By default, one of the segments hidden in the applicationHost.config is ‘bin’ and HP’s download of software contains bin folders. 

Removing the bin entry from the hiddenSegments section of applicationHost.config resolves the issue.

Hope it helps.

Installing SCCM R3 Clients

In order to fully utilise the Power-Management functionality of SCCM R3 you will need to upgrade the clients using the microsoft KB977384

This latest hotfix includes support for ConfigMgr with International Client Pack.

However when you push Clients from SCCM R3, using the included Client install files you will still find that you have Client version 4.00.6487.2000 (or 4.00.6487.2700 if you have ICP).

The new Client version is 4.00.6487.2157 (or 4.00.6487.2857 with ICP).

So after installing new clients in your R3 environment you will still need to push the hotfix. I tried putting the patch in the ‘patch’ folder of the ConfigMgr Client installation source, and tried using the OSD Task Sequence.

The worst thing that happened when using as part of a Task Sequence is if you are deploying to an off-domain machine, and using ConfigMgr Client installation parameters in the original package, then the parameters get lost when the hotfix reinstalls the client, leaving you with errors in the LocationServices.log about failing to find the SLP.

So the choices are testing with the patch folder or the “patch=” parameter or rolling out the client after the machines have been deployed and have joined the domain, providing you are AD-integrated. I prefer the latter, although it seems a little long-winded I know I won’t have a problem in OSD, and I don’t really need R3 functionality during the OSD phase.

In any case, I need to roll out software updates after the OSD phase anyway because the Install Software Updates section for Windows 7 isn’t working properly. Waiting on Microsoft for that one…

Set PoSh ExecutionPolicy For SCCM TS

I have some powershell scripts to run as part of an OSD Task Sequence, to configure things like Power Plans and Optical Drive lettering for instance.
The first thing I want to do is set the execution policy on the target machine to RemoteSigned.

How do you remotely change the execution policy on a machine when you cannot run a script against it?

You can use the input parameters of the powershell.exe (EXE):

use the command switch to input a script block.

powershell.exe -noprofile -command “& {set-executionpolicy remotesigned -force}”

Use ‘noprofile’ to run without loading a powershell profile and the ‘-force’ switch to suppress the confirmation request.

For SCCM, create a Software Distribution package containing the scripts, then create a program with the command line as above.