How to capture a Windows 10 image like a boss with MDT!

In this guide we are going to create a brand new Windows 10 1709 reference image with MDT 8450.

Software Requirements

  • A server or a desktop/laptop computer (In this guide we will be using a Desktop)
  • Windows 10 Enterprise v1709 ISO
  • Windows ADK for Windows 10 v1709 (Download Here)
  • MDT 8450 (Download Here)
  • Hyper-V

Step 1 – Install Windows ADK for Windows 10 with the following features enabled:

  • Deployment Tools
  • Windows Preinstallation Environment (Windows PE)
  • Imaging and Configuration Designer (ICD)
  • Configuration Designer
  • User State Migration Tool (USMT)

Step 2 – Install MDT 8450.  (You can use all of the default settings)

Step 3 – Create a share that will be used to store your captured WIM.

$CaptureShare = "C:\CaptureShare"

New-Item -Path $CaptureShare -ItemType Directory
New-SMBShare –Name “CaptureShare” –Path $CaptureShare -FullAccess Everyone

Step 4 – Copy your Windows 10 Enterprise v1709 ISO to C:\ISO and rename the ISO to 1709.ISO

Step 5 – Setup MDT by running the following Powershell Script (Must run elevated):

If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(` 
    [Security.Principal.WindowsBuiltInRole] "Administrator")) { 
    Write-Warning "Oops, you need to run this script from an elevated PowerShell prompt!`nPlease start the PowerShell prompt as an Administrator and re-run the script." 
    Write-Warning "Aborting script..." 
    Break 
} 

$ISO = "C:\ISO\1709.ISO"

Add-PSSnapIn Microsoft.BDD.PSSnapIn -ErrorAction SilentlyContinue 

# Create Deployment Share
$DeploymentShare = "C:\Capture_DeploymentShare"

New-Item -Path $DeploymentShare -ItemType Directory 
New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "$DeploymentShare" -Description "Capture Deployment Share" -NetworkPath "\\$env:COMPUTERNAME\Capture_DeploymentShare$" | Add-MDTPersistentDrive 
New-SmbShare –Name MDTProduction$ –Path $DeploymentShare –ChangeAccess Everyone
New-Item -Path $DeploymentShare\Scripts\Custom -ItemType Directory

$DSName=(Get-MDTPersistentDrive)[0].Name 
$PhysicalPath=(Get-MDTPersistentDrive)[0].Path 
$Description=(Get-MDTPersistentDrive)[0].Description

# Mount ISO
Mount-DiskImage "$ISO"
$MountedDrive = (Get-DiskImage -ImagePath "$ISO"  | Get-Volume).DriveLetter + ":\"
$DSPath = $DSName + ":"

# Add the operating system
$OS = Import-MDTOperatingSystem -Path "$DSPath\Operating Systems" -SourcePath "$MountedDrive" -DestinationFolder "Windows 10 1709" -Verbose

# Dismount ISO
Dismount-DiskImage "$ISO"

Import-MDTTaskSequence -Path "$DSPath\Task Sequences" -Name "Windows 10 1709 Capture" -Template "Client.xml" -Comments "Build and Capture Windows 10" -ID "WIN10" -Version "1.0" -OperatingSystemPath "$DSPath\Operating Systems\$($OS.name[2])" -FullName "Windows 10 1709 Capture"

# Setup Media
New-Item -Path C:\MEDIA001 -ItemType Directory 
New-PSDrive -Name "DS002" -PSProvider MDTProvider -Root "$DeploymentShare" 
New-Item -Path "DS002:\Media" -Enable "True" -Name "MEDIA001" -Comments "" -Root "C:\MEDIA001" -SelectionProfile "Everything" -SupportX86 "False" -SupportX64 "True" -GenerateISO "False" -ISOName "LiteTouchMedia.iso" -Force -Verbose 
New-PSDrive -Name "MEDIA001" -PSProvider "MDTProvider" -Root "C:\MEDIA001\Content\Deploy" -Description "MDT Production Media" -Force -Verbose 

