3')
79 | Set-Content -Path $UnattendXML -Value $unattend
80 | }
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/src/DSCResources/cMDTBuildOperatingSystem/cMDTBuildOperatingSystem.psm1:
--------------------------------------------------------------------------------
1 | enum Ensure
2 | {
3 | Absent
4 | Present
5 | }
6 |
7 | [DscResource()]
8 | class cMDTBuildOperatingSystem
9 | {
10 | [DscProperty()]
11 | [Ensure]$Ensure = "Present"
12 |
13 | [DscProperty(Key)]
14 | [string]$Name
15 |
16 | [DscProperty(Key)]
17 | [string]$Path
18 |
19 | [DscProperty(Key)]
20 | [string]$SourcePath
21 |
22 | [DscProperty()]
23 | [bool]$CustomImage = $false
24 |
25 | [DscProperty(Mandatory)]
26 | [string]$PSDriveName
27 |
28 | [DscProperty(Mandatory)]
29 | [string]$PSDrivePath
30 |
31 | [void] Set()
32 | {
33 | if ($this.ensure -eq [Ensure]::Present)
34 | {
35 | if ( !$this.Test() ) {
36 | $this.ImportOperatingSystem()
37 | }
38 | }
39 | else
40 | {
41 | Invoke-RemovePath -Path "$($this.PSDrivePath)\Operating Systems\$($this.Name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath -Verbose
42 | If ( $this.Test() ) { Write-Error "Cannot remove '$($this.PSDrivePath)\Operating Systems\$($this.Name)'" }
43 | }
44 | }
45 |
46 | [bool] Test()
47 | {
48 | $present = $false
49 | if ( !$this.CustomImage ) {
50 | $present = Invoke-TestPath -Path "$($this.PSDrivePath)\Operating Systems\$($this.Name)\sources\install.wim"
51 | }
52 | else {
53 | $image = $this.GetCustomImage()
54 | $present = Invoke-TestPath -Path "$($this.PSDrivePath)\Operating Systems\$($this.Name)\$image"
55 | }
56 | return $present
57 | }
58 |
59 | [cMDTBuildOperatingSystem] Get()
60 | {
61 | return $this
62 | }
63 |
64 | [void] ImportOperatingSystem()
65 | {
66 | Import-MDTModule
67 | New-PSDrive -Name $this.PSDriveName -PSProvider "MDTProvider" -Root $this.PSDrivePath -Verbose:$false
68 |
69 | Try {
70 | $ErrorActionPreference = "Stop"
71 | if ( !$this.CustomImage ) {
72 | Import-MDTOperatingSystem -Path $this.Path -SourcePath $this.SourcePath -DestinationFolder $this.Name -Verbose
73 | }
74 | else {
75 | $image = $this.GetCustomImage()
76 | if ($image -like "*.wim") {
77 | Import-MDTOperatingSystem -path $this.Path -SourceFile "$($this.SourcePath)\$image" -DestinationFolder $this.Name -Verbose
78 | }
79 | }
80 | $ErrorActionPreference = "Continue"
81 | }
82 | Catch [Microsoft.Management.Infrastructure.CimException] {
83 | If ($_.FullyQualifiedErrorId -notlike "*ItemAlreadyExists*") {
84 | throw $_
85 | }
86 | }
87 | Finally {
88 | }
89 | }
90 |
91 | [string] GetCustomImage()
92 | {
93 | $file = Get-Item -Path "$($this.SourcePath)\*.wim" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
94 | if ($file) {
95 | return $file.Name
96 | }
97 | else {
98 | return ""
99 | }
100 | }
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | #---------------------------------#
2 | # environment configuration #
3 | #---------------------------------#
4 | version: 3.0.0.{build}
5 | image: Visual Studio 2022
6 |
7 | #---------------------------------#
8 | # install configuration #
9 | #---------------------------------#
10 | install:
11 | - ps: |
12 | Write-Host "Install prerequisites" -ForegroundColor Yellow
13 | $null = Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction Stop
14 | $module = "C:\Program Files\WindowsPowerShell\Modules\Pester"
15 | $null = takeown /F $module /A /R
16 | $null = icacls $module /reset
17 | $null = icacls $module /grant "*S-1-5-32-544:F" /inheritance:d /T
18 | $null = Remove-Item -Path $module -Recurse -Force -Confirm:$false
19 | Install-Module -Name PSScriptAnalyzer -Repository PSGallery -Force -ErrorAction Stop
20 | Install-Module -Name Pester -Repository PSGallery -Force -ErrorAction Stop
21 | Install-Module -Name xSmbShare -Repository PSGallery -Force -ErrorAction Stop
22 | Install-Module -Name cNtfsAccessControl -Repository PSGallery -Force -ErrorAction Stop
23 | # Validate
24 | $RequiredModules = 'PSScriptAnalyzer','Pester','xSmbShare','cNtfsAccessControl'
25 | $InstalledModules = Get-Module -Name $RequiredModules -ListAvailable
26 | Write-Host "Installed modules:" -ForegroundColor Yellow
27 | $InstalledModules | ft Name, Version
28 | if ( ($InstalledModules.count -lt $RequiredModules.Count) -or ($Null -eq $InstalledModules)) {
29 | throw "Required modules are missing."
30 | } else {
31 | Write-Host "All required modules found!" -ForegroundColor Green
32 | }
33 |
34 | #---------------------------------#
35 | # build configuration #
36 | #---------------------------------#
37 | build_script:
38 | - ps: |
39 | Write-Host "Build project" -ForegroundColor Yellow
40 | Write-Host "Build version : $env:APPVEYOR_BUILD_VERSION"
41 | Write-Host "Branch : $env:APPVEYOR_REPO_BRANCH"
42 | Write-Host "Repo : $env:APPVEYOR_REPO_NAME"
43 | Write-Host "Current dir : $pwd"
44 | Write-Host "Build cMDTBuildLab..." -ForegroundColor Yellow
45 | . .\Build.ps1
46 | Write-Host "...completed!" -ForegroundColor Green
47 | - cd ..\Build\cMDTBuildLab
48 | - mkdir "%ProgramFiles%\WindowsPowerShell\Modules\cMDTBuildLab"
49 | - copy cMDTBuildLab.psm1 "%ProgramFiles%\WindowsPowerShell\Modules\cMDTBuildLab"
50 | - copy cMDTBuildLab.psd1 "%ProgramFiles%\WindowsPowerShell\Modules\cMDTBuildLab"
51 |
52 | #---------------------------------#
53 | # test configuration #
54 | #---------------------------------#
55 | test_script:
56 | - ps: |
57 | Write-Host "Running Test script" -ForegroundColor Yellow
58 | Import-Module Pester
59 | $BuildDir = 'C:\Projects\Build\cMDTBuildLab'
60 | $testResultsFile = "$BuildDir\TestsResults.xml"
61 | $config = [PesterConfiguration]::Default
62 | $config.TestResult.Enabled = $true
63 | $config.TestResult.OutputPath = $testResultsFile
64 | $config.Output.Verbosity = 'Detailed'
65 | $config.Run.PassThru = $true
66 | $config.Run.Path = "$BuildDir\Tests\cMDTBuildLab.tests.ps1"
67 |
68 | $res = Invoke-Pester -Configuration $config
69 | $URI = "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)"
70 | $WC = New-Object 'System.Net.WebClient'
71 | Write-Host "About to upload file: $(Resolve-Path $testResultsFile)"
72 | try {
73 | $WC.UploadFile($URI, (Resolve-Path $testResultsFile))
74 | } catch {
75 | Write-Host "Uploading failed!" -ForegroundColor Red
76 | }
77 |
78 | if (($res.FailedCount -gt 0) -or ($res.PassedCount -eq 0) -or ($null -eq $res)) {
79 | throw "$($res.FailedCount) tests failed."
80 | } else {
81 | Write-Host "All tests passed!" -ForegroundColor Green
82 | }
83 |
--------------------------------------------------------------------------------
/src/Deploy/Download_MDT_Prereqs.ps1:
--------------------------------------------------------------------------------
1 | Configuration DownloadMDTPrereqs
2 | {
3 | Import-Module -Name PSDesiredStateConfiguration, cMDTBuildLab
4 | Import-DscResource -ModuleName PSDesiredStateConfiguration
5 | Import-DscResource -ModuleName cMDTBuildLab
6 |
7 | node $AllNodes.Where{$_.Role -match "MDT Server"}.NodeName
8 | {
9 | LocalConfigurationManager {
10 | RebootNodeIfNeeded = $AllNodes.RebootNodeIfNeeded
11 | ConfigurationMode = $AllNodes.ConfigurationMode
12 | }
13 |
14 | cMDTBuildPreReqs MDTPreReqs {
15 | DownloadPath = $Node.SourcePath
16 | }
17 |
18 | WindowsFeature DataDeduplication {
19 | Ensure = "Present"
20 | Name = "FS-Data-Deduplication"
21 | }
22 |
23 | <#
24 | Package ADK {
25 | Ensure = "Present"
26 | Name = "Windows Assessment and Deployment Kit - Windows 10"
27 | Path = "$($Node.SourcePath)\ADK\adksetup.exe"
28 | ProductId = "9346016b-6620-4841-8ea4-ad91d3ea02b5"
29 | Arguments = "/Features OptionId.DeploymentTools /norestart /quiet /ceip off"
30 | ReturnCode = 0
31 | DependsOn = "[cMDTBuildPreReqs]MDTPreReqs"
32 | }
33 |
34 | Package WinPE {
35 | Ensure = "Present"
36 | Name = "Windows Assessment and Deployment Kit Windows Preinstallation Environment Add-ons - Windows 10"
37 | Path = "$($Node.SourcePath)\WindowsPE\adkwinpesetup.exe"
38 | ProductId = "353df250-4ecc-4656-a950-4df93078a5fd"
39 | Arguments = "/Features OptionId.WindowsPreinstallationEnvironment /norestart /quiet /ceip off"
40 | ReturnCode = 0
41 | DependsOn = "[cMDTBuildPreReqs]MDTPreReqs"
42 | }
43 |
44 | Package MDT {
45 | Ensure = "Present"
46 | Name = "Microsoft Deployment Toolkit (6.3.8456.1000)"
47 | Path = "$($Node.SourcePath)\MDT\MicrosoftDeploymentToolkit_x64.msi"
48 | ProductId = "2E6CD7B9-9D00-4B04-882F-E6971BC9A763"
49 | ReturnCode = 0
50 | DependsOn = '[Package]ADK','[Package]WinPE'
51 | }
52 |
53 | File KB4564442x86
54 | {
55 | Ensure = "Present"
56 | SourcePath = "$($Node.SourcePath)\KB4564442\x86\microsoft.bdd.utility.dll"
57 | DestinationPath = "%ProgramFiles%\Microsoft Deployment Toolkit\Templates\Distribution\Tools\x86\microsoft.bdd.utility.dll"
58 | Checksum = "SHA-256"
59 | Force = $true
60 | DependsOn = "[Package]MDT"
61 | }
62 |
63 | File KB4564442x64
64 | {
65 | Ensure = "Present"
66 | SourcePath = "$($Node.SourcePath)\KB4564442\x64\microsoft.bdd.utility.dll"
67 | DestinationPath = "%ProgramFiles%\Microsoft Deployment Toolkit\Templates\Distribution\Tools\x64\microsoft.bdd.utility.dll"
68 | Checksum = "SHA-256"
69 | Force = $true
70 | DependsOn = "[Package]MDT"
71 | }
72 | #>
73 | }
74 | }
75 |
76 | #Set configuration data
77 | $ConfigurationData = @{
78 | AllNodes =
79 | @(
80 | @{
81 | #Global Settings for the configuration of Desired State Local Configuration Manager:
82 | NodeName = "*"
83 | RebootNodeIfNeeded = $true
84 | ConfigurationMode = "ApplyOnly"
85 | },
86 | @{
87 | #Node Settings for the configuration of an MDT Server:
88 | NodeName = "$env:computername"
89 | Role = "MDT Server"
90 | #Sources for download/Prereqs
91 | SourcePath = "E:\Source"
92 | }
93 | )
94 | }
95 |
96 | #Create DSC MOF job
97 | DownloadMDTPrereqs -OutputPath "$PSScriptRoot\DownloadMDTPrereqs" -ConfigurationData $ConfigurationData
98 |
99 | #Set DSC LocalConfigurationManager
100 | Set-DscLocalConfigurationManager -Path "$PSScriptRoot\DownloadMDTPrereqs" -Verbose
101 |
102 | #Start DSC MOF job
103 | Start-DscConfiguration -Wait -Force -Verbose -ComputerName "$env:computername" -Path "$PSScriptRoot\DownloadMDTPrereqs"
104 |
105 | #Set data deduplication
106 | Enable-DedupVolume -Volume "E:"
107 | Set-DedupVolume -Volume "E:" -MinimumFileAgeDays 1
108 |
109 | Write-Output ""
110 | Write-Output "Download MDT Prereqs is completed!"
111 |
--------------------------------------------------------------------------------
/src/cMDTBuildLabPrereqs.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | Prereqs = @(
3 | @{
4 | # Microsoft Deployment Toolkit (Build: 6.3.8456.1000)
5 | Name = "MDT"
6 | URI = "https://download.microsoft.com/download/3/3/9/339BE62D-B4B8-4956-B58D-73C4685FC492/MicrosoftDeploymentToolkit_x64.msi"
7 | Folder = "MDT"
8 | File = "MicrosoftDeploymentToolkit_x64.msi"
9 | }
10 | @{
11 | # MDT Hotfix KB4564442 (https://support.microsoft.com/help/4564442)
12 | # URI = "https://download.microsoft.com/download/3/0/6/306AC1B2-59BE-43B8-8C65-E141EF287A5E/KB4564442/MDT_KB4564442.exe"
13 | Name = "KB4564442"
14 | URI = "Sources\KB4564442.zip"
15 | Folder = "KB4564442"
16 | File = "KB4564442.zip"
17 | }
18 | @{
19 | # Windows Assessment and Deployment Kit v.2004 (Build: 10.1.19041.1)
20 | Name = "ADK"
21 | URI = "https://download.microsoft.com/download/8/6/c/86c218f3-4349-4aa5-beba-d05e48bbc286/adk/adksetup.exe"
22 | Folder = "ADK"
23 | File = "adksetup.exe"
24 | }
25 | @{
26 | # Windows PE v.2004 (Build: 10.1.19041.1)
27 | Name = "WinPE"
28 | URI = "https://download.microsoft.com/download/3/c/2/3c2b23b2-96a0-452c-b9fd-6df72266e335/adkwinpeaddons/adkwinpesetup.exe"
29 | Folder = "WindowsPE"
30 | File = "adkwinpesetup.exe"
31 | }
32 | @{
33 | # Install script for Visual C++ runtimes
34 | Name = "VS++Application"
35 | # External URIs
36 | #URI = "https://raw.githubusercontent.com/DeploymentBunny/Files/master/Tools/Install-X86-X64-C%2B%2B/Install-MicrosoftVisualC%2B%2Bx86x64.wsf"
37 | #URI = "https://github.com/pvs043/Files/blob/master/Tools/Install-X86-X64-C%2B%2B/Install-MicrosoftVisualC%2B%2Bx86x64.wsf"
38 | URI = "Sources\Install-MicrosoftVisualCx86x64.wsf"
39 | Folder = "VC++"
40 | File = "Install-MicrosoftVisualCx86x64.wsf"
41 | }
42 | @{
43 | Name = "VS2013x86"
44 | URI = "https://aka.ms/highdpimfc2013x86enu"
45 | Folder = "VC++\Source\VS2013"
46 | File = "vcredist_x86.exe"
47 | }
48 | @{
49 | Name = "VS2013x64"
50 | URI = "https://aka.ms/highdpimfc2013x64enu"
51 | Folder = "VC++\Source\VS2013"
52 | File = "vcredist_x64.exe"
53 | }
54 | @{
55 | Name = "VS2022x86"
56 | URI = "https://aka.ms/vs/17/release/vc_redist.x86.exe"
57 | Folder = "VC++\Source\VS2022"
58 | File = "vc_redist.x86.exe"
59 | }
60 | @{
61 | Name = "VS2022x64"
62 | URI = "https://aka.ms/vs/17/release/vc_redist.x64.exe"
63 | Folder = "VC++\Source\VS2022"
64 | File = "vc_redist.x64.exe"
65 | }
66 | @{
67 | # Action - CleanupBeforeSysprep
68 | Name = "CleanupBeforeSysprep"
69 | URI = "https://raw.githubusercontent.com/DeploymentBunny/Files/master/Tools/Action-CleanupBeforeSysprep/Action-CleanupBeforeSysprep.wsf"
70 | Folder = "CleanupBeforeSysprep"
71 | File = "Action-CleanupBeforeSysprep.wsf"
72 | }
73 | @{
74 | # Configure - Set Control+Shift Keyboard Toggle
75 | Name = "KeyboardToggle"
76 | URI = "Sources\Toggle.reg"
77 | Folder = "KeyboardToggle"
78 | File = "Toggle.reg"
79 | }
80 | @{
81 | # Configure - Firewall rules
82 | Name = "ConfigureFirewall"
83 | URI = "Sources\Config-NetFwRules.ps1"
84 | Folder = "ConfigureFirewall"
85 | File = "Config-NetFwRules.ps1"
86 | }
87 | @{
88 | # Configure - Set Start Layout (script)
89 | Name = "CustomizeDefaultProfile"
90 | URI = "Sources\Customize-DefaultProfile.ps1"
91 | Folder = "Set-Startlayout"
92 | File = "Customize-DefaultProfile.ps1"
93 | }
94 | @{
95 | # Configure - Set Start Layout (Windows 10)
96 | Name = "StartLayout10"
97 | URI = "Sources\Default_Start_Screen_Layout_10.xml"
98 | Folder = "Set-Startlayout"
99 | File = "Default_Start_Screen_Layout_10.xml"
100 | }
101 | @{
102 | # Configure - Remove Windows 10 Default Applications
103 | Name = "RemoveDefaultApps"
104 | URI = "https://raw.githubusercontent.com/SCConfigMgr/ConfigMgr/master/Operating%20System%20Deployment/Invoke-RemoveBuiltinApps.ps1"
105 | Folder = "RemoveDefaultApps"
106 | File = "Invoke-RemoveBuiltinApps.ps1"
107 | }
108 | )
109 | }
110 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 | slnx.sqlite
10 |
11 | # User-specific files (MonoDevelop/Xamarin Studio)
12 | *.userprefs
13 |
14 | # Build results
15 | [Dd]ebug/
16 | [Dd]ebugPublic/
17 | [Rr]elease/
18 | [Rr]eleases/
19 | [Xx]64/
20 | [Xx]86/
21 | [Bb]uild/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 |
26 | # Visual Studio 2015 cache/options directory
27 | .vs/
28 | # Uncomment if you have tasks that create the project's static files in wwwroot
29 | #wwwroot/
30 |
31 | # MSTest test Results
32 | [Tt]est[Rr]esult*/
33 | [Bb]uild[Ll]og.*
34 |
35 | # NUNIT
36 | *.VisualState.xml
37 | TestResult.xml
38 |
39 | # Build Results of an ATL Project
40 | [Dd]ebugPS/
41 | [Rr]eleasePS/
42 | dlldata.c
43 |
44 | # DNX
45 | project.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | *.pssproj
74 | *.sln
75 | .gitignore
76 | .gitattributes
77 |
78 | # Chutzpah Test files
79 | _Chutzpah*
80 |
81 | # Visual C++ cache files
82 | ipch/
83 | *.aps
84 | *.ncb
85 | *.opendb
86 | *.opensdf
87 | *.sdf
88 | *.cachefile
89 | *.VC.db
90 |
91 | # Visual Studio profiler
92 | *.psess
93 | *.vsp
94 | *.vspx
95 | *.sap
96 |
97 | # TFS 2012 Local Workspace
98 | $tf/
99 |
100 | # Guidance Automation Toolkit
101 | *.gpState
102 |
103 | # ReSharper is a .NET coding add-in
104 | _ReSharper*/
105 | *.[Rr]e[Ss]harper
106 | *.DotSettings.user
107 |
108 | # JustCode is a .NET coding add-in
109 | .JustCode
110 |
111 | # TeamCity is a build add-in
112 | _TeamCity*
113 |
114 | # DotCover is a Code Coverage Tool
115 | *.dotCover
116 |
117 | # NCrunch
118 | _NCrunch_*
119 | .*crunch*.local.xml
120 | nCrunchTemp_*
121 |
122 | # MightyMoose
123 | *.mm.*
124 | AutoTest.Net/
125 |
126 | # Web workbench (sass)
127 | .sass-cache/
128 |
129 | # Installshield output folder
130 | [Ee]xpress/
131 |
132 | # DocProject is a documentation generator add-in
133 | DocProject/buildhelp/
134 | DocProject/Help/*.HxT
135 | DocProject/Help/*.HxC
136 | DocProject/Help/*.hhc
137 | DocProject/Help/*.hhk
138 | DocProject/Help/*.hhp
139 | DocProject/Help/Html2
140 | DocProject/Help/html
141 |
142 | # Click-Once directory
143 | publish/
144 |
145 | # Publish Web Output
146 | *.[Pp]ublish.xml
147 | *.azurePubxml
148 |
149 | # TODO: Un-comment the next line if you do not want to checkin
150 | # your web deploy settings because they may include unencrypted
151 | # passwords
152 | #*.pubxml
153 | *.publishproj
154 |
155 | # NuGet Packages
156 | *.nupkg
157 | # The packages folder can be ignored because of Package Restore
158 | **/packages/*
159 | # except build/, which is used as an MSBuild target.
160 | !**/packages/build/
161 | # Uncomment if necessary however generally it will be regenerated when needed
162 | #!**/packages/repositories.config
163 | # NuGet v3's project.json files produces more ignoreable files
164 | *.nuget.props
165 | *.nuget.targets
166 |
167 | # Microsoft Azure Build Output
168 | csx/
169 | *.build.csdef
170 |
171 | # Microsoft Azure Emulator
172 | ecf/
173 | rcf/
174 |
175 | # Microsoft Azure ApplicationInsights config file
176 | ApplicationInsights.config
177 |
178 | # Windows Store app package directory
179 | AppPackages/
180 | BundleArtifacts/
181 |
182 | # Visual Studio cache files
183 | # files ending in .cache can be ignored
184 | *.[Cc]ache
185 | # but keep track of directories ending in .cache
186 | !*.[Cc]ache/
187 |
188 | # Others
189 | ClientBin/
190 | [Ss]tyle[Cc]op.*
191 | ~$*
192 | *~
193 | *.dbmdl
194 | *.dbproj.schemaview
195 | *.pfx
196 | *.publishsettings
197 | node_modules/
198 | orleans.codegen.cs
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # LightSwitch generated files
244 | GeneratedArtifacts/
245 | ModelManifest.xml
246 |
247 | # Paket dependency manager
248 | .paket/paket.exe
249 |
250 | # FAKE - F# Make
251 | .fake/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cMDTBuildLab
2 |
3 | cMDTBuildLab is a Powershell Module to help automize deployment Windows Reference Images on MDT Server with Desired State Configuration.
4 | cMDTBuildLab is a fork from [cMDT module](https://github.com/servicedudes/cmdt).
5 |
6 | ## Releases
7 |
8 | ### GitHub master branch
9 | [![Build status][appveyor-badge-master]][appveyor-build-master]
10 | This is the branch containing the latest release, published at PowerShell Gallery.
11 |
12 | ### GitHub dev branch
13 | [![Build status][appveyor-badge-dev]][appveyor-build-dev]
14 | This is the development branch with latest changes.
15 |
16 | ### PowerShell Gallery
17 | [![PowerShell Gallery Version][psgallery-version-badge]][psgallery]
18 | [![PowerShell Gallery Downloads][psgallery-badge]][psgallery]
19 | Official repository - latest module version and download count.
20 |
21 | ## Version
22 | 3.0.0
23 |
24 | See version history at [Project Site](https://github.com/pvs043/cMDTBuildLab/wiki/Version-History)
25 |
26 | ## Tech
27 |
28 | Prerequisites for infrastructure:
29 | * Domain Controller: DC01 (Windows 2012 R2 or above)
30 | * Windows Update Server (WSUS): WU01 (Windows 2012 R2 or above)
31 | * Deployment server: MDT01 (Windows 2016/2019)
32 | Disk C: - System
33 | Disk E: - DATA
34 | (Disk D: is used for Temp in Azure or Virtual DVD for on-premise deploy)
35 | * Hyper-V Host: HV01 (Windows 2012 R2 or above)
36 | * Original Microsoft media (ISO) images:
37 | Windows 10 Version 22H2 (November 2022)
38 | Windows 2019 (April 2019)
39 |
40 | This module is tested on Windows 2016/2019 server, but it will be worked on Windows 10 or Windows 2012 R2/Windows 8.1 + WMF 5.1.
41 |
42 | The following prerequisites automatically downloaded with the cMDTBuildPreReqs DSC resource:
43 | * [MicrosoftDeploymentToolkit_x64](https://docs.microsoft.com/en-us/sccm/mdt/) - Microsoft Deployment Toolkit (MDT) (Build 6.3.8456.1000)
44 | * [ADK & WinPE v.2004](https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install) - Windows PE, v.2004 (Build: 10.1.19041.1)
45 | * [Visual C++ runtimes](https://support.microsoft.com/en-us/kb/2977003) - 2013,2015-2022
46 |
47 | If your MDT01 host does not have direct connection to Internet, run DSC configuration from Deploy\Download_MDT_Prereqs.ps1 at Windows machine connected to Internet.
48 |
49 | ## Quick start
50 |
51 | See [Project Documentation](https://github.com/pvs043/cMDTBuildLab/wiki/Quick-Start).
52 |
53 | ## DscResources
54 |
55 | The cMDTBuildLab Module contain the following DscResources:
56 |
57 | * [cMDTBuildApplication](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildApplication)
58 | * [cMDTBuildBootstrapIni](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildBootstrapIni)
59 | * [cMDTBuildCustomize](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildCustomize)
60 | * [cMDTBuildCustomSettingsIni](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildCustomSettingsIni)
61 | * [cMDTBuildDirectory](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildDirectory)
62 | * [cMDTBuildOperatingSystem](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildOperatingSystem)
63 | * [cMDTBuildPackage](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildPackage)
64 | * [cMDTBuildPersistentDrive](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildPersistentDrive)
65 | * [cMDTBuildPreReqs](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildPreReqs)
66 | * [cMDTBuildSelectionProfile](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildSelectionProfile)
67 | * [cMDTBuildTaskSequence](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildTaskSequence)
68 | * [cMDTBuildTaskSequenceCustomize](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildTaskSequenceCustomize)
69 | * [cMDTBuildUpdateBootImage](https://github.com/pvs043/cMDTBuildLab/wiki/cMDTBuildUpdateBootImage)
70 |
71 | ## Development
72 |
73 | Want to contribute? Great!
74 |
75 | Requiest for the new features at [GitHub](https://github.com/pvs043/cMDTBuildLab/issues).
76 | Thanks to Community: [Issue](https://github.com/pvs043/cMDTBuildLab/issues) and [Pull Requests](https://github.com/pvs043/cMDTBuildLab/pulls)
77 |
78 | ## Respects
79 |
80 | [Johan Arwidmark](http://deploymentresearch.com/Research): Deployment Research
81 | [Mikael Nystrom](https://anothermike2.wordpress.com): The Deployment Bunny
82 | [Jason Helmick](https://twitter.com/theJasonHelmick), [Jeffrey Snover](https://twitter.com/@jsnover)
83 |
84 | ## License
85 |
86 | **Free usage**
87 |
88 | [appveyor-badge-master]: https://ci.appveyor.com/api/projects/status/h8qth51otb888a7v?branch=master&svg=true
89 | [appveyor-build-master]: https://ci.appveyor.com/project/pvs043/cmdtbuildlab/branch/master?fullLog=true
90 | [appveyor-badge-dev]: https://ci.appveyor.com/api/projects/status/h8qth51otb888a7v?branch=dev&svg=true
91 | [appveyor-build-dev]: https://ci.appveyor.com/project/pvs043/cmdtbuildlab/branch/dev?fullLog=true
92 | [psgallery-badge]: https://img.shields.io/powershellgallery/dt/cmdtbuildlab.svg
93 | [psgallery]: https://www.powershellgallery.com/packages/cmdtbuildlab
94 | [psgallery-version-badge]: https://img.shields.io/powershellgallery/v/cmdtbuildlab.svg
95 | [psgallery-version]: https://www.powershellgallery.com/packages/cmdtbuildlab
96 |
--------------------------------------------------------------------------------
/src/DSCResources/cMDTBuildPreReqs/cMDTBuildPreReqs.psm1:
--------------------------------------------------------------------------------
1 | enum Ensure
2 | {
3 | Absent
4 | Present
5 | }
6 |
7 | [DscResource()]
8 | class cMDTBuildPreReqs
9 | {
10 | [DscProperty()]
11 | [Ensure]$Ensure = "Present"
12 |
13 | [DscProperty(Key)]
14 | [string]$DownloadPath
15 |
16 | [void] Set()
17 | {
18 | Write-Verbose "Starting Set MDT PreReqs..."
19 | [hashtable]$DownloadFiles = Get-ConfigurationData -ConfigurationData "$($PSScriptRoot)\cMDTBuildLabPrereqs.psd1"
20 |
21 | if ($this.ensure -eq [Ensure]::Present) {
22 | $present = $this.TestDownloadPath()
23 | if ($present) {
24 | Write-Verbose " Download folder present!"
25 | }
26 | else {
27 | New-Item -Path $this.DownloadPath -ItemType Directory -Force
28 | }
29 |
30 | # Set all files:
31 | ForEach ($file in $downloadFiles.Prereqs) {
32 | $folder = "$($this.DownloadPath)\$($file.Folder)"
33 | if (Test-Path -Path "$folder\$($file.File)") {
34 | Write-Verbose " $($file.Name) already present!"
35 | }
36 | else {
37 | Write-Verbose " Creating $($file.Name) folder..."
38 | New-Item -Path $folder -ItemType Directory -Force
39 | if ($file.URI -like "*/*") {
40 | $this.WebClientDownload($file.URI, "$folder\$($file.File)")
41 | }
42 | else {
43 | $this.CopyFromSource("$($PSScriptRoot)\$($file.URI)", "$folder\$($file.File)")
44 | }
45 |
46 | # Workaround for external source scripts from GitHub: change EOL
47 | if ($file.Name -eq "CleanupBeforeSysprep" -or $file.Name -eq "RemoveDefaultApps") {
48 | $script = Get-Content -Path "$folder\$($file.File)"
49 | if ($script -notlike '*`r`n*') {
50 | $script.Replace('`n','`r`n')
51 | Set-Content -Path "$folder\$($file.File)" -Value $script
52 | }
53 | }
54 | # Download ADK Installers
55 | if ($file.Name -eq "ADK" -or $file.Name -eq "WinPE") {
56 | Write-Verbose " Download $($file.Name) installers..."
57 | Start-Process -FilePath "$folder\$($file.File)" -ArgumentList "/layout $Folder /norestart /quiet /ceip off" -Wait
58 | }
59 | # Unpack MDT hotfix
60 | if ($file.Name -eq "KB4564442") {
61 | Expand-Archive -Path "$folder\$($file.File)" -DestinationPath $folder
62 | }
63 | }
64 | }
65 | }
66 | else {
67 | $this.RemoveDirectory("")
68 | }
69 | Write-Verbose "MDT PreReqs set completed!"
70 | }
71 |
72 | [bool] Test()
73 | {
74 | Write-Verbose "Testing MDT PreReqs..."
75 | $present = $this.TestDownloadPath()
76 | [hashtable]$DownloadFiles = Get-ConfigurationData -ConfigurationData "$($PSScriptRoot)\cMDTBuildLabPrereqs.psd1"
77 |
78 | if ($this.ensure -eq [Ensure]::Present) {
79 | Write-Verbose " Testing for download path.."
80 | if($present) {
81 | Write-Verbose " Download path found!"
82 | }
83 | Else {
84 | Write-Verbose " Download path not found!"
85 | return $false
86 | }
87 |
88 | ForEach ($File in $downloadFiles.Prereqs) {
89 | Write-Verbose " Testing for $($File.Name)..."
90 | $present = (Test-Path -Path "$($this.DownloadPath)\$($File.Folder)\$($File.File)")
91 | Write-Verbose " $present"
92 | if(!$Present) {return $false}
93 | }
94 | }
95 | else {
96 | if ($Present) {$present = $false}
97 | else {$present = $true}
98 | }
99 |
100 | Write-Verbose "Test completed!"
101 | return $present
102 | }
103 |
104 | [cMDTBuildPreReqs] Get()
105 | {
106 | return $this
107 | }
108 |
109 | [bool] TestDownloadPath()
110 | {
111 | $present = $false
112 | if (Test-Path -Path $this.DownloadPath -ErrorAction Ignore) {
113 | $present = $true
114 | }
115 | return $present
116 | }
117 |
118 | [void] WebClientDownload($Source,$Target)
119 | {
120 | $WebClient = New-Object System.Net.WebClient
121 | Write-Verbose " Downloading file $($Source)"
122 | Write-Verbose " Downloading to $($Target)"
123 | $WebClient.DownloadFile($Source, $Target)
124 | }
125 |
126 | [void] CopyFromSource($Source,$Target)
127 | {
128 | Write-Verbose " Copying $($Target)"
129 | Copy-Item -Path $Source -Destination $Target
130 | }
131 |
132 | [void] RemoveDirectory($referencefile = "")
133 | {
134 | Remove-Item -Path $this.DownloadPath -Force -Verbose
135 | }
136 | }
137 |
138 |
--------------------------------------------------------------------------------
/src/DSCResources/cMDTBuildUpdateBootImage/cMDTBuildUpdateBootImage.psm1:
--------------------------------------------------------------------------------
1 | enum Ensure
2 | {
3 | Absent
4 | Present
5 | }
6 |
7 | [DscResource()]
8 | class cMDTBuildUpdateBootImage
9 | {
10 | [DscProperty(Key)]
11 | [string]$Version
12 |
13 | [DscProperty(Key)]
14 | [string]$PSDeploymentShare
15 |
16 | [DscProperty(Mandatory)]
17 | [string]$PSDrivePath
18 |
19 | [DscProperty()]
20 | [string]$ExtraDirectory
21 |
22 | [DscProperty()]
23 | [string]$BackgroundFile
24 |
25 | [DscProperty()]
26 | [string]$LiteTouchWIMDescription
27 |
28 | [DscProperty()]
29 | [bool]$CreateISOx64 = $false
30 |
31 | [void] Set()
32 | {
33 | $this.UpdateBootImage()
34 | }
35 |
36 | [bool] Test()
37 | {
38 | Return ($this.VerifyVersion())
39 | }
40 |
41 | [cMDTBuildUpdateBootImage] Get()
42 | {
43 | return $this
44 | }
45 |
46 | [bool] VerifyVersion()
47 | {
48 | [bool]$match = $false
49 |
50 | if ((Get-Content -Path "$($this.PSDrivePath)\Boot\CurrentBootImage.version" -ErrorAction Ignore) -eq $this.Version) {
51 | $match = $true
52 | }
53 | return $match
54 | }
55 |
56 | [void] UpdateBootImage()
57 | {
58 | Import-MDTModule
59 | New-PSDrive -Name $this.PSDeploymentShare -PSProvider "MDTProvider" -Root $this.PSDrivePath -Verbose:$false
60 |
61 | If ([string]::IsNullOrEmpty($($this.ExtraDirectory))) {
62 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x64.ExtraDirectory -Value ""
63 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.ExtraDirectory -Value ""
64 | }
65 | ElseIf (Invoke-TestPath -Path "$($this.PSDrivePath)\$($this.ExtraDirectory)") {
66 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x64.ExtraDirectory -Value "$($this.PSDrivePath)\$($this.ExtraDirectory)"
67 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.ExtraDirectory -Value "$($this.PSDrivePath)\$($this.ExtraDirectory)"
68 | }
69 |
70 | If ([string]::IsNullOrEmpty($($this.BackgroundFile))) {
71 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x64.BackgroundFile -Value ""
72 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.BackgroundFile -Value ""
73 | }
74 | ElseIf (Invoke-TestPath -Path "$($this.PSDrivePath)\$($this.BackgroundFile)") {
75 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x64.BackgroundFile -Value "$($this.PSDrivePath)\$($this.BackgroundFile)"
76 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.BackgroundFile -Value "$($this.PSDrivePath)\$($this.BackgroundFile)"
77 | }
78 | Else {
79 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x64.BackgroundFile -Value $this.BackgroundFile
80 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.BackgroundFile -Value $this.BackgroundFile
81 | }
82 |
83 | If ($this.LiteTouchWIMDescription) {
84 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x64.LiteTouchWIMDescription -Value "$($this.LiteTouchWIMDescription) x64 $($this.Version)"
85 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.LiteTouchWIMDescription -Value "$($this.LiteTouchWIMDescription) x86 $($this.Version)"
86 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x64.LiteTouchISOName -Value "$($this.LiteTouchWIMDescription)_x64.iso".Replace(' ','_')
87 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.LiteTouchISOName -Value "$($this.LiteTouchWIMDescription)_x86.iso".Replace(' ','_')
88 | }
89 |
90 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x64.SelectionProfile -Value "Nothing"
91 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.SelectionProfile -Value "Nothing"
92 |
93 | # for offline remove Windows default apps
94 | # Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.FeaturePacks -Value "winpe-dismcmdlets,winpe-mdac,winpe-netfx,winpe-powershell,winpe-storagewmi"
95 |
96 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x64.GenerateLiteTouchISO -Value $this.CreateISOx64
97 | Set-ItemProperty "$($this.PSDeploymentShare):" -Name Boot.x86.GenerateLiteTouchISO -Value $true
98 |
99 | #The Update-MDTDeploymentShare command crashes WMI when run from inside DSC. This section is a work around.
100 | workflow Update-DeploymentShare {
101 | param (
102 | [string]$PSDeploymentShare,
103 | [string]$PSDrivePath
104 | )
105 | InlineScript {
106 | Import-MDTModule
107 | New-PSDrive -Name $Using:PSDeploymentShare -PSProvider "MDTProvider" -Root $Using:PSDrivePath -Verbose:$false
108 | Update-MDTDeploymentShare -Path "$($Using:PSDeploymentShare):" -Force:$true -Compress:$true
109 | }
110 | }
111 | Update-DeploymentShare $this.PSDeploymentShare $this.PSDrivePath
112 | Set-Content -Path "$($this.PSDrivePath)\Boot\CurrentBootImage.version" -Value "$($this.Version)"
113 | }
114 | }
115 |
116 |
--------------------------------------------------------------------------------
/src/Sources/Install-MicrosoftVisualCx86x64.wsf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
133 |
134 |
--------------------------------------------------------------------------------
/Build.ps1:
--------------------------------------------------------------------------------
1 | $moduleName = "cMDTBuildLab"
2 | $moduleGuid = "df45de26-88b1-4a95-98af-b798fde1424f"
3 | $year = (Get-Date).Year
4 | $moduleVersion = "3.0.0"
5 | $releaseNotes = "
6 | * Remove Windows 7/8.1/2012R2 deployments and prerequisites
7 | * Update download URLs for MDT
8 | * Add MDT Hotfix KB4564442 (Build: 6.3.8456.1001)
9 | * Update ADK (v.2004)
10 | * Download ADK installers (cMDTBuildLabPrereqs)
11 | * Update VC++ prerequisites (VC++ 2022)
12 | * New design for AppVeyor tests
13 | "
14 | $allResources = @( Get-ChildItem -Path $PSScriptRoot\src\DSCResources\*.psm1 -ErrorAction SilentlyContinue -Recurse | Sort-Object)
15 | $allFunctions = @( Get-ChildItem -Path $PSScriptRoot\src\Public\*.ps1 -ErrorAction SilentlyContinue -Recurse | Sort-Object)
16 | $buildDir = "C:\Projects"
17 | $combinedModule = "$BuildDir\Build\$moduleName\$ModuleName.psm1"
18 | $manifestFile = "$BuildDir\Build\$moduleName\$ModuleName.psd1"
19 | [string]$dscResourcesToExport = $null
20 |
21 | $ensureDefiniton = @"
22 | enum Ensure
23 | {
24 | Present
25 | Absent
26 | }
27 |
28 |
29 | "@
30 |
31 | # Init module script
32 | [string]$combinedResources = $ensureDefiniton
33 |
34 | # Prepare DSC resources
35 | Foreach ($resource in @($allResources)) {
36 | Write-Output "Add Resource: $resource"
37 | Try {
38 | $resourceContent = Get-Content $resource -Raw -Encoding UTF8
39 | $combinedResources += $resourceContent.Substring($resourceContent.IndexOf("[DscResource()]"))
40 |
41 | if ($resourceContent -match 'class\s*(?\w*)[\r\t]') {
42 | foreach ($match in $Matches.ClassName) {
43 | [string]$dscResourcesToExport += "'$match',"
44 | }
45 | }
46 | }
47 | Catch {
48 | throw $_
49 | }
50 | }
51 |
52 | # Prepare Functions
53 | Foreach ($function in @($allFunctions)) {
54 | Write-Output "Add Function: $function"
55 | Try {
56 | $functionContent = Get-Content $function -Raw
57 | $combinedResources += $functionContent.Substring($functionContent.IndexOf("Function"))
58 | }
59 | Catch {
60 | throw $_
61 | }
62 | }
63 |
64 | # Prepare Manifest
65 | $dscResourcesToExport = $dscResourcesToExport.TrimEnd(",")
66 | $ManifestDefinition = @"
67 | @{
68 |
69 | # Script module or binary module file associated with this manifest.
70 | RootModule = '$moduleName.psm1'
71 |
72 | DscResourcesToExport = @($dscResourcesToExport)
73 |
74 | FunctionsToExport = @('Import-MDTModule','Invoke-ExpandArchive','Invoke-RemovePath','Invoke-TestPath','Get-ConfigurationData')
75 |
76 | # Version number of this module.
77 | ModuleVersion = '$moduleVersion'
78 |
79 | # ID used to uniquely identify this module
80 | GUID = '$moduleGuid'
81 |
82 | # Author of this module
83 | Author = 'Pavel Andreev'
84 |
85 | # Company or vendor of this module
86 | CompanyName = ''
87 |
88 | # Copyright statement for this module
89 | Copyright = '(c) $Year Pavel Andreev. All rights reserved.'
90 |
91 | # Description of the functionality provided by this module
92 | Description = 'A DSC Module to help automize deployment Windows Reference Images on MDT Server'
93 |
94 | # Project site link
95 | HelpInfoURI = 'https://github.com/pvs043/cMDTBuildLab/wiki'
96 |
97 | # Minimum version of the Windows PowerShell engine required by this module
98 | PowerShellVersion = '5.1'
99 |
100 | # Modules that must be imported into the global environment prior to importing this module
101 | RequiredModules = @('cNtfsAccessControl',
102 | @{ModuleName = 'xSmbShare'; ModuleVersion = '2.0.0.0';}
103 | )
104 |
105 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
106 | PrivateData = @{
107 |
108 | PSData = @{
109 |
110 | # Tags applied to this module. These help with module discovery in online galleries.
111 | Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResource', 'MDT', 'MicrosoftDeploymentToolkit', 'Deploy')
112 |
113 | # A URL to the license for this module.
114 | LicenseUri = 'https://github.com/pvs043/cMDTBuildLab/blob/master/LICENSE'
115 |
116 | # A URL to the main website for this project.
117 | ProjectUri = 'https://github.com/pvs043/cMDTBuildLab'
118 |
119 | # A URL to an icon representing this module.
120 | # IconUri = ''
121 |
122 | # ReleaseNotes of this module
123 | ReleaseNotes = '$releaseNotes'
124 | } # End of PSData hashtable
125 |
126 | } # End of PrivateData hashtable
127 |
128 | # Name of the Windows PowerShell host required by this module
129 | # PowerShellHostName = ''
130 | }
131 | "@
132 |
133 | # Create Build dir
134 | If (Test-Path -Path "$buildDir\Build") { Remove-Item -Path "$buildDir\Build" -Recurse -Force}
135 | $null = New-Item -ItemType Directory -Path "$buildDir\Build\$moduleName"
136 |
137 | # Build module from sources
138 | Set-Content -Path $combinedModule -Value $combinedResources
139 | Set-Content -Path $manifestFile -Value $ManifestDefinition
140 | Copy-Item -Path "$PSScriptRoot\src\cMDTBuildLabPrereqs.psd1" -Destination "$BuildDir\Build\$moduleName\cMDTBuildLabPrereqs.psd1" -Force
141 |
142 | # Add artefacts
143 | Copy-Item -Path "$PSScriptRoot\src\Deploy" -Destination "$BuildDir\Build\$moduleName\Deploy" -Recurse -Force
144 | Copy-Item -Path "$PSScriptRoot\src\Examples" -Destination "$BuildDir\Build\$moduleName\Examples" -Recurse -Force
145 | Copy-Item -Path "$PSScriptRoot\src\Sources" -Destination "$BuildDir\Build\$moduleName\Sources" -Recurse -Force
146 | Copy-Item -Path "$PSScriptRoot\src\Tests" -Destination "$BuildDir\Build\$moduleName\Tests" -Recurse -Force
147 | Copy-Item -Path "$PSScriptRoot\README.md" -Destination "$BuildDir\Build\$moduleName\Readme.md" -Force
148 | Copy-Item -Path "$PSScriptRoot\LICENSE" -Destination "$BuildDir\Build\$moduleName\LICENSE" -Force
149 |
--------------------------------------------------------------------------------
/src/Deploy/Import-ISO.ps1:
--------------------------------------------------------------------------------
1 | ###
2 | # Script for prepare Windows distributives
3 | #
4 | # 1. Download source Windows ISO from VLSC, MSDN on Evaluation portals
5 | # 2. Save to folders on MDT server or File server:
6 | # \\server\ISO
7 | # + Windows 10
8 | # + 14393.0.160715-1616.RS1_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X86FRE_EN-US.ISO
9 | # + Windows 2016
10 | # + en_windows_server_2016_x64_dvd_9718492.iso
11 | # [...]
12 | # 3. Run this script as Administrator and get info of the images:
13 | # Import-ISO.ps1 -ISOPath '\\server\ISO' -Verbose
14 | # 4. Edit parameters at TaskSequences section of the Deploy_MDT_Server_ConfigurationData.psd1:
15 | # TaskSequences = @(
16 | # @{
17 | # Ensure = "Present"
18 | # Name = "Windows 7 x86"
19 | # Path = "Windows 7"
20 | # OSName = "Windows 7\Windows 7 ENTERPRISE in Windows 7 x86 install.wim"
21 | # [...]
22 | # 5. Select needed images for your Build Lab ($destinations)
23 | # 6. Import Windows sources from ISO:
24 | # Import-ISO.ps1 -ISOPath '\\server\ISO' -Unpack -Verbose
25 | #
26 | # Author: @sundmoon (https://github.com/sundmoon)
27 | #
28 | ###
29 |
30 | [CmdletBinding()]
31 |
32 | param(
33 | [parameter(Mandatory = $true, HelpMessage="Enter Source path for ISO files directory tree (local folder or file share name)")]
34 | [String]$ISOPath,
35 |
36 | [parameter(Mandatory = $false)]
37 | [String]$dstPath = 'E:\Source',
38 |
39 | [switch]$Unpack
40 | )
41 |
42 | $destinations = @(
43 | @{
44 | Name = "Windows 7 ENTERPRISE"
45 | Lang = "Russian"
46 | Arch = "x32"
47 | Build = "59024"
48 | Dest = "Windows7x86"
49 | }
50 | @{
51 | Name = "Windows 7 ENTERPRISE"
52 | Lang = "Russian"
53 | Arch = "x64"
54 | Build = "59028"
55 | Dest = "Windows7x64"
56 | }
57 | @{
58 | Name = "Windows 10 Enterprise"
59 | Lang = "Russian"
60 | Arch = "x32"
61 | Build = "03151"
62 | Dest = "Windows10x86"
63 | }
64 | @{
65 | Name = "Windows 10 Enterprise"
66 | Lang = "Russian"
67 | Arch = "x64"
68 | Build = "03152"
69 | Dest = "Windows10x64"
70 | }
71 | @{
72 | Name = "Windows Server 2016 SERVERSTANDARD"
73 | Lang = "English"
74 | Arch = "x64"
75 | Build = "70526"
76 | Dest = "Windows2016"
77 | }
78 | @{
79 | Name = "Windows Server 2019 SERVERSTANDARD"
80 | Lang = "English"
81 | Arch = "x64"
82 | Build = "02970"
83 | Dest = "Windows2019"
84 | }
85 | )
86 |
87 | if (!(Test-Path -Path $ISOPath)) {
88 | Write-Warning -Message "Could not find ISO store at $ISOPath. Aborting..."
89 | Break
90 | }
91 |
92 | #best effort to parse conventional iso names for meaningful tokens, may not match all possible iso names
93 | function Get-ISOToken {
94 | param (
95 | [string]$FullName
96 | )
97 |
98 | #you may add your languages here or amend the logic to encompass more complex variants
99 | $Lang = switch -regex ($FullName) {
100 | '\\en_windows_|_English_' {'English'}
101 | '\\ru_windows_|_Russian_' {'Russian'}
102 | default {'Unknown/Custom'}
103 | }
104 |
105 | #extracting 5 or more sequential digits
106 | [regex]$rx='\d{5,}'
107 | $Build = $rx.Match($FullName).value
108 |
109 | #mining for Arch
110 | $x64 = $FullName -match 'x64' -or $FullName -match '64BIT'
111 | $x32 = $FullName -match 'x32' -or $FullName -match 'x86' -or $FullName -match '32BIT'
112 | If ($x64 -and $x32) { $Arch = 'x32_x64' }
113 | elseif ($x64) { $Arch = 'x64' }
114 | elseif ($x32) { $Arch = 'x32' }
115 |
116 | @{
117 | Lang = $Lang
118 | Arch = $Arch
119 | Build = $Build
120 | }
121 | }
122 |
123 | function Get-ISO
124 | {
125 | [cmdletbinding()]
126 | param (
127 | [Parameter(Mandatory = $true,
128 | ValueFromPipeline = $true)]
129 | [System.IO.FileInfo[]]$ISO,
130 |
131 | [switch]$Unpack
132 | )
133 |
134 | process {
135 | Mount-DiskImage -ImagePath $PSItem.FullName
136 | $ISOImage = Get-DiskImage -ImagePath $PSItem.fullname | Get-Volume
137 | $ISODrive = [string]$ISOImage.DriveLetter+':'
138 | $InstallImage = "$ISODrive\sources\install.wim"
139 | if (!(Test-Path -Path $InstallImage ))
140 | {
141 | Write-Warning -Message "Could not find install.wim in $($PSItem.FullName)"
142 | }
143 | else
144 | {
145 | Write-Verbose -Message "Processing $PSItem"
146 |
147 | #assuming wim image format, don't mess with esd, swm etc. for now
148 | $images = Get-WindowsImage -ImagePath $ISODrive\sources\install.wim
149 | Write-Verbose -Message "Images count: $($images.count)"
150 | foreach ($image in $images)
151 | {
152 | $tokens = Get-ISOToken $PSItem.FullName
153 | [PSCustomObject]@{
154 | 'FullName' = $PSItem.FullName
155 | 'Name' = $image.ImageName
156 | 'Language' = $tokens['Lang']
157 | 'Arch' = $tokens['Arch']
158 | 'Build' = $tokens['Build']
159 | 'ImageIndex' = $image.ImageIndex
160 | 'ImageSize' = "{0:N2}" -f ($image.ImageSize / 1GB) + " Gb"
161 | }
162 |
163 | #unpack images to MDT source folder
164 | if ($Unpack)
165 | {
166 | foreach ($dst in $destinations) {
167 | if ($dst.Name -eq $image.ImageName -and $dst.Lang -eq $tokens['Lang'] -and $dst.Arch -eq $tokens['Arch'] -and $dst.Build -eq $tokens['Build'])
168 | {
169 | $unpackPath = "$dstPath\$($dst.Dest)"
170 | Write-Output "Unpack $Name from $($PSItem.FullName) to $unpackPath"
171 | if (Test-Path $unpackPath)
172 | {
173 | Write-Warning "Remove directory $unpackPath"
174 | Remove-Item $unpackPath -Recurse -Force
175 | }
176 | New-Item $unpackPath -ItemType Directory
177 | Copy-Item -Path "$ISODrive\*" -Destination $unpackPath -Recurse
178 | Write-Output "Done!"
179 | }
180 | }
181 | }
182 | }
183 | }
184 | Dismount-DiskImage -ImagePath $PSItem.fullname
185 | }
186 | }
187 |
188 | $ISO = Get-ChildItem $ISOPath -Recurse -Filter *.iso
189 | $ISO | Get-ISO -Unpack:$Unpack -Verbose
190 |
--------------------------------------------------------------------------------
/src/Deploy/Deploy_MDT_Server_ConfigurationData_Lite.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | AllNodes =
3 | @(
4 | @{
5 |
6 | #Global Settings for the configuration of Desired State Local Configuration Manager:
7 | NodeName = "*"
8 | PSDscAllowPlainTextPassword = $true
9 | RebootNodeIfNeeded = $true
10 | ConfigurationMode = "ApplyAndAutoCorrect"
11 | ConfigurationModeFrequencyMins = 120
12 | RefreshFrequencyMins = 120
13 | },
14 |
15 | @{
16 | #Node Settings for the configuration of an MDT Server:
17 | NodeName = "$env:computername"
18 | Role = "MDT Server"
19 |
20 | #Sources for download/Prereqs
21 | SourcePath = "E:\Source"
22 |
23 | #MDT deoployment share paths
24 | PSDriveName = "MDT001"
25 | PSDrivePath = "E:\MDTBuildLab"
26 | PSDriveShareName = "MDTBuildLab$"
27 |
28 | #Operating system MDT directory information
29 | OSDirectories = @(
30 | @{ OperatingSystem = "Windows 10" }
31 | )
32 |
33 | #MDT Application Folder Structure
34 | ApplicationFolderStructure = @(
35 | @{
36 | Folder = "Core"
37 | SubFolders = @(
38 | @{ SubFolder = "Configure" }
39 | @{ SubFolder = "Microsoft" }
40 | )
41 | }
42 | @{ Folder = "Common Applications" }
43 | )
44 |
45 | PackagesFolderStructure = @(
46 | @{ Folder = "Windows 10 x64" }
47 | )
48 |
49 | #Operating systems to import to MDT
50 | OperatingSystems = @(
51 | @{
52 | Name = "Windows 10 x64"
53 | Path = "Windows 10"
54 | SourcePath = "Windows10x64"
55 | }
56 | )
57 |
58 | #Applications to import
59 | Applications = @(
60 | @{
61 | Name = "Install - Microsoft Visual C++"
62 | Path = "\Applications\Core\Microsoft"
63 | CommandLine = "cscript.exe Install-MicrosoftVisualCx86x64.wsf"
64 | ApplicationSourcePath = "VC++"
65 | }
66 | @{
67 | Name = "Configure - Set Control+Shift Keyboard Toggle"
68 | Path = "\Applications\Core\Configure"
69 | CommandLine = "reg import Toggle.reg"
70 | ApplicationSourcePath = "KeyboardToggle"
71 | }
72 | @{
73 | Name = "Action - CleanupBeforeSysprep"
74 | Path = "\Applications\Core\Configure"
75 | CommandLine = "cscript.exe Action-CleanupBeforeSysprep.wsf"
76 | ApplicationSourcePath = "CleanupBeforeSysprep"
77 | }
78 | @{
79 | Name = "Configure - Set Start Layout"
80 | Path = "\Applications\Core\Configure"
81 | CommandLine = "powershell.exe -ExecutionPolicy Bypass -File .\Customize-DefaultProfile.ps1"
82 | ApplicationSourcePath = "Set-Startlayout"
83 | }
84 | )
85 |
86 | #Selection profile creation
87 | SelectionProfiles = @(
88 | @{
89 | Name = "Windows 10 x64"
90 | Comments = "Packages for Windows 10 x64"
91 | IncludePath = "Packages\Windows 10 x64"
92 | }
93 | )
94 |
95 | #Task sqeuences; are dependent on imported Operating system and Applications in MDT
96 | TaskSequences = @(
97 | @{
98 | Name = "Windows 10 x64"
99 | Path = "Windows 10"
100 | OSName = "Windows 10\Windows 10 Enterprise Evaluation in Windows 10 x64 install.wim"
101 | OrgName = "BuildLab"
102 | Template = "Client.xml"
103 | ID = "REFW10X64-001"
104 | Customize = @(
105 | # Set Product Key needed for MSDN/Evalution Windows distributives only. Skip this step if your ISO sources is VLCS.
106 | @{
107 | Name = "Set Product Key"
108 | Type = "Set Task Sequence Variable"
109 | GroupName = "Initialization"
110 | Description = "KMS Client Setup Keys: https://docs.microsoft.com/en-us/windows-server/get-started/kmsclientkeys"
111 | TSVarName = "ProductKey"
112 | TSVarValue = "NPPR9-FWDCX-D2C8J-H872K-2YT43"
113 | }
114 | # Workaround for disable UEFI disk format
115 | @{
116 | Name = "Disable UEFI"
117 | Type = "Set Task Sequence Variable"
118 | GroupName = "Preinstall"
119 | Description = "Disable UEFI disk format"
120 | TSVarName = "IsUEFI"
121 | TSVarValue = "False"
122 | AddAfter = "Gather local only"
123 | }
124 | @{
125 | Name = "Apply Patches"
126 | Type = "Install Updates Offline"
127 | GroupName = "Preinstall"
128 | SelectionProfile = "Windows 10 x64"
129 | }
130 | @{
131 | Name = "Windows Update (Pre-Application Installation)"
132 | Type = "Run Command Line"
133 | GroupName = "State Restore"
134 | Disable = "false"
135 | }
136 | @{
137 | Name = "Custom Tasks (Pre-Windows Update)"
138 | Type = "Group"
139 | GroupName = "State Restore"
140 | AddAfter = "Tattoo"
141 | }
142 | @{
143 | Name = "Custom Tasks"
144 | Type = "Group"
145 | GroupName = "State Restore"
146 | NewName = "Custom Tasks (Post-Windows Update)"
147 | }
148 | @{
149 | Name = "Cleanup before Sysprep"
150 | Type = "Group"
151 | GroupName = "State Restore"
152 | AddAfter = "Apply Local GPO Package"
153 | }
154 | @{
155 | Name = "Install - Microsoft NET Framework 3.5.1"
156 | Type = "Install Roles and Features"
157 | GroupName = "State Restore"
158 | SubGroup = "Custom Tasks (Pre-Windows Update)"
159 | OSName = "Windows 10"
160 | OSFeatures = "NetFx3"
161 | }
162 | @{
163 | Name = "Configure - Disable SMB 1.0"
164 | Type = "Run Command Line"
165 | GroupName = "State Restore"
166 | SubGroup = "Custom Tasks (Pre-Windows Update)"
167 | Command = 'powershell.exe -Command "Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -NoRestart"'
168 | AddAfter = "Install - Microsoft NET Framework 3.5.1"
169 | }
170 | @{
171 | Name = "Install - Microsoft Visual C++"
172 | Type = "Install Application"
173 | GroupName = "State Restore"
174 | SubGroup = "Custom Tasks (Pre-Windows Update)"
175 | AddAfter = "Configure - Disable SMB 1.0"
176 | }
177 | @{
178 | Name = "Configure - Set Control+Shift Keyboard Toggle"
179 | Type = "Install Application"
180 | GroupName = "State Restore"
181 | SubGroup = "Custom Tasks (Pre-Windows Update)"
182 | AddAfter = "Install - Microsoft Visual C++"
183 | }
184 | @{
185 | Name = "Configure - Set Start Layout"
186 | Type = "Install Application"
187 | GroupName = "State Restore"
188 | SubGroup = "Custom Tasks (Pre-Windows Update)"
189 | AddAfter = "Configure - Set Control+Shift Keyboard Toggle"
190 | }
191 | @{
192 | Name = "Configure - Enable App-V Client"
193 | Type = "Run Command Line"
194 | GroupName = "State Restore"
195 | SubGroup = "Custom Tasks (Pre-Windows Update)"
196 | AddAfter = "Configure - Set Start Layout"
197 | Command = 'powershell.exe -ExecutionPolicy ByPass -Command "Enable-Appv; Set-AppvClientConfiguration -EnablePackageScripts 1"'
198 | }
199 | @{
200 | Name = "Suspend1"
201 | Type = "Run Command Line"
202 | GroupName = "State Restore"
203 | SubGroup = "Custom Tasks (Pre-Windows Update)"
204 | Disable = "true"
205 | Command = 'cscript.exe "%SCRIPTROOT%\LTISuspend.wsf"'
206 | AddAfter = "Configure - Enable App-V Client"
207 | }
208 | @{
209 | Name = "Configure - Remove Windows Default Applications"
210 | Type = "Run PowerShell Script"
211 | GroupName = "State Restore"
212 | SubGroup = "Custom Tasks (Pre-Windows Update)"
213 | PSCommand = "%SCRIPTROOT%\Invoke-RemoveBuiltinApps.ps1"
214 | AddAfter = "Suspend1"
215 | }
216 | @{
217 | Name = "Suspend2"
218 | Type = "Run Command Line"
219 | GroupName = "State Restore"
220 | SubGroup = "Custom Tasks (Pre-Windows Update)"
221 | Disable = "true"
222 | Command = 'cscript.exe "%SCRIPTROOT%\LTISuspend.wsf"'
223 | AddAfter = "Configure - Remove Windows Default Applications"
224 | }
225 | @{
226 | Name = "Restart Computer"
227 | Type = "Restart Computer"
228 | GroupName = "State Restore"
229 | SubGroup = "Custom Tasks (Pre-Windows Update)"
230 | AddAfter = "Suspend2"
231 | }
232 | @{
233 | Name = "Action - CleanupBuildWSUS"
234 | Type = "Run Command Line"
235 | GroupName = "State Restore"
236 | SubGroup = "Cleanup before Sysprep"
237 | Command = "reg delete HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate /f"
238 | }
239 | @{
240 | Name = "Action - CleanupBeforeSysprep"
241 | Type = "Install Application"
242 | GroupName = "State Restore"
243 | SubGroup = "Cleanup before Sysprep"
244 | AddAfter = "Action - CleanupBuildWSUS"
245 | }
246 | @{
247 | Name = "Restart Computer"
248 | Type = "Restart Computer"
249 | GroupName = "State Restore"
250 | SubGroup = "Cleanup before Sysprep"
251 | AddAfter = "Action - CleanupBeforeSysprep"
252 | }
253 | )
254 | }
255 | )
256 |
257 | #Custom folder/files to add to the MDT
258 | CustomSettings = @(
259 | @{
260 | Name = "Invoke-RemoveBuiltinApps.ps1"
261 | SourcePath = "RemoveDefaultApps"
262 | }
263 | )
264 |
265 | #Custom settings and boot ini file management
266 | CustomizeIniFiles = @(
267 | @{
268 | Name = "CustomSettingsIni"
269 | Path = "\Control\CustomSettings.ini"
270 | Company = "Build Lab"
271 | TimeZoneName = "Russian Standard Time"
272 | WSUSServer = "http://fqdn:port"
273 | UserLocale = "en-US"
274 | KeyboardLocale = "en-US;ru-RU"
275 | }
276 | @{
277 | Name = "BootstrapIni"
278 | Path = "\Control\Bootstrap.ini"
279 | }
280 | )
281 |
282 | #Boot image creation and management
283 | BootImage = @(
284 | @{
285 | Version = "1.0"
286 | ExtraDirectory = "Extra"
287 | BackgroundFile = "%INSTALLDIR%\Samples\Background.bmp"
288 | LiteTouchWIMDescription = "MDT Build Lab"
289 | }
290 | )
291 | }
292 | )
293 | }
294 |
--------------------------------------------------------------------------------
/src/DSCResources/cMDTBuildTaskSequenceCustomize/cMDTBuildTaskSequenceCustomize.psm1:
--------------------------------------------------------------------------------
1 | enum Ensure
2 | {
3 | Absent
4 | Present
5 | }
6 |
7 | [DscResource()]
8 | class cMDTBuildTaskSequenceCustomize
9 | {
10 | # Task Sequence File
11 | [DscProperty(Key)]
12 | [string]$TSFile
13 |
14 | # Step name
15 | [DscProperty(Key)]
16 | [string]$Name
17 |
18 | # New step name
19 | [DscProperty()]
20 | [string]$NewName
21 |
22 | # Step type
23 | [DscProperty(Mandatory)]
24 | [string]$Type
25 |
26 | # Group for step
27 | [DscProperty(Mandatory)]
28 | [string]$GroupName
29 |
30 | # SubGroup for step
31 | [DscProperty()]
32 | [string]$SubGroup
33 |
34 | # Enable/Disable step
35 | [DscProperty()]
36 | [string]$Disable
37 |
38 | # Description
39 | [DscProperty()]
40 | [string]$Description
41 |
42 | # Add this step after that step
43 | [DscProperty()]
44 | [string]$AddAfter
45 |
46 | # TS variable name
47 | [DscProperty()]
48 | [string]$TSVarName
49 |
50 | # TS variable value
51 | [DscProperty()]
52 | [string]$TSVarValue
53 |
54 | # OS name for OS features
55 | [DscProperty()]
56 | [string]$OSName
57 |
58 | # OS features
59 | [DscProperty()]
60 | [string]$OSFeatures
61 |
62 | # Command line for 'Run Command line' step
63 | [DscProperty()]
64 | [string]$Command
65 |
66 | # Start directory for 'Run Command line' step
67 | [DscProperty()]
68 | [string]$StartIn
69 |
70 | # Command line for 'Run PowerShell Script' step
71 | [DscProperty()]
72 | [string]$PSCommand
73 |
74 | # Parameters to Pass to PS Script
75 | [DscProperty()]
76 | [string]$PSParameters
77 |
78 | # Selection profile for 'Apply Patches' step
79 | [DscProperty()]
80 | [string]$SelectionProfile
81 |
82 | [DscProperty(Mandatory)]
83 | [string]$PSDriveName
84 |
85 | [DscProperty(Mandatory)]
86 | [string]$PSDrivePath
87 |
88 | [void] Set()
89 | {
90 | $TS = $this.LoadTaskSequence()
91 |
92 | # Set node:
93 | # $group - 1st level
94 | # $AddGroup - Group to add
95 | # $Step - Step (or Group) to add
96 | # $AfterStep - Insert after this step (may be null)
97 | $group = $TS.sequence.group | Where-Object {$_.Name -eq $this.GroupName}
98 | if ($this.Type -eq "Group") {
99 | $step = $group.group | Where-Object {$_.Name -eq $this.Name}
100 | }
101 | else {
102 | $step = $group.step | Where-Object {$_.Name -eq $this.Name}
103 | }
104 |
105 | if ($this.SubGroup) {
106 | $AddGroup = $group.group | Where-Object {$_.name -eq $this.SubGroup}
107 | $AfterStep = $addGroup.step | Where-Object {$_.Name -eq $this.AddAfter}
108 | }
109 | else {
110 | $addGroup = $group
111 | $AfterStep = $group.step | Where-Object {$_.Name -eq $this.AddAfter}
112 | }
113 |
114 | if ($step) {
115 | # Change existing step or group
116 | if ($this.Disable -ne "") {
117 | $step.disable = $this.Disable
118 | }
119 | if ($this.NewName -ne "") {
120 | $step.Name = $this.NewName
121 | }
122 | if ($this.SelectionProfile -ne "") {
123 | $step.defaultVarList.variable.'#text' = $this.SelectionProfile
124 | }
125 | }
126 | else {
127 | # Create new step or group
128 | if ($this.Type -eq "Group") {
129 | $newStep = $TS.CreateElement("group")
130 | $newStep.SetAttribute("expand", "true")
131 | }
132 | else {
133 | $newStep = $TS.CreateElement("step")
134 | }
135 |
136 | # Set common attributes
137 | $newStep.SetAttribute("name", $this.Name)
138 | if ($this.Disable -ne "") {
139 | $newStep.SetAttribute("disable", $this.Disable)
140 | }
141 | else {
142 | $newStep.SetAttribute("disable", "false")
143 | }
144 | $newStep.SetAttribute("continueOnError", "false")
145 | if ($this.Description -ne "") {
146 | $newStep.SetAttribute("description", $this.Description)
147 | }
148 | else {
149 | $newStep.SetAttribute("description", "")
150 | }
151 |
152 | # Create new step
153 | switch ($this.Type) {
154 | "Set Task Sequence Variable" {
155 | $this.SetTaskSequenceVariable($TS, $newStep)
156 | }
157 | "Install Roles and Features" {
158 | $this.InstallRolesAndFeatures($TS, $newStep)
159 | }
160 | "Install Application" {
161 | $this.AddApplication($TS, $newStep)
162 | }
163 | "Run Command Line" {
164 | $this.RunCommandLine($TS, $newStep)
165 | }
166 | "Run PowerShell Script" {
167 | $this.RunPowerShellScript($TS, $newStep)
168 | }
169 | "Restart Computer" {
170 | $this.RestartComputer($TS, $newStep)
171 | }
172 | }
173 |
174 | # Insert new step into TS
175 | if ($AfterStep) {
176 | $AddGroup.InsertAfter($newStep, $AfterStep) | Out-Null
177 | }
178 | else {
179 | $AddGroup.AppendChild($newStep) | Out-Null
180 | }
181 | }
182 |
183 | $TS.Save($this.TSFile)
184 | }
185 |
186 | [bool] Test()
187 | {
188 | $TS = $this.LoadTaskSequence()
189 | $present = $false
190 |
191 | $group = $TS.sequence.group | Where-Object {$_.Name -eq $this.GroupName}
192 | if ($this.Type -eq "Group") {
193 | $step = $group.group | Where-Object {$_.Name -eq $this.Name}
194 | }
195 | else {
196 | $step = $group.step | Where-Object {$_.Name -eq $this.Name}
197 | }
198 |
199 | if (!$this.AddAfter) {
200 | if ($step) {
201 | if ($this.Disable -eq "true") {
202 | $present = ($step.disable -eq $this.Disable)
203 | }
204 | if (!$present) {
205 | if ($this.SelectionProfile -ne "") {
206 | $present = ($step.defaultVarList.variable.'#text' -eq $this.SelectionProfile)
207 | }
208 | if ($step.Name -eq "Set Product Key") {
209 | $present = ($step.defaultVarList.variable[1].'#text' -eq $this.TSVarValue)
210 | }
211 | if ($step.Name -like "Windows Update (*-Application Installation)") {
212 | $present = ($step.disable -eq $this.disable)
213 | }
214 | }
215 | }
216 | else {
217 | if ($this.NewName -ne "") {
218 | # For rename "Custom Tasks" group only
219 | $present = ( ($group.group | Where-Object {$_.Name -eq $this.NewName}) )
220 | }
221 | elseif ($this.SubGroup) {
222 | $addGroup = $group.group | Where-Object {$_.name -eq $this.SubGroup}
223 | $present = ( ($addGroup.step | Where-Object {$_.Name -eq $this.Name}) )
224 | }
225 | }
226 | }
227 | else {
228 | if ($this.Type -eq "Group") {
229 | $present = ( ($group.group | Where-Object {$_.Name -eq $this.Name}) )
230 | }
231 | else {
232 | $AddGroup = $group
233 | if ($this.SubGroup) {
234 | $AddGroup = $group.group | Where-Object {$_.name -eq $this.SubGroup}
235 | }
236 | $present = ( ($addGroup.step | Where-Object {$_.Name -eq $this.Name}) )
237 | }
238 | }
239 |
240 | return $present
241 | }
242 |
243 | [cMDTBuildTaskSequenceCustomize] Get()
244 | {
245 | return $this
246 | }
247 |
248 | [xml] LoadTaskSequence()
249 | {
250 | $tsPath = $this.TSFile
251 | $xml = [xml](Get-Content $tsPath)
252 | return $xml
253 | }
254 |
255 | [void] SetTaskSequenceVariable($TS, $Step)
256 | {
257 | $Step.SetAttribute("type", "SMS_TaskSequence_SetVariableAction")
258 | $Step.SetAttribute("successCodeList", "0 3010")
259 |
260 | $varList = $TS.CreateElement("defaultVarList")
261 | $varName = $TS.CreateElement("variable")
262 | $varName.SetAttribute("name", "VariableName")
263 | $varName.SetAttribute("property", "VariableName")
264 | $varName.AppendChild($TS.CreateTextNode($this.TSVarName)) | Out-Null
265 | $varList.AppendChild($varName) | Out-Null
266 |
267 | $varName = $TS.CreateElement("variable")
268 | $varName.SetAttribute("name", "VariableValue")
269 | $varName.SetAttribute("property", "VariableValue")
270 | $varName.AppendChild($TS.CreateTextNode($this.TSVarValue)) | Out-Null
271 | $varList.AppendChild($varName) | Out-Null
272 |
273 | $action = $TS.CreateElement("action")
274 | $action.AppendChild($TS.CreateTextNode('cscript.exe "%SCRIPTROOT%\ZTISetVariable.wsf"')) | Out-Null
275 |
276 | $Step.AppendChild($varList) | Out-Null
277 | $Step.AppendChild($action) | Out-Null
278 | }
279 |
280 | [void] InstallRolesAndFeatures($TS, $Step)
281 | {
282 | $OSIndex = @{
283 | "Windows 7" = 4
284 | "Windows 8.1" = 10
285 | "Windows 2012 R2" = 11
286 | "Windows 10" = 13
287 | "Windows 2016" = 14
288 | }
289 |
290 | $Step.SetAttribute("successCodeList", "0 3010")
291 | $Step.SetAttribute("type", "BDD_InstallRoles")
292 | $Step.SetAttribute("runIn", "WinPEandFullOS")
293 |
294 | $varList = $TS.CreateElement("defaultVarList")
295 | $varName = $TS.CreateElement("variable")
296 | $varName.SetAttribute("name", "OSRoleIndex")
297 | $varName.SetAttribute("property", "OSRoleIndex")
298 | $varName.AppendChild($TS.CreateTextNode($OSIndex.$($this.OSName))) | Out-Null
299 | $varList.AppendChild($varName) | Out-Null
300 |
301 | $varName = $TS.CreateElement("variable")
302 | $varName.SetAttribute("name", "OSRoles")
303 | $varName.SetAttribute("property", "OSRoles")
304 | $varList.AppendChild($varName) | Out-Null
305 |
306 | $varName = $TS.CreateElement("variable")
307 | $varName.SetAttribute("name", "OSRoleServices")
308 | $varName.SetAttribute("property", "OSRoleServices")
309 | $varList.AppendChild($varName) | Out-Null
310 |
311 | $varName = $TS.CreateElement("variable")
312 | $varName.SetAttribute("name", "OSFeatures")
313 | $varName.SetAttribute("property", "OSFeatures")
314 | $varName.AppendChild($TS.CreateTextNode($this.OSFeatures)) | Out-Null
315 | $varList.AppendChild($varName) | Out-Null
316 |
317 | $action = $TS.CreateElement("action")
318 | $action.AppendChild($TS.CreateTextNode('cscript.exe "%SCRIPTROOT%\ZTIOSRole.wsf"')) | Out-Null
319 |
320 | $Step.AppendChild($varList) | Out-Null
321 | $Step.AppendChild($action) | Out-Null
322 | }
323 |
324 | [void] AddApplication($TS, $Step)
325 | {
326 | $Step.SetAttribute("successCodeList", "0 3010")
327 | $Step.SetAttribute("type", "BDD_InstallApplication")
328 | $Step.SetAttribute("runIn", "WinPEandFullOS")
329 |
330 | $varList = $TS.CreateElement("defaultVarList")
331 | $varName = $TS.CreateElement("variable")
332 | $varName.SetAttribute("name", "ApplicationGUID")
333 | $varName.SetAttribute("property", "ApplicationGUID")
334 |
335 | # Get Application GUID
336 | Import-MDTModule
337 | New-PSDrive -Name $this.PSDriveName -PSProvider "MDTProvider" -Root $this.PSDrivePath -Verbose:$false | Out-Null
338 | $App = Get-ChildItem -Path "$($this.PSDriveName):\Applications" -Recurse | Where-Object { $_.Name -eq $this.Name }
339 |
340 | $varName.AppendChild($TS.CreateTextNode($($App.guid))) | Out-Null
341 | $varList.AppendChild($varName) | Out-Null
342 |
343 | $varName = $TS.CreateElement("variable")
344 | $varName.SetAttribute("name", "ApplicationSuccessCodes")
345 | $varName.SetAttribute("property", "ApplicationSuccessCodes")
346 | $varName.AppendChild($TS.CreateTextNode("0 3010")) | Out-Null
347 | $varList.AppendChild($varName) | Out-Null
348 |
349 | $action = $TS.CreateElement("action")
350 | $action.AppendChild($TS.CreateTextNode('cscript.exe "%SCRIPTROOT%\ZTIApplications.wsf"')) | Out-Null
351 |
352 | $Step.AppendChild($varList) | Out-Null
353 | $Step.AppendChild($action) | Out-Null
354 | }
355 |
356 | [void] RunCommandLine($TS, $Step)
357 | {
358 | $Step.SetAttribute("startIn", $this.StartIn)
359 | $Step.SetAttribute("successCodeList", "0 3010")
360 | $Step.SetAttribute("type", "SMS_TaskSequence_RunCommandLineAction")
361 | $Step.SetAttribute("runIn", "WinPEandFullOS")
362 |
363 | $varList = $TS.CreateElement("defaultVarList")
364 | $varName = $TS.CreateElement("variable")
365 | $varName.SetAttribute("name", "PackageID")
366 | $varName.SetAttribute("property", "PackageID")
367 | $varList.AppendChild($varName) | Out-Null
368 |
369 | $varName = $TS.CreateElement("variable")
370 | $varName.SetAttribute("name", "RunAsUser")
371 | $varName.SetAttribute("property", "RunAsUser")
372 | $varName.AppendChild($TS.CreateTextNode("false")) | Out-Null
373 | $varList.AppendChild($varName) | Out-Null
374 |
375 | $varName = $TS.CreateElement("variable")
376 | $varName.SetAttribute("name", "SMSTSRunCommandLineUserName")
377 | $varName.SetAttribute("property", "SMSTSRunCommandLineUserName")
378 | $varList.AppendChild($varName) | Out-Null
379 |
380 | $varName = $TS.CreateElement("variable")
381 | $varName.SetAttribute("name", "SMSTSRunCommandLineUserPassword")
382 | $varName.SetAttribute("property", "SMSTSRunCommandLineUserPassword")
383 | $varList.AppendChild($varName) | Out-Null
384 |
385 | $varName = $TS.CreateElement("variable")
386 | $varName.SetAttribute("name", "LoadProfile")
387 | $varName.SetAttribute("property", "LoadProfile")
388 | $varName.AppendChild($TS.CreateTextNode("false")) | Out-Null
389 | $varList.AppendChild($varName) | Out-Null
390 |
391 | $action = $TS.CreateElement("action")
392 | $action.AppendChild($TS.CreateTextNode($this.Command)) | Out-Null
393 |
394 | $Step.AppendChild($varList) | Out-Null
395 | $Step.AppendChild($action) | Out-Null
396 | }
397 |
398 | [void] RunPowerShellScript($TS, $Step)
399 | {
400 | $Step.SetAttribute("successCodeList", "0 3010")
401 | $Step.SetAttribute("type", "BDD_RunPowerShellAction")
402 |
403 | $varList = $TS.CreateElement("defaultVarList")
404 | $varName = $TS.CreateElement("variable")
405 | $varName.SetAttribute("name", "ScriptName")
406 | $varName.SetAttribute("property", "ScriptName")
407 | $varName.AppendChild($TS.CreateTextNode($this.PSCommand)) | Out-Null
408 |
409 | $varList.AppendChild($varName) | Out-Null
410 | $varName = $TS.CreateElement("variable")
411 | $varName.SetAttribute("name", "Parameters")
412 | $varName.SetAttribute("property", "Parameters")
413 | $varName.AppendChild($TS.CreateTextNode($this.PSParameters)) | Out-Null
414 | $varList.AppendChild($varName) | Out-Null
415 |
416 | $varName = $TS.CreateElement("variable")
417 | $varName.SetAttribute("name", "PackageID")
418 | $varName.SetAttribute("property", "PackageID")
419 | $varList.AppendChild($varName) | Out-Null
420 |
421 | $action = $TS.CreateElement("action")
422 | $action.AppendChild($TS.CreateTextNode('cscript.exe "%SCRIPTROOT%\ZTIPowerShell.wsf"')) | Out-Null
423 |
424 | $Step.AppendChild($varList) | Out-Null
425 | $Step.AppendChild($action) | Out-Null
426 | }
427 |
428 | [void] RestartComputer($TS, $Step)
429 | {
430 | $Step.SetAttribute("successCodeList", "0 3010")
431 | $Step.SetAttribute("type", "SMS_TaskSequence_RebootAction")
432 | $Step.SetAttribute("runIn", "WinPEandFullOS")
433 |
434 | $varList = $TS.CreateElement("defaultVarList")
435 | $varName = $TS.CreateElement("variable")
436 | $varName.SetAttribute("name", "Message")
437 | $varName.SetAttribute("property", "Message")
438 | $varList.AppendChild($varName) | Out-Null
439 |
440 | $varName = $TS.CreateElement("variable")
441 | $varName.SetAttribute("name", "MessageTimeout")
442 | $varName.SetAttribute("property", "MessageTimeout")
443 | $varName.AppendChild($TS.CreateTextNode("60")) | Out-Null
444 | $varList.AppendChild($varName) | Out-Null
445 |
446 | $varName = $TS.CreateElement("variable")
447 | $varName.SetAttribute("name", "Target")
448 | $varName.SetAttribute("property", "Target")
449 | $varList.AppendChild($varName) | Out-Null
450 |
451 | $action = $TS.CreateElement("action")
452 | $action.AppendChild($TS.CreateTextNode("smsboot.exe /target:WinPE")) | Out-Null
453 |
454 | $Step.AppendChild($varList) | Out-Null
455 | $Step.AppendChild($action) | Out-Null
456 | }
457 | }
458 |
459 |
--------------------------------------------------------------------------------
/src/Deploy/ImageFactoryV3-Build.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | ImageFactory 3.2
4 | .DESCRIPTION
5 | Run this script for build Windows Reference Images on remote Hyper-V host
6 | .EXAMPLE
7 | Edit config ImageFactoryV3.xml with your settings:
8 |
9 |
10 | AutoBuild@build.lab
11 | AutoBuild@build.lab
12 | smtp.build.lab
13 |
14 | E:\MDTBuildLab
15 | REF
16 |
17 |
18 | 4
19 | 0
20 | HV01
21 | Network Switch
22 | E:\Build
23 | E:\Build\ISO
24 | 60
25 | 2
26 |
27 |
28 |
29 | Run ImageFactoryV3-Build.ps1 at MDT host
30 | .NOTES
31 | Created: 2016-11-24
32 | Version: 3.1
33 | Updated: 2017-02-23
34 | Version: 3.2
35 |
36 | Author : Mikael Nystrom
37 | Twitter: @mikael_nystrom
38 | Blog : http://deploymentbunny.com
39 |
40 | Disclaimer:
41 | This script is provided 'AS IS' with no warranties, confers no rights and
42 | is not supported by the author.
43 |
44 | Modyfy : Pavel Andreev
45 | E-mail : pvs043@outlook.com
46 | Date : 2017-02-27
47 | Project: cMDTBuildLab (https://github.com/pvs043/cMDTBuildLab/wiki)
48 |
49 | Changes:
50 | * Remove dependency for PsIni module
51 | * Remove cleaning of MDT Captures folder: each new captured WIM is builded with timestamp date at file name for history tracking,
52 | you can delete or move old images from external scripts
53 | * Run Reference VMs as Job at Hyper-V host: it's faster
54 | * Remove "ConcurrentRunningVMs" param from config: cMDTBuildLab build maximum to 8 concurrent VMs.
55 | Tune need count with count of reference Task Sequences in the REF folder
56 | * Remove cleaning of CustomSettings.ini after build: this is a job for DSC configuration.
57 | Configure DSCLocalConfigurationManager on MDT server with
58 | ConfigurationMode = "ApplyAndAutoCorrect"
59 | ConfigurationModeFrequencyMins = 60
60 | * Possibility of sending build results to E-mail
61 |
62 | .LINK
63 | http://www.deploymentbunny.com
64 | https://github.com/pvs043/cMDTBuildLab/wiki
65 | #>
66 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseUsingScopeModifierInNewRunspaces')]
67 |
68 | [cmdletbinding(SupportsShouldProcess=$True)]
69 |
70 | Param(
71 | [parameter(mandatory=$false)]
72 | [ValidateSet($True,$False)]
73 | $UpdateBootImage = $False
74 | )
75 |
76 | Function Get-VIARefTaskSequence
77 | {
78 | Param(
79 | $RefTaskSequenceFolder
80 | )
81 | $RefTaskSequences = Get-ChildItem $RefTaskSequenceFolder
82 |
83 | Foreach ($RefTaskSequence in $RefTaskSequences) {
84 | New-Object PSObject -Property @{
85 | TaskSequenceID = $RefTaskSequence.ID
86 | Name = $RefTaskSequence.Name
87 | Comments = $RefTaskSequence.Comments
88 | Version = $RefTaskSequence.Version
89 | Enabled = $RefTaskSequence.enable
90 | LastModified = $RefTaskSequence.LastModifiedTime
91 | }
92 | }
93 | }
94 |
95 | Function Test-VIAHypervConnection
96 | {
97 | Param(
98 | $Computername,
99 | $ISOFolder,
100 | $VMFolder,
101 | $VMSwitchName
102 | )
103 |
104 | #Verify SMB access
105 | $Result = Test-NetConnection -ComputerName $Computername -CommonTCPPort SMB
106 | If ($Result.TcpTestSucceeded -eq $true) {Write-Verbose "SMB Connection to $Computername is ok"} else {Write-Warning "SMB Connection to $Computername is NOT ok"; Return $False}
107 |
108 | #Verify WinRM access
109 | $Result = Test-NetConnection -ComputerName $Computername -CommonTCPPort WINRM
110 | If ($Result.TcpTestSucceeded -eq $true) {Write-Verbose "WINRM Connection to $Computername is ok"} else {Write-Warning "WINRM Connection to $Computername is NOT ok"; Return $False}
111 |
112 | #Verify that Microsoft-Hyper-V-Management-PowerShell is installed
113 | Invoke-Command -ComputerName $Computername -ScriptBlock {
114 | $Result = (Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-Management-PowerShell)
115 | Write-Verbose "$($Result.DisplayName) is $($Result.State)"
116 | If ($($Result.State) -ne "Enabled") {Write-Warning "$($Result.DisplayName) is not Enabled"; Return $False}
117 | }
118 |
119 | #Verify that Microsoft-Hyper-V-Management-PowerShell is installed
120 | Invoke-Command -ComputerName $Computername -ScriptBlock {
121 | $Result = (Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V)
122 | If ($($Result.State) -ne "Enabled") {Write-Warning "$($Result.DisplayName) is not Enabled"; Return $False}
123 | }
124 |
125 | #Verify that Hyper-V is running
126 | Invoke-Command -ComputerName $Computername -ScriptBlock {
127 | $Result = (Get-Service -Name vmms)
128 | Write-Verbose "$($Result.DisplayName) is $($Result.Status)"
129 | If ($($Result.Status) -ne "Running") {Write-Warning "$($Result.DisplayName) is not Running"; Return $False}
130 | }
131 |
132 | #Verify that the ISO Folder is created
133 | Invoke-Command -ComputerName $Computername -ScriptBlock {
134 | Param(
135 | $ISOFolder
136 | )
137 | New-Item -Path $ISOFolder -ItemType Directory -Force | Out-Null
138 | } -ArgumentList $ISOFolder
139 |
140 | #Verify that the VM Folder is created
141 | Invoke-Command -ComputerName $Computername -ScriptBlock {
142 | Param(
143 | $VMFolder
144 | )
145 | New-Item -Path $VMFolder -ItemType Directory -Force | Out-Null
146 | } -ArgumentList $VMFolder
147 |
148 | #Verify that the VMSwitch exists
149 | Invoke-Command -ComputerName $Computername -ScriptBlock {
150 | Param(
151 | $VMSwitchName
152 | )
153 | if (((Get-VMSwitch | Where-Object -Property Name -EQ -Value $VMSwitchName).count) -eq "1") {Write-Verbose "Found $VMSwitchName"} else {Write-Warning "No swtch with the name $VMSwitchName found"; Return $False}
154 | } -ArgumentList $VMSwitchName
155 | Return $true
156 | }
157 |
158 | Function Update-Log
159 | {
160 | [cmdletbinding(SupportsShouldProcess=$True)]
161 |
162 | Param(
163 | [Parameter(
164 | Mandatory=$true,
165 | ValueFromPipeline=$true,
166 | ValueFromPipelineByPropertyName=$true,
167 | Position=0
168 | )]
169 | [string]$Data,
170 |
171 | [Parameter(
172 | Mandatory=$false,
173 | ValueFromPipeline=$true,
174 | ValueFromPipelineByPropertyName=$true,
175 | Position=0
176 | )]
177 | [string]$Solution = $Solution,
178 |
179 | [Parameter(
180 | Mandatory=$false,
181 | ValueFromPipeline=$true,
182 | ValueFromPipelineByPropertyName=$true,
183 | Position=1
184 | )]
185 | [validateset('Information','Warning','Error')]
186 | [string]$Class = "Information"
187 |
188 | )
189 |
190 | process {
191 | $LogString = "$Solution, $Data, $Class, $(Get-Date)"
192 | $HostString = "$Solution, $Data, $(Get-Date)"
193 |
194 | Add-Content -Path $Log -Value $LogString
195 | switch ($Class)
196 | {
197 | 'Information'{
198 | Write-Output $HostString
199 | }
200 | 'Warning'{
201 | Write-Warning $HostString
202 | }
203 | 'Error'{
204 | Write-Error $HostString
205 | }
206 | Default {}
207 | }
208 | }
209 | }
210 |
211 | #Inititial Settings
212 | Clear-Host
213 | $Log = "$($PSScriptRoot)\ImageFactoryV3ForHyper-V.log"
214 | $XMLFile = "$($PSScriptRoot)\ImageFactoryV3.xml"
215 | $Solution = "IMF32"
216 | Update-Log -Data "Imagefactory 3.2 (Hyper-V)"
217 | Update-Log -Data "Logfile is $Log"
218 | Update-Log -Data "XMLfile is $XMLfile"
219 |
220 | #Importing modules
221 | Update-Log -Data "Importing modules"
222 | Import-Module 'C:\Program Files\Microsoft Deployment Toolkit\Bin\MicrosoftDeploymentToolkit.psd1' -ErrorAction Stop -WarningAction Stop
223 |
224 | #Read Settings from XML
225 | Update-Log -Data "Reading from $XMLFile"
226 | [xml]$Settings = Get-Content $XMLFile -ErrorAction Stop -WarningAction Stop
227 |
228 | #Verify Connection to DeploymentRoot
229 | Update-Log -Data "Verify Connection to DeploymentRoot"
230 | $Result = Test-Path -Path $Settings.Settings.MDT.DeploymentShare
231 | If ($Result -ne $true) {Update-Log -Data "Cannot access $($Settings.Settings.MDT.DeploymentShare), will break"; break}
232 |
233 | #Connect to MDT
234 | Update-Log -Data "Connect to MDT"
235 | $Root = $Settings.Settings.MDT.DeploymentShare
236 | if ( !(Get-PSDrive -Name 'MDTBuild' -ErrorAction SilentlyContinue) ) {
237 | $MDTPSDrive = New-PSDrive -Name MDTBuild -PSProvider MDTProvider -Root $Root -ErrorAction Stop
238 | Update-Log -Data "Connected to $($MDTPSDrive.Root)"
239 | }
240 |
241 | #Get MDT Settings
242 | Update-Log -Data "Get MDT Settings"
243 | $MDTSettings = Get-ItemProperty 'MDTBuild:'
244 |
245 | #Check if we should update the boot image
246 | Update-Log -Data "Check if we should update the boot image"
247 | If($UpdateBootImage -eq $True){
248 | #Update boot image
249 | Update-Log -Data "Updating boot image, please wait"
250 | Update-MDTDeploymentShare -Path MDT: -ErrorAction Stop
251 | }
252 |
253 | #Verify access to boot image
254 | Update-Log -Data "Verify access to boot image"
255 | $MDTImage = $($Settings.Settings.MDT.DeploymentShare) + "\boot\" + $($MDTSettings.'Boot.x86.LiteTouchISOName')
256 | if((Test-Path -Path $MDTImage) -eq $true) {Update-Log -Data "Access to $MDTImage is ok"} else {Write-Warning "Could not access $MDTImage"; BREAK}
257 |
258 | #Get TaskSequences
259 | Update-Log -Data "Get TaskSequences"
260 | $RefTaskSequences = Get-VIARefTaskSequence -RefTaskSequenceFolder "MDTBuild:\Task Sequences\$($Settings.Settings.MDT.RefTaskSequenceFolderName)" | where-object Enabled -eq $true
261 |
262 | #Get TaskSequencesIDs
263 | $RefTaskSequenceIDs = $RefTaskSequences.TasksequenceID
264 | Update-Log -Data "Found $($RefTaskSequenceIDs.count) TaskSequences to work on"
265 |
266 | #check task sequence count
267 | if ($RefTaskSequenceIDs.count -eq 0) {
268 | Update-Log -Data "Sorry, could not find any TaskSequences to work with"
269 | BREAK
270 | }
271 |
272 | #Get detailed info
273 | Update-Log -Data "Get detailed info about the task sequences"
274 | $Result = Get-VIARefTaskSequence -RefTaskSequenceFolder "MDTBuild:\Task Sequences\$($Settings.Settings.MDT.RefTaskSequenceFolderName)" | Where-Object Enabled -eq $true
275 | foreach($obj in ($Result | Select-Object TaskSequenceID,Name,Version)){
276 | $data = "$($obj.TaskSequenceID) $($obj.Name) $($obj.Version)"
277 | Update-Log -Data $data
278 | }
279 |
280 | #Verify Connection to Hyper-V host
281 | Update-Log -Data "Verify Connection to Hyper-V host"
282 | $Result = Test-VIAHypervConnection -Computername $Settings.Settings.HyperV.Computername -ISOFolder $Settings.Settings.HyperV.ISOLocation -VMFolder $Settings.Settings.HyperV.VMLocation -VMSwitchName $Settings.Settings.HyperV.SwitchName
283 | If ($Result -ne $true) {Update-Log -Data "$($Settings.Settings.HyperV.Computername) is not ready, will break"; break}
284 |
285 | #Upload boot image to Hyper-V host
286 | Update-Log -Data "Upload boot image to Hyper-V host"
287 | $DestinationFolder = "\\" + $($Settings.Settings.HyperV.Computername) + "\" + $($Settings.Settings.HyperV.ISOLocation -replace ":","$")
288 | Copy-Item -Path $MDTImage -Destination $DestinationFolder -Force
289 |
290 | #Create the VM's on Host
291 | Update-Log -Data "Create the VM's on Host"
292 | Foreach ($Ref in $RefTaskSequenceIDs) {
293 | $VMName = $ref
294 | $VMMemory = [int]$($Settings.Settings.HyperV.StartUpRAM) * 1GB
295 | $VMPath = $($Settings.Settings.HyperV.VMLocation)
296 | $VMBootimage = $($Settings.Settings.HyperV.ISOLocation) + "\" + $($MDTImage | Split-Path -Leaf)
297 | $VMVHDSize = [int]$($Settings.Settings.HyperV.VHDSize) * 1GB
298 | $VMVlanID = $($Settings.Settings.HyperV.VLANID)
299 | $VMVCPU = $($Settings.Settings.HyperV.NoCPU)
300 | $VMSwitch = $($Settings.Settings.HyperV.SwitchName)
301 |
302 | Invoke-Command -ComputerName $($Settings.Settings.HyperV.Computername) -ScriptBlock {
303 | Param(
304 | $VMName,
305 | $VMMemory,
306 | $VMPath,
307 | $VMBootimage,
308 | $VMVHDSize,
309 | $VMVlanID,
310 | $VMVCPU,
311 | $VMSwitch
312 | )
313 |
314 | Write-Verbose "Hyper-V host is $env:COMPUTERNAME"
315 | Write-Verbose "Working on $VMName"
316 | #Check if VM exist
317 | if (!((Get-VM | Where-Object -Property Name -EQ -Value $VMName).count -eq 0)) {Write-Warning -Message "VM exist"; Break}
318 |
319 | #Create VM
320 | $VM = New-VM -Name $VMName -MemoryStartupBytes $VMMemory -Path $VMPath -NoVHD -Generation 1
321 | Write-Verbose "$($VM.Name) is created"
322 |
323 | #Disable dynamic memory
324 | Set-VMMemory -VM $VM -DynamicMemoryEnabled $false
325 | Write-Verbose "Dynamic memory is disabled on $($VM.Name)"
326 |
327 | #Connect to VMSwitch
328 | Connect-VMNetworkAdapter -VMNetworkAdapter (Get-VMNetworkAdapter -VM $VM) -SwitchName $VMSwitch
329 | Write-Verbose "$($VM.Name) is connected to $VMSwitch"
330 |
331 | #Set vCPU
332 | if ($VMVCPU -ne "1") {
333 | $Result = Set-VMProcessor -Count $VMVCPU -VM $VM -Passthru
334 | Write-Verbose "$($VM.Name) has $($Result.count) vCPU"
335 | }
336 |
337 | #Set VLAN
338 | If ($VMVlanID -ne "0") {
339 | $Result = Set-VMNetworkAdapterVlan -VlanId $VMVlanID -Access -VM $VM -Passthru
340 | Write-Verbose "$($VM.Name) is configured for VLANid $($Result.NativeVlanId)"
341 | }
342 |
343 | #Create empty disk
344 | $VHD = $VMName + ".vhdx"
345 | $result = New-VHD -Path "$VMPath\$VMName\Virtual Hard Disks\$VHD" -SizeBytes $VMVHDSize -Dynamic -ErrorAction Stop
346 | Write-Verbose "$($result.Path) is created for $($VM.Name)"
347 |
348 | #Add VHDx
349 | $result = Add-VMHardDiskDrive -VMName $VMName -Path "$VMPath\$VMName\Virtual Hard Disks\$VHD" -Passthru
350 | Write-Verbose "$($result.Path) is attached to $VMName"
351 |
352 | #Connect ISO
353 | $result = Set-VMDvdDrive -VMName $VMName -Path $VMBootimage -Passthru
354 | Write-Verbose "$($result.Path) is attached to $VMName"
355 |
356 | #Set Notes
357 | Set-VM -VMName $VMName -Notes "REFIMAGE"
358 |
359 | } -ArgumentList $VMName,$VMMemory,$VMPath,$VMBootimage,$VMVHDSize,$VMVlanID,$VMVCPU,$VMSwitch
360 | }
361 |
362 | #Get BIOS Serialnumber from each VM and update the customsettings.ini file
363 | Update-Log -Data "Get BIOS Serialnumber from each VM and update the customsettings.ini file"
364 | $IniFile = "$($Settings.settings.MDT.DeploymentShare)\Control\CustomSettings.ini"
365 |
366 | Foreach($Ref in $RefTaskSequenceIDs) {
367 | #Get BIOS Serailnumber from the VM
368 | $BIOSSerialNumber = Invoke-Command -ComputerName $($Settings.Settings.HyperV.Computername) -ScriptBlock {
369 | Param(
370 | $VMName
371 | )
372 | #$VMObject = Get-CimInstance -Namespace root\virtualization\v2 -ClassName Msvm_ComputerSystem | Where-Object {$_.ElementName -eq $VMName}
373 | #(Get-CimAssociatedInstance $VMObject | Where-Object {$_.Caption -eq 'BIOS'}).SerialNumber
374 | # PSSCriptAnalyzer warning, but work
375 | $VMObject = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "ElementName = '$VMName'"
376 | $VMObject.GetRelated('Msvm_VirtualSystemSettingData').BIOSSerialNumber
377 | } -ArgumentList $Ref
378 |
379 | #Update CustomSettings.ini
380 | $CustomSettings = Get-Content -Path $IniFile
381 |
382 | $CustomSettings += "
383 | [$BIOSSerialNumber]
384 | OSDComputerName=$Ref
385 | TaskSequenceID=$Ref
386 | BackupFile=#left(""$Ref"", len(""$Ref"")-3) & year(date) & right(""0"" & month(date), 2) & right(""0"" & day(date), 2)#.wim
387 | DoCapture=YES
388 | SkipTaskSequence=YES
389 | SkipCapture=YES"
390 | Set-Content -Path $IniFile -Value $CustomSettings
391 | }
392 |
393 | #Test for CustomSettings.ini changes
394 | #Read-Host -Prompt "Waiting"
395 |
396 | #Start VM's on Host
397 | Update-Log -Data "Start VM's on Host"
398 | Foreach ($Ref in $RefTaskSequences) {
399 | $VMName = $Ref.TasksequenceID
400 | $ImageName = $Ref.Name
401 | $ReportFrom = $($Settings.Settings.ReportFrom)
402 | $ReportTo = $($Settings.Settings.ReportTo)
403 | $ReportSmtp = $($Settings.Settings.ReportSmtp)
404 |
405 | Invoke-Command -ComputerName $($Settings.Settings.HyperV.Computername) -ScriptBlock {
406 | param(
407 | $VMName,
408 | $ImageName,
409 | $ReportFrom,
410 | $ReportTo,
411 | $ReportSmtp
412 | )
413 |
414 | Write-Output "Starting VM: $($VmName)"
415 | Start-VM -Name $VMName
416 | Start-Sleep 60
417 | $VM = Get-VM -Name $VMName
418 | $StartTime = Get-Date
419 | while ($VM.State -eq "Running") {
420 | Start-Sleep "90"
421 | $VM = Get-VM -Name $VMName
422 | }
423 | $EndTime = Get-Date
424 | $ElapsedTime = $EndTime - $StartTime
425 | $hours = [math]::floor($ElapsedTime.TotalHours)
426 | $mins = [int]$ElapsedTime.TotalMinutes - $hours*60
427 | $report = "Image [$ImageName] was builded at $hours h. $mins min."
428 | Write-Output $report
429 |
430 | # Send Report
431 | If ($ReportFrom -and $ReportTo -and $ReportSmtp) {
432 | $subject = "Image $ImageName"
433 | $encoding = [System.Text.Encoding]::UTF8
434 | Send-MailMessage -From $ReportFrom -To $ReportTo -Subject $subject -SmtpServer $ReportSmtp -Encoding $encoding -BodyAsHtml $report
435 | }
436 |
437 | # Remove reference VM
438 | Write-Output "Deleting $($VM.Name) on $($VM.Computername) at $($VM.ConfigurationLocation)"
439 | Remove-VM -VM $VM -Force
440 | Remove-Item -Path $VM.ConfigurationLocation -Recurse -Force
441 |
442 | } -ArgumentList $VMName,$ImageName,$ReportFrom,$ReportTo,$ReportSmtp -AsJob -JobName $VMName
443 | }
444 |
--------------------------------------------------------------------------------
/src/Deploy/Deploy_MDT_Server.ps1:
--------------------------------------------------------------------------------
1 | Configuration DeployMDTServerContract
2 | {
3 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingCmdletAliases')]
4 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments')]
5 |
6 | Param(
7 | [Parameter(Mandatory=$true, HelpMessage = "Enter password for MDT Account")]
8 | [PSCredential]$Credentials
9 | )
10 |
11 | Import-Module -Name PSDesiredStateConfiguration, xSmbShare, cNtfsAccessControl, cMDTBuildLab
12 | Import-DscResource -ModuleName PSDesiredStateConfiguration
13 | Import-DscResource -ModuleName xSmbShare
14 | Import-DscResource -ModuleName cNtfsAccessControl
15 | Import-DscResource -ModuleName cMDTBuildLab
16 |
17 | node $AllNodes.Where{$_.Role -match "MDT Server"}.NodeName
18 | {
19 | LocalConfigurationManager {
20 | RebootNodeIfNeeded = $AllNodes.RebootNodeIfNeeded
21 | ConfigurationMode = $AllNodes.ConfigurationMode
22 | ConfigurationModeFrequencyMins = $AllNodes.ConfigurationModeFrequencyMins
23 | RefreshFrequencyMins = $AllNodes.RefreshFrequencyMins
24 | }
25 |
26 | cMDTBuildPreReqs MDTPreReqs {
27 | DownloadPath = $Node.SourcePath
28 | }
29 |
30 | $DomainUser = ($Credentials.UserName).Split('\\')
31 | if ($DomainUser.Count -eq 1) {
32 | $MDTUserDomain = $Node.NodeName
33 | $MDTUserName = $Credentials.UserName
34 | User MDTAccessAccount {
35 | Ensure = "Present"
36 | UserName = $MDTUserName
37 | FullName = $MDTUserName
38 | Password = $Credentials
39 | PasswordChangeRequired = $false
40 | PasswordNeverExpires = $true
41 | Description = "Managed Client Administrator Account"
42 | Disabled = $false
43 | }
44 | }
45 | else {
46 | $MDTUserDomain = $DomainUser[0]
47 | $MDTUserName = $DomainUser[1]
48 | }
49 |
50 | WindowsFeature DataDeduplication {
51 | Ensure = "Present"
52 | Name = "FS-Data-Deduplication"
53 | }
54 |
55 | Package ADK {
56 | Ensure = "Present"
57 | Name = "Windows Assessment and Deployment Kit - Windows 10"
58 | Path = "$($Node.SourcePath)\ADK\adksetup.exe"
59 | ProductId = "9346016b-6620-4841-8ea4-ad91d3ea02b5"
60 | Arguments = "/Features OptionId.DeploymentTools /norestart /quiet /ceip off"
61 | ReturnCode = 0
62 | DependsOn = "[cMDTBuildPreReqs]MDTPreReqs"
63 | }
64 |
65 | Package WinPE {
66 | Ensure = "Present"
67 | Name = "Windows Assessment and Deployment Kit Windows Preinstallation Environment Add-ons - Windows 10"
68 | Path = "$($Node.SourcePath)\WindowsPE\adkwinpesetup.exe"
69 | ProductId = "353df250-4ecc-4656-a950-4df93078a5fd"
70 | Arguments = "/Features OptionId.WindowsPreinstallationEnvironment /norestart /quiet /ceip off"
71 | ReturnCode = 0
72 | DependsOn = "[cMDTBuildPreReqs]MDTPreReqs"
73 | }
74 |
75 | Package MDT {
76 | Ensure = "Present"
77 | Name = "Microsoft Deployment Toolkit (6.3.8456.1000)"
78 | Path = "$($Node.SourcePath)\MDT\MicrosoftDeploymentToolkit_x64.msi"
79 | ProductId = "2E6CD7B9-9D00-4B04-882F-E6971BC9A763"
80 | ReturnCode = 0
81 | DependsOn = '[Package]ADK','[Package]WinPE'
82 | }
83 |
84 | File KB4564442x86
85 | {
86 | Ensure = "Present"
87 | SourcePath = "$($Node.SourcePath)\KB4564442\x86\microsoft.bdd.utility.dll"
88 | DestinationPath = "%ProgramFiles%\Microsoft Deployment Toolkit\Templates\Distribution\Tools\x86\microsoft.bdd.utility.dll"
89 | Checksum = "SHA-256"
90 | Force = $true
91 | DependsOn = "[Package]MDT"
92 | }
93 |
94 | File KB4564442x64
95 | {
96 | Ensure = "Present"
97 | SourcePath = "$($Node.SourcePath)\KB4564442\x64\microsoft.bdd.utility.dll"
98 | DestinationPath = "%ProgramFiles%\Microsoft Deployment Toolkit\Templates\Distribution\Tools\x64\microsoft.bdd.utility.dll"
99 | Checksum = "SHA-256"
100 | Force = $true
101 | DependsOn = "[Package]MDT"
102 | }
103 |
104 | cMDTBuildDirectory DeploymentFolder {
105 | Name = $Node.PSDrivePath.Replace("$($Node.PSDrivePath.Substring(0,2))\","")
106 | Path = $Node.PSDrivePath.Substring(0,2)
107 | DependsOn = "[Package]MDT"
108 | }
109 |
110 | # Set FullAccess rights for MDT Deployment Share to Everyone
111 | $objSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-1-0")
112 | $objUser = $objSID.Translate([System.Security.Principal.NTAccount])
113 | $userName = $objUser.Value
114 | xSmbShare FolderDeploymentShare {
115 | Ensure = "Present"
116 | Name = $Node.PSDriveShareName
117 | Path = $Node.PSDrivePath
118 | FullAccess = $userName
119 | FolderEnumerationMode = "AccessBased"
120 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
121 | }
122 |
123 | cMDTBuildPersistentDrive DeploymentPSDrive {
124 | Name = $Node.PSDriveName
125 | Path = $Node.PSDrivePath
126 | Description = $Node.PSDrivePath.Replace("$($Node.PSDrivePath.Substring(0,2))\","")
127 | NetworkPath = "\\$($Node.NodeName)\$($Node.PSDriveShareName)"
128 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
129 | }
130 |
131 | ForEach ($OSDirectory in $Node.OSDirectories) {
132 | [string]$OSVersion = ""
133 | $OSDirectory.GetEnumerator() | % {
134 | If ($_.key -eq "OperatingSystem") { $OSVersion = $_.value }
135 | }
136 |
137 | cMDTBuildDirectory $OSVersion.Replace(' ','') {
138 | Name = $OSVersion
139 | Path = "$($Node.PSDriveName):\Operating Systems"
140 | PSDriveName = $Node.PSDriveName
141 | PSDrivePath = $Node.PSDrivePath
142 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
143 | }
144 |
145 | cMDTBuildDirectory "TS$($OSVersion.Replace(' ',''))" {
146 | Name = $OSVersion
147 | Path = "$($Node.PSDriveName):\Task Sequences"
148 | PSDriveName = $Node.PSDriveName
149 | PSDrivePath = $Node.PSDrivePath
150 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
151 | }
152 | }
153 |
154 | # Task Sequence folder for autobuild
155 | cMDTBuildDirectory "TSREF" {
156 | Name = "REF"
157 | Path = "$($Node.PSDriveName):\Task Sequences"
158 | PSDriveName = $Node.PSDriveName
159 | PSDrivePath = $Node.PSDrivePath
160 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
161 | }
162 |
163 | ForEach ($PkgFolder in $Node.PackagesFolderStructure) {
164 | [string]$Folder = ""
165 | $PkgFolder.GetEnumerator() | % {
166 | If ($_.key -eq "Folder") { $Folder = $_.value }
167 | }
168 |
169 | cMDTBuildDirectory "PKG$($Folder.Replace(' ',''))" {
170 | Name = $Folder
171 | Path = "$($Node.PSDriveName):\Packages"
172 | PSDriveName = $Node.PSDriveName
173 | PSDrivePath = $Node.PSDrivePath
174 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
175 | }
176 | }
177 |
178 | ForEach ($CurrentApplicationFolder in $Node.ApplicationFolderStructure) {
179 | [string]$ApplicationFolder = ""
180 | $CurrentApplicationFolder.GetEnumerator() | % {
181 | If ($_.key -eq "Folder") { $ApplicationFolder = $_.value }
182 | }
183 |
184 | cMDTBuildDirectory "AF$($ApplicationFolder.Replace(' ',''))" {
185 | Name = $ApplicationFolder
186 | Path = "$($Node.PSDriveName):\Applications"
187 | PSDriveName = $Node.PSDriveName
188 | PSDrivePath = $Node.PSDrivePath
189 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
190 | }
191 |
192 | ForEach ($CurrentApplicationSubFolder in $CurrentApplicationFolder.SubFolders) {
193 | [string]$ApplicationSubFolder = ""
194 | $CurrentApplicationSubFolder.GetEnumerator() | % {
195 | If ($_.key -eq "SubFolder") { $ApplicationSubFolder = $_.value }
196 | }
197 |
198 | cMDTBuildDirectory "ASF$($ApplicationSubFolder.Replace(' ',''))" {
199 | Name = $ApplicationSubFolder
200 | Path = "$($Node.PSDriveName):\Applications\$ApplicationFolder"
201 | PSDriveName = $Node.PSDriveName
202 | PSDrivePath = $Node.PSDrivePath
203 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
204 | }
205 | }
206 | }
207 |
208 | ForEach ($SelectionProfile in $Node.SelectionProfiles) {
209 | [string]$Name = ""
210 | [string]$Comments = ""
211 | [string]$IncludePath = ""
212 | $SelectionProfile.GetEnumerator() | % {
213 | If ($_.key -eq "Name") { $Name = $_.value }
214 | If ($_.key -eq "Comments") { $Comments = $_.value }
215 | If ($_.key -eq "IncludePath") { $IncludePath = $_.value }
216 | }
217 |
218 | cMDTBuildSelectionProfile $Name.Replace(' ','') {
219 | Name = $Name
220 | Comments = $Comments
221 | IncludePath = $IncludePath
222 | PSDriveName = $Node.PSDriveName
223 | PSDrivePath = $Node.PSDrivePath
224 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
225 | }
226 | }
227 |
228 | ForEach ($OperatingSystem in $Node.OperatingSystems) {
229 | [string]$Name = ""
230 | [string]$Path = ""
231 | [string]$SourcePath = ""
232 |
233 | $OperatingSystem.GetEnumerator() | % {
234 | If ($_.key -eq "Name") { $Name = $_.value }
235 | If ($_.key -eq "Path") { $Path = "$($Node.PSDriveName):\Operating Systems\$($_.value)" }
236 | If ($_.key -eq "SourcePath") { $SourcePath = "$($Node.SourcePath)\$($_.value)" }
237 | }
238 |
239 | cMDTBuildOperatingSystem $Name.Replace(' ','') {
240 | Name = $Name
241 | Path = $Path
242 | SourcePath = $SourcePath
243 | PSDriveName = $Node.PSDriveName
244 | PSDrivePath = $Node.PSDrivePath
245 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
246 | }
247 | }
248 |
249 | ForEach ($Application in $Node.Applications) {
250 | [string]$Name = ""
251 | [string]$Path = ""
252 | [string]$CommandLine = ""
253 | [string]$ApplicationSourcePath = ""
254 |
255 | $Application.GetEnumerator() | % {
256 | If ($_.key -eq "Name") { $Name = $_.value }
257 | If ($_.key -eq "Path") { $Path = "$($Node.PSDriveName):$($_.value)" }
258 | If ($_.key -eq "CommandLine") { $CommandLine = $_.value }
259 | If ($_.key -eq "ApplicationSourcePath") { $ApplicationSourcePath = "$($Node.SourcePath)\$($_.value)" }
260 | }
261 |
262 | cMDTBuildApplication $Name.Replace(' ','') {
263 | Name = $Name
264 | Path = $Path
265 | CommandLine = $CommandLine
266 | ApplicationSourcePath = $ApplicationSourcePath
267 | Enabled = "True"
268 | PSDriveName = $Node.PSDriveName
269 | PSDrivePath = $Node.PSDrivePath
270 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
271 | }
272 | }
273 |
274 | ForEach ($Package in $Node.Packages) {
275 | [string]$Name = ""
276 | [string]$Path = ""
277 | [string]$PackageSourcePath = ""
278 |
279 | $Package.GetEnumerator() | % {
280 | If ($_.key -eq "Name") { $Name = $_.value }
281 | If ($_.key -eq "Path") { $Path = "$($Node.PSDriveName):$($_.value)" }
282 | If ($_.key -eq "PackageSourcePath") { $PackageSourcePath = "$($Node.SourcePath)\$($_.value)" }
283 | }
284 |
285 | cMDTBuildPackage $Name.Replace(' ','') {
286 | Name = $Name
287 | Path = $Path
288 | PackageSourcePath = $PackageSourcePath
289 | PSDriveName = $Node.PSDriveName
290 | PSDrivePath = $Node.PSDrivePath
291 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
292 | }
293 | }
294 |
295 | ForEach ($TaskSequence in $Node.TaskSequences) {
296 | [string]$Name = ""
297 | [string]$Path = ""
298 | [string]$OSName = ""
299 | [string]$Template = ""
300 | [string]$ID = ""
301 | [string]$OrgName = ""
302 |
303 | $TaskSequence.GetEnumerator() | % {
304 | If ($_.key -eq "Name") { $Name = $_.value }
305 | If ($_.key -eq "Path") { $Path = "$($Node.PSDriveName):\Task Sequences\$($_.value)" }
306 | If ($_.key -eq "OSName") { $OSName = "$($Node.PSDriveName):\Operating Systems\$($_.value)" }
307 | If ($_.key -eq "Template") { $Template = $_.value }
308 | If ($_.key -eq "ID") { $ID = $_.value }
309 | If ($_.key -eq "OrgName") { $OrgName = $_.value }
310 | }
311 |
312 | # Create Task Sequence for one OS image
313 | cMDTBuildTaskSequence $Name.Replace(' ','') {
314 | Name = $Name
315 | Path = $Path
316 | OSName = $OSName
317 | Template = $Template
318 | ID = $ID
319 | OrgName = $OrgName
320 | PSDriveName = $Node.PSDriveName
321 | PSDrivePath = $Node.PSDrivePath
322 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
323 | }
324 |
325 | # Customize Task Sequence for one OS image
326 | ForEach ($TSCustomize in $TaskSequence.Customize) {
327 | [string]$Name = ""
328 | [string]$NewName = ""
329 | [string]$Type = ""
330 | [string]$GroupName = ""
331 | [string]$SubGroup = ""
332 | [string]$Disable = ""
333 | [string]$AddAfter = ""
334 | [string]$Description = ""
335 | [string]$TSVarName = "" # for MDT variable only
336 | [string]$TSVarValue = "" # for MDT variable only
337 | [string]$OSName = "" # for OS features only
338 | [string]$OSFeatures = "" # for OS features only
339 | [string]$Command = "" # for Run Command line only
340 | [string]$StartIn = "" # for Run Command line only
341 | [string]$PSCommand = "" # for Run PowerShell Script only
342 | [string]$PSParameters = "" # for Run PowerShell Script only
343 | [string]$SelectionProfile = "" # for Install Updates Offline only
344 |
345 | $TSCustomize.GetEnumerator() | % {
346 | If ($_.key -eq "Name") { $Name = $_.value }
347 | If ($_.key -eq "NewName") { $NewName = $_.value }
348 | If ($_.key -eq "Type") { $Type = $_.value }
349 | If ($_.key -eq "GroupName") { $GroupName = $_.value }
350 | If ($_.key -eq "SubGroup") { $SubGroup = $_.value }
351 | If ($_.key -eq "Disable") { $Disable = $_.value }
352 | If ($_.key -eq "AddAfter") { $AddAfter = $_.value }
353 | if ($_.key -eq "Description") { $Description = $_.value }
354 | if ($_.key -eq "TSVarName") { $TSVarName = $_.value }
355 | if ($_.key -eq "TSVarValue") { $TSVarValue = $_.value }
356 | If ($_.key -eq "OSName") { $OSName = $_.value }
357 | If ($_.key -eq "OSFeatures") { $OSFeatures = $_.value }
358 | If ($_.key -eq "Command") { $Command = $_.value }
359 | If ($_.key -eq "StartIn") { $StartIn = $_.value }
360 | If ($_.key -eq "PSCommand") { $PSCommand = $_.value }
361 | If ($_.key -eq "PSParameters") { $PSParameters = $_.value }
362 | If ($_.key -eq "SelectionProfile") { $SelectionProfile = $_.value }
363 | }
364 |
365 | # Current TS XML file name
366 | $TSFile = "$($Node.PSDrivePath)\Control\$($ID)\ts.xml"
367 |
368 | $CustomResource = $ID + '-' + $Name.Replace(' ','')
369 | cMDTBuildTaskSequenceCustomize $CustomResource {
370 | TSFile = $TSFile
371 | Name = $Name
372 | NewName = $NewName
373 | Type = $Type
374 | GroupName = $GroupName
375 | SubGroup = $SubGroup
376 | Disable = $Disable
377 | AddAfter = $AddAfter
378 | Description = $Description
379 | TSVarName = $TSVarName
380 | TSVarValue = $TSVarValue
381 | OSName = $OSName
382 | OSFeatures = $OSFeatures
383 | Command = $Command
384 | StartIn = $StartIn
385 | PSCommand = $PSCommand
386 | PSParameters = $PSParameters
387 | SelectionProfile = $SelectionProfile
388 | PSDriveName = $Node.PSDriveName
389 | PSDrivePath = $Node.PSDrivePath
390 | }
391 | }
392 | }
393 |
394 | ForEach ($CustomSetting in $Node.CustomSettings) {
395 | [string]$Name = ""
396 | [string]$SourcePath = ""
397 | [string[]]$TestFiles = ""
398 |
399 | $CustomSetting.GetEnumerator() | % {
400 | If ($_.key -eq "Name") { $Name = $_.value }
401 | If ($_.key -eq "SourcePath") { $SourcePath = "$($Node.SourcePath)\$($_.value)" }
402 | If ($_.key -eq "TestFiles") { $TestFiles = $_.value }
403 | }
404 |
405 | cMDTBuildCustomize $Name.Replace(' ','') {
406 | Name = $Name
407 | SourcePath = $SourcePath
408 | TargetPath = "Scripts"
409 | Path = $Node.PSDrivePath
410 | TestFiles = $TestFiles
411 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
412 | }
413 | }
414 |
415 | ForEach ($IniFile in $Node.CustomizeIniFiles) {
416 | [string]$Name = ""
417 | [string]$Path = ""
418 | [string]$Company = ""
419 | [string]$TimeZomeName = ""
420 | [string]$WSUSServer = ""
421 | [string]$UserLocale = ""
422 | [string]$KeyboardLocale = ""
423 |
424 | $IniFile.GetEnumerator() | % {
425 | If ($_.key -eq "Name") { $Name = $_.value }
426 | If ($_.key -eq "Path") { $Path = "$($Node.PSDrivePath)$($_.value)" }
427 | If ($_.key -eq "Company") { $Company = $_.value }
428 | If ($_.key -eq "TimeZoneName") { $TimeZoneName = $_.value }
429 | If ($_.key -eq "WSUSServer") { $WSUSServer = $_.value }
430 | If ($_.key -eq "UserLocale") { $UserLocale = $_.value }
431 | If ($_.key -eq "KeyboardLocale") { $KeyboardLocale = $_.value }
432 | }
433 |
434 | If ($Company) { $Company = "_SMSTSORGNAME=$($Company)" } Else { $Company = ";_SMSTSORGNAME=" }
435 | If ($TimeZoneName) { $TimeZoneName = "TimeZoneName=$($TimeZoneName)" } Else { $TimeZoneName = ";TimeZoneName=" }
436 | If ($WSUSServer) { $WSUSServer = "WSUSServer=$($WSUSServer)" } Else { $WSUSServer = ";WSUSServer=" }
437 | If ($UserLocale) { $UserLocale = "UserLocale=$($UserLocale)" } Else { $UserLocale = ";UserLocale=" }
438 | If ($KeyboardLocale) { $KeyboardLocale = "KeyboardLocale=$($KeyboardLocale)" } Else { $KeyboardLocale = ";KeyboardLocale=" }
439 |
440 | If ($Name -eq "CustomSettingsIni") {
441 | cMDTBuildCustomSettingsIni ini {
442 | Path = $Path
443 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
444 | Content = @"
445 | [Settings]
446 | Priority=Serialnumber,Default
447 |
448 | [Default]
449 | $($Company)
450 | OSInstall=Y
451 | HideShell=YES
452 | ApplyGPOPack=NO
453 | UserDataLocation=NONE
454 | DoNotCreateExtraPartition=YES
455 | JoinWorkgroup=WORKGROUP
456 | $($TimeZoneName)
457 | $($WSUSServer)
458 | ;SLShare=%DeployRoot%\Logs
459 | FinishAction=SHUTDOWN
460 |
461 | ;Set keyboard layout
462 | $($UserLocale)
463 | $($KeyboardLocale)
464 |
465 | ComputerBackupLocation=NETWORK
466 | BackupShare=\\$($Node.NodeName)\$($Node.PSDriveShareName)
467 | BackupDir=Captures
468 | ;BackupFile=#left("%TaskSequenceID%", len("%TaskSequenceID%")-3) & year(date) & right("0" & month(date), 2) & right("0" & day(date), 2)#.wim
469 | ;DoCapture=YES
470 |
471 | ;Disable all wizard pages
472 | SkipAdminPassword=YES
473 | SkipApplications=YES
474 | SkipBitLocker=YES
475 | SkipComputerBackup=YES
476 | SkipComputerName=YES
477 | SkipDomainMembership=YES
478 | SkipFinalSummary=YES
479 | SkipLocaleSelection=YES
480 | SkipPackageDisplay=YES
481 | SkipProductKey=YES
482 | SkipRoles=YES
483 | SkipSummary=YES
484 | SkipTimeZone=YES
485 | SkipUserData=YES
486 | SkipTaskSequence=NO
487 | SkipCapture=NO
488 | "@
489 | }
490 | }
491 |
492 | If ($Name -eq "BootstrapIni") {
493 | cMDTBuildBootstrapIni ini {
494 | Path = $Path
495 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
496 | Content = @"
497 | [Settings]
498 | Priority=Default
499 |
500 | [Default]
501 | DeployRoot=\\$($Node.NodeName)\$($Node.PSDriveShareName)
502 | SkipBDDWelcome=YES
503 |
504 | ;MDT Connect Account
505 | UserID=$MDTUserName
506 | UserPassword=$($Credentials.GetNetworkCredential().password)
507 | UserDomain=$MDTUserDomain
508 | "@
509 | }
510 | }
511 | }
512 |
513 | ForEach ($Image in $Node.BootImage) {
514 | [string]$Version = ""
515 | [string]$ExtraDirectory = ""
516 | [string]$BackgroundFile = ""
517 | [string]$LiteTouchWIMDescription = ""
518 |
519 | $Image.GetEnumerator() | % {
520 | If ($_.key -eq "Version") { $Version = $_.value }
521 | If ($_.key -eq "ExtraDirectory") { $ExtraDirectory = $_.value }
522 | If ($_.key -eq "BackgroundFile") { $BackgroundFile = $_.value }
523 | If ($_.key -eq "LiteTouchWIMDescription") { $LiteTouchWIMDescription = $_.value }
524 | }
525 |
526 | cMDTBuildUpdateBootImage updateBootImage {
527 | Version = $Version
528 | PSDeploymentShare = $Node.PSDriveName
529 | PSDrivePath = $Node.PSDrivePath
530 | ExtraDirectory = $ExtraDirectory
531 | BackgroundFile = $BackgroundFile
532 | LiteTouchWIMDescription = $LiteTouchWIMDescription
533 | DependsOn = "[cMDTBuildDirectory]DeploymentFolder"
534 | }
535 | }
536 |
537 | cNtfsPermissionEntry AssignPermissionsMDT {
538 | Ensure = "Present"
539 | Path = $Node.PSDrivePath
540 | Principal = "$MDTUserDomain\$MDTUserName"
541 | AccessControlInformation = @(
542 | cNtfsAccessControlInformation {
543 | AccessControlType = "Allow"
544 | FileSystemRights = "ReadAndExecute"
545 | Inheritance = "ThisFolderSubfoldersAndFiles"
546 | NoPropagateInherit = $false
547 | }
548 | )
549 | DependsOn = "[cMDTBuildPersistentDrive]DeploymentPSDrive"
550 | }
551 |
552 | cNtfsPermissionEntry AssignPermissionsCaptures {
553 | Ensure = "Present"
554 | Path = "$($Node.PSDrivePath)\Captures"
555 | Principal = "$MDTUserDomain\$MDTUserName"
556 | AccessControlInformation = @(
557 | cNtfsAccessControlInformation {
558 | AccessControlType = "Allow"
559 | FileSystemRights = "Modify"
560 | Inheritance = "ThisFolderSubfoldersAndFiles"
561 | NoPropagateInherit = $false
562 | }
563 | )
564 | DependsOn = "[cMDTBuildPersistentDrive]DeploymentPSDrive"
565 | }
566 | }
567 | }
568 |
569 | #Get password for MDT Account
570 | $Cred = Get-Credential -UserName "SVCMDTConnect001" -Message "Enter password for MDT Account"
571 | #$Cred = Get-Credential -UserName "Domain\User" -Message "Enter password for MDT Account"
572 |
573 | #Get configuration data
574 | [hashtable]$ConfigurationData = Get-ConfigurationData -ConfigurationData "$PSScriptRoot\Deploy_MDT_Server_ConfigurationData.psd1"
575 | #[hashtable]$ConfigurationData = Get-ConfigurationData -ConfigurationData "$PSScriptRoot\Deploy_MDT_Server_ConfigurationData_Lite.psd1" # Only Windows 10 x64 Evaluation
576 |
577 | #Create DSC MOF job
578 | DeployMDTServerContract -OutputPath "$PSScriptRoot\MDT-Deploy_MDT_Server" -ConfigurationData $ConfigurationData -Credentials $Cred
579 |
580 | #Set DSC LocalConfigurationManager
581 | $winrmArgs = 'set winrm/config @{MaxEnvelopeSizekb="8192"}'
582 | start-process "winrm" -ArgumentList $winrmArgs -NoNewWindow
583 | Set-DscLocalConfigurationManager -Path "$PSScriptRoot\MDT-Deploy_MDT_Server" -Verbose
584 |
585 | #Start DSC MOF job
586 | Start-DscConfiguration -Wait -Force -Verbose -ComputerName "$env:computername" -Path "$PSScriptRoot\MDT-Deploy_MDT_Server"
587 |
588 | #Set data deduplication
589 | #Enable-DedupVolume -Volume "E:"
590 | #Set-DedupVolume -Volume "E:" -MinimumFileAgeDays 1
591 |
592 | Write-Output ""
593 | Write-Output "Deploy MDT Server Builder completed!"
594 |
--------------------------------------------------------------------------------
/src/Deploy/Deploy_MDT_Server_ConfigurationData.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | AllNodes =
3 | @(
4 | @{
5 |
6 | #Global Settings for the configuration of Desired State Local Configuration Manager:
7 | NodeName = "*"
8 | PSDscAllowPlainTextPassword = $true
9 | RebootNodeIfNeeded = $true
10 | ConfigurationMode = "ApplyAndAutoCorrect"
11 | ConfigurationModeFrequencyMins = 120
12 | RefreshFrequencyMins = 120
13 | },
14 |
15 | @{
16 | #Node Settings for the configuration of an MDT Server:
17 | NodeName = "$env:computername"
18 | Role = "MDT Server"
19 |
20 | #Sources for download/Prereqs
21 | SourcePath = "E:\Source"
22 |
23 | #MDT deoployment share paths
24 | PSDriveName = "MDT001"
25 | PSDrivePath = "E:\MDTBuildLab"
26 | PSDriveShareName = "MDTBuildLab$"
27 |
28 | #Operating system MDT directory information
29 | OSDirectories = @(
30 | @{OperatingSystem = "Windows 10"}
31 | @{OperatingSystem = "Windows Server"}
32 | )
33 |
34 | #Packages Folder Structure
35 | PackagesFolderStructure = @(
36 | @{Folder = "Windows 10 x86"}
37 | @{Folder = "Windows 10 x64"}
38 | )
39 |
40 | #MDT Application Folder Structure
41 | ApplicationFolderStructure = @(
42 | @{
43 | Folder = "Core"
44 | SubFolders = @(
45 | @{SubFolder = "Configure"}
46 | @{SubFolder = "Microsoft"}
47 | )
48 | }
49 | @{
50 | Folder = "Common Applications"
51 | }
52 | )
53 |
54 | #Selection profile creation
55 | SelectionProfiles = @(
56 | @{
57 | Name = "Windows 10 x86"
58 | Comments = "Packages for Windows 10 x86"
59 | IncludePath = "Packages\Windows 10 x86"
60 | }
61 | @{
62 | Name = "Windows 10 x64"
63 | Comments = "Packages for Windows 10 x64"
64 | IncludePath = "Packages\Windows 10 x64"
65 | }
66 | )
67 |
68 | #Operating systems to import to MDT
69 | OperatingSystems = @(
70 | @{
71 | Name = "Windows 10 x86"
72 | Path = "Windows 10"
73 | SourcePath = "Windows10x86"
74 | }
75 | @{
76 | Name = "Windows 10 x64"
77 | Path = "Windows 10"
78 | SourcePath = "Windows10x64"
79 | }
80 | @{
81 | Name = "Windows 2019"
82 | Path = "Windows Server"
83 | SourcePath = "Windows2019"
84 | }
85 | )
86 |
87 | #Applications to import
88 | Applications = @(
89 | @{
90 | Name = "Install - Microsoft Visual C++"
91 | Path = "\Applications\Core\Microsoft"
92 | CommandLine = "cscript.exe Install-MicrosoftVisualCx86x64.wsf"
93 | ApplicationSourcePath = "VC++"
94 | }
95 | @{
96 | Name = "Configure - Set Control+Shift Keyboard Toggle"
97 | Path = "\Applications\Core\Configure"
98 | CommandLine = "reg import Toggle.reg"
99 | ApplicationSourcePath = "KeyboardToggle"
100 | }
101 | @{
102 | Name = "Action - CleanupBeforeSysprep"
103 | Path = "\Applications\Core\Configure"
104 | CommandLine = "cscript.exe Action-CleanupBeforeSysprep.wsf"
105 | ApplicationSourcePath = "CleanupBeforeSysprep"
106 | }
107 | @{
108 | Name = "Configure - Firewall rules"
109 | Path = "\Applications\Core\Configure"
110 | CommandLine = "powershell.exe -ExecutionPolicy Bypass -File .\Config-NetFwRules.ps1"
111 | ApplicationSourcePath = "ConfigureFirewall"
112 | }
113 | @{
114 | Name = "Configure - Set Start Layout"
115 | Path = "\Applications\Core\Configure"
116 | CommandLine = "powershell.exe -ExecutionPolicy Bypass -File .\Customize-DefaultProfile.ps1"
117 | ApplicationSourcePath = "Set-Startlayout"
118 | }
119 | )
120 |
121 | #Packages
122 | Packages = @(
123 | )
124 |
125 | #Task sqeuences; are dependent on imported Operating system and Applications in MDT
126 | TaskSequences = @(
127 | @{
128 | Name = "Windows 10 x86"
129 | Path = "Windows 10"
130 | OSName = "Windows 10\Windows 10 Enterprise in Windows 10 x86 install.wim"
131 | OrgName = "BuildLab"
132 | Template = "Client.xml"
133 | ID = "REFW10X86-001"
134 | Customize = @(
135 | # Set Product Key needed for MSDN/Evalution Windows distributives only. Skip this step if your ISO sources is VLSC.
136 | @{
137 | Name = "Set Product Key"
138 | Type = "Set Task Sequence Variable"
139 | GroupName = "Initialization"
140 | Description = "KMS Client Setup Keys: https://docs.microsoft.com/en-us/windows-server/get-started/kmsclientkeys"
141 | TSVarName = "ProductKey"
142 | TSVarValue = "NPPR9-FWDCX-D2C8J-H872K-2YT43"
143 | Disable = "true"
144 | }
145 | # Workaround for disable UEFI disk format
146 | @{
147 | Name = "Disable UEFI"
148 | Type = "Set Task Sequence Variable"
149 | GroupName = "Preinstall"
150 | Description = "Disable UEFI disk format"
151 | TSVarName = "IsUEFI"
152 | TSVarValue = "False"
153 | AddAfter = "Gather local only"
154 | }
155 | @{
156 | Name = "Apply Patches"
157 | Type = "Install Updates Offline"
158 | GroupName = "Preinstall"
159 | SelectionProfile = "Windows 10 x86"
160 | }
161 | @{
162 | Name = "Windows Update (Pre-Application Installation)"
163 | Type = "Run Command Line"
164 | GroupName = "State Restore"
165 | Disable = "false"
166 | }
167 | @{
168 | Name = "Custom Tasks (Pre-Windows Update)"
169 | Type = "Group"
170 | GroupName = "State Restore"
171 | AddAfter = "Tattoo"
172 | }
173 | @{
174 | Name = "Custom Tasks"
175 | Type = "Group"
176 | GroupName = "State Restore"
177 | NewName = "Custom Tasks (Post-Windows Update)"
178 | }
179 | @{
180 | Name = "Cleanup before Sysprep"
181 | Type = "Group"
182 | GroupName = "State Restore"
183 | AddAfter = "Apply Local GPO Package"
184 | }
185 | @{
186 | Name = "Install - Microsoft NET Framework 3.5.1"
187 | Type = "Install Roles and Features"
188 | GroupName = "State Restore"
189 | SubGroup = "Custom Tasks (Pre-Windows Update)"
190 | OSName = "Windows 10"
191 | OSFeatures = "NetFx3"
192 | }
193 | @{
194 | Name = "Install - Microsoft Visual C++"
195 | Type = "Install Application"
196 | GroupName = "State Restore"
197 | SubGroup = "Custom Tasks (Pre-Windows Update)"
198 | AddAfter = "Install - Microsoft NET Framework 3.5.1"
199 | }
200 | @{
201 | Name = "Configure - Disable SMB 1.0"
202 | Type = "Run Command Line"
203 | GroupName = "State Restore"
204 | SubGroup = "Custom Tasks (Pre-Windows Update)"
205 | Command = 'powershell.exe -Command "Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -NoRestart"'
206 | AddAfter = "Install - Microsoft Visual C++"
207 | }
208 | @{
209 | Name = "Configure - Set Control+Shift Keyboard Toggle"
210 | Type = "Install Application"
211 | GroupName = "State Restore"
212 | SubGroup = "Custom Tasks (Pre-Windows Update)"
213 | AddAfter = "Configure - Disable SMB 1.0"
214 | }
215 | @{
216 | Name = "Configure - Set Start Layout"
217 | Type = "Install Application"
218 | GroupName = "State Restore"
219 | SubGroup = "Custom Tasks (Pre-Windows Update)"
220 | AddAfter = "Configure - Set Control+Shift Keyboard Toggle"
221 | }
222 | @{
223 | Name = "Configure - Enable App-V Client"
224 | Type = "Run Command Line"
225 | GroupName = "State Restore"
226 | SubGroup = "Custom Tasks (Pre-Windows Update)"
227 | AddAfter = "Configure - Set Start Layout"
228 | Command = 'powershell.exe -ExecutionPolicy ByPass -Command "Enable-Appv; Set-AppvClientConfiguration -EnablePackageScripts 1"'
229 | }
230 | @{
231 | Name = "Suspend1"
232 | Type = "Run Command Line"
233 | GroupName = "State Restore"
234 | SubGroup = "Custom Tasks (Pre-Windows Update)"
235 | Disable = "true"
236 | Command = 'cscript.exe "%SCRIPTROOT%\LTISuspend.wsf"'
237 | AddAfter = "Configure - Enable App-V Client"
238 | }
239 | @{
240 | Name = "Configure - Remove Windows Default Applications"
241 | Type = "Run PowerShell Script"
242 | GroupName = "State Restore"
243 | SubGroup = "Custom Tasks (Pre-Windows Update)"
244 | PSCommand = "%SCRIPTROOT%\Invoke-RemoveBuiltinApps.ps1"
245 | AddAfter = "Suspend1"
246 | }
247 | @{
248 | Name = "Suspend2"
249 | Type = "Run Command Line"
250 | GroupName = "State Restore"
251 | SubGroup = "Custom Tasks (Pre-Windows Update)"
252 | Disable = "true"
253 | Command = 'cscript.exe "%SCRIPTROOT%\LTISuspend.wsf"'
254 | AddAfter = "Configure - Remove Windows Default Applications"
255 | }
256 | @{
257 | Name = "Restart Computer"
258 | Type = "Restart Computer"
259 | GroupName = "State Restore"
260 | SubGroup = "Custom Tasks (Pre-Windows Update)"
261 | AddAfter = "Suspend2"
262 | }
263 | @{
264 | Name = "Action - CleanupBuildWSUS"
265 | Type = "Run Command Line"
266 | GroupName = "State Restore"
267 | SubGroup = "Cleanup before Sysprep"
268 | Command = "reg delete HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate /f"
269 | }
270 | @{
271 | Name = "Action - CleanupBeforeSysprep"
272 | Type = "Install Application"
273 | GroupName = "State Restore"
274 | SubGroup = "Cleanup before Sysprep"
275 | AddAfter = "Action - CleanupBuildWSUS"
276 | }
277 | @{
278 | Name = "Restart Computer 1"
279 | Type = "Restart Computer"
280 | GroupName = "State Restore"
281 | SubGroup = "Cleanup before Sysprep"
282 | AddAfter = "Action - CleanupBeforeSysprep"
283 | }
284 | )
285 | }
286 | @{
287 | Name = "Windows 10 x64"
288 | Path = "Windows 10"
289 | OSName = "Windows 10\Windows 10 Enterprise in Windows 10 x64 install.wim"
290 | OrgName = "BuildLab"
291 | Template = "Client.xml"
292 | ID = "REFW10X64-001"
293 | Customize = @(
294 | # Set Product Key needed for MSDN/Evalution Windows distributives only. Skip this step if your ISO sources is VLSC.
295 | @{
296 | Name = "Set Product Key"
297 | Type = "Set Task Sequence Variable"
298 | GroupName = "Initialization"
299 | Description = "KMS Client Setup Keys: https://docs.microsoft.com/en-us/windows-server/get-started/kmsclientkeys"
300 | TSVarName = "ProductKey"
301 | TSVarValue = "NPPR9-FWDCX-D2C8J-H872K-2YT43"
302 | Disable = "true"
303 | }
304 | # Workaround for disable UEFI disk format
305 | @{
306 | Name = "Disable UEFI"
307 | Type = "Set Task Sequence Variable"
308 | GroupName = "Preinstall"
309 | Description = "Disable UEFI disk format"
310 | TSVarName = "IsUEFI"
311 | TSVarValue = "False"
312 | AddAfter = "Gather local only"
313 | }
314 | @{
315 | Name = "Apply Patches"
316 | Type = "Install Updates Offline"
317 | GroupName = "Preinstall"
318 | SelectionProfile = "Windows 10 x64"
319 | }
320 | @{
321 | Name = "Windows Update (Pre-Application Installation)"
322 | Type = "Run Command Line"
323 | GroupName = "State Restore"
324 | Disable = "false"
325 | }
326 | @{
327 | Name = "Custom Tasks (Pre-Windows Update)"
328 | Type = "Group"
329 | GroupName = "State Restore"
330 | AddAfter = "Tattoo"
331 | }
332 | @{
333 | Name = "Custom Tasks"
334 | Type = "Group"
335 | GroupName = "State Restore"
336 | NewName = "Custom Tasks (Post-Windows Update)"
337 | }
338 | @{
339 | Name = "Cleanup before Sysprep"
340 | Type = "Group"
341 | GroupName = "State Restore"
342 | AddAfter = "Apply Local GPO Package"
343 | }
344 | @{
345 | Name = "Install - Microsoft NET Framework 3.5.1"
346 | Type = "Install Roles and Features"
347 | GroupName = "State Restore"
348 | SubGroup = "Custom Tasks (Pre-Windows Update)"
349 | OSName = "Windows 10"
350 | OSFeatures = "NetFx3"
351 | }
352 | @{
353 | Name = "Install - Microsoft Visual C++"
354 | Type = "Install Application"
355 | GroupName = "State Restore"
356 | SubGroup = "Custom Tasks (Pre-Windows Update)"
357 | AddAfter = "Install - Microsoft NET Framework 3.5.1"
358 | }
359 | @{
360 | Name = "Configure - Disable SMB 1.0"
361 | Type = "Run Command Line"
362 | GroupName = "State Restore"
363 | SubGroup = "Custom Tasks (Pre-Windows Update)"
364 | Command = 'powershell.exe -Command "Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -NoRestart"'
365 | AddAfter = "Install - Microsoft Visual C++"
366 | }
367 | @{
368 | Name = "Configure - Set Control+Shift Keyboard Toggle"
369 | Type = "Install Application"
370 | GroupName = "State Restore"
371 | SubGroup = "Custom Tasks (Pre-Windows Update)"
372 | AddAfter = "Configure - Disable SMB 1.0"
373 | }
374 | @{
375 | Name = "Configure - Set Start Layout"
376 | Type = "Install Application"
377 | GroupName = "State Restore"
378 | SubGroup = "Custom Tasks (Pre-Windows Update)"
379 | AddAfter = "Configure - Set Control+Shift Keyboard Toggle"
380 | }
381 | @{
382 | Name = "Configure - Enable App-V Client"
383 | Type = "Run Command Line"
384 | GroupName = "State Restore"
385 | SubGroup = "Custom Tasks (Pre-Windows Update)"
386 | AddAfter = "Configure - Set Start Layout"
387 | Command = 'powershell.exe -ExecutionPolicy ByPass -Command "Enable-Appv; Set-AppvClientConfiguration -EnablePackageScripts 1"'
388 | }
389 | @{
390 | Name = "Suspend1"
391 | Type = "Run Command Line"
392 | GroupName = "State Restore"
393 | SubGroup = "Custom Tasks (Pre-Windows Update)"
394 | Disable = "true"
395 | Command = 'cscript.exe "%SCRIPTROOT%\LTISuspend.wsf"'
396 | AddAfter = "Configure - Enable App-V Client"
397 | }
398 | @{
399 | Name = "Configure - Remove Windows Default Applications"
400 | Type = "Run PowerShell Script"
401 | GroupName = "State Restore"
402 | SubGroup = "Custom Tasks (Pre-Windows Update)"
403 | PSCommand = "%SCRIPTROOT%\Invoke-RemoveBuiltinApps.ps1"
404 | AddAfter = "Suspend1"
405 | }
406 | @{
407 | Name = "Suspend2"
408 | Type = "Run Command Line"
409 | GroupName = "State Restore"
410 | SubGroup = "Custom Tasks (Pre-Windows Update)"
411 | Disable = "true"
412 | Command = 'cscript.exe "%SCRIPTROOT%\LTISuspend.wsf"'
413 | AddAfter = "Configure - Remove Windows Default Applications"
414 | }
415 | @{
416 | Name = "Restart Computer"
417 | Type = "Restart Computer"
418 | GroupName = "State Restore"
419 | SubGroup = "Custom Tasks (Pre-Windows Update)"
420 | AddAfter = "Suspend2"
421 | }
422 | @{
423 | Name = "Action - CleanupBuildWSUS"
424 | Type = "Run Command Line"
425 | GroupName = "State Restore"
426 | SubGroup = "Cleanup before Sysprep"
427 | Command = "reg delete HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate /f"
428 | }
429 | @{
430 | Name = "Action - CleanupBeforeSysprep"
431 | Type = "Install Application"
432 | GroupName = "State Restore"
433 | SubGroup = "Cleanup before Sysprep"
434 | AddAfter = "Action - CleanupBuildWSUS"
435 | }
436 | @{
437 | Name = "Restart Computer 1"
438 | Type = "Restart Computer"
439 | GroupName = "State Restore"
440 | SubGroup = "Cleanup before Sysprep"
441 | AddAfter = "Action - CleanupBeforeSysprep"
442 | }
443 | )
444 | }
445 | @{
446 | Name = "Windows 2019"
447 | Path = "Windows Server"
448 | OSName = "Windows Server\Windows Server 2019 SERVERSTANDARD in Windows 2019 install.wim"
449 | OrgName = "BuildLab"
450 | Template = "Server.xml"
451 | ID = "REFW2019-001"
452 | Customize = @(
453 | # Set Product Key needed for MSDN/Evalution Windows distributives only. Skip this step if your ISO sources is VLSC.
454 | @{
455 | Name = "Set Product Key"
456 | Type = "Set Task Sequence Variable"
457 | GroupName = "Initialization"
458 | Description = "KMS Client Setup Keys: https://docs.microsoft.com/en-us/windows-server/get-started/kmsclientkeys"
459 | TSVarName = "ProductKey"
460 | TSVarValue = "N69G4-B89J2-4G8F4-WWYCC-J464C"
461 | Disable = "true"
462 | }
463 | # Workaround for disable UEFI disk format
464 | @{
465 | Name = "Disable UEFI"
466 | Type = "Set Task Sequence Variable"
467 | GroupName = "Preinstall"
468 | Description = "Disable UEFI disk format"
469 | TSVarName = "IsUEFI"
470 | TSVarValue = "False"
471 | AddAfter = "Gather local only"
472 | }
473 | @{
474 | Name = "Apply Patches"
475 | Type = "Install Updates Offline"
476 | GroupName = "Preinstall"
477 | SelectionProfile = "Windows 10 x64"
478 | }
479 | @{
480 | Name = "Windows Update (Pre-Application Installation)"
481 | Type = "Run Command Line"
482 | GroupName = "State Restore"
483 | Disable = "false"
484 | }
485 | @{
486 | Name = "Custom Tasks (Pre-Windows Update)"
487 | Type = "Group"
488 | GroupName = "State Restore"
489 | AddAfter = "Tattoo"
490 | }
491 | @{
492 | Name = "Custom Tasks"
493 | Type = "Group"
494 | GroupName = "State Restore"
495 | NewName = "Custom Tasks (Post-Windows Update)"
496 | }
497 | @{
498 | Name = "Cleanup before Sysprep"
499 | Type = "Group"
500 | GroupName = "State Restore"
501 | AddAfter = "Apply Local GPO Package"
502 | }
503 | @{
504 | Name = "Configure - Firewall rules"
505 | Type = "Install Application"
506 | GroupName = "State Restore"
507 | SubGroup = "Custom Tasks (Pre-Windows Update)"
508 | AddAfter = "Cleanup before Sysprep"
509 | }
510 | @{
511 | Name = "Configure - Set Control+Shift Keyboard Toggle"
512 | Type = "Install Application"
513 | GroupName = "State Restore"
514 | SubGroup = "Custom Tasks (Pre-Windows Update)"
515 | AddAfter = "Configure - Firewall rules"
516 | }
517 | @{
518 | Name = "Restart Computer"
519 | Type = "Restart Computer"
520 | GroupName = "State Restore"
521 | SubGroup = "Custom Tasks (Pre-Windows Update)"
522 | Disable = "true"
523 | AddAfter = "Configure - Set Control+Shift Keyboard Toggle"
524 | }
525 | @{
526 | Name = "Action - CleanupBuildWSUS"
527 | Type = "Run Command Line"
528 | GroupName = "State Restore"
529 | SubGroup = "Cleanup before Sysprep"
530 | Command = "reg delete HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate /f"
531 | }
532 | @{
533 | Name = "Action - CleanupBeforeSysprep"
534 | Type = "Install Application"
535 | GroupName = "State Restore"
536 | SubGroup = "Cleanup before Sysprep"
537 | AddAfter = "Action - CleanupBuildWSUS"
538 | }
539 | @{
540 | Name = "Restart Computer 1"
541 | Type = "Restart Computer"
542 | GroupName = "State Restore"
543 | SubGroup = "Cleanup before Sysprep"
544 | AddAfter = "Action - CleanupBeforeSysprep"
545 | }
546 | )
547 | }
548 | )
549 |
550 | #Custom folder/files to add to the MDT
551 | CustomSettings = @(
552 | @{
553 | Name = "Invoke-RemoveBuiltinApps.ps1"
554 | SourcePath = "RemoveDefaultApps"
555 | }
556 | )
557 |
558 | #Custom settings and boot ini file management
559 | CustomizeIniFiles = @(
560 | @{
561 | Name = "CustomSettingsIni"
562 | Path = "\Control\CustomSettings.ini"
563 | Company = "Build Lab"
564 | TimeZoneName = "Russian Standard Time"
565 | WSUSServer = "http://fqdn:port"
566 | UserLocale = "en-US"
567 | KeyboardLocale = "en-US;ru-RU"
568 | }
569 | @{
570 | Ensure = "Present"
571 | Name = "BootstrapIni"
572 | Path = "\Control\Bootstrap.ini"
573 | }
574 | )
575 |
576 | #Boot image creation and management
577 | BootImage = @(
578 | @{
579 | Version = "1.0"
580 | ExtraDirectory = "Extra"
581 | BackgroundFile = "%INSTALLDIR%\Samples\Background.bmp"
582 | LiteTouchWIMDescription = "MDT Build Lab"
583 | }
584 | )
585 | }
586 | )
587 | }
588 |
--------------------------------------------------------------------------------