Pete Hinchley: Install and Configure Microsoft Deployment Toolkit 2013 using PowerShell

In this article I will show how you can install and configure Microsoft Deployment Toolkit 2013 using PowerShell.

You will need the following:

To install MDT 2013 Update 1, open an elevated PowerShell prompt and run:

& msiexec /i MicrosoftDeploymentToolkit2013_x64.msi /qb

Next, run the following code to create a new deployment share, import the operating system source files, and create a client task sequence for deploying the operating system. Note: The code assumes the Windows 10 media is mounted as D:\.

$Folder = "C:\DeploymentShare"
$Share  = "DeploymentShare$"

Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1"

New-Item -Path $Folder -Type Directory
New-SmbShare –Name $Share –Path $Folder –FullAccess EVERYONE

New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root $Folder -NetworkPath "\\$ENV:COMPUTERNAME\$Share" -Description "Deployment Share" | Add-MDTPersistentDrive

$os = Import-MDTOperatingSystem -Path "DS001:\Operating Systems" -SourcePath "D:\" -DestinationFolder "Windows 10"

Import-MDTTaskSequence -Path "DS001:\Task Sequences" -Name "Windows 10" -Template "Client.xml" -Comments "Build and Capture Windows 10" -ID "WIN10" -Version "1.0" -OperatingSystemPath "DS001:\Operating Systems\$($os.Name)" -FullName "LAB" -OrgName "LAB" -AdminPassword (Read-Host "Password")

Update-MDTDeploymentShare -Path "DS001:"

To enable the option to capture the operating system we need to remove SkipCapture=Y from CustomSettings.ini:

$File = "C:\DeploymentShare\Control\CustomSettings.ini"
(Get-Content $file | Select-String -Pattern 'SkipCapture' -NotMatch) | Set-Content $file

You can now boot a virtual machine off the C:\DeploymentShare\Boot\LiteTouchPE_x64.iso media to initiate the build and capture of Windows 10.

Assuming you have installed MDT on your primary SCCM site server, as a bonus, let's integrate MDT with the Configuration Manager Console. You can do this by running the following command, and progressing through the wizard:

"C:\Program Files\Microsoft Deployment Toolkit\Bin\Mi
crosoft.BDD.WizLauncher.exe" ConfigureSCCM

Alternatively, you can open an elevated PowerShell prompt and run the following code (adjusting the variables as appropriate for your environment):

$SiteCode   = "LAB"
$SiteServer = "BORIS.LAB.HINCHLEY.NET"

$MDT  = "C:\Program Files\Microsoft Deployment Toolkit"
$SCCM = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole"
$MOF  = "$SCCM\Bin\Microsoft.BDD.CM12Actions.mof"

Copy-Item "$MDT\Bin\Microsoft.BDD.CM12Actions.dll" "$SCCM\Bin\Microsoft.BDD.CM12Actions.dll"
Copy-Item "$MDT\Bin\Microsoft.BDD.Workbench.dll" "$SCCM\Bin\Microsoft.BDD.Workbench.dll"
Copy-Item "$MDT\Bin\Microsoft.BDD.ConfigManager.dll" "$SCCM\Bin\Microsoft.BDD.ConfigManager.dll"
Copy-Item "$MDT\Bin\Microsoft.BDD.CM12Wizards.dll" "$SCCM\Bin\Microsoft.BDD.CM12Wizards.dll"
Copy-Item "$MDT\Bin\Microsoft.BDD.PSSnapIn.dll" "$SCCM\Bin\Microsoft.BDD.PSSnapIn.dll"
Copy-Item "$MDT\Bin\Microsoft.BDD.Core.dll" "$SCCM\Bin\Microsoft.BDD.Core.dll"
Copy-Item "$MDT\SCCM\Microsoft.BDD.CM12Actions.mof" $MOF
Copy-Item "$MDT\Templates\CM12Extensions\*" "$SCCM\XmlStorage\Extensions\" -Force -Recurse
(Get-Content $MOF).Replace('%SMSSERVER%', $SiteServer).Replace('%SMSSITECODE%', $SiteCode) | Set-Content $MOF
& "C:\Windows\System32\wbem\mofcomp.exe" "$SCCM\Bin\Microsoft.BDD.CM12Actions.mof"

What about modifying the MDT task sequence to include an application? Let's assume you have a PowerShell script named C:\Media\Branding\Configure.ps1 that writes the current date into the registry:

New-ItemProperty -Path HKLM:Software -Name CaptureTime -Value $(Get-Date -UFormat "%Y/%m/%d")

We can use the following command to wrap the script in an MDT application:

$app = Import-MDTApplication `
  -Enable $true `
  -Path "DS001:\Applications" `
  -Name "Branding" `
  -ShortName "Branding" `
  -DisplayName "Branding" `
  -CommandLine "powershell.exe -ExecutionPolicy Bypass -File Configure.ps1" `
  -WorkingDirectory ".\Applications\Branding" `
  -ApplicationSourcePath "C:\Media\Branding" `
  -DestinationFolder "Branding"

And now the tricky bit, to add the new application into the task sequence under the existing Custom Tasks folder:

$step = [xml]@"
<step type="BDD_InstallApplication" name="Branding" description="" disable="false" continueOnError="false" runIn="WinPEandFullOS" successCodeList="0 3010">
<defaultVarList>
  <variable name="ApplicationGUID" property="ApplicationGUID">$($x.GUID)</variable>
  <variable name="ApplicationSuccessCodes" property="ApplicationSuccessCodes">0 3010</variable>
</defaultVarList>
<action>cscript.exe "%SCRIPTROOT%\ZTIApplications.wsf"</action>
</step>
"@

$ts    = "C:\DeploymentShare\Control\WIN10\ts.xml"
$xml   = [xml](Get-Content $ts)
$node  = $xml.ImportNode($step.get_DocumentElement(), $true)
$tasks = $xml.Sequence.Group.Group | ? Name -eq "Custom Tasks"
$tasks.AppendChild($node)
$xml.Save($ts)

Note: The MDT cmdlets typical require access to the MDT "drive" (in my case: DS001). The provider can be restored into a new PowerShell session as follows:

Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1"
Restore-MDTPersistentDrive

The application that we just added to the task sequence will run after Windows is installed, but if we wanted to run a PowerShell-based script prior to the installation of the operating system, we would need to add support for PowerShell to WinPE. We can do this via the following commands:

$settings = "C:\DeploymentShare\Control\Settings.xml"
$xml = [xml](Get-Content $settings)
$xml.Settings.Item("Boot.x86.FeaturePacks")."#text" = "winpe-mdac,winpe-netfx,winpe-powershell"
$xml.Settings.Item("Boot.x64.FeaturePacks")."#text" = "winpe-mdac,winpe-netfx,winpe-powershell"
$xml.Save($settings)

Update-MDTDeploymentShare -Path "DS001:" -Force

The approach outlined above mimics the process of selecting the required WinPE features through the MDT Deployment Workbench, and relies on an update of the Deployment Share for the features to be injected into the WIM. You can manually achieve the same outcome by using Dism to explicitly inject the features as shown:

# This example only updates the x64 boot WIM.
mkdir C:\Mount

$adk = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit"

dism /Mount-Wim /WimFile:C:\DeploymentShare\Boot\LiteTouchPE_x64.wim /MountDir:C:\Mount /Index:1

& "$adk\Deployment Tools\amd64\DISM\dism.exe" /Add-Package /Image:C:\Mount /PackagePath:"$adk\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-NetFx.cab"
& "$adk\Deployment Tools\amd64\DISM\dism.exe" /Add-Package /Image:C:\Mount /PackagePath:"$adk\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-NetFx_en-us.cab"

& "$adk\Deployment Tools\amd64\DISM\dism.exe" /Add-Package /Image:C:\Mount /PackagePath:"$adk\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-PowerShell.cab"
& "$adk\Deployment Tools\amd64\DISM\dism.exe" /Add-Package /Image:C:\Mount /PackagePath:"$adk\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-PowerShell_en-us.cab"

dism /UnMount-Wim /MountDir:C:\Mount /Commit
rmdir C:\Mount

The above process does not inform MDT the x64 boot WIM has been updated, and hence, if the Deployment Share is refreshed, the changes made to the WIM will be lost. Therefore I recommend updating settings.xml directly, as previously shown.

Note: The name of the features "known" to WinPE (i.e. listed within the Properties pane of the Deployment Workbench) are defined within C:\Program Files\Microsoft Deployment Toolkit\Bin\FeatureNames.xml.