Step 6 – Add Applications (Optional)
In order to keep this guide as short possible, I decided to skip this part and provide the following link that explains how to add Microsoft Office to your MDT Deployment Share:
https://blogs.technet.microsoft.com/odsupport/2016/02/18/deploying-office-2016-2013-or-2010-using-the-microsoft-deployment-toolkit/

Step 7 – Customize Windows 10 1709
First we will need to navigate to your Task Sequence and goto Custom Tasks.

Task Sequence

Next we are going to add the following customizations:

Install .Net Framework 3.5

    • Goto “Add” and navigate to General > Run Command Line
    • Name: Install NetFX3
    • Command Line:
DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:"D:\Deploy\Operating Systems\Windows 10 1709\sources\sxs"

Configure the Default profile
In order to configure the default profile, you will need to create a batch file and use the script that I posted in my “How to automatically configure your default profile in Windows 10” post. You can find the post here.

    • Name the script configure_default_profile.bat
    • Copy the script to C:\Capture_DeploymentShare\Scripts\Custom
    • Open your task sequence
    • Goto “Add” and navigate to General > Run Command Line
    • Name: Configure Default Profile
    • Command Line:
cmd /c %SCRIPTROOT%\Custom\configure_default_profile.bat

Add custom wallpaper
In order to add a custom wallpaper, we will be using Jörgen Nilsson’s method which can be found here. Essentially, you will be replacing the wallpapers in “C:\windows\web\4k\Wallpaper\Windows” and “C:\windows\web\Wallpaper\Windows” with your own custom wallpaper. To make this work you will need 9 copies of your wallpaper with the following names:
img0.jpg, img0_1024x768.jpg, img0_1200x1920.jpg, img0_1366x768.jpg, img0_1600x2560.jpg, img0_2160x3840.jpg, img0_2560x1600.jpg, img0_3840x2160.jpg, img0_768x1024.jpg, img0_768x1366.jpg, and LockScreen.jpg (Wallpaper that will be used for the lockscreen)

Function Set-FilePermissions {
    param (
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$File,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$User,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$Control,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$Access
    )
 
    $ACL = Get-ACL "$File"
    Set-Acl -Path "$File" -AclObject $ACL
    $Permission = New-Object  system.security.accesscontrol.filesystemaccessrule("$User","$Control","$Access")
    $Acl.SetAccessRule($Permission)
    Set-Acl -Path "$File" -AclObject $ACL
 
}
 
Function Set-FileOwnership {
    param (
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$File,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$User
    )
 
    $ACL = Get-ACL "$File"
    $Group = New-Object System.Security.Principal.NTAccount("$User")
    $ACL.SetOwner($Group)
    Set-Acl -Path "$File" -AclObject $ACL
 
}

# Configure MDT Variables
$TSenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$ScriptRoot = $TSenv.Value("ScriptRoot")

# Update Standard Wallpaper
Set-FileOwnership -File "C:\windows\web\Wallpaper\Windows\img0.jpg" -User Administrators
Set-FilePermissions -File "C:\windows\web\Wallpaper\Windows\img0.jpg" -User Administrators -Control FullControl -Access Allow
Copy-Item "$ScriptRoot\Deploy\Scripts\Custom\Wallpaper\img0.jpg" -Destination "C:\windows\web\Wallpaper\Windows\img0.jpg"
Copy-Item "$ScriptRoot\Deploy\Scripts\Custom\Wallpaper\LockScreen.jpg" -Destination "C:\LockScreen.jpg"
# Update 4k Wallpapers
$Wallpapers = Get-ChildItem "C:\windows\web\4k\Wallpaper\Windows"
ForEach($Wallpaper in $Wallpapers) {
    Set-FileOwnership -File $Wallpaper.FullName -User Administrators
    Set-FilePermissions -File $Wallpaper.FullName -User Administrators -Control FullControl -Access Allow
 
    $FileName = $Wallpaper.Name
    $FilePath = $Wallpaper.FullName
 
    Copy-Item "$ScriptRoot\Deploy\Scripts\Custom\Wallpaper\$FileName" -Destination $Wallpaper.FullName -Force
}
  • Name the script update_wallpaper.ps1
  • Copy the script to C:\Capture_DeploymentShare\Scripts\Custom
  • Create the following folder: C:\Capture_DeploymentShare\Scripts\Custom\Wallpaper
  • Copy your wallpapers to C:\Capture_DeploymentShare\Scripts\Custom\Wallpaper
  • Open your task sequence
  • Goto “Add” and navigate to General > Run PowerShell Script
  • Name: Update Wallpaper
  • Powershell Script: %SCRIPTROOT%\Custom\Update_Wallpaper.ps1

Add a custom Start Menu and Taskbar
Please visit my earlier post “Customize a Windows 10 Start Layout” to learn how to export a start layout that can be used to customize your Start Menu. You can click here to visit the post.
For taskbar customizations, I would recommend visiting Microsoft’s official document that explains how to customize your taskbar, which you can find here.

    • Export your Start layout
    • Copy your exported start layout(LayoutModification.xml) to C:\Capture_DeploymentShare\Scripts\Custom
    • Open your task sequence
    • Goto “Add” and navigate to General > Run Command Line
    • Name: Update Start Layout
    • Command Line:
powershell.exe -executionpolicy bypass -command Import-StartLayout -LayoutPath "%SCRIPTROOT%\Custom\LayoutModification.xml" -MountPath $env:SystemDrive\

Remove Window Store Apps
The following script will leave Alarms & Clock, Calculator, Camera, Connect, Maps, Microsoft Store, Mixed Reality Portal(Cannot be removed), Paint 3D, Photos, Sticky Notes, Voice Recorder and Weather:

$AppsList = "Microsoft.3DBuilder","microsoft.windowscommunicationsapps","Microsoft.MicrosoftOfficeHub","Microsoft.SkypeApp","Microsoft.Getstarted","Microsoft.ZuneMusic","Microsoft.MicrosoftSolitaireCollection","Microsoft.ZuneVideo","Microsoft.Office.OneNote","Microsoft.People","Microsoft.XboxApp", "Microsoft.Messaging", "Microsoft.Microsoft3DViewer", "Microsoft.WindowsFeedbackHub", "Microsoft.GetHelp", "Microsoft.OneConnect"

ForEach ($App in $AppsList)
{
  $PackageFullName = (Get-AppxPackage $App).PackageFullName
  $ProPackageFullName = (Get-AppxProvisionedPackage -online | where {$_.Displayname -eq $App}).PackageName
  write-host $PackageFullName
  Write-Host $ProPackageFullName

  if ($PackageFullName)
  {
    Write-Host “Removing Package: $App”
    remove-AppxPackage -package $PackageFullName
  }
  else
  {
    Write-Host “Unable to find package: $App”
  }

  if ($ProPackageFullName)
  {
    Write-Host “Removing Provisioned Package: $ProPackageFullName”
    Remove-AppxProvisionedPackage -online -packagename $ProPackageFullName
  }
  else
  {
    Write-Host “Unable to find provisioned package: $App”
  }
}

  • Copy the script above and name the script remove_apps.ps1
  • Copy the script to C:\Capture_DeploymentShare\Scripts\Custom
  • Open your task sequence
  • Goto “Add” and navigate to General > Run PowerShell Script
  • Name: Remove Apps
  • Powershell Script: %SCRIPTROOT%\Custom\Remove_Apps.ps1

Configure computer settings
You can use the following script below to get you started with configuring your computer settings. Feel free to add or remove as needed:

# Disable Telemetry
Write-Host "Disabling Telemetry..."
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection" -Name "AllowTelemetry" -Type DWord -Value 0

# Disable Wi-Fi Sense
Write-Host "Disabling Wi-Fi Sense..."
If (!(Test-Path "HKLM:\SOFTWARE\Microsoft\PolicyManager\default\WiFi\AllowWiFiHotSpotReporting")) {
    New-Item -Path "HKLM:\SOFTWARE\Microsoft\PolicyManager\default\WiFi\AllowWiFiHotSpotReporting" -Force | Out-Null
}
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\PolicyManager\default\WiFi\AllowWiFiHotSpotReporting" -Name "Value" -Type DWord -Value 0
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\PolicyManager\default\WiFi\AllowAutoConnectToWiFiSenseHotspots" -Name "Value" -Type DWord -Value 0

# Disable Location Tracking
Write-Host "Disabling Location Tracking..."
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Sensor\Overrides\{BFA794E4-F964-4FDB-90F6-51056BFE4B44}" -Name "SensorPermissionState" -Type DWord -Value 0
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\lfsvc\Service\Configuration" -Name "Status" -Type DWord -Value 0

# Disable Cortana
Write-Host "Disabling Cortana..."
If (!(Test-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search")) {
    New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search" -Force | Out-Null
}
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search" -Name "AllowCortana" -Type DWord -Value 0

# Restrict Windows Update P2P only to local network
Write-Host "Restricting Windows Update P2P only to local network..."
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Name "DODownloadMode" -Type DWord -Value 1
If (!(Test-Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\DeliveryOptimization")) {
    New-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\DeliveryOptimization" | Out-Null
}
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\DeliveryOptimization" -Name "SystemSettingsDownloadMode" -Type DWord -Value 3

# Remove AutoLogger file and restrict directory
Write-Host "Removing AutoLogger file and restricting directory..."
$autoLoggerDir = "$env:PROGRAMDATA\Microsoft\Diagnosis\ETLLogs\AutoLogger"
If (Test-Path "$autoLoggerDir\AutoLogger-Diagtrack-Listener.etl") {
    Remove-Item "$autoLoggerDir\AutoLogger-Diagtrack-Listener.etl"
}
icacls $autoLoggerDir /deny SYSTEM:`(OI`)`(CI`)F | Out-Null

# Stop and disable Diagnostics Tracking Service
Write-Host "Stopping and disabling Diagnostics Tracking Service..."
Stop-Service "DiagTrack"
Set-Service "DiagTrack" -StartupType Disabled

# Disable Windows Defender
Write-Host "Disabling Windows Defender..."
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows Defender" -Name "DisableAntiSpyware" -Type DWord -Value 1

# Disable Windows Update automatic restart
Write-Host "Disabling Windows Update automatic restart..."
Set-ItemProperty -Path "HKLM:\Software\Microsoft\WindowsUpdate\UX\Settings" -Name "UxOption" -Type DWord -Value 1

# Stop and disable Home Groups services
Write-Host "Stopping and disabling Home Groups services..."
Stop-Service "HomeGroupListener"
Set-Service "HomeGroupListener" -StartupType Disabled
Stop-Service "HomeGroupProvider"
Set-Service "HomeGroupProvider" -StartupType Disabled

# Disable Remote Assistance
Write-Host "Disabling Remote Assistance..."
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Remote Assistance" -Name "fAllowToGetHelp" -Type DWord -Value 0

# Disable Lock screen
Write-Host "Disabling Lock screen..."
If (!(Test-Path "HKLM:\Software\Policies\Microsoft\Windows\Personalization")) {
  New-Item -Path "HKLM:\Software\Policies\Microsoft\Windows\Personalization" | Out-Null
}
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\Personalization" -Name "NoLockScreen" -Type DWord -Value 1

#Setup LockScreen
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization" -Name LockScreenImage -Type "String" -Value "C:\LockScreen.jpg"

# Set Photo Viewer as default for bmp, gif, jpg and png
Write-Host "Setting Photo Viewer as default for bmp, gif, jpg, png and tif..."
If (!(Test-Path "HKCR:")) {
    New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null
}
ForEach ($type in @("Paint.Picture", "giffile", "jpegfile", "pngfile")) {
    New-Item -Path $("HKCR:\$type\shell\open") -Force | Out-Null
    New-Item -Path $("HKCR:\$type\shell\open\command") | Out-Null
    Set-ItemProperty -Path $("HKCR:\$type\shell\open") -Name "MuiVerb" -Type ExpandString -Value "@%ProgramFiles%\Windows Photo Viewer\photoviewer.dll,-3043"
    Set-ItemProperty -Path $("HKCR:\$type\shell\open\command") -Name "(Default)" -Type ExpandString -Value "%SystemRoot%\System32\rundll32.exe `"%ProgramFiles%\Windows Photo Viewer\PhotoViewer.dll`", ImageView_Fullscreen %1"
}

# Show Photo Viewer in "Open with..."
Write-Host "Showing Photo Viewer in `"Open with...`""
If (!(Test-Path "HKCR:")) {
    New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null
}
New-Item -Path "HKCR:\Applications\photoviewer.dll\shell\open\command" -Force | Out-Null
New-Item -Path "HKCR:\Applications\photoviewer.dll\shell\open\DropTarget" -Force | Out-Null
Set-ItemProperty -Path "HKCR:\Applications\photoviewer.dll\shell\open" -Name "MuiVerb" -Type String -Value "@photoviewer.dll,-3043"
Set-ItemProperty -Path "HKCR:\Applications\photoviewer.dll\shell\open\command" -Name "(Default)" -Type ExpandString -Value "%SystemRoot%\System32\rundll32.exe `"%ProgramFiles%\Windows Photo Viewer\PhotoViewer.dll`", ImageView_Fullscreen %1"
Set-ItemProperty -Path "HKCR:\Applications\photoviewer.dll\shell\open\DropTarget" -Name "Clsid" -Type String -Value "{FFE2A43C-56B9-4bf5-9A79-CC6D4285608A}"
 
# Disable Windows 10 Initial Login Splash
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name EnableFirstLogonAnimation -Type "dword" -Value "0"

#Disable Consumer Features
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows" -Name CloudContent
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" -Name DisableWindowsConsumerFeatures -Type "dword" -Value "1"

#Disable Driver Updates
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DriverSearching' -Name 'SearchOrderConfig' -Value '3'
  • Copy the script above and name the script machine_config.ps1
  • Copy the script to C:\Capture_DeploymentShare\Scripts\Custom
  • Open your task sequence
  • Goto “Add” and navigate to General > Run PowerShell Script
  • Name: Machine Config
  • Powershell Script: %SCRIPTROOT%\Custom\machine_config.ps1

Example of the complete task sequence:
Completed Task Sequence

Step 8 – Configure the deployment share rules
To configure the deployment share rules, you will need to modify the Bootstrap.ini and CustomSettings.ini. Since we are using an offline media, you will need to make the changes in Advanced Configuration > Media and then navigate to MEDIA001.

In the MEDIA001 Properties, click on the Rules tab and then click on the Edit BootStrap.ini button.
Copy the following into Bootstrap.ini and then save the file:
[Settings]
Priority=Default

[Default]
SkipBDDWelcome=YES

Now close the notepad window and copy the following into the rules field:
Note: In this example, my WSUS server is http://wsus01.joseespitia.com:8530. Please make sure to put in your own WSUS information!
[Settings]
Priority=Default

[Default]
_SMSTSORGNAME=Windows 10 1709 Capture
DoCapture=YES
OSInstall=Y
TimeZoneName=Eastern Standard Time
WSUSServer=http://wsus01.joseespitia.com:8530


SkipAdminPassword=YES
SkipProductKey=YES
SkipComputerName=YES
SkipDomainMembership=YES
SkipUserData=YES
SkipLocaleSelection=YES
SkipTimeZone=YES
SkipApplications=YES
SkipBitLocker=YES
SkipSummary=YES
SkipRoles=YES
SkipCapture=NO
SkipFinalSummary=YES

Example:
CustomSettings

Step 9- Create the ISO
While you are in MEDIA001 Properties, you will need to navigate to the General tab.
In the General section, check Generate a Lite Touch Bootable ISO image and click OK.
Then right click MEDIA001 and select Update Media Content.

This will generate a stand alone MDT ISO that you can mount to Hyper-V and your end result should look something like this:

Windows 10 image

Windows 10 image

7 Comments

  1. Very nice post, thanks! :)
    Ps – think about adding Let’s encrypt to your site 😉

  2. Great post! Nice breakdown, but it didn’t even come close to working.

  3. You have some nice customization’s here. One thing, it looks like you are using a defined WSUS server for updates during the task sequence. If that server isn’t going to be the one that services your workstations post deployment you should clear it out from your image. You basically add a script at the end that stops the windows update service and you delete the following registry key.

    HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate

  4. Benjamin Guan

    Hi Jose,

    I found that your script command to remove Edge browser from taskbar is not working on Windows 10 1709, can you help on getting the right command to unpinned the Edge from task bar?

    Thanks,
    Ben

Leave a Reply