├── .gitignore ├── Build.ps1 ├── LICENSE ├── README.md ├── appveyor.yml └── src ├── DSCResources ├── cMDTBuildApplication │ └── cMDTBuildApplication.psm1 ├── cMDTBuildBootstrapIni │ └── cMDTBuildBootstrapIni.psm1 ├── cMDTBuildCustomSettingsIni │ └── cMDTBuildCustomSettingsIni.psm1 ├── cMDTBuildCustomize │ └── cMDTBuildCustomize.psm1 ├── cMDTBuildDirectory │ └── cMDTBuildDirectory.psm1 ├── cMDTBuildOperatingSystem │ └── cMDTBuildOperatingSystem.psm1 ├── cMDTBuildPackage │ └── cMDTBuildPackage.psm1 ├── cMDTBuildPersistentDrive │ └── cMDTBuildPersistentDrive.psm1 ├── cMDTBuildPreReqs │ └── cMDTBuildPreReqs.psm1 ├── cMDTBuildSelectionProfile │ └── cMDTBuildSelectionProfile.psm1 ├── cMDTBuildTaskSequence │ └── cMDTBuildTaskSequence.psm1 ├── cMDTBuildTaskSequenceCustomize │ └── cMDTBuildTaskSequenceCustomize.psm1 └── cMDTBuildUpdateBootImage │ └── cMDTBuildUpdateBootImage.psm1 ├── Deploy ├── Deploy_MDT_Server.ps1 ├── Deploy_MDT_Server_ConfigurationData.psd1 ├── Deploy_MDT_Server_ConfigurationData_Lite.psd1 ├── Download_MDT_Prereqs.ps1 ├── ImageFactoryV3-Build.ps1 ├── ImageFactoryV3.xml └── Import-ISO.ps1 ├── Examples ├── Example-cMDTBuildApplication.ps1 ├── Example-cMDTBuildBootstrapIni.ps1 ├── Example-cMDTBuildCustomSettingsIni.ps1 ├── Example-cMDTBuildCustomize.ps1 ├── Example-cMDTBuildDirectory.ps1 ├── Example-cMDTBuildOperatingSystem.ps1 ├── Example-cMDTBuildPackage.ps1 ├── Example-cMDTBuildPersistentDrive.ps1 ├── Example-cMDTBuildPreReqs.ps1 ├── Example-cMDTBuildSelectionProfile.ps1 ├── Example-cMDTBuildTaskSequence.ps1 ├── Example-cMDTBuildTaskSequenceCustomize.ps1 └── Example-cMDTBuildUpdateBootImage.ps1 ├── Public ├── Get-ConfigurationData.ps1 ├── Import-MDTModule.ps1 ├── Invoke-ExpandArchive.ps1 ├── Invoke-RemovePath.ps1 └── Invoke-TestPath.ps1 ├── Sources ├── Config-NetFwRules.ps1 ├── Customize-DefaultProfile.ps1 ├── Default_Start_Screen_Layout_10.xml ├── Install-MicrosoftVisualCx86x64.wsf ├── KB4564442.zip └── Toggle.reg ├── Tests ├── Test-cMDTBuildApplication.ps1 ├── Test-cMDTBuildBootstrapIni.ps1 ├── Test-cMDTBuildCustomSettingsIni.ps1 ├── Test-cMDTBuildCustomize.ps1 ├── Test-cMDTBuildDirectory.ps1 ├── Test-cMDTBuildOperatingSystem.ps1 ├── Test-cMDTBuildPackage.ps1 ├── Test-cMDTBuildPersistentDrive.ps1 ├── Test-cMDTBuildPreReqs.ps1 ├── Test-cMDTBuildSelectionProfile.ps1 ├── Test-cMDTBuildTaskSequence.ps1 ├── Test-cMDTBuildTaskSequenceCustomize.ps1 ├── Test-cMDTBuildUpdateBootImage.ps1 └── cMDTBuildLab.tests.ps1 └── cMDTBuildLabPrereqs.psd1 /.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/ -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Pavel Andreev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/DSCResources/cMDTBuildApplication/cMDTBuildApplication.psm1: -------------------------------------------------------------------------------- 1 | enum Ensure 2 | { 3 | Absent 4 | Present 5 | } 6 | 7 | [DscResource()] 8 | class cMDTBuildApplication 9 | { 10 | [DscProperty()] 11 | [Ensure]$Ensure = "Present" 12 | 13 | [DscProperty(Key)] 14 | [string]$Name 15 | 16 | [DscProperty(Key)] 17 | [string]$Path 18 | 19 | [DscProperty(Mandatory)] 20 | [string]$Enabled 21 | 22 | [DscProperty(Mandatory)] 23 | [string]$CommandLine 24 | 25 | [DscProperty(Mandatory)] 26 | [string]$ApplicationSourcePath 27 | 28 | [DscProperty(Mandatory)] 29 | [string]$PSDriveName 30 | 31 | [DscProperty(Mandatory)] 32 | [string]$PSDrivePath 33 | 34 | [void] Set() 35 | { 36 | if ($this.ensure -eq [Ensure]::Present) { 37 | $present = Invoke-TestPath -Path "$($this.path)\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath 38 | if ( !$present ) { 39 | $this.ImportApplication() 40 | } 41 | } 42 | else { 43 | Invoke-RemovePath -Path "$($this.path)\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath -Verbose 44 | } 45 | } 46 | 47 | [bool] Test() 48 | { 49 | $present = Invoke-TestPath -Path "$($this.path)\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath 50 | 51 | if ($this.Ensure -eq [Ensure]::Present) { 52 | return $present 53 | } 54 | else { 55 | return -not $present 56 | } 57 | } 58 | 59 | [cMDTBuildApplication] Get() 60 | { 61 | return $this 62 | } 63 | 64 | [void] ImportApplication() 65 | { 66 | Import-MDTModule 67 | New-PSDrive -Name $this.PSDriveName -PSProvider "MDTProvider" -Root $this.PSDrivePath -Verbose:$false 68 | Import-MDTApplication -Path $this.Path -Enable $this.Enabled -Name $this.Name -ShortName $this.Name ` 69 | -CommandLine $this.CommandLine -WorkingDirectory ".\Applications\$($this.Name)" ` 70 | -ApplicationSourcePath $this.ApplicationSourcePath -DestinationFolder $this.Name -Verbose 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/DSCResources/cMDTBuildBootstrapIni/cMDTBuildBootstrapIni.psm1: -------------------------------------------------------------------------------- 1 | enum Ensure 2 | { 3 | Absent 4 | Present 5 | } 6 | 7 | [DscResource()] 8 | class cMDTBuildBootstrapIni 9 | { 10 | [DscProperty()] 11 | [Ensure]$Ensure = "Present" 12 | 13 | [DscProperty(Key)] 14 | [string]$Path 15 | 16 | [DscProperty()] 17 | [string]$Content 18 | 19 | [void] Set() 20 | { 21 | if ($this.Ensure -eq [Ensure]::Present) { 22 | $this.SetContent() 23 | } 24 | else { 25 | $this.SetDefaultContent() 26 | } 27 | } 28 | 29 | [bool] Test() 30 | { 31 | $present = $this.TestFileContent() 32 | 33 | if ($this.Ensure -eq [Ensure]::Present) { 34 | return $present 35 | } 36 | else { 37 | return -not $present 38 | } 39 | } 40 | 41 | [cMDTBuildBootstrapIni] Get() 42 | { 43 | return $this 44 | } 45 | 46 | [bool] TestFileContent() 47 | { 48 | $present = $false 49 | $existingConfiguration = Get-Content -Path $this.Path -Raw #-Encoding UTF8 50 | 51 | if ($existingConfiguration -eq $this.Content.Replace("`n","`r`n")) { 52 | $present = $true 53 | } 54 | return $present 55 | } 56 | 57 | [void] SetContent() 58 | { 59 | Set-Content -Path $this.Path -Value $this.Content.Replace("`n","`r`n") -NoNewline -Force #-Encoding UTF8 60 | } 61 | 62 | [void] SetDefaultContent() 63 | { 64 | $defaultContent = @" 65 | [Settings] 66 | Priority=Default 67 | 68 | [Default] 69 | 70 | "@ 71 | Set-Content -Path $this.Path -Value $defaultContent -NoNewline -Force #-Encoding UTF8 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/DSCResources/cMDTBuildCustomSettingsIni/cMDTBuildCustomSettingsIni.psm1: -------------------------------------------------------------------------------- 1 | enum Ensure 2 | { 3 | Absent 4 | Present 5 | } 6 | 7 | [DscResource()] 8 | class cMDTBuildCustomSettingsIni 9 | { 10 | [DscProperty()] 11 | [Ensure]$Ensure = "Present" 12 | 13 | [DscProperty(Key)] 14 | [string]$Path 15 | 16 | [DscProperty()] 17 | [string]$Content 18 | 19 | [void] Set() 20 | { 21 | if ($this.Ensure -eq [Ensure]::Present) { 22 | $this.SetContent() 23 | } 24 | else { 25 | $this.SetDefaultContent() 26 | } 27 | } 28 | 29 | [bool] Test() 30 | { 31 | $present = $this.TestFileContent() 32 | if ($this.Ensure -eq [Ensure]::Present) { 33 | return $present 34 | } 35 | else { 36 | return -not $present 37 | } 38 | } 39 | 40 | [cMDTBuildCustomSettingsIni] Get() 41 | { 42 | return $this 43 | } 44 | 45 | [bool] TestFileContent() 46 | { 47 | $present = $false 48 | $existingConfiguration = Get-Content -Path $this.Path -Raw #-Encoding UTF8 49 | if ($existingConfiguration -eq $this.Content.Replace("`n","`r`n")) { 50 | $present = $true 51 | } 52 | return $present 53 | } 54 | 55 | [void] SetContent() 56 | { 57 | Set-Content -Path $this.Path -Value $this.Content.Replace("`n","`r`n") -NoNewline -Force #-Encoding UTF8 58 | } 59 | 60 | [void] SetDefaultContent() 61 | { 62 | $defaultContent = @" 63 | [Settings] 64 | Priority=Default 65 | Properties=MyCustomProperty 66 | 67 | [Default] 68 | OSInstall=Y 69 | SkipCapture=YES 70 | SkipAdminPassword=NO 71 | SkipProductKey=YES 72 | 73 | "@ 74 | Set-Content -Path $this.Path -Value $defaultContent -NoNewline -Force #-Encoding UTF8 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/DSCResources/cMDTBuildCustomize/cMDTBuildCustomize.psm1: -------------------------------------------------------------------------------- 1 | enum Ensure 2 | { 3 | Absent 4 | Present 5 | } 6 | 7 | [DscResource()] 8 | class cMDTBuildCustomize 9 | { 10 | [DscProperty()] 11 | [Ensure]$Ensure = "Present" 12 | 13 | [DscProperty(Key)] 14 | [string]$Name 15 | 16 | [DscProperty(Key)] 17 | [string]$Path 18 | 19 | [DscProperty(Mandatory)] 20 | [string]$SourcePath 21 | 22 | [DscProperty(Mandatory)] 23 | [string]$TargetPath 24 | 25 | [DscProperty()] 26 | [string[]]$TestFiles 27 | 28 | [void] Set() 29 | { 30 | $filename = "$($this.SourcePath)\$($this.name)" 31 | $extractfolder = "$($this.path)\$($this.TargetPath)" 32 | 33 | if ($this.ensure -eq [Ensure]::Present) { 34 | if ($filename -like '*.zip') { 35 | Invoke-ExpandArchive -Source $filename -Target $extractfolder -Verbose 36 | } 37 | else { 38 | Copy-Item -Path $filename $extractfolder -Verbose 39 | } 40 | } 41 | } 42 | 43 | [bool] Test() 44 | { 45 | $present = $true 46 | if ($this.Name -like '*.zip') { 47 | foreach ($file in $this.TestFiles) { 48 | if ( !(Test-Path -Path "$($this.path)\$($this.TargetPath)\$($file)") ) { 49 | $present = $false 50 | break 51 | } 52 | } 53 | } 54 | else { 55 | if ( !(Test-Path -Path "$($this.path)\$($this.TargetPath)\$($this.name)") ) { 56 | $present = $false 57 | } 58 | } 59 | 60 | if ($this.Ensure -eq [Ensure]::Present) { 61 | return $present 62 | } 63 | else { 64 | return -not $present 65 | } 66 | } 67 | 68 | [cMDTBuildCustomize] Get() 69 | { 70 | return $this 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/DSCResources/cMDTBuildDirectory/cMDTBuildDirectory.psm1: -------------------------------------------------------------------------------- 1 | enum Ensure 2 | { 3 | Absent 4 | Present 5 | } 6 | 7 | [DscResource()] 8 | class cMDTBuildDirectory 9 | { 10 | [DscProperty()] 11 | [Ensure]$Ensure = "Present" 12 | 13 | [DscProperty(Key)] 14 | [string]$Path 15 | 16 | [DscProperty(Key)] 17 | [string]$Name 18 | 19 | [DscProperty()] 20 | [string]$PSDriveName 21 | 22 | [DscProperty()] 23 | [string]$PSDrivePath 24 | 25 | [void] Set() 26 | { 27 | if ($this.ensure -eq [Ensure]::Present) { 28 | $this.CreateDirectory() 29 | } 30 | else 31 | { 32 | if (($this.PSDrivePath) -and ($this.PSDriveName)) { 33 | Invoke-RemovePath -Path "$($this.path)\$($this.Name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath -Verbose 34 | } 35 | else { 36 | Invoke-RemovePath -Path "$($this.path)\$($this.Name)" -Verbose 37 | } 38 | } 39 | } 40 | 41 | [bool] Test() 42 | { 43 | if (($this.PSDrivePath) -and ($this.PSDriveName)) { 44 | $present = Invoke-TestPath -Path "$($this.path)\$($this.Name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath -Verbose 45 | } 46 | else { 47 | $present = Invoke-TestPath -Path "$($this.path)\$($this.Name)" -Verbose 48 | } 49 | 50 | if ($this.Ensure -eq [Ensure]::Present) { 51 | return $present 52 | } 53 | else { 54 | return -not $present 55 | } 56 | } 57 | 58 | [cMDTBuildDirectory] Get() 59 | { 60 | return $this 61 | } 62 | 63 | [void] CreateDirectory() 64 | { 65 | if (($this.PSDrivePath) -and ($this.PSDriveName)) { 66 | Import-MDTModule 67 | New-PSDrive -Name $this.PSDriveName -PSProvider "MDTProvider" -Root $this.PSDrivePath -Verbose:$false | ` 68 | New-Item -ItemType Directory -Path "$($this.path)\$($this.Name)" -Verbose 69 | } 70 | else { 71 | New-Item -ItemType Directory -Path "$($this.path)\$($this.Name)" -Verbose 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/DSCResources/cMDTBuildPackage/cMDTBuildPackage.psm1: -------------------------------------------------------------------------------- 1 | enum Ensure 2 | { 3 | Absent 4 | Present 5 | } 6 | 7 | [DscResource()] 8 | class cMDTBuildPackage 9 | { 10 | [DscProperty()] 11 | [Ensure]$Ensure = "Present" 12 | 13 | [DscProperty(Key)] 14 | [string]$Name 15 | 16 | [DscProperty(Key)] 17 | [string]$Path 18 | 19 | [DscProperty(Mandatory)] 20 | [string]$PackageSourcePath 21 | 22 | [DscProperty(Mandatory)] 23 | [string]$PSDriveName 24 | 25 | [DscProperty(Mandatory)] 26 | [string]$PSDrivePath 27 | 28 | [void] Set() 29 | { 30 | if ($this.ensure -eq [Ensure]::Present) { 31 | $present = Invoke-TestPath -Path "$($this.path)\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath 32 | if ( !$present ) { 33 | $this.ImportPackage() 34 | } 35 | } 36 | else { 37 | Invoke-RemovePath -Path "$($this.path)\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath -Verbose 38 | } 39 | } 40 | 41 | [bool] Test() 42 | { 43 | $present = Invoke-TestPath -Path "$($this.path)\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath 44 | 45 | if ($this.Ensure -eq [Ensure]::Present) { 46 | return $present 47 | } 48 | else { 49 | return -not $present 50 | } 51 | } 52 | 53 | [cMDTBuildPackage] Get() 54 | { 55 | return $this 56 | } 57 | 58 | [void] ImportPackage() 59 | { 60 | # The Import-MDTPackage command crashes WMI when run from inside DSC. Using workflow is a work around. 61 | workflow Import-Pkg { 62 | [CmdletBinding()] 63 | param( 64 | [string]$PSDriveName, 65 | [string]$PSDrivePath, 66 | [string]$Path, 67 | [string]$PackageSource 68 | ) 69 | InlineScript { 70 | Import-MDTModule 71 | New-PSDrive -Name $Using:PSDriveName -PSProvider "MDTProvider" -Root $Using:PSDrivePath -Verbose:$false 72 | Import-MDTPackage -Path $Using:Path -SourcePath $Using:PackageSource -Verbose 73 | } 74 | } 75 | Import-Pkg -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath -Path $this.Path -PackageSource $this.PackageSourcePath 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /src/DSCResources/cMDTBuildPersistentDrive/cMDTBuildPersistentDrive.psm1: -------------------------------------------------------------------------------- 1 | enum Ensure 2 | { 3 | Absent 4 | Present 5 | } 6 | 7 | [DscResource()] 8 | class cMDTBuildPersistentDrive 9 | { 10 | [DscProperty()] 11 | [Ensure] $Ensure = "Present" 12 | 13 | [DscProperty(Key)] 14 | [string]$Path 15 | 16 | [DscProperty(Key)] 17 | [string]$Name 18 | 19 | [DscProperty(Mandatory)] 20 | [string]$Description 21 | 22 | [DscProperty(Mandatory)] 23 | [string]$NetworkPath 24 | 25 | [void] Set() 26 | { 27 | if ($this.ensure -eq [Ensure]::Present) { 28 | $this.CreateDirectory() 29 | } 30 | else { 31 | $this.RemoveDirectory() 32 | } 33 | } 34 | 35 | [bool] Test() 36 | { 37 | $present = $this.TestDirectoryPath() 38 | if ($this.Ensure -eq [Ensure]::Present) { 39 | return $present 40 | } 41 | else { 42 | return -not $present 43 | } 44 | } 45 | 46 | [cMDTBuildPersistentDrive] Get() 47 | { 48 | return $this 49 | } 50 | 51 | [bool] TestDirectoryPath() 52 | { 53 | $present = $false 54 | Import-MDTModule 55 | if (Test-Path -Path $this.Path -PathType Container -ErrorAction Ignore) { 56 | $mdtShares = (GET-MDTPersistentDrive -ErrorAction SilentlyContinue) 57 | If ($mdtShares) { 58 | ForEach ($share in $mdtShares) { 59 | If ($share.Name -eq $this.Name) { 60 | $present = $true 61 | } 62 | } 63 | } 64 | } 65 | return $present 66 | } 67 | 68 | [void] CreateDirectory() 69 | { 70 | Import-MDTModule 71 | New-PSDrive -Name $this.Name -PSProvider "MDTProvider" -Root $this.Path -Description $this.Description -NetworkPath $this.NetworkPath -Verbose:$false | ` 72 | Add-MDTPersistentDrive -Verbose 73 | } 74 | 75 | [void] RemoveDirectory() 76 | { 77 | Import-MDTModule 78 | Write-Verbose -Message "Removing MDTPersistentDrive $($this.Name)" 79 | New-PSDrive -Name $this.Name -PSProvider "MDTProvider" -Root $this.Path -Description $this.Description -NetworkPath $this.NetworkPath -Verbose:$false | ` 80 | Remove-MDTPersistentDrive -Verbose 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /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/cMDTBuildSelectionProfile/cMDTBuildSelectionProfile.psm1: -------------------------------------------------------------------------------- 1 | enum Ensure 2 | { 3 | Absent 4 | Present 5 | } 6 | 7 | [DscResource()] 8 | class cMDTBuildSelectionProfile 9 | { 10 | [DscProperty()] 11 | [Ensure]$Ensure = "Present" 12 | 13 | [DscProperty(Key)] 14 | [string]$Name 15 | 16 | [DscProperty()] 17 | [string]$Comments 18 | 19 | [DscProperty(Mandatory)] 20 | [string]$IncludePath 21 | 22 | [DscProperty(Mandatory)] 23 | [string]$PSDriveName 24 | 25 | [DscProperty(Mandatory)] 26 | [string]$PSDrivePath 27 | 28 | [void] Set() 29 | { 30 | if ($this.ensure -eq [Ensure]::Present) { 31 | $this.ImportSelectionProfile() 32 | } 33 | else { 34 | Invoke-RemovePath -Path "$($this.PSDriveName):\Selection Profiles\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath -Verbose 35 | } 36 | } 37 | 38 | [bool] Test() 39 | { 40 | $present = Invoke-TestPath -Path "$($this.PSDriveName):\Selection Profiles\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath 41 | if ($this.Ensure -eq [Ensure]::Present) { 42 | return $present 43 | } 44 | else { 45 | return -not $present 46 | } 47 | } 48 | 49 | [cMDTBuildSelectionProfile] Get() 50 | { 51 | return $this 52 | } 53 | 54 | [void] ImportSelectionProfile() 55 | { 56 | Import-MDTModule 57 | New-PSDrive -Name $this.PSDriveName -PSProvider "MDTProvider" -Root $this.PSDrivePath -Verbose:$false 58 | New-Item -Path "$($this.PSDriveName):\Selection Profiles" -enable "True" -Name $this.Name -Comments $this.Comments -Definition "" -ReadOnly "False" -Verbose 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /src/DSCResources/cMDTBuildTaskSequence/cMDTBuildTaskSequence.psm1: -------------------------------------------------------------------------------- 1 | enum Ensure 2 | { 3 | Absent 4 | Present 5 | } 6 | 7 | [DscResource()] 8 | class cMDTBuildTaskSequence 9 | { 10 | 11 | [DscProperty()] 12 | [Ensure]$Ensure = "Present" 13 | 14 | [DscProperty(Key)] 15 | [string]$Name 16 | 17 | [DscProperty(Key)] 18 | [string]$Path 19 | 20 | [DscProperty(Mandatory)] 21 | [string]$OSName 22 | 23 | [DscProperty(Mandatory)] 24 | [string]$Template 25 | 26 | [DscProperty()] 27 | [string]$Comments = "Build Reference Image" 28 | 29 | [DscProperty(Mandatory)] 30 | [string]$ID 31 | 32 | [DscProperty()] 33 | [string]$Owner = "Windows User" 34 | 35 | [DscProperty(Mandatory)] 36 | [string]$OrgName 37 | 38 | [DscProperty(Mandatory)] 39 | [string]$PSDriveName 40 | 41 | [DscProperty(Mandatory)] 42 | [string]$PSDrivePath 43 | 44 | [void] Set() 45 | { 46 | if ($this.ensure -eq [Ensure]::Present) { 47 | $this.ImportTaskSequence() 48 | } 49 | else { 50 | Invoke-RemovePath -Path "$($this.path)\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath -Verbose 51 | } 52 | } 53 | 54 | [bool] Test() 55 | { 56 | $present = Invoke-TestPath -Path "$($this.path)\$($this.name)" -PSDriveName $this.PSDriveName -PSDrivePath $this.PSDrivePath 57 | if ($this.Ensure -eq [Ensure]::Present) { 58 | return $present 59 | } 60 | else { 61 | return -not $present 62 | } 63 | } 64 | 65 | [cMDTBuildTaskSequence] Get() 66 | { 67 | return $this 68 | } 69 | 70 | [void] ImportTaskSequence() 71 | { 72 | Import-MDTModule 73 | New-PSDrive -Name $this.PSDriveName -PSProvider "MDTProvider" -Root $this.PSDrivePath -Verbose:$false 74 | Import-MDTTaskSequence -path $this.Path -Name $this.Name -Template $this.Template -Comments $this.Comments -ID $this.ID -Version "1.0" -OperatingSystemPath $this.OSName -FullName $this.Owner -OrgName $this.OrgName -HomePage "about:blank" -Verbose 75 | # Disable Windows autoupdate 76 | $UnattendXML = "$($this.PSDrivePath)\control\$($this.ID)\Unattend.xml" 77 | $unattend = Get-Content -Path $UnattendXML 78 | $unattend = $unattend.Replace('1','3') 79 | Set-Content -Path $UnattendXML -Value $unattend 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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/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/ImageFactoryV3.xml: -------------------------------------------------------------------------------- 1 | 2 | AutoBuild@build.lab 3 | AutoBuild@build.lab 4 | smtp.build.lab 5 | 6 | E:\MDTBuildLab 7 | REF 8 | 9 | 10 | 4 11 | 0 12 | HV01 13 | Network Switch 14 | E:\Build 15 | E:\Build\ISO 16 | 60 17 | 2 18 | 19 | 20 | -------------------------------------------------------------------------------- /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/Examples/Example-cMDTBuildApplication.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildApplication Example 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildApplication WMF5 { 10 | Ensure = "Present" 11 | Name = "Install - Windows Management Framework 5.0 - x64" 12 | Path = "\Applications\Core\Microsoft" 13 | CommandLine = "wusa.exe Win8.1AndW2K12R2-KB3134758-x64.msu /quiet /norestart" 14 | ApplicationSourcePath = "WMF50x64" 15 | Enabled = "True" 16 | PSDriveName = $PSDriveName 17 | PSDrivePath = $PSDrivePath 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildBootstrapIni.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildBootstrapIni Example 3 | # 4 | $PSDrivePath = "E:\MDTBuildLab" 5 | 6 | Configuration MDTServer 7 | { 8 | cMDTBuildBootstrapIni ini { 9 | Ensure = "Present" 10 | Path = "$($PSDrivePath)\Control\Bootstrap.ini" 11 | Content = @" 12 | [Settings] 13 | Priority=Default 14 | 15 | [Default] 16 | DeployRoot=\\$($ComputerName)\DeploymentShare$ 17 | SkipBDDWelcome=YES 18 | 19 | ;MDT Connect Account 20 | UserID=$($UserName) 21 | UserPassword=$($Password) 22 | UserDomain=$($env:COMPUTERNAME) 23 | "@ 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildCustomSettingsIni.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildCustomSettingsIni Example 3 | # 4 | $ComputerName = "$env:computername" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | $Company = "MDT Build Lab" 7 | $TimeZoneName = "Ekaterinburg Standard Time" 8 | $WSUSServer = "http://fqdn:port" 9 | $UserLocale = "en-US" 10 | $KeyboardLocale = "en-US;ru-RU" 11 | 12 | Configuration MDTServer 13 | { 14 | cMDTBuildCustomSettingsIni ini { 15 | Ensure = "Present" 16 | Path = "$($PSDrivePath)\Control\CustomSettings.ini" 17 | Content = @" 18 | [Settings] 19 | Priority=Init,Default 20 | Properties=VMNameAlias 21 | 22 | [Init] 23 | UserExit=ReadKVPData.vbs 24 | VMNameAlias=#SetVMNameAlias()# 25 | 26 | [Default] 27 | $($Company) 28 | OSInstall=Y 29 | HideShell=YES 30 | ApplyGPOPack=NO 31 | UserDataLocation=NONE 32 | DoNotCreateExtraPartition=YES 33 | JoinWorkgroup=WORKGROUP 34 | $($TimeZoneName) 35 | $($WSUSServer) 36 | ;SLShare=%DeployRoot%\Logs 37 | TaskSequenceID=%VMNameAlias% 38 | FinishAction=SHUTDOWN 39 | 40 | ;Set keyboard layout 41 | $($UserLocale) 42 | $($KeyboardLocale) 43 | 44 | ComputerBackupLocation=NETWORK 45 | BackupShare=\\$($ComputerName)\DeploymentShare$ 46 | BackupDir=Captures 47 | BackupFile=#left("%TaskSequenceID%", len("%TaskSequenceID%")-3) & year(date) & right("0" & month(date), 2) & right("0" & day(date), 2)#.wim 48 | DoCapture=YES 49 | 50 | ;Disable all wizard pages 51 | SkipAdminPassword=YES 52 | SkipApplications=YES 53 | SkipBitLocker=YES 54 | SkipCapture=YES 55 | SkipComputerBackup=YES 56 | SkipComputerName=YES 57 | SkipDomainMembership=YES 58 | SkipFinalSummary=YES 59 | SkipLocaleSelection=YES 60 | SkipPackageDisplay=YES 61 | SkipProductKey=YES 62 | SkipRoles=YES 63 | SkipSummary=YES 64 | SkipTimeZone=YES 65 | SkipUserData=YES 66 | SkipTaskSequence=YES 67 | "@ 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildCustomize.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildCustomize Example 3 | # 4 | $PSDrivePath = "E:\MDTBuildLab" 5 | $SourcePath = "E:\Source" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildCustomize ExtraFiles { 10 | Ensure = "Present" 11 | Name = "ExtraFiles.zip" 12 | Path = $PSDrivePath 13 | SourcePath = "$($SourcePath)\Scripts" 14 | TestFiles = @("Script1.vbs", "Script2.vbs") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildDirectory.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildDirectory Example 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildDirectory Windows10 { 10 | Ensure = "Present" 11 | Name = "Windows 10" 12 | Path = "$($PSDriveName):\Operating Systems" 13 | PSDriveName = $PSDriveName 14 | PSDrivePath = $PSDrivePath 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildOperatingSystem.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildOperatingSystem Example 3 | # 4 | $SourcePath = "E:\Source" 5 | $PSDriveName = "MDT001" 6 | $PSDrivePath = "E:\MDTBuildLab" 7 | 8 | Configuration MDTServer 9 | { 10 | cMDTBuildOperatingSystem Win10x64 { 11 | Ensure = "Present" 12 | Name = "Windows 10 x64" 13 | Path = "Windows 10" 14 | SourcePath = "$SourcePath\Windows10x64" 15 | PSDriveName = $PSDriveName 16 | PSDrivePath = $PSDrivePath 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildPackage.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildPackage Example 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildPackage KB3125574_x64 { 10 | Ensure = "Present" 11 | Name = "Package_for_KB3125574 neutral amd64 6.1.4.4" 12 | Path = "Packages\Windows 7" 13 | PackageSourcePath = "Update for Windows 7 for x64-based Systems (KB3125574)" 14 | PSDriveName = $PSDriveName 15 | PSDrivePath = $PSDrivePath 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildPersistentDrive.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildPersistentDrive Example 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | $ComputerName = "$env:computername" 7 | 8 | Configuration MDTServer 9 | { 10 | cMDTBuildPersistentDrive DeploymentPSDrive { 11 | Ensure = "Present" 12 | Name = $PSDriveName 13 | Path = $PSDrivePath 14 | Description = "MDT Build Share" 15 | NetworkPath = "\\$ComputerName\DeploymentShare$" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildPreReqs.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildPreReqs Example 3 | # 4 | $SourcePath = "E:\Source" 5 | 6 | Configuration MDTServer 7 | { 8 | cMDTBuildPreReqs MDTPreReqs { 9 | Ensure = "Present" 10 | DownloadPath = "$SourcePath" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildSelectionProfile.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildSelectionProfile Example 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildSelectionProfile Win10x64 { 10 | Ensure = "Present" 11 | Name = "Windows 10 x64" 12 | Comments = "Packages for Windows 10 x64" 13 | IncludePath = "Packages\Windows 10 x64" 14 | PSDriveName = $PSDriveName 15 | PSDrivePath = $PSDrivePath 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildTaskSequence.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildTaskSequence Example 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildTaskSequence Win10x64 { 10 | Ensure = "Present" 11 | Name = "Windows 10 x64" 12 | Path = "Windows 10" 13 | OSName = "Windows 10\Windows 10 Enterprise in Windows 10 x64 install.wim" 14 | OrgName = "BuildLab" 15 | Template = "Client.xml" 16 | ID = "REFW10X64-001" 17 | PSDriveName = $PSDriveName 18 | PSDrivePath = $PSDrivePath 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildTaskSequenceCustomize.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildTaskSequenceCustomize Example 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | $TSID = "REFW10X64-001" 7 | $TSFile = "$($PSDrivePath)\Control\$($TSID)\ts.xml" 8 | 9 | Configuration MDTServer 10 | { 11 | cMDTBuildTaskSequenceCustomize AddFeatures { 12 | TSFile = $TSFile 13 | Name = "Install - Microsoft NET Framework 3.5.1" 14 | Type = "Install Roles and Features" 15 | GroupName = "State Restore" 16 | SubGroup = "Custom Tasks (Pre-Windows Update)" 17 | OSName = "Windows 10" 18 | OSFeatures = "NetFx3,TelnetClient" 19 | PSDriveName = $PSDriveName 20 | PSDrivePath = $PSDrivePath 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Examples/Example-cMDTBuildUpdateBootImage.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildUpdateBootImage Example 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildUpdateBootImage updateBootImage { 10 | Version = "1.0" 11 | PSDeploymentShare = $PSDriveName 12 | PsDrivePath = $PSDrivePath 13 | ExtraDirectory = "Extra" 14 | BackgroundFile = "%INSTALLDIR%\Samples\Background.bmp" 15 | LiteTouchWIMDescription = "MDT Build Lab" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Public/Get-ConfigurationData.ps1: -------------------------------------------------------------------------------- 1 | 2 | Function Get-ConfigurationData 3 | { 4 | [CmdletBinding()] 5 | [OutputType([hashtable])] 6 | Param ( 7 | [Parameter(Mandatory)] 8 | [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformation()] 9 | [hashtable] $ConfigurationData 10 | ) 11 | return $ConfigurationData 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/Public/Import-MDTModule.ps1: -------------------------------------------------------------------------------- 1 | 2 | Function Import-MDTModule 3 | { 4 | If ( -Not (Get-Module MicrosoftDeploymentToolkit) ) { 5 | Import-Module "$env:ProgramFiles\Microsoft Deployment Toolkit\Bin\MicrosoftDeploymentToolkit.psd1" -ErrorAction Stop -Global -Verbose:$False 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/Public/Invoke-ExpandArchive.ps1: -------------------------------------------------------------------------------- 1 | 2 | Function Invoke-ExpandArchive 3 | { 4 | [cmdletbinding(SupportsShouldProcess=$True,ConfirmImpact="Low")] 5 | [OutputType([bool])] 6 | param( 7 | [Parameter(Mandatory=$True)] 8 | [ValidateNotNullorEmpty()] 9 | [string]$Source, 10 | [Parameter(Mandatory=$True)] 11 | [ValidateNotNullorEmpty()] 12 | [string]$Target 13 | ) 14 | 15 | [bool]$Verbosity 16 | If ($PSBoundParameters.Verbose) { $Verbosity = $True } 17 | Else { $Verbosity = $False } 18 | 19 | Write-Verbose "Expanding archive $($Source) to $($Target)" 20 | Expand-Archive $Source -DestinationPath $Target -Force -Verbose:$Verbosity 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/Public/Invoke-RemovePath.ps1: -------------------------------------------------------------------------------- 1 | 2 | Function Invoke-RemovePath 3 | { 4 | [cmdletbinding(SupportsShouldProcess=$True,ConfirmImpact="Low")] 5 | [OutputType([bool])] 6 | param( 7 | [Parameter(Mandatory=$True)] 8 | [ValidateNotNullorEmpty()] 9 | [string]$Path, 10 | [Parameter()] 11 | [string]$PSDriveName, 12 | [Parameter()] 13 | [string]$PSDrivePath 14 | ) 15 | 16 | [bool]$Verbosity 17 | If ($PSBoundParameters.Verbose) { $Verbosity = $True } 18 | Else { $Verbosity = $False } 19 | 20 | if (($PSDrivePath) -and ($PSDriveName)) { 21 | Import-MDTModule 22 | New-PSDrive -Name $PSDriveName -PSProvider "MDTProvider" -Root $PSDrivePath -Verbose:$False | ` 23 | Remove-Item -Path "$($Path)" -Force -Verbose:$Verbosity 24 | } 25 | else { 26 | Remove-Item -Path "$($Path)" -Force -Verbose:$Verbosity 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/Public/Invoke-TestPath.ps1: -------------------------------------------------------------------------------- 1 | 2 | Function Invoke-TestPath 3 | { 4 | [cmdletbinding(SupportsShouldProcess=$True,ConfirmImpact="Low")] 5 | [OutputType([bool])] 6 | param( 7 | [Parameter(Mandatory=$True)] 8 | [ValidateNotNullorEmpty()] 9 | [string]$Path, 10 | [Parameter()] 11 | [string]$PSDriveName, 12 | [Parameter()] 13 | [string]$PSDrivePath 14 | ) 15 | 16 | [bool]$present = $false 17 | 18 | if (($PSDrivePath) -and ($PSDriveName)) { 19 | Import-MDTModule 20 | if (New-PSDrive -Name $PSDriveName -PSProvider "MDTProvider" -Root $PSDrivePath -Verbose:$false | ` 21 | Test-Path -Path "$($Path)" -ErrorAction Ignore) { 22 | $present = $true 23 | } 24 | } 25 | else { 26 | if (Test-Path -Path "$($Path)" -ErrorAction Ignore) { 27 | $present = $true 28 | } 29 | } 30 | return $present 31 | } -------------------------------------------------------------------------------- /src/Sources/Config-NetFwRules.ps1: -------------------------------------------------------------------------------- 1 | Enable-NetFirewallRule -DisplayGroup "File and Printer Sharing" -Verbose 2 | Enable-NetFirewallRule -DisplayGroup "File and Printer Sharing over SMBDirect" -Verbose 3 | Enable-NetFirewallRule -DisplayGroup "Remote Desktop" -Verbose 4 | Enable-NetFirewallRule -DisplayGroup "Remote Shutdown" -Verbose 5 | Enable-NetFirewallRule -DisplayGroup "Windows Management Instrumentation (WMI)" -Verbose 6 | -------------------------------------------------------------------------------- /src/Sources/Customize-DefaultProfile.ps1: -------------------------------------------------------------------------------- 1 | $winVer = (Get-CimInstance -ClassName win32_operatingsystem).version 2 | if ($winVer -like '10.0*') { 3 | # Windows 10 4 | Import-StartLayout -LayoutPath "Z:\Applications\Configure - Set Start Layout\Default_Start_Screen_Layout_10.xml" -MountPath $env:SystemDrive\ 5 | } 6 | -------------------------------------------------------------------------------- /src/Sources/Default_Start_Screen_Layout_10.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Sources/Install-MicrosoftVisualCx86x64.wsf: -------------------------------------------------------------------------------- 1 | 2 | 133 | 134 | -------------------------------------------------------------------------------- /src/Sources/KB4564442.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pvs043/cMDTBuildLab/0dbaf4456cb369ffc9293c42e15b329420dbeceb/src/Sources/KB4564442.zip -------------------------------------------------------------------------------- /src/Sources/Toggle.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pvs043/cMDTBuildLab/0dbaf4456cb369ffc9293c42e15b329420dbeceb/src/Sources/Toggle.reg -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildApplication.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildApplication Test 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildApplication WMF5 { 10 | Ensure = "Present" 11 | Name = "Install - Windows Management Framework 5.0 - x64" 12 | Path = "\Applications\Core\Microsoft" 13 | CommandLine = "wusa.exe Win8.1AndW2K12R2-KB3134758-x64.msu /quiet /norestart" 14 | ApplicationSourcePath = "WMF50x64" 15 | Enabled = "True" 16 | PSDriveName = $PSDriveName 17 | PSDrivePath = $PSDrivePath 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildBootstrapIni.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildBootstrapIni Test 3 | # 4 | $PSDrivePath = "E:\MDTBuildLab" 5 | 6 | Configuration MDTServer 7 | { 8 | cMDTBuildBootstrapIni ini { 9 | Ensure = "Present" 10 | Path = "$($PSDrivePath)\Control\Bootstrap.ini" 11 | Content = @" 12 | [Settings] 13 | Priority=Default 14 | 15 | [Default] 16 | DeployRoot=\\$($ComputerName)\DeploymentShare$ 17 | SkipBDDWelcome=YES 18 | 19 | ;MDT Connect Account 20 | UserID=$($UserName) 21 | UserPassword=$($Password) 22 | UserDomain=$($env:COMPUTERNAME) 23 | "@ 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildCustomSettingsIni.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildCustomSettingsIni Test 3 | # 4 | $ComputerName = "$env:computername" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | $Company = "MDT Build Lab" 7 | $TimeZoneName = "Ekaterinburg Standard Time" 8 | $WSUSServer = "http://fqdn:port" 9 | $UserLocale = "en-US" 10 | $KeyboardLocale = "en-US;ru-RU" 11 | 12 | Configuration MDTServer 13 | { 14 | cMDTBuildCustomSettingsIni ini { 15 | Ensure = "Present" 16 | Path = "$($PSDrivePath)\Control\CustomSettings.ini" 17 | Content = @" 18 | [Settings] 19 | Priority=Init,Default 20 | Properties=VMNameAlias 21 | 22 | [Init] 23 | UserExit=ReadKVPData.vbs 24 | VMNameAlias=#SetVMNameAlias()# 25 | 26 | [Default] 27 | $($Company) 28 | OSInstall=Y 29 | HideShell=YES 30 | ApplyGPOPack=NO 31 | UserDataLocation=NONE 32 | DoNotCreateExtraPartition=YES 33 | JoinWorkgroup=WORKGROUP 34 | $($TimeZoneName) 35 | $($WSUSServer) 36 | ;SLShare=%DeployRoot%\Logs 37 | TaskSequenceID=%VMNameAlias% 38 | FinishAction=SHUTDOWN 39 | 40 | ;Set keyboard layout 41 | $($UserLocale) 42 | $($KeyboardLocale) 43 | 44 | ComputerBackupLocation=NETWORK 45 | BackupShare=\\$($ComputerName)\DeploymentShare$ 46 | BackupDir=Captures 47 | BackupFile=#left("%TaskSequenceID%", len("%TaskSequenceID%")-3) & year(date) & right("0" & month(date), 2) & right("0" & day(date), 2)#.wim 48 | DoCapture=YES 49 | 50 | ;Disable all wizard pages 51 | SkipAdminPassword=YES 52 | SkipApplications=YES 53 | SkipBitLocker=YES 54 | SkipCapture=YES 55 | SkipComputerBackup=YES 56 | SkipComputerName=YES 57 | SkipDomainMembership=YES 58 | SkipFinalSummary=YES 59 | SkipLocaleSelection=YES 60 | SkipPackageDisplay=YES 61 | SkipProductKey=YES 62 | SkipRoles=YES 63 | SkipSummary=YES 64 | SkipTimeZone=YES 65 | SkipUserData=YES 66 | SkipTaskSequence=YES 67 | "@ 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildCustomize.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildCustomize Test 3 | # 4 | $PSDrivePath = "E:\MDTBuildLab" 5 | $SourcePath = "E:\Source" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildCustomize PEExtraFiles { 10 | Ensure = "Present" 11 | Name = "ExtraFiles" 12 | Path = $PSDrivePath 13 | SourcePath = "$($SourcePath)\Scripts" 14 | TestFiles = @("Script1.vbs", "Script2.vbs") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildDirectory.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildDirectory Test 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildDirectory Windows10 { 10 | Ensure = "Present" 11 | Name = "Windows 10" 12 | Path = "$($PSDriveName):\Operating Systems" 13 | PSDriveName = $PSDriveName 14 | PSDrivePath = $PSDrivePath 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildOperatingSystem.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildOperatingSystem Test 3 | # 4 | $SourcePath = "E:\Source" 5 | $PSDriveName = "MDT001" 6 | $PSDrivePath = "E:\MDTBuildLab" 7 | 8 | Configuration MDTServer 9 | { 10 | cMDTBuildOperatingSystem Win10x64 { 11 | Ensure = "Present" 12 | Name = "Windows 10 x64" 13 | Path = "Windows 10" 14 | SourcePath = "$SourcePath\Windows10x64" 15 | PSDriveName = $PSDriveName 16 | PSDrivePath = $PSDrivePath 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildPackage.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildPackage Test 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildPackage KB3125574_x64 { 10 | Ensure = "Present" 11 | Name = "Package_for_KB3125574 neutral amd64 6.1.4.4" 12 | Path = "Packages\Windows 7" 13 | PackageSourcePath = "Update for Windows 7 for x64-based Systems (KB3125574)" 14 | PSDriveName = $PSDriveName 15 | PSDrivePath = $PSDrivePath 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildPersistentDrive.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildPersistentDrive Test 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | $ComputerName = "$env:computername" 7 | 8 | Configuration MDTServer 9 | { 10 | cMDTBuildPersistentDrive DeploymentPSDrive { 11 | Ensure = "Present" 12 | Name = $PSDriveName 13 | Path = $PSDrivePath 14 | Description = "MDT Build Share" 15 | NetworkPath = "\\$ComputerName\DeploymentShare$" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildPreReqs.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildPreReqs Test 3 | # 4 | $SourcePath = "E:\Source" 5 | 6 | Configuration MDTServer 7 | { 8 | cMDTBuildPreReqs MDTPreReqs { 9 | Ensure = "Present" 10 | DownloadPath = "$SourcePath" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildSelectionProfile.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildSelectionProfile Test 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildSelectionProfile Win10x64 { 10 | Ensure = "Present" 11 | Name = "Windows 10 x64" 12 | Comments = "Packages for Windows 10 x64" 13 | IncludePath = "Packages\Windows 10 x64" 14 | PSDriveName = $PSDriveName 15 | PSDrivePath = $PSDrivePath 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildTaskSequence.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildTaskSequence Test 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildTaskSequence Win10x64 { 10 | Ensure = "Present" 11 | Name = "Windows 10 x64" 12 | Path = "Windows 10" 13 | OSName = "Windows 10\Windows 10 Enterprise in Windows 10 x64 install.wim" 14 | OrgName = "BuildLab" 15 | Template = "Client.xml" 16 | ID = "REFW10X64-001" 17 | PSDriveName = $PSDriveName 18 | PSDrivePath = $PSDrivePath 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildTaskSequenceCustomize.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildTaskSequenceCustomize Test 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | $TSID = "REFW10X64-001" 7 | $TSFile = "$($PSDrivePath)\Control\$($TSID)\ts.xml" 8 | 9 | Configuration MDTServer 10 | { 11 | cMDTBuildTaskSequenceCustomize AddFeatures { 12 | TSFile = $TSFile 13 | Name = "Install - Microsoft NET Framework 3.5.1" 14 | Type = "Install Roles and Features" 15 | GroupName = "State Restore" 16 | SubGroup = "Custom Tasks (Pre-Windows Update)" 17 | OSName = "Windows 10" 18 | OSFeatures = "NetFx3,TelnetClient" 19 | PSDriveName = $PSDriveName 20 | PSDrivePath = $PSDrivePath 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Tests/Test-cMDTBuildUpdateBootImage.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # cMDTBuildUpdateBootImage Test 3 | # 4 | $PSDriveName = "MDT001" 5 | $PSDrivePath = "E:\MDTBuildLab" 6 | 7 | Configuration MDTServer 8 | { 9 | cMDTBuildUpdateBootImage updateBootImage { 10 | Version = "1.0" 11 | PSDeploymentShare = $PSDriveName 12 | PsDrivePath = $PSDrivePath 13 | ExtraDirectory = "Extra" 14 | BackgroundFile = "%INSTALLDIR%\Samples\Background.bmp" 15 | LiteTouchWIMDescription = "MDT Build Lab" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Tests/cMDTBuildLab.tests.ps1: -------------------------------------------------------------------------------- 1 | $Rules = Get-ScriptAnalyzerRule | Where-Object RuleName -NotIn @('PSUseShouldProcessForStateChangingFunctions','PSAvoidUsingWMICmdlet','PSReviewUnusedParameter') 2 | $modules = Get-ChildItem -Path $PSScriptRoot\..\* -Include @('cMDTBuildLab.psm1','cMDTBuildLabPrereqs.psd1') 3 | $modules += Get-ChildItem -Path $PSScriptRoot\..\Deploy -Recurse -Include @('*.ps1','*.psd1') 4 | $modules += Get-ChildItem -Path $PSScriptRoot\..\Examples -Recurse -Include @('*.ps1','*.psd1') 5 | $modules += Get-ChildItem -Path $PSScriptRoot\..\Sources -Recurse -Include @('*.ps1','*.psd1') 6 | 7 | foreach ($module in $modules) { 8 | Describe "Script analyzer for $($module.Name)" { 9 | foreach ($rule in $rules) { 10 | It "$($module.Name) passes the $($rule.CommonName) validation" -TestCases @{ Module = $module.FullName; Rule = $rule.RuleName } { 11 | $output = $null 12 | $results = Invoke-ScriptAnalyzer -Path $Module -IncludeRule $Rule 13 | if ($results.count -eq 1) { 14 | $output = "$($results.Message) at line $($results.Line)" 15 | } 16 | elseif ($results.count -gt 1) { 17 | foreach ($result in $results) { 18 | $output += "$($result.Message) at line $($result.Line)`r`n" 19 | } 20 | } 21 | $output | should -Be $null 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------