diff --git a/Scripts/1_Prereq.ps1 b/Scripts/1_Prereq.ps1 index 25d78dfd..8f5e612d 100644 --- a/Scripts/1_Prereq.ps1 +++ b/Scripts/1_Prereq.ps1 @@ -1,43 +1,39 @@ -# Verify Running as Admin -$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") -If (!( $isAdmin )) { - Write-Host "-- Restarting as Administrator" -ForegroundColor Cyan ; Sleep -Seconds 1 - Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs - exit -} - -# Skipping 10 lines because if running when all prereqs met, statusbar covers powershell output - +<# +.Synopsis + Preps for the creation of the Lab VMs +.Description + Creates the required directories; downloads the tools diskspd.exe, convert-windowsimage.ps1, and the required DSC resources +.EXAMPLE + .\1_Prereq.ps1 +.EXAMPLE + .\1_Prereq.ps1 -verbose +#> +#Requires -RunAsAdministrator +[CmdletBinding()] +param() + +Write-Verbose -Message "Skipping 10 lines because if running when all prereqs met, statusbar covers powershell output" 1..10 |% { Write-Host ""} -# Functions -function Get-WindowsBuildNumber { - $os = Get-WmiObject -Class Win32_OperatingSystem - return [int]($os.BuildNumber) -} +Write-Verbose -Message "Loading Functions" +. .\functions.ps1 - -# Get workdirectory and Start Time -$workdir = Split-Path $script:MyInvocation.MyCommand.Path -Start-Transcript -Path $workdir'\Prereq.log' +Write-Verbose -Message "Get workdirectory and Start Time" +$workdir = Get-ScriptDirectory +Start-Transcript -Path $workdir'\Prereq.log' -Append $StartDateTime = get-date -Write-host "Script started at $StartDateTime" - -# Checking for Compatible OS -Write-Host "Checking if OS is Windows 10 TH2/Server 2016 TP4 or newer" -ForegroundColor Cyan +Write-Information -MessageData "Script started at $StartDateTime" -InformationAction Continue +Write-Verbose -Message "Checking if OS is Windows 10 TH2/Server 2016 TP4 or newer" $BuildNumber=Get-WindowsBuildNumber if ($BuildNumber -ge 10586){ - Write-Host "`t OS is Windows 10 TH2/Server 2016 TP4 or newer" -ForegroundColor Green + Write-Verbose -Message "OS is Windows 10 TH2/Server 2016 TP4 or newer" }else{ - Write-Host "`t Windows 10/ Server 2016 not detected. Exiting" -ForegroundColor Red - Write-Host "Press any key to continue ..." - $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL - $HOST.UI.RawUI.Flushinputbuffer() - Exit + Write-Error "Windows 10/ Server 2016 not detected. Exiting" + Exit } -# Checking Folder Structure +Write-Verbose -Message "Checking Folder Structure" 'OSClient','OSServer','Tools\DSC','Tools\ToolsVHD\DiskSpd','OSServer\Packages','OSClient\Packages' | ForEach-Object { if (!( Test-Path "$Workdir\$_" )) { New-Item -Type Directory -Path "$workdir\$_" } } @@ -52,72 +48,73 @@ If (!( Test-Path -Path "$workdir\OSServer\Packages\Copy_MSU_or_Cab_packages_here If (!( Test-Path -Path "$workdir\OSClient\Packages\Copy_MSU_or_Cab_packages_here.txt" )) { New-Item -Path "$workdir\OSClient\Packages\" -Name Copy_MSU_or_Cab_packages_here.txt -ItemType File } -# Downloading diskspd if its not in tools folder +Write-Verbose -Message "Downloading diskspd if its not in tools folder" Switch ( Test-Path -Path "$workdir\Tools\ToolsVHD\DiskSpd\diskspd.exe" ) { $false { - Write-Host "Downloading diskspd" -ForegroundColor Cyan + Write-Information -MessageData "Downloading diskspd" -InformationAction Continue + try{ + $webcontent = Invoke-WebRequest -Uri aka.ms/diskspd + $downloadurl = $webcontent.BaseResponse.ResponseUri.AbsoluteUri.Substring(0,$webcontent.BaseResponse.ResponseUri.AbsoluteUri.LastIndexOf('/'))+($webcontent.Links | where-object { $_.'data-url' -match '/Diskspd.*zip$' }|Select-Object -ExpandProperty "data-url") + Invoke-WebRequest -Uri $downloadurl -OutFile "$workdir\Tools\ToolsVHD\DiskSpd\diskspd.zip" + }catch{ + Write-Error -Message "Failed to download diskspd!" + } + Write-Verbose -Message "Unnzipping and extracting just diskspd.exe x64" + Expand-Archive "$workdir\Tools\ToolsVHD\DiskSpd\diskspd.zip" -DestinationPath "$workdir\Tools\ToolsVHD\DiskSpd\Unzip" + Copy-Item -Path (Get-ChildItem -Path "$workdir\tools\toolsvhd\diskspd\" -Recurse | Where-Object {$_.Directory -like '*amd64fre*' -and $_.name -eq 'diskspd.exe' }).fullname -Destination "$workdir\Tools\ToolsVHD\DiskSpd\" + Remove-Item -Path "$workdir\Tools\ToolsVHD\DiskSpd\diskspd.zip" + Remove-Item -Path "$workdir\Tools\ToolsVHD\DiskSpd\Unzip" -Recurse -Force - $webcontent = Invoke-WebRequest -Uri aka.ms/diskspd - $downloadurl = $webcontent.BaseResponse.ResponseUri.AbsoluteUri.Substring(0,$webcontent.BaseResponse.ResponseUri.AbsoluteUri.LastIndexOf('/'))+($webcontent.Links | where-object { $_.'data-url' -match '/Diskspd.*zip$' }|Select-Object -ExpandProperty "data-url") - Invoke-WebRequest -Uri $downloadurl -OutFile "$workdir\Tools\ToolsVHD\DiskSpd\diskspd.zip" - # Unnzipping and extracting just diskspd.exe x64 - Expand-Archive "$workdir\Tools\ToolsVHD\DiskSpd\diskspd.zip" -DestinationPath "$workdir\Tools\ToolsVHD\DiskSpd\Unzip" - Copy-Item -Path (Get-ChildItem -Path "$workdir\tools\toolsvhd\diskspd\" -Recurse | Where-Object {$_.Directory -like '*amd64fre*' -and $_.name -eq 'diskspd.exe' }).fullname -Destination "$workdir\Tools\ToolsVHD\DiskSpd\" - Remove-Item -Path "$workdir\Tools\ToolsVHD\DiskSpd\diskspd.zip" - Remove-Item -Path "$workdir\Tools\ToolsVHD\DiskSpd\Unzip" -Recurse -Force } } -# Download convert-windowsimage if its not in tools folder +Write-Verbose -Message "Download convert-windowsimage if its not in tools folder" Switch ( Test-Path -Path "$workdir\Tools\convert-windowsimage.ps1" ) { $false { - Write-Host "Downloading Convert-WindowsImage" -ForegroundColor Cyan - Invoke-WebRequest -Uri https://raw.githubusercontent.com/Microsoft/Virtualization-Documentation/master/hyperv-tools/Convert-WindowsImage/Convert-WindowsImage.ps1 -OutFile "$workdir\Tools\convert-windowsimage.ps1" + Write-Information -MessageData "Downloading Convert-WindowsImage" -InformationAction Continue + try { + Invoke-WebRequest -Uri https://raw.githubusercontent.com/Microsoft/Virtualization-Documentation/master/hyperv-tools/Convert-WindowsImage/Convert-WindowsImage.ps1 -OutFile "$workdir\Tools\convert-windowsimage.ps1" + } + catch { + Write-Error -Message "Failed to download Convert-WindowsImage!" + } } } -# Specify needed modules and required version +Write-Verbose -Message "Specify needed modules and required version" $modules=("xActiveDirectory","2.10.0.0"),("xDHCpServer","1.3.0.0"),("xNetworking","2.8.0.0"),("xPSDesiredStateConfiguration","3.9.0.0") -# Downloading modules into Tools folder if needed. +Write-Verbose -Message "Downloading modules into Tools folder if needed." foreach ($module in $modules){ - #testing if modules are present - Write-Host "Testing if modules are present" -ForegroundColor Cyan + Write-Verbose -Message "Testing if modules are present" $modulename=$module[0] $moduleversion=$module[1] if (!(Test-Path $workdir'\Tools\DSC\'$modulename'\')){ - Write-Host "Module $module not found... Downloading" - ### Install NuGET package provider ### - if ((Get-PackageProvider -Name NuGet) -eq $null){ + Write-Information -MessageData "Module $module not found... Downloading" + if ((Get-PackageProvider -Name NuGet) -eq $null){ + Write-Verbose -Message "Install NuGET package provider" Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force } Find-DscResource -moduleName $modulename -RequiredVersion $moduleversion | Save-Module -Path $workdir'\Tools\DSC' }else{ - Write-Host "Module $modulename version found... Skipping Download" + Write-Information -MessageData "Module $modulename version found... Skipping Download" -InformationAction Continue } } -# Installing modules if needed +Write-Verbose -Message "Installing modules if needed" foreach ($module in $modules) { - Write-Host "Testing DSC Module $module Presence" -ForegroundColor Cyan - # Check if Module is installed + Write-Information -MessageData "Testing DSC Module $module Presence" if ((Get-DscResource -Module $Module[0] | where-object {$_.version -eq $module[1]}) -eq $Null) { - # module is not installed - install it - Write-Host "Module $module will be installed" + Write-Information -MessageData "Module $module will be installed" -InformationAction Continue $modulename=$module[0] $moduleversion=$module[1] Copy-item -Path "$workdir\Tools\DSC\$modulename" -Destination "C:\Program Files\WindowsPowerShell\Modules" -Recurse -Force - Write-Host "Module was installed." -ForegroundColor Green + Write-Information -MessageData "Module was installed." -InformationAction Continue Get-DscResource -Module $modulename - Write-Host "" } else { - # module is already installed - Write-Host "Module $Module is already installed" - Write-Host "" + Write-Information -MessageData "Module $Module is already installed" -InformationAction Continue } } # finishing -Write-Host "Script finished at $(Get-date) and took $(((get-date) - $StartDateTime).TotalMinutes) Minutes" -Stop-Transcript -Write-Host "Press any key to continue..." -ForegroundColor Green -$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL \ No newline at end of file +Write-Information -MessageData "Script finished at $(Get-date) and took $(((get-date) - $StartDateTime).TotalMinutes) Minutes" -InformationAction Continue +Stop-Transcript \ No newline at end of file diff --git a/Scripts/2_CreateParentDisks.ps1 b/Scripts/2_CreateParentDisks.ps1 index d6f79635..1782896f 100644 --- a/Scripts/2_CreateParentDisks.ps1 +++ b/Scripts/2_CreateParentDisks.ps1 @@ -1,234 +1,100 @@ -# Verify Running as Admin -$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") -If (!( $isAdmin )) { - Write-Host "-- Restarting as Administrator" -ForegroundColor Cyan ; Sleep -Seconds 1 - Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs - exit -} +#Requires -RunAsAdministrator +[CmdletBinding()] +param() -###Get workdirectory### -$workdir=Split-Path $script:MyInvocation.MyCommand.Path +Write-Verbose -Message "Loading Functions" +. .\functions.ps1 -###Start LOG### -Start-Transcript -Path $workdir\CreateParentDisks.log +Write-Verbose -Message "Get workdirectory and Start Time" +$workdir = Get-ScriptDirectory +Start-Transcript -Path $workdir\CreateParentDisks.log -Append $StartDateTime = get-date -Write-host "Script started at $StartDateTime" - -#Temp variables +Write-Output "Script started at $StartDateTime" -##Load Variables.... +Write-Verbose -Message "Load Variables Script" . "$($workdir)\variables.ps1" -#Variables -################################## +#region Variables $AdminPassword=$LabConfig.AdminPassword $Switchname='DC_HydrationSwitch' $VMName='DC' -################################## - - -############# -# Functions # -############# - -#Create Unattend for VHD -Function Create-UnattendFileVHD{ - param ( - [parameter(Mandatory=$true)] - [string] - $Computername, - [parameter(Mandatory=$true)] - [string] - $AdminPassword, - [parameter(Mandatory=$true)] - [string] - $Path - ) +#endregion - if ( Test-Path "$path\Unattend.xml" ) { - Remove-Item "$Path\Unattend.xml" - } - $unattendFile = New-Item "$Path\Unattend.xml" -type File - $fileContent = @" - - - - - - 1 - - - - - $Computername - PFE - Contoso - - - - - - - $AdminPassword - true</PlainText> - </AdministratorPassword> - </UserAccounts> - <OOBE> - <HideEULAPage>true</HideEULAPage> - <SkipMachineOOBE>true</SkipMachineOOBE> - <SkipUserOOBE>true</SkipUserOOBE> - </OOBE> - </component> - </settings> -</unattend> - -"@ - - Set-Content -path $unattendFile -value $fileContent - - #return the file object - Return $unattendFile -} -############## -# Lets start # -############## -#Check if Hyper-V is installed. -Write-Host "Checking if Hyper-V is installed" -ForegroundColor Cyan +Write-Information -MessageData "Checking if Hyper-V is installed" -InformationAction Continue if ((Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V).state -eq 'Enabled'){ - Write-Host "`t Hyper-V is Installed" -ForegroundColor Green + Write-Verbose -Message "Hyper-V is Installed" }else{ - Write-Host "`t Hyper-V not installed. Please install hyper-v feature including Hyper-V management tools. Exiting" -ForegroundColor Red - Write-Host "Press any key to continue ..." - $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL - $HOST.UI.RawUI.Flushinputbuffer() + Write-Error "Hyper-V not installed. Please install hyper-v feature including Hyper-V management tools. Exiting" Exit } -## Test for unpacked media - detect install.wim +Write-Verbose -Message "Test for unpacked media - detect install.wim" If (Test-Path -Path "$workdir\OSServer\Sources\install.wim"){ - Write-Host "ISO content found under $workdir\OSServer folder" -ForegroundColor Green - $ServerMediaPath="$workdir\OSServer" + $ServerMediaPath="$workdir\OSServer" + Write-Information -MessageData "Server install.wim found under $ServerMediaPath " -InformationAction Continue }else{ - ## Test for ISO and if no ISO found, open file dialog to select one - If (Test-Path -Path "$workdir\OSServer"){ - $ISOServer = Get-ChildItem -Path "$workdir\OSServer" -Recurse -Include '*.iso' -ErrorAction SilentlyContinue - } - - if ( -not [bool]($ISOServer)){ - Write-Host "No ISO found in $Workdir\OSServer" -ForegroundColor Green - Write-Host "please select ISO file with Windows Server 2016 wim file. Please use TP5 and newer" -ForegroundColor Green - - [reflection.assembly]::loadwithpartialname(“System.Windows.Forms”) - $openFile = New-Object System.Windows.Forms.OpenFileDialog - $openFile.Filter = “iso files (*.iso)|*.iso|All files (*.*)|*.*” - If($openFile.ShowDialog() -eq “OK”) - { - Write-Output "File $openfile.name selected" -ForegroundColor Green - } - if (!$openFile.FileName){ - Write-Host "Iso was not selected... Exitting" -ForegroundColor Red - Write-Host "Press any key to continue ..." - $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL - $HOST.UI.RawUI.Flushinputbuffer() - Exit - } - $ISOServer = Mount-DiskImage -ImagePath $openFile.FileName -PassThru - }else { - Write-Host "Found ISO $($ISOServer.FullName)" -ForegroundColor Green - $ISOServer = Mount-DiskImage -ImagePath $ISOServer.FullName -PassThru - } + Write-Verbose -Message "Test for Server ISO and mount if found" + $ISOServer = Mount-ISO -Path "$workdir\OSServer" $ServerMediaPath = (Get-Volume -DiskImage $ISOServer).DriveLetter+':' } If ($LabConfig.CreateClientParent -eq "Yes"){ If (Test-Path -Path "$workdir\OSClient\Sources\install.wim"){ - Write-Host "ISO content found under $workdir\OSClient folder" -ForegroundColor Green - $ClientMediaPath="$workdir\OSClient" + $ClientMediaPath="$workdir\OSClient" + Write-Information -MessageData "Client install.wim found under $ClientMediaPath" -InformationAction Continue }else{ - ## Test for ISO and if no ISO found, open file dialog to select one - If (Test-Path -Path "$workdir\OSClient"){ - $ISOClient = Get-ChildItem -Path "$workdir\OSClient" -Recurse -Include '*.iso' -ErrorAction SilentlyContinue - } - - if ( -not [bool]($ISOClient)){ - Write-Host "No ISO found in $Workdir\OSOSClient" -ForegroundColor Green - Write-Host "please select ISO file with Windows 10 wim file. Please use 10586 and newer" -ForegroundColor Green - - [reflection.assembly]::loadwithpartialname(“System.Windows.Forms”) - $openFile = New-Object System.Windows.Forms.OpenFileDialog - $openFile.Filter = “iso files (*.iso)|*.iso|All files (*.*)|*.*” - If($openFile.ShowDialog() -eq “OK”){ - Write-Output "File $openfile.name selected" -ForegroundColor Green - } - if (!$openFile.FileName){ - Write-Host "Iso was not selected... Exitting" -ForegroundColor Red - Write-Host "Press any key to continue ..." - $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL - $HOST.UI.RawUI.Flushinputbuffer() - Exit - } - $ISOClient = Mount-DiskImage -ImagePath $openFile.FileName -PassThru - }else { - Write-Host "Found ISO $($ISOClient.FullName)" -ForegroundColor Green - $ISOClient = Mount-DiskImage -ImagePath $ISOClient.FullName -PassThru - } + Write-Verbose -Message "Test for Client ISO and mount if found" + $ISOClient = Mount-ISO -Path "$workdir\OSClient" $ClientMediaPath = (Get-Volume -DiskImage $ISOClient).DriveLetter+':' } } -#grab server packages +Write-Verbose -Message "grab packages" $ServerPackages=Get-ChildItem "$workdir\OSServer\Packages" -Recurse | where {$_.Extension -eq ".msu" -or $_.Extension -eq ".cab"} -$ClientPackages=Get-ChildItem "$workdir\OSClient\Packages" -Recurse | where {$_.Extension -eq ".msu" -or $_.Extension -eq ".cab"} - if ($ServerPackages -ne $null){ -Write-Host "Server Packages Found" -ForegroundColor Cyan -$ServerPackages | ForEach-Object {Write-Host $_.Name} + Write-Information -MessageData "Server Packages Found" -InformationAction Continue + $ServerPackages | ForEach-Object {Write-Information -MessageData $_.Name -InformationAction Continue} } +If ($LabConfig.CreateClientParent -eq "Yes"){ + $ClientPackages=Get-ChildItem "$workdir\OSClient\Packages" -Recurse | where {$_.Extension -eq ".msu" -or $_.Extension -eq ".cab"} + if ($ClientPackages -ne $null){ + Write-Information -MessageData "Client Packages Found" -InformationAction Continue + $ClientPackages | ForEach-Object {Write-Information -MessageData $_.Name -InformationAction Continue} + } -if ($ClientPackages -ne $null){ -Write-Host "Client Packages Found" -ForegroundColor Cyan -$ClientPackages | ForEach-Object {Write-Host $_.Name} +} +("ParentDisks","Temp","Temp\mountdir","Temp\packages","Tools\dism")|ForEach-Object{ + If(!(Test-Path -Path $_)){ + Write-Verbose -Message "Creating $workdir\$_" + New-Item -Type Directory -Path "$workdir\$_" + } } -New-Item -Type Directory -Path "$workdir\ParentDisks" -New-Item -Type Directory -Path "$workdir\Temp" -Force -New-Item -Type Directory -Path "$workdir\Temp\mountdir" -New-Item -Type Directory -Path "$workdir\Tools\dism" -New-Item -Type Directory -Path "$workdir\Temp\packages" . "$workdir\tools\convert-windowsimage.ps1" +Write-Verbose -Message "Create Server Core Parent disk" Convert-WindowsImage -SourcePath $ServerMediaPath'\sources\install.wim' -Edition ServerDataCenterCore -VHDPath $workdir'\ParentDisks\Win2016Core_G2.vhdx' -SizeBytes 30GB -VHDFormat VHDX -DiskLayout UEFI -#Create client OS VHD If ($LabConfig.CreateClientParent -eq "Yes"){ -Convert-WindowsImage -SourcePath $ClientMediaPath'\sources\install.wim' -Edition $LabConfig.ClientEdition -VHDPath $workdir'\ParentDisks\Win10_G2.vhdx' -SizeBytes 30GB -VHDFormat VHDX -DiskLayout UEFI + Write-Verbose -Message "Create Client Parent disk" + Convert-WindowsImage -SourcePath $ClientMediaPath'\sources\install.wim' -Edition $LabConfig.ClientEdition -VHDPath $workdir'\ParentDisks\Win10_G2.vhdx' -SizeBytes 30GB -VHDFormat VHDX -DiskLayout UEFI } - -#copy dism tools  -   +Write-Verbose -Message "copy dism tools from Server Image" Copy-Item -Path $ServerMediaPath'\sources\api*downlevel*.dll' -Destination $workdir\Tools\dism Copy-Item -Path $ServerMediaPath'\sources\*provider*' -Destination $workdir\Tools\dism Copy-Item -Path $ServerMediaPath'\sources\*dism*' -Destination $workdir\Tools\dism Copy-Item -Path $ServerMediaPath'\nanoserver\packages\*' -Destination $workdir\Temp\packages\ -Recurse - #Old way #Todo: use the tool for NanoServer if (Test-Path -Path $ServerMediaPath'\nanoserver\Packages\en-us\*en-us*'){ #vnext version + Write-Verbose -Message "Create Nano Server Parent disk" Convert-WindowsImage -SourcePath $ServerMediaPath'\Nanoserver\NanoServer.wim' -edition 2 -VHDPath $workdir'\ParentDisks\Win2016Nano_G2.vhdx' -SizeBytes 30GB -VHDFormat VHDX -DiskLayout UEFI &"$workdir\Tools\dism\dism" /Mount-Image /ImageFile:$workdir\Parentdisks\Win2016Nano_G2.vhdx /Index:1 /MountDir:$workdir\Temp\mountdir &"$workdir\Tools\dism\dism" /Add-Package /PackagePath:$workdir\Temp\packages\Microsoft-NanoServer-DSC-Package.cab /Image:$workdir\Temp\mountdir @@ -242,7 +108,8 @@ if (Test-Path -Path $ServerMediaPath'\nanoserver\Packages\en-us\*en-us*'){ &"$workdir\Tools\dism\dism" /Add-Package /PackagePath:$workdir\Temp\packages\Microsoft-NanoServer-SCVMM-Package.cab /Image:$workdir\Temp\mountdir &"$workdir\Tools\dism\dism" /Add-Package /PackagePath:$workdir\Temp\packages\en-us\Microsoft-NanoServer-SCVMM-Package_en-us.cab /Image:$workdir\Temp\mountdir &"$workdir\Tools\dism\dism" /Unmount-Image /MountDir:$workdir\Temp\mountdir /Commit - + + Write-Verbose -Message "Create Nano Server HV Parent disk" Copy-Item -Path "$workdir\Parentdisks\Win2016Nano_G2.vhdx" -Destination "$workdir\ParentDisks\Win2016NanoHV_G2.vhdx"   &"$workdir\Tools\dism\dism" /Mount-Image /ImageFile:$workdir\Parentdisks\Win2016NanoHV_G2.vhdx /Index:1 /MountDir:$workdir\Temp\mountdir @@ -252,36 +119,32 @@ if (Test-Path -Path $ServerMediaPath'\nanoserver\Packages\en-us\*en-us*'){ &"$workdir\Tools\dism\dism" /Add-Package /PackagePath:$workdir\Temp\packages\en-us\Microsoft-NanoServer-SCVMM-Compute-Package_en-us.cab /Image:$workdir\Temp\mountdir &"$workdir\Tools\dism\dism" /Unmount-Image /MountDir:$workdir\Temp\mountdir /Commit - #do some servicing - 'Win2016Core_G2.vhdx','Win2016Nano_G2.vhdx','Win2016NanoHV_G2.vhdx' | ForEach-Object { - &"$workdir\Tools\dism\dism" /Mount-Image /ImageFile:$workdir\Parentdisks\$_ /Index:1 /MountDir:$workdir\Temp\mountdir - $ServerPackages | ForEach-Object { - $packagepath=$_.FullName - &"$workdir\Tools\dism\dism" /Add-Package /PackagePath:$packagepath /Image:$workdir\Temp\mountdir - } - &"$workdir\Tools\dism\dism" /Unmount-Image /MountDir:$workdir\Temp\mountdir /Commit - } - - If ($LabConfig.CreateClientParent -eq "Yes"){ - &"$workdir\Tools\dism\dism" /Mount-Image /ImageFile:$workdir\Parentdisks\Win10_G2.vhdx /Index:1 /MountDir:$workdir\Temp\mountdir - $ClientPackages | ForEach-Object { - $packagepath=$_.FullName - &"$workdir\Tools\dism\dism" /Add-Package /PackagePath:$packagepath /Image:$workdir\Temp\mountdir - } - &"$workdir\Tools\dism\dism" /Unmount-Image /MountDir:$workdir\Temp\mountdir /Commit - } - }else{ - - Write-Host "`t Please use Windows Server TP5 and newer. Exiting" -ForegroundColor Red - Write-Host "Press any key to continue ..." - $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL - $HOST.UI.RawUI.Flushinputbuffer() + Write-Error "Please use Windows Server TP5 and newer. Exiting" Exit } -#create Tools VHDX +Write-Verbose -Message "do some servicing" +'Win2016Core_G2.vhdx','Win2016Nano_G2.vhdx','Win2016NanoHV_G2.vhdx' | ForEach-Object { + &"$workdir\Tools\dism\dism" /Mount-Image /ImageFile:$workdir\Parentdisks\$_ /Index:1 /MountDir:$workdir\Temp\mountdir + $ServerPackages | ForEach-Object { + $packagepath=$_.FullName + &"$workdir\Tools\dism\dism" /Add-Package /PackagePath:$packagepath /Image:$workdir\Temp\mountdir + } + &"$workdir\Tools\dism\dism" /Unmount-Image /MountDir:$workdir\Temp\mountdir /Commit +} + +If ($LabConfig.CreateClientParent -eq "Yes"){ + &"$workdir\Tools\dism\dism" /Mount-Image /ImageFile:$workdir\Parentdisks\Win10_G2.vhdx /Index:1 /MountDir:$workdir\Temp\mountdir + $ClientPackages | ForEach-Object { + $packagepath=$_.FullName + &"$workdir\Tools\dism\dism" /Add-Package /PackagePath:$packagepath /Image:$workdir\Temp\mountdir + } + &"$workdir\Tools\dism\dism" /Unmount-Image /MountDir:$workdir\Temp\mountdir /Commit +} + +Write-Verbose -Message "create Tools VHDX" $vhd=New-VHD -Path "$workdir\ParentDisks\tools.vhdx" -SizeBytes 30GB -Dynamic $VHDMount = Mount-VHD $vhd.Path -Passthru @@ -296,21 +159,19 @@ if (!$VHDPathTest){ } if ($VHDPathTest){ - Write-Host "Found $workdir\Tools\ToolsVHD\*, copying files into VHDX" + Write-Information -MessageData "Found $workdir\Tools\ToolsVHD\*, copying files into VHDX" -InformationAction Continue Copy-Item -Path "$workdir\Tools\ToolsVHD\*" -Destination ($vhddiskpart.DriveLetter+':\') -Recurse -Force }else{ - write-host "Files not found" - Write-Host "Add required tools into $workdir\Tools\toolsVHD and Press any key to continue..." -ForegroundColor Green + Write-Warning -Message "Files not found" + Write-Warning -Message "Add required tools into $workdir\Tools\toolsVHD and Press any key to continue..." $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL Copy-Item -Path "$workdir\Tools\ToolsVHD\*" -Destination ($vhddiskpart.DriveLetter+':\') -Recurse -Force } Dismount-VHD $vhddisk.Number -############## -# Hydrate DC # -############## +#region Hydrate DC $workdir $vhdpath=$workdir+'\LAB\'+$VMName+'\Virtual Hard Disks\'+$VMName+'.vhdx' $VMPath=$Workdir+'\LAB\' @@ -648,42 +509,50 @@ Copy-Item -Path "$workdir\Temp\config\dc.meta.mof" -Destination "$workdir\Temp\m #Start and wait for configuration -$DC | Start-VM - -$VMStartupTime = 250 -Write-host "Configuring DC takes a while" -Write-host "Initial configuration in progress. Sleeping $VMStartupTime seconds" -Start-Sleep $VMStartupTime - -do{ - $test=Invoke-Command -VMGuid $DC.id -ScriptBlock {Get-DscConfigurationStatus} -Credential $cred -ErrorAction SilentlyContinue - if ($test -eq $null) { - Write-Host "Configuration in Progress. Sleeping 10 seconds" - }else{ - Write-Host "Current DSC state: $($test.status), ResourncesNotInDesiredState: $($test.resourcesNotInDesiredState.count), ResourncesInDesiredState: $($test.resourcesInDesiredState.count). Sleeping 10 seconds" - Write-Host "Invoking DSC Configuration again" - Invoke-Command -VMGuid $DC.id -ScriptBlock {Start-DscConfiguration -UseExisting} -Credential $cred - } - Start-Sleep 10 -}until ($test.Status -eq 'Success' -and $test.rebootrequested -eq $false) -$test - -Invoke-Command -VMGuid $DC.id -ScriptBlock {redircmp 'OU=Workshop,DC=corp,DC=contoso,DC=com'} -Credential $cred -ErrorAction SilentlyContinue +try{ + $DC | Start-VM -ErrorAction Stop + + $VMStartupTime = 250 + Write-Information -MessageData "Configuring DC takes a while" -InformationAction Continue + Write-Information -MessageData "Initial configuration in progress. Sleeping $VMStartupTime seconds" -InformationAction Continue + Start-Sleep $VMStartupTime + + do{ + $test=Invoke-Command -VMGuid $DC.id -ScriptBlock {Get-DscConfigurationStatus} -Credential $cred -ErrorAction SilentlyContinue + if ($test -eq $null) { + Write-Information -MessageData "Configuration in Progress. Sleeping 10 seconds" -InformationAction Continue + }else{ + Write-Information -MessageData "Current DSC state: $($test.status), ResourncesNotInDesiredState: $($test.resourcesNotInDesiredState.count), ResourncesInDesiredState: $($test.resourcesInDesiredState.count). Sleeping 10 seconds" -InformationAction Continue + Write-Information -MessageData "Invoking DSC Configuration again" -InformationAction Continue + Invoke-Command -VMGuid $DC.id -ScriptBlock {Start-DscConfiguration -UseExisting} -Credential $cred + } + Start-Sleep 10 + }until ($test.Status -eq 'Success' -and $test.rebootrequested -eq $false) + $test -$DC | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter -$DC | Stop-VM + Invoke-Command -VMGuid $DC.id -ScriptBlock {redircmp 'OU=Workshop,DC=corp,DC=contoso,DC=com'} -Credential $cred -ErrorAction SilentlyContinue + $DC | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter + $DC | Stop-VM +}catch{ + $VMStartError = $true + Write-Warning -Message "Unable to Start VM!! " +} +#endregion -#cleanup +#region cleanup -###Backup VM Configuration ### -Copy-Item -Path "$vmpath\$VMNAME\Virtual Machines\" -Destination "$vmpath\$VMNAME\Virtual Machines_Bak\" -Recurse +Write-Verbose -Message "Backup VM Configuration" +If($VMStartError -eq $false){ + Copy-Item -Path "$vmpath\$VMNAME\Virtual Machines\" -Destination "$vmpath\$VMNAME\Virtual Machines_Bak\" -Recurse +} $DC | Remove-VM -Force Remove-Item -Path "$vmpath\$VMNAME\Virtual Machines\" -Recurse -Rename-Item -Path "$vmpath\$VMNAME\Virtual Machines_Bak\" -NewName 'Virtual Machines' -Compress-Archive -Path "$vmpath\$VMNAME\Virtual Machines\" -DestinationPath "$vmpath\$VMNAME\Virtual Machines.zip" - -###Cleanup The rest ### +If($VMStartError -eq $false){ + Rename-Item -Path "$vmpath\$VMNAME\Virtual Machines_Bak\" -NewName 'Virtual Machines' + Compress-Archive -Path "$vmpath\$VMNAME\Virtual Machines\" -DestinationPath "$vmpath\$VMNAME\Virtual Machines.zip" +} +Write-Verbose -Message "Cleanup the VMSwitch, temp directory, and Unmount any disk Images" Remove-VMSwitch -Name $Switchname -Force -ErrorAction SilentlyContinue if ($ISOServer -ne $Null){ $ISOServer | Dismount-DiskImage @@ -694,9 +563,6 @@ $ISOClient | Dismount-DiskImage } Remove-Item -Path "$workdir\temp" -Force -Recurse - -Write-Host "Script finished at $(Get-date) and took $(((get-date) - $StartDateTime).TotalMinutes) Minutes" - -Stop-Transcript -Write-Host "Job Done. Press any key to continue..." -ForegroundColor Green -$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL \ No newline at end of file +#endregion +Write-Output "Script finished at $(Get-date) and took $(((get-date) - $StartDateTime).TotalMinutes) Minutes" +Stop-Transcript \ No newline at end of file diff --git a/Scripts/Functions.ps1 b/Scripts/Functions.ps1 new file mode 100644 index 00000000..5d4cfd53 --- /dev/null +++ b/Scripts/Functions.ps1 @@ -0,0 +1,206 @@ +<# +Function script to hold all the common fuctions - Longterm this should be a module +#> +<# +.Synopsis + Check the supplied path for a single ISO and Mount it. Will display a dialog to prompt for a new path if a ISO is not found in the supplied path. +.EXAMPLE + Mount-ISO -path .\OSSever +#> +function Mount-ISO { + [CmdletBinding()] + param ( + #Path + [parameter(Mandatory=$true)] + [string]$Path + ) + Write-Verbose -Message "Test for ISO and if no ISO found, open file dialog to select one" + If (Test-Path -Path $Path){ + $ISO = Get-ChildItem -Path $Path -Recurse -Include '*.iso' -ErrorAction SilentlyContinue + } + + if (!($ISO)){ + Write-Verbose -Message "No ISO found in $Path" + [reflection.assembly]::loadwithpartialname("System.Windows.Forms") + $openFile = New-Object System.Windows.Forms.OpenFileDialog + $openFile.Title = "Select a Windows OS install ISO" + $openFile.Filter = "iso files (*.iso)|*.iso|All files (*.*)|*.*" + If($openFile.ShowDialog() -eq "OK") + { + Write-Verbose -Message "File $openfile.name selected" + } + if (!$openFile.FileName){ + throw "Iso was not selected... Exiting" + } + $ISO = Mount-DiskImage -ImagePath $openFile.FileName -PassThru + }else { + If($ISO.count -eq 1){ + Write-Verbose -Message "Found ISO $($ISO.FullName)" + Mount-DiskImage -ImagePath $ISO.FullName -PassThru + }else{ + throw "There can be only one!! Ensure that there is only one iso file in $Path " + } + } +} + +<# +.Synopsis + Create a unattened.xml +.EXAMPLE + Create-UnattendFileBlob -Blob $blob.Substring(0,$blob.Length-1) -AdminPassword "NeverUsePasswordAsAPassword" +#> +Function Create-UnattendFileVHD{ + [CmdletBinding()] + param ( + #ComputerName + [parameter(Mandatory=$true)] + [string]$Computername, + #Local Admin Password + [parameter(Mandatory=$true)] + [string]$AdminPassword, + #Path + [parameter(Mandatory=$true)] + [string]$Path + ) + + if ( Test-Path "$path\Unattend.xml" ) { + Remove-Item "$Path\Unattend.xml" + } + $unattendFile = New-Item "$Path\Unattend.xml" -type File + $fileContent = @" +<?xml version='1.0' encoding='utf-8'?> +<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + + <settings pass="offlineServicing"> + <component + xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + language="neutral" + name="Microsoft-Windows-PartitionManager" + processorArchitecture="amd64" + publicKeyToken="31bf3856ad364e35" + versionScope="nonSxS" + > + <SanPolicy>1</SanPolicy> + </component> + </settings> + <settings pass="specialize"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <ComputerName>$Computername</ComputerName> + <RegisteredOwner>PFE</RegisteredOwner> + <RegisteredOrganization>Contoso</RegisteredOrganization> + </component> + </settings> + <settings pass="oobeSystem"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <UserAccounts> + <AdministratorPassword> + <Value>$AdminPassword</Value> + <PlainText>true</PlainText> + </AdministratorPassword> + </UserAccounts> + <OOBE> + <HideEULAPage>true</HideEULAPage> + <SkipMachineOOBE>true</SkipMachineOOBE> + <SkipUserOOBE>true</SkipUserOOBE> + </OOBE> + </component> + </settings> +</unattend> + +"@ + + Set-Content -path $unattendFile -value $fileContent + + #return the file object + Return $unattendFile +} + +<# +.Synopsis + Create a unattened.xml with domain join Blob +.EXAMPLE + Create-UnattendFileBlob -Blob $blob.Substring(0,$blob.Length-1) -AdminPassword "NeverUsePasswordAsAPassword" +#> +Function Create-UnattendFileBlob{ + [CmdletBinding()] + param( + #Domain join Blob + [parameter(Mandatory=$true)] + [string]$Blob, + #Local Admin Password + [parameter(Mandatory=$true)] + [string]$AdminPassword, + #Path to create Unattend.xml + [parameter()] + [string]$Path="Unattend.xml" + ) + + if(Test-Path -Path $Path){remove-item -Path $Path} + $unattendFile = New-Item -Path $path -type File + $fileContent = @" +<?xml version='1.0' encoding='utf-8'?> +<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + + <settings pass="offlineServicing"> + <component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <OfflineIdentification> + <Provisioning> + <AccountData>$Blob</AccountData> + </Provisioning> + </OfflineIdentification> + </component> + </settings> + + <settings pass="oobeSystem"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <UserAccounts> + <AdministratorPassword> + <Value>$AdminPassword</Value> + <PlainText>true</PlainText> + </AdministratorPassword> + </UserAccounts> + <TimeZone>Pacific Standard Time</TimeZone> + </component> + </settings> + + <settings pass="specialize"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <RegisteredOwner>PFE</RegisteredOwner> + <RegisteredOrganization>Contoso</RegisteredOrganization> + </component> + </settings> +</unattend> + +"@ + + Set-Content -Path $unattendFile -Value $fileContent + + #return the file object + $unattendFile|Write-Output +} + +<# +.Synopsis + returns current Script Directory +.EXAMPLE + Get-ScriptDirectory +#> +Function Get-ScriptDirectory{ + [CmdletBinding()] + param() + Split-Path -Path $script:MyInvocation.MyCommand.Path|Write-Output +} + +<# +.Synopsis + returns Windows Build Number +.EXAMPLE + Get-ScriptDirectory +#> +function Get-WindowsBuildNumber { + [CmdletBinding()] + param() + $os = Get-CimInstance -ClassName Win32_OperatingSystem + [int]($os.BuildNumber)|Write-Output +} \ No newline at end of file