From 32c32d34929e568f0693d70327193c1525fc9b2b Mon Sep 17 00:00:00 2001 From: "Michael Bernstein (DPLAT)" Date: Wed, 12 Jun 2024 16:09:42 -0700 Subject: [PATCH 1/7] Refactor DC Hydration function out from 2_CreateParentDisks.ps1 - Refactor the DC Hydration code out - Add a mode to avoid dehydrating the DC and to instead create it during deployment - Add support for Server 2025 images by name --- Scripts/0_DCHydrate.ps1 | 631 +++++++++++++++++++++++++++++ Scripts/2_CreateParentDisks.ps1 | 680 +++----------------------------- Scripts/3_Deploy.ps1 | 59 ++- build.ps1 | 2 +- 4 files changed, 728 insertions(+), 644 deletions(-) create mode 100644 Scripts/0_DCHydrate.ps1 diff --git a/Scripts/0_DCHydrate.ps1 b/Scripts/0_DCHydrate.ps1 new file mode 100644 index 00000000..e335f28f --- /dev/null +++ b/Scripts/0_DCHydrate.ps1 @@ -0,0 +1,631 @@ +#region CreateUnattendFileVHD + +#Create Unattend for VHD + Function CreateUnattendFileVHD { + param ( + [parameter(Mandatory=$true)] + [string] + $Computername, + [parameter(Mandatory=$true)] + [string] + $AdminPassword, + [parameter(Mandatory=$true)] + [string] + $Path, + [parameter(Mandatory=$true)] + [string] + $TimeZone + ) + + if ( Test-Path "$path\Unattend.xml" ) { + Remove-Item "$Path\Unattend.xml" + } + $unattendFile = New-Item "$Path\Unattend.xml" -type File + + $fileContent = @" + + + + + + 1 + + + + + $Computername + $oeminformation + PFE + Contoso + + + + + + + $AdminPassword + true</PlainText> + </AdministratorPassword> + </UserAccounts> + <OOBE> + <HideEULAPage>true</HideEULAPage> + <SkipMachineOOBE>true</SkipMachineOOBE> + <SkipUserOOBE>true</SkipUserOOBE> + </OOBE> + <TimeZone>$TimeZone</TimeZone> + </component> + </settings> +</unattend> + +"@ + + Set-Content -path $unattendFile -value $fileContent + + #return the file object + Return $unattendFile + } +#endregion + +#region Hydrate DC +function Hydrate-DC { + + param( + [parameter(Mandatory=$true)] + [string]$DCName, + + [parameter(Mandatory=$true)] + [string]$VhdPath, + + [parameter(Mandatory=$true)] + [string]$VmPath, + + [parameter(Mandatory=$true)] + [string]$SwitchName, + + [parameter(Mandatory=$true)] + [string]$TimeZone, + + [parameter(Mandatory=$true)] + [string]$DhcpScope, + + [parameter(Mandatory=$true)] + [string]$AdminPassword) + + WriteInfoHighlighted "Starting DC Hydration" + $dcHydrationStartTime = Get-Date + + #DCHP scope + $ReverseDnsRecord = $DhcpScope -replace '^(\d+)\.(\d+)\.\d+\.(\d+)$','$3.$2.$1.in-addr.arpa' + $DhcpScope = $DhcpScope.Substring(0,$DhcpScope.Length-1) + + #If the switch does not already exist, then create a switch with the name $SwitchName + if (-not [bool](Get-VMSwitch -Name $SwitchName -ErrorAction SilentlyContinue)) { + WriteInfoHighlighted "`t Creating temp hydration switch $SwitchName" + New-VMSwitch -SwitchType Private -Name $SwitchName + } + + #create VM DC + WriteInfoHighlighted "`t Creating DC VM" + if ($LabConfig.DCVMVersion){ + $DC=New-VM -Name $DCName -VHDPath $VhdPath -MemoryStartupBytes 2GB -path $VmPath -SwitchName $SwitchName -Generation 2 -Version $LabConfig.DCVMVersion + }else{ + $DC=New-VM -Name $DCName -VHDPath $VhdPath -MemoryStartupBytes 2GB -path $VmPath -SwitchName $SwitchName -Generation 2 + } + $DC | Set-VMProcessor -Count 2 + $DC | Set-VMMemory -DynamicMemoryEnabled $true -MinimumBytes 2GB + if ($LabConfig.Secureboot -eq $False) {$DC | Set-VMFirmware -EnableSecureBoot Off} + if ($DC.AutomaticCheckpointsEnabled -eq $True){ + $DC | Set-VM -AutomaticCheckpointsEnabled $False + } + if ($LabConfig.InstallSCVMM -eq "Yes"){ + #SCVMM 2022 requires 4GB of memory + $DC | Set-VMMemory -StartupBytes 4GB -MinimumBytes 4GB + } + + #Apply Unattend to VM + if ($VMVersion.Build -ge 17763){ + $oeminformation=@" + <OEMInformation> + <SupportProvider>MSLab</SupportProvider> + <SupportURL>https://aka.ms/mslab</SupportURL> + </OEMInformation> +"@ + }else{ + $oeminformation=$null + } + + WriteInfoHighlighted "`t Applying Unattend and copying Powershell DSC Modules" + if (Test-Path $mountdir){ + Remove-Item -Path $mountdir -Recurse -Force + } + if (Test-Path "$PSScriptRoot\Temp\unattend"){ + Remove-Item -Path "$PSScriptRoot\Temp\unattend.xml" + } + $unattendfile=CreateUnattendFileVHD -Computername $DCName -AdminPassword $AdminPassword -path "$PSScriptRoot\temp\" -TimeZone $TimeZone + New-item -type directory -Path $mountdir -force + [System.Version]$VMVersion=(Get-WindowsImage -ImagePath $VHDPath -Index 1).Version + Mount-WindowsImage -Path $mountdir -ImagePath $VHDPath -Index 1 + Use-WindowsUnattend -Path $mountdir -UnattendPath $unattendFile + #&"$PSScriptRoot\Temp\dism\dism" /mount-image /imagefile:$VhdPath /index:1 /MountDir:$mountdir + #&"$PSScriptRoot\Temp\dism\dism" /image:$mountdir /Apply-Unattend:$unattendfile + New-item -type directory -Path "$mountdir\Windows\Panther" -force + Copy-Item -Path $unattendfile -Destination "$mountdir\Windows\Panther\unattend.xml" -force + Copy-Item -Path "$PSScriptRoot\Temp\DSC\*" -Destination "$mountdir\Program Files\WindowsPowerShell\Modules\" -Recurse -force + + #Create credentials for DSC + + $username = "$($LabConfig.DomainNetbiosName)\Administrator" + $password = $AdminPassword + $secstr = New-Object -TypeName System.Security.SecureString + $password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)} + $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr + + #Create DSC configuration + configuration DCHydration + { + param + ( + [Parameter(Mandatory)] + [pscredential]$safemodeAdministratorCred, + + [Parameter(Mandatory)] + [pscredential]$domainCred, + + [Parameter(Mandatory)] + [pscredential]$NewADUserCred + + ) + + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion "6.3.0" + Import-DscResource -ModuleName DnsServerDsc -ModuleVersion "3.0.0" + Import-DSCResource -ModuleName NetworkingDSC -ModuleVersion "9.0.0" + Import-DSCResource -ModuleName xDHCPServer -ModuleVersion "3.1.1" + Import-DSCResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion "9.1.0" + Import-DscResource -ModuleName PSDesiredStateConfiguration + + + Node $AllNodes.Where{$_.Role -eq "Parent DC"}.Nodename + + { + WindowsFeature ADDSInstall + { + Ensure = "Present" + Name = "AD-Domain-Services" + } + + WindowsFeature FeatureGPMC + { + Ensure = "Present" + Name = "GPMC" + DependsOn = "[WindowsFeature]ADDSInstall" + } + + WindowsFeature FeatureADPowerShell + { + Ensure = "Present" + Name = "RSAT-AD-PowerShell" + DependsOn = "[WindowsFeature]ADDSInstall" + } + + WindowsFeature FeatureADAdminCenter + { + Ensure = "Present" + Name = "RSAT-AD-AdminCenter" + DependsOn = "[WindowsFeature]ADDSInstall" + } + + WindowsFeature FeatureADDSTools + { + Ensure = "Present" + Name = "RSAT-ADDS-Tools" + DependsOn = "[WindowsFeature]ADDSInstall" + } + + WindowsFeature FeatureDNSTools + { + Ensure = "Present" + Name = "RSAT-DNS-Server" + DependsOn = "[WindowsFeature]ADDSInstall" + } + + ADDomain FirstDS + { + DomainName = $Node.DomainName + Credential = $domainCred + SafemodeAdministratorPassword = $safemodeAdministratorCred + DomainNetbiosName = $node.DomainNetbiosName + DependsOn = "[WindowsFeature]ADDSInstall" + } + + WaitForADDomain DscForestWait + { + DomainName = $Node.DomainName + Credential = $domainCred + DependsOn = "[ADDomain]FirstDS" + } + + ADOrganizationalUnit DefaultOU + { + Name = $Node.DefaultOUName + Path = $Node.DomainDN + ProtectedFromAccidentalDeletion = $true + Description = 'Default OU for all user and computer accounts' + Ensure = 'Present' + DependsOn = "[ADDomain]FirstDS" + } + + ADUser SQL_SA + { + DomainName = $Node.DomainName + Credential = $domainCred + UserName = "SQL_SA" + Password = $NewADUserCred + Ensure = "Present" + DependsOn = "[ADOrganizationalUnit]DefaultOU" + Description = "SQL Service Account" + Path = "OU=$($Node.DefaultOUName),$($Node.DomainDN)" + PasswordNeverExpires = $true + } + + ADUser SQL_Agent + { + DomainName = $Node.DomainName + Credential = $domainCred + UserName = "SQL_Agent" + Password = $NewADUserCred + Ensure = "Present" + DependsOn = "[ADOrganizationalUnit]DefaultOU" + Description = "SQL Agent Account" + Path = "OU=$($Node.DefaultOUName),$($Node.DomainDN)" + PasswordNeverExpires = $true + } + + ADUser Domain_Admin + { + DomainName = $Node.DomainName + Credential = $domainCred + UserName = $Node.DomainAdminName + Password = $NewADUserCred + Ensure = "Present" + DependsOn = "[ADOrganizationalUnit]DefaultOU" + Description = "DomainAdmin" + Path = "OU=$($Node.DefaultOUName),$($Node.DomainDN)" + PasswordNeverExpires = $true + } + + ADUser VMM_SA + { + DomainName = $Node.DomainName + Credential = $domainCred + UserName = "VMM_SA" + Password = $NewADUserCred + Ensure = "Present" + DependsOn = "[ADUser]Domain_Admin" + Description = "VMM Service Account" + Path = "OU=$($Node.DefaultOUName),$($Node.DomainDN)" + PasswordNeverExpires = $true + } + + ADGroup DomainAdmins + { + GroupName = "Domain Admins" + DependsOn = "[ADUser]VMM_SA" + MembersToInclude = "VMM_SA",$Node.DomainAdminName + } + + ADGroup SchemaAdmins + { + GroupName = "Schema Admins" + GroupScope = "Universal" + DependsOn = "[ADUser]VMM_SA" + MembersToInclude = $Node.DomainAdminName + } + + ADGroup EntAdmins + { + GroupName = "Enterprise Admins" + GroupScope = "Universal" + DependsOn = "[ADUser]VMM_SA" + MembersToInclude = $Node.DomainAdminName + } + + ADUser AdministratorNeverExpires + { + DomainName = $Node.DomainName + UserName = "Administrator" + Ensure = "Present" + DependsOn = "[ADDomain]FirstDS" + PasswordNeverExpires = $true + } + + IPaddress IP + { + IPAddress = ($DhcpScope+"1/24") + AddressFamily = 'IPv4' + InterfaceAlias = 'Ethernet' + } + WindowsFeature DHCPServer + { + Ensure = "Present" + Name = "DHCP" + DependsOn = "[ADDomain]FirstDS" + } + + Service DHCPServer #since insider 17035 dhcpserver was not starting for some reason + { + Name = "DHCPServer" + State = "Running" + DependsOn = "[WindowsFeature]DHCPServer" + } + + WindowsFeature DHCPServerManagement + { + Ensure = "Present" + Name = "RSAT-DHCP" + DependsOn = "[WindowsFeature]DHCPServer" + } + + + xDhcpServerScope ManagementScope + { + Ensure = 'Present' + ScopeId = ($DhcpScope+"0") + IPStartRange = ($DhcpScope+"10") + IPEndRange = ($DhcpScope+"254") + Name = 'ManagementScope' + SubnetMask = '255.255.255.0' + LeaseDuration = '00:08:00' + State = 'Active' + AddressFamily = 'IPv4' + DependsOn = "[Service]DHCPServer" + } + + # Setting scope gateway + DhcpScopeOptionValue 'ScopeOptionGateway' + { + OptionId = 3 + Value = ($DhcpScope+"1") + ScopeId = ($DhcpScope+"0") + VendorClass = '' + UserClass = '' + AddressFamily = 'IPv4' + DependsOn = "[xDhcpServerScope]ManagementScope" + } + + # Setting scope DNS servers + DhcpScopeOptionValue 'ScopeOptionDNS' + { + OptionId = 6 + Value = ($DhcpScope+"1") + ScopeId = ($DhcpScope+"0") + VendorClass = '' + UserClass = '' + AddressFamily = 'IPv4' + DependsOn = "[xDhcpServerScope]ManagementScope" + } + + # Setting scope DNS domain name + DhcpScopeOptionValue 'ScopeOptionDNSDomainName' + { + OptionId = 15 + Value = $Node.DomainName + ScopeId = ($DhcpScope+"0") + VendorClass = '' + UserClass = '' + AddressFamily = 'IPv4' + DependsOn = "[xDhcpServerScope]ManagementScope" + } + + xDhcpServerAuthorization LocalServerActivation + { + IsSingleInstance = 'Yes' + Ensure = 'Present' + } + + WindowsFeature DSCServiceFeature + { + Ensure = "Present" + Name = "DSC-Service" + } + + DnsServerADZone addReverseADZone + { + Name = $ReverseDnsRecord + DynamicUpdate = "Secure" + ReplicationScope = "Forest" + Ensure = "Present" + DependsOn = "[DhcpScopeOptionValue]ScopeOptionGateway" + } + + If ($LabConfig.PullServerDC){ + xDscWebService PSDSCPullServer + { + UseSecurityBestPractices = $false + Ensure = "Present" + EndpointName = "PSDSCPullServer" + Port = 8080 + PhysicalPath = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer" + CertificateThumbPrint = "AllowUnencryptedTraffic" + ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" + ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" + State = "Started" + DependsOn = "[WindowsFeature]DSCServiceFeature" + } + + File RegistrationKeyFile + { + Ensure = 'Present' + Type = 'File' + DestinationPath = "$env:ProgramFiles\WindowsPowerShell\DscService\RegistrationKeys.txt" + Contents = $Node.RegistrationKey + } + } + } + } + + $ConfigData = @{ + + AllNodes = @( + @{ + Nodename = $DCName + Role = "Parent DC" + DomainAdminName=$LabConfig.DomainAdminName + DomainName = $LabConfig.DomainName + DomainNetbiosName = $LabConfig.DomainNetbiosName + DomainDN = $LabConfig.DN + DefaultOUName=$LabConfig.DefaultOUName + RegistrationKey='14fc8e72-5036-4e79-9f89-5382160053aa' + PSDscAllowPlainTextPassword = $true + PsDscAllowDomainUser= $true + RetryCount = 50 + RetryIntervalSec = 30 + } + ) + } + + #create LCM config + [DSCLocalConfigurationManager()] + configuration LCMConfig + { + Node DC + { + Settings + { + RebootNodeIfNeeded = $true + ActionAfterReboot = 'ContinueConfiguration' + } + } + } + + #create DSC MOF files + WriteInfoHighlighted "`t Creating DSC Configs for DC" + LCMConfig -OutputPath "$PSScriptRoot\Temp\config" -ConfigurationData $ConfigData + DCHydration -OutputPath "$PSScriptRoot\Temp\config" -ConfigurationData $ConfigData -safemodeAdministratorCred $cred -domainCred $cred -NewADUserCred $cred + + #copy DSC MOF files to DC + WriteInfoHighlighted "`t Copying DSC configurations (pending.mof and metaconfig.mof)" + New-item -type directory -Path "$PSScriptRoot\Temp\config" -ErrorAction Ignore + Copy-Item -path "$PSScriptRoot\Temp\config\dc.mof" -Destination "$mountdir\Windows\system32\Configuration\pending.mof" + Copy-Item -Path "$PSScriptRoot\Temp\config\dc.meta.mof" -Destination "$mountdir\Windows\system32\Configuration\metaconfig.mof" + + #close VHD and apply changes + WriteInfoHighlighted "`t Applying changes to VHD" + Dismount-WindowsImage -Path $mountdir -Save + #&"$PSScriptRoot\Temp\dism\dism" /Unmount-Image /MountDir:$mountdir /Commit + + #Start DC VM and wait for configuration + WriteInfoHighlighted "`t Starting DC" + $DC | Start-VM + + $VMStartupTime = 250 + WriteInfoHighlighted "`t Configuring DC using DSC takes a while." + WriteInfo "`t `t Initial configuration in progress. Sleeping $VMStartupTime seconds" + Start-Sleep $VMStartupTime + $i=1 + do{ + $test=Invoke-Command -VMGuid $DC.id -ScriptBlock {Get-DscConfigurationStatus} -Credential $cred -ErrorAction SilentlyContinue + if ($test -eq $null) { + WriteInfo "`t `t Configuration in Progress. Sleeping 10 seconds" + Start-Sleep 10 + }elseif ($test.status -ne "Success" -and $i -eq 1) { + WriteInfo "`t `t Current DSC state: $($test.status), ResourncesNotInDesiredState: $($test.resourcesNotInDesiredState.count), ResourncesInDesiredState: $($test.resourcesInDesiredState.count)." + WriteInfoHighlighted "`t `t Invoking DSC Configuration again" + Invoke-Command -VMGuid $DC.id -ScriptBlock {Start-DscConfiguration -UseExisting} -Credential $cred + $i++ + }elseif ($test.status -ne "Success" -and $i -gt 1) { + WriteInfo "`t `t Current DSC state: $($test.status), ResourncesNotInDesiredState: $($test.resourcesNotInDesiredState.count), ResourncesInDesiredState: $($test.resourcesInDesiredState.count)." + WriteInfoHighlighted "`t `t Restarting DC" + Invoke-Command -VMGuid $DC.id -ScriptBlock {Restart-Computer} -Credential $cred + }elseif ($test.status -eq "Success" ) { + WriteInfo "`t `t Current DSC state: $($test.status), ResourncesNotInDesiredState: $($test.resourcesNotInDesiredState.count), ResourncesInDesiredState: $($test.resourcesInDesiredState.count)." + WriteInfoHighlighted "`t `t DSC Configured DC Successfully" + } + }until ($test.Status -eq 'Success' -and $test.rebootrequested -eq $false) + $test + + #configure default OU where new Machines will be created using redircmp and add reverse lookup zone (as setting reverse lookup does not work with DSC) + Invoke-Command -VMGuid $DC.id -Credential $cred -ErrorAction SilentlyContinue -ArgumentList $LabConfig -ScriptBlock { + Param($LabConfig); + redircmp "OU=$($LabConfig.DefaultOUName),$($LabConfig.DN)" + Add-DnsServerPrimaryZone -NetworkID ($DhcpScope+"/24") -ReplicationScope "Forest" + } + #install SCVMM or its prereqs if specified so + if (($LabConfig.InstallSCVMM -eq "Yes") -or ($LabConfig.InstallSCVMM -eq "SQL") -or ($LabConfig.InstallSCVMM -eq "ADK") -or ($LabConfig.InstallSCVMM -eq "Prereqs")){ + $DC | Add-VMHardDiskDrive -Path $toolsVHD.Path + } + + if ($LabConfig.InstallSCVMM -eq "Yes"){ + WriteInfoHighlighted "Installing System Center Virtual Machine Manager and its prerequisites" + Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { + Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force + d:\scvmm\1_SQL_Install.ps1 + d:\scvmm\2_ADK_Install.ps1 + #install prereqs + if (Test-Path "D:\SCVMM\SCVMM\Prerequisites\VCRedist\amd64\vcredist_x64.exe"){ + Start-Process -FilePath "D:\SCVMM\SCVMM\Prerequisites\VCRedist\amd64\vcredist_x64.exe" -ArgumentList "/passive /quiet /norestart" -Wait + } + Restart-Computer + } + Start-Sleep 10 + + WriteInfoHighlighted "$($DC.name) was restarted, waiting for Active Directory on $($DC.name) to be started." + do{ + $test=Invoke-Command -VMGuid $DC.id -Credential $cred -ArgumentList $LabConfig -ErrorAction SilentlyContinue -ScriptBlock { + param($LabConfig); + Get-ADComputer -Filter * -SearchBase "$($LabConfig.DN)" -ErrorAction SilentlyContinue} + Start-Sleep 5 + } + until ($test -ne $Null) + WriteSuccess "Active Directory on $($DC.name) is up." + + Start-Sleep 30 #Wait as sometimes VMM failed to install without this. + Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { + Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force + d:\scvmm\3_SCVMM_Install.ps1 + } + } + + if ($LabConfig.InstallSCVMM -eq "SQL"){ + WriteInfoHighlighted "Installing SQL" + Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { + Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force + d:\scvmm\1_SQL_Install.ps1 + } + } + + if ($LabConfig.InstallSCVMM -eq "ADK"){ + WriteInfoHighlighted "Installing ADK" + Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { + Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force + d:\scvmm\2_ADK_Install.ps1 + } + } + + if ($LabConfig.InstallSCVMM -eq "Prereqs"){ + WriteInfoHighlighted "Installing System Center VMM Prereqs" + Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { + Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force + d:\scvmm\1_SQL_Install.ps1 + d:\scvmm\2_ADK_Install.ps1 + } + } + + if (($LabConfig.InstallSCVMM -eq "Yes") -or ($LabConfig.InstallSCVMM -eq "SQL") -or ($LabConfig.InstallSCVMM -eq "ADK") -or ($LabConfig.InstallSCVMM -eq "Prereqs")){ + $DC | Get-VMHardDiskDrive | Where-Object path -eq $toolsVHD.Path | Remove-VMHardDiskDrive + } + + WriteInfo "`t Disconnecting VMNetwork Adapter from DC" + $DC | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter + + $dcHydrationEndTime = Get-Date + Return $DC +} +#endregion diff --git a/Scripts/2_CreateParentDisks.ps1 b/Scripts/2_CreateParentDisks.ps1 index cbe931ca..3c9cef94 100644 --- a/Scripts/2_CreateParentDisks.ps1 +++ b/Scripts/2_CreateParentDisks.ps1 @@ -14,79 +14,7 @@ If (-not $isAdmin) { #region Functions . $PSScriptRoot\0_Shared.ps1 # [!build-include-inline] - - #Create Unattend for VHD - Function CreateUnattendFileVHD { - param ( - [parameter(Mandatory=$true)] - [string] - $Computername, - [parameter(Mandatory=$true)] - [string] - $AdminPassword, - [parameter(Mandatory=$true)] - [string] - $Path, - [parameter(Mandatory=$true)] - [string] - $TimeZone - ) - - 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> - $oeminformation - <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> - <TimeZone>$TimeZone</TimeZone> - </component> - </settings> -</unattend> - -"@ - - Set-Content -path $unattendFile -value $fileContent - - #return the file object - Return $unattendFile - } +. $PSScriptRoot\0_DCHydrate.ps1 # [!build-include-inline] #endregion @@ -155,11 +83,6 @@ If (-not $isAdmin) { #Grab Installation type $WindowsInstallationType=Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\' -Name InstallationType - #DCHP scope - $DHCPscope = $LabConfig.DHCPscope - $ReverseDNSrecord = $DHCPscope -replace '^(\d+)\.(\d+)\.\d+\.(\d+)$','$3.$2.$1.in-addr.arpa' - $DHCPscope = $DHCPscope.Substring(0,$DHCPscope.Length-1) - #endregion #region Check prerequisites @@ -274,10 +197,10 @@ If (-not $isAdmin) { } $ISOServer = Mount-DiskImage -ImagePath $ServerISOItem.FullName -PassThru }else{ - WriteInfoHighlighted "Please select ISO image with Windows Server 2016, 2019, 2022 or Server Insider" + WriteInfoHighlighted "Please select ISO image with Windows Server 2016, 2019, 2022, 2025 or Server Insider" [reflection.assembly]::loadwithpartialname("System.Windows.Forms") $openFile = New-Object System.Windows.Forms.OpenFileDialog -Property @{ - Title="Please select ISO image with Windows Server 2016, 2019, 2022 or Server Insider" + Title="Please select ISO image with Windows Server 2016, 2019, 2022, 2025 or Server Insider" } $openFile.Filter = "iso files (*.iso)|*.iso|All files (*.*)|*.*" If($openFile.ShowDialog() -eq "OK"){ @@ -409,7 +332,21 @@ If (-not $isAdmin) { if ($LabConfig.DCEdition -gt 2){ $LabConfig.DCEdition=2 } - }elseif ($BuildNumber -gt 20348){ + }elseif ($BuildNumber -eq 26100){ + #Windows Server 2025 + $ServerVHDs += @{ + Kind = "Full" + Edition="4" + VHDName="Win2025_G2.vhdx" + Size=127GB + } + $ServerVHDs += @{ + Kind = "Core" + Edition="3" + VHDName="Win2025Core_G2.vhdx" + Size=127GB + } + }elseif ($BuildNumber -gt 23100){ #Windows Sever Insider $ServerVHDs += @{ Kind = "Full" @@ -557,561 +494,54 @@ If (-not $isAdmin) { $vhdStatusInfo[$toolsVhdStatus.Kind] = $toolsVhdStatus #endregion -#region Hydrate DC - if (-not $DCFilesExists){ - WriteInfoHighlighted "Starting DC Hydration" - $dcHydrationStartTime = Get-Date - - $vhdpath="$PSScriptRoot\LAB\$DCName\Virtual Hard Disks\$DCName.vhdx" - $VMPath="$PSScriptRoot\LAB\" - - #reuse VHD if already created - $DCVHDName=($ServerVHDs | Where-Object Edition -eq $LabConfig.DCEdition).VHDName - if ((($DCVHDName) -ne $null) -and (Test-Path -Path "$PSScriptRoot\ParentDisks\$DCVHDName")){ - WriteSuccess "`t $DCVHDName found, reusing, and copying to $vhdpath" - New-Item -Path "$VMPath\$DCName" -Name "Virtual Hard Disks" -ItemType Directory - Copy-Item -Path "$PSScriptRoot\ParentDisks\$DCVHDName" -Destination $vhdpath - }else{ - #Create Parent VHD - WriteInfoHighlighted "`t Creating VHD for DC" - if ($packages){ - Convert-WindowsImage -SourcePath "$($ServerMediaDriveLetter):\sources\install.wim" -Edition $LabConfig.DCEdition -VHDPath $vhdpath -SizeBytes 60GB -VHDFormat VHDX -DiskLayout UEFI -package $packages - }else{ - Convert-WindowsImage -SourcePath "$($ServerMediaDriveLetter):\sources\install.wim" -Edition $LabConfig.DCEdition -VHDPath $vhdpath -SizeBytes 60GB -VHDFormat VHDX -DiskLayout UEFI - } - } - - #Get VM Version - [System.Version]$VMVersion=(Get-WindowsImage -ImagePath $VHDPath -Index 1).Version - WriteInfo "`t VM Version is $($VMVersion.Build).$($VMVersion.Revision)" - - #If the switch does not already exist, then create a switch with the name $SwitchName - if (-not [bool](Get-VMSwitch -Name $Switchname -ErrorAction SilentlyContinue)) { - WriteInfoHighlighted "`t Creating temp hydration switch $Switchname" - New-VMSwitch -SwitchType Private -Name $Switchname - } - - #create VM DC - WriteInfoHighlighted "`t Creating DC VM" - if ($LabConfig.DCVMVersion){ - $DC=New-VM -Name $DCName -VHDPath $vhdpath -MemoryStartupBytes 2GB -path $vmpath -SwitchName $Switchname -Generation 2 -Version $LabConfig.DCVMVersion - }else{ - $DC=New-VM -Name $DCName -VHDPath $vhdpath -MemoryStartupBytes 2GB -path $vmpath -SwitchName $Switchname -Generation 2 - } - $DC | Set-VMProcessor -Count 2 - $DC | Set-VMMemory -DynamicMemoryEnabled $true -MinimumBytes 2GB - if ($LabConfig.Secureboot -eq $False) {$DC | Set-VMFirmware -EnableSecureBoot Off} - if ($DC.AutomaticCheckpointsEnabled -eq $True){ - $DC | Set-VM -AutomaticCheckpointsEnabled $False - } - if ($LabConfig.InstallSCVMM -eq "Yes"){ - #SCVMM 2022 requires 4GB of memory - $DC | Set-VMMemory -StartupBytes 4GB -MinimumBytes 4GB - } - - #Apply Unattend to VM - if ($VMVersion.Build -ge 17763){ - $oeminformation=@" - <OEMInformation> - <SupportProvider>MSLab</SupportProvider> - <SupportURL>https://aka.ms/mslab</SupportURL> - </OEMInformation> -"@ - }else{ - $oeminformation=$null - } - - WriteInfoHighlighted "`t Applying Unattend and copying Powershell DSC Modules" - if (Test-Path $mountdir){ - Remove-Item -Path $mountdir -Recurse -Force - } - if (Test-Path "$PSScriptRoot\Temp\unattend"){ - Remove-Item -Path "$PSScriptRoot\Temp\unattend.xml" - } - $unattendfile=CreateUnattendFileVHD -Computername $DCName -AdminPassword $AdminPassword -path "$PSScriptRoot\temp\" -TimeZone $TimeZone - New-item -type directory -Path $mountdir -force - [System.Version]$VMVersion=(Get-WindowsImage -ImagePath $VHDPath -Index 1).Version - Mount-WindowsImage -Path $mountdir -ImagePath $VHDPath -Index 1 - Use-WindowsUnattend -Path $mountdir -UnattendPath $unattendFile - #&"$PSScriptRoot\Temp\dism\dism" /mount-image /imagefile:$vhdpath /index:1 /MountDir:$mountdir - #&"$PSScriptRoot\Temp\dism\dism" /image:$mountdir /Apply-Unattend:$unattendfile - New-item -type directory -Path "$mountdir\Windows\Panther" -force - Copy-Item -Path $unattendfile -Destination "$mountdir\Windows\Panther\unattend.xml" -force - Copy-Item -Path "$PSScriptRoot\Temp\DSC\*" -Destination "$mountdir\Program Files\WindowsPowerShell\Modules\" -Recurse -force - - #Create credentials for DSC - - $username = "$($LabConfig.DomainNetbiosName)\Administrator" - $password = $AdminPassword - $secstr = New-Object -TypeName System.Security.SecureString - $password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)} - $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr - - #Create DSC configuration - configuration DCHydration - { - param - ( - [Parameter(Mandatory)] - [pscredential]$safemodeAdministratorCred, - - [Parameter(Mandatory)] - [pscredential]$domainCred, - - [Parameter(Mandatory)] - [pscredential]$NewADUserCred - - ) - - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion "6.3.0" - Import-DscResource -ModuleName DnsServerDsc -ModuleVersion "3.0.0" - Import-DSCResource -ModuleName NetworkingDSC -ModuleVersion "9.0.0" - Import-DSCResource -ModuleName xDHCPServer -ModuleVersion "3.1.1" - Import-DSCResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion "9.1.0" - Import-DscResource -ModuleName PSDesiredStateConfiguration - - - Node $AllNodes.Where{$_.Role -eq "Parent DC"}.Nodename - - { - WindowsFeature ADDSInstall - { - Ensure = "Present" - Name = "AD-Domain-Services" - } - - WindowsFeature FeatureGPMC - { - Ensure = "Present" - Name = "GPMC" - DependsOn = "[WindowsFeature]ADDSInstall" - } - - WindowsFeature FeatureADPowerShell - { - Ensure = "Present" - Name = "RSAT-AD-PowerShell" - DependsOn = "[WindowsFeature]ADDSInstall" - } - - WindowsFeature FeatureADAdminCenter - { - Ensure = "Present" - Name = "RSAT-AD-AdminCenter" - DependsOn = "[WindowsFeature]ADDSInstall" - } - - WindowsFeature FeatureADDSTools - { - Ensure = "Present" - Name = "RSAT-ADDS-Tools" - DependsOn = "[WindowsFeature]ADDSInstall" - } - - WindowsFeature FeatureDNSTools - { - Ensure = "Present" - Name = "RSAT-DNS-Server" - DependsOn = "[WindowsFeature]ADDSInstall" - } - - ADDomain FirstDS - { - DomainName = $Node.DomainName - Credential = $domainCred - SafemodeAdministratorPassword = $safemodeAdministratorCred - DomainNetbiosName = $node.DomainNetbiosName - DependsOn = "[WindowsFeature]ADDSInstall" - } - - WaitForADDomain DscForestWait - { - DomainName = $Node.DomainName - Credential = $domainCred - DependsOn = "[ADDomain]FirstDS" - } - - ADOrganizationalUnit DefaultOU - { - Name = $Node.DefaultOUName - Path = $Node.DomainDN - ProtectedFromAccidentalDeletion = $true - Description = 'Default OU for all user and computer accounts' - Ensure = 'Present' - DependsOn = "[ADDomain]FirstDS" - } - - ADUser SQL_SA - { - DomainName = $Node.DomainName - Credential = $domainCred - UserName = "SQL_SA" - Password = $NewADUserCred - Ensure = "Present" - DependsOn = "[ADOrganizationalUnit]DefaultOU" - Description = "SQL Service Account" - Path = "OU=$($Node.DefaultOUName),$($Node.DomainDN)" - PasswordNeverExpires = $true - } - - ADUser SQL_Agent - { - DomainName = $Node.DomainName - Credential = $domainCred - UserName = "SQL_Agent" - Password = $NewADUserCred - Ensure = "Present" - DependsOn = "[ADOrganizationalUnit]DefaultOU" - Description = "SQL Agent Account" - Path = "OU=$($Node.DefaultOUName),$($Node.DomainDN)" - PasswordNeverExpires = $true - } - - ADUser Domain_Admin - { - DomainName = $Node.DomainName - Credential = $domainCred - UserName = $Node.DomainAdminName - Password = $NewADUserCred - Ensure = "Present" - DependsOn = "[ADOrganizationalUnit]DefaultOU" - Description = "DomainAdmin" - Path = "OU=$($Node.DefaultOUName),$($Node.DomainDN)" - PasswordNeverExpires = $true - } - - ADUser VMM_SA - { - DomainName = $Node.DomainName - Credential = $domainCred - UserName = "VMM_SA" - Password = $NewADUserCred - Ensure = "Present" - DependsOn = "[ADUser]Domain_Admin" - Description = "VMM Service Account" - Path = "OU=$($Node.DefaultOUName),$($Node.DomainDN)" - PasswordNeverExpires = $true - } - - ADGroup DomainAdmins - { - GroupName = "Domain Admins" - DependsOn = "[ADUser]VMM_SA" - MembersToInclude = "VMM_SA",$Node.DomainAdminName - } - - ADGroup SchemaAdmins - { - GroupName = "Schema Admins" - GroupScope = "Universal" - DependsOn = "[ADUser]VMM_SA" - MembersToInclude = $Node.DomainAdminName - } - - ADGroup EntAdmins - { - GroupName = "Enterprise Admins" - GroupScope = "Universal" - DependsOn = "[ADUser]VMM_SA" - MembersToInclude = $Node.DomainAdminName - } - - ADUser AdministratorNeverExpires - { - DomainName = $Node.DomainName - UserName = "Administrator" - Ensure = "Present" - DependsOn = "[ADDomain]FirstDS" - PasswordNeverExpires = $true - } - - IPaddress IP - { - IPAddress = ($DHCPscope+"1/24") - AddressFamily = 'IPv4' - InterfaceAlias = 'Ethernet' - } - WindowsFeature DHCPServer - { - Ensure = "Present" - Name = "DHCP" - DependsOn = "[ADDomain]FirstDS" - } - - Service DHCPServer #since insider 17035 dhcpserver was not starting for some reason - { - Name = "DHCPServer" - State = "Running" - DependsOn = "[WindowsFeature]DHCPServer" - } - - WindowsFeature DHCPServerManagement - { - Ensure = "Present" - Name = "RSAT-DHCP" - DependsOn = "[WindowsFeature]DHCPServer" - } - - - xDhcpServerScope ManagementScope - { - Ensure = 'Present' - ScopeId = ($DHCPscope+"0") - IPStartRange = ($DHCPscope+"10") - IPEndRange = ($DHCPscope+"254") - Name = 'ManagementScope' - SubnetMask = '255.255.255.0' - LeaseDuration = '00:08:00' - State = 'Active' - AddressFamily = 'IPv4' - DependsOn = "[Service]DHCPServer" - } - - # Setting scope gateway - DhcpScopeOptionValue 'ScopeOptionGateway' - { - OptionId = 3 - Value = ($DHCPscope+"1") - ScopeId = ($DHCPscope+"0") - VendorClass = '' - UserClass = '' - AddressFamily = 'IPv4' - DependsOn = "[xDhcpServerScope]ManagementScope" - } - - # Setting scope DNS servers - DhcpScopeOptionValue 'ScopeOptionDNS' - { - OptionId = 6 - Value = ($DHCPscope+"1") - ScopeId = ($DHCPscope+"0") - VendorClass = '' - UserClass = '' - AddressFamily = 'IPv4' - DependsOn = "[xDhcpServerScope]ManagementScope" - } - - # Setting scope DNS domain name - DhcpScopeOptionValue 'ScopeOptionDNSDomainName' - { - OptionId = 15 - Value = $Node.DomainName - ScopeId = ($DHCPscope+"0") - VendorClass = '' - UserClass = '' - AddressFamily = 'IPv4' - DependsOn = "[xDhcpServerScope]ManagementScope" - } - - xDhcpServerAuthorization LocalServerActivation - { - IsSingleInstance = 'Yes' - Ensure = 'Present' - } - - WindowsFeature DSCServiceFeature - { - Ensure = "Present" - Name = "DSC-Service" - } - - DnsServerADZone addReverseADZone - { - Name = $ReverseDNSrecord - DynamicUpdate = "Secure" - ReplicationScope = "Forest" - Ensure = "Present" - DependsOn = "[DhcpScopeOptionValue]ScopeOptionGateway" - } - - If ($LabConfig.PullServerDC){ - xDscWebService PSDSCPullServer - { - UseSecurityBestPractices = $false - Ensure = "Present" - EndpointName = "PSDSCPullServer" - Port = 8080 - PhysicalPath = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer" - CertificateThumbPrint = "AllowUnencryptedTraffic" - ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" - ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" - State = "Started" - DependsOn = "[WindowsFeature]DSCServiceFeature" - } - - File RegistrationKeyFile - { - Ensure = 'Present' - Type = 'File' - DestinationPath = "$env:ProgramFiles\WindowsPowerShell\DscService\RegistrationKeys.txt" - Contents = $Node.RegistrationKey - } - } - } - } - - $ConfigData = @{ - - AllNodes = @( - @{ - Nodename = $DCName - Role = "Parent DC" - DomainAdminName=$LabConfig.DomainAdminName - DomainName = $LabConfig.DomainName - DomainNetbiosName = $LabConfig.DomainNetbiosName - DomainDN = $LabConfig.DN - DefaultOUName=$LabConfig.DefaultOUName - RegistrationKey='14fc8e72-5036-4e79-9f89-5382160053aa' - PSDscAllowPlainTextPassword = $true - PsDscAllowDomainUser= $true - RetryCount = 50 - RetryIntervalSec = 30 - } - ) - } - - #create LCM config - [DSCLocalConfigurationManager()] - configuration LCMConfig - { - Node DC - { - Settings - { - RebootNodeIfNeeded = $true - ActionAfterReboot = 'ContinueConfiguration' - } - } - } - - #create DSC MOF files - WriteInfoHighlighted "`t Creating DSC Configs for DC" - LCMConfig -OutputPath "$PSScriptRoot\Temp\config" -ConfigurationData $ConfigData - DCHydration -OutputPath "$PSScriptRoot\Temp\config" -ConfigurationData $ConfigData -safemodeAdministratorCred $cred -domainCred $cred -NewADUserCred $cred - - #copy DSC MOF files to DC - WriteInfoHighlighted "`t Copying DSC configurations (pending.mof and metaconfig.mof)" - New-item -type directory -Path "$PSScriptRoot\Temp\config" -ErrorAction Ignore - Copy-Item -path "$PSScriptRoot\Temp\config\dc.mof" -Destination "$mountdir\Windows\system32\Configuration\pending.mof" - Copy-Item -Path "$PSScriptRoot\Temp\config\dc.meta.mof" -Destination "$mountdir\Windows\system32\Configuration\metaconfig.mof" - - #close VHD and apply changes - WriteInfoHighlighted "`t Applying changes to VHD" - Dismount-WindowsImage -Path $mountdir -Save - #&"$PSScriptRoot\Temp\dism\dism" /Unmount-Image /MountDir:$mountdir /Commit - - #Start DC VM and wait for configuration - WriteInfoHighlighted "`t Starting DC" - $DC | Start-VM - - $VMStartupTime = 250 - WriteInfoHighlighted "`t Configuring DC using DSC takes a while." - WriteInfo "`t `t Initial configuration in progress. Sleeping $VMStartupTime seconds" - Start-Sleep $VMStartupTime - $i=1 - do{ - $test=Invoke-Command -VMGuid $DC.id -ScriptBlock {Get-DscConfigurationStatus} -Credential $cred -ErrorAction SilentlyContinue - if ($test -eq $null) { - WriteInfo "`t `t Configuration in Progress. Sleeping 10 seconds" - Start-Sleep 10 - }elseif ($test.status -ne "Success" -and $i -eq 1) { - WriteInfo "`t `t Current DSC state: $($test.status), ResourncesNotInDesiredState: $($test.resourcesNotInDesiredState.count), ResourncesInDesiredState: $($test.resourcesInDesiredState.count)." - WriteInfoHighlighted "`t `t Invoking DSC Configuration again" - Invoke-Command -VMGuid $DC.id -ScriptBlock {Start-DscConfiguration -UseExisting} -Credential $cred - $i++ - }elseif ($test.status -ne "Success" -and $i -gt 1) { - WriteInfo "`t `t Current DSC state: $($test.status), ResourncesNotInDesiredState: $($test.resourcesNotInDesiredState.count), ResourncesInDesiredState: $($test.resourcesInDesiredState.count)." - WriteInfoHighlighted "`t `t Restarting DC" - Invoke-Command -VMGuid $DC.id -ScriptBlock {Restart-Computer} -Credential $cred - }elseif ($test.status -eq "Success" ) { - WriteInfo "`t `t Current DSC state: $($test.status), ResourncesNotInDesiredState: $($test.resourcesNotInDesiredState.count), ResourncesInDesiredState: $($test.resourcesInDesiredState.count)." - WriteInfoHighlighted "`t `t DSC Configured DC Successfully" - } - }until ($test.Status -eq 'Success' -and $test.rebootrequested -eq $false) - $test - - #configure default OU where new Machines will be created using redircmp and add reverse lookup zone (as setting reverse lookup does not work with DSC) - Invoke-Command -VMGuid $DC.id -Credential $cred -ErrorAction SilentlyContinue -ArgumentList $LabConfig -ScriptBlock { - Param($LabConfig); - redircmp "OU=$($LabConfig.DefaultOUName),$($LabConfig.DN)" - Add-DnsServerPrimaryZone -NetworkID ($DHCPscope+"/24") -ReplicationScope "Forest" - } - #install SCVMM or its prereqs if specified so - if (($LabConfig.InstallSCVMM -eq "Yes") -or ($LabConfig.InstallSCVMM -eq "SQL") -or ($LabConfig.InstallSCVMM -eq "ADK") -or ($LabConfig.InstallSCVMM -eq "Prereqs")){ - $DC | Add-VMHardDiskDrive -Path $toolsVHD.Path - } - - if ($LabConfig.InstallSCVMM -eq "Yes"){ - WriteInfoHighlighted "Installing System Center Virtual Machine Manager and its prerequisites" - Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { - Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force - d:\scvmm\1_SQL_Install.ps1 - d:\scvmm\2_ADK_Install.ps1 - #install prereqs - if (Test-Path "D:\SCVMM\SCVMM\Prerequisites\VCRedist\amd64\vcredist_x64.exe"){ - Start-Process -FilePath "D:\SCVMM\SCVMM\Prerequisites\VCRedist\amd64\vcredist_x64.exe" -ArgumentList "/passive /quiet /norestart" -Wait - } - Restart-Computer - } - Start-Sleep 10 - - WriteInfoHighlighted "$($DC.name) was restarted, waiting for Active Directory on $($DC.name) to be started." - do{ - $test=Invoke-Command -VMGuid $DC.id -Credential $cred -ArgumentList $LabConfig -ErrorAction SilentlyContinue -ScriptBlock { - param($LabConfig); - Get-ADComputer -Filter * -SearchBase "$($LabConfig.DN)" -ErrorAction SilentlyContinue} - Start-Sleep 5 - } - until ($test -ne $Null) - WriteSuccess "Active Directory on $($DC.name) is up." - - Start-Sleep 30 #Wait as sometimes VMM failed to install without this. - Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { - Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force - d:\scvmm\3_SCVMM_Install.ps1 - } - } - - if ($LabConfig.InstallSCVMM -eq "SQL"){ - WriteInfoHighlighted "Installing SQL" - Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { - Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force - d:\scvmm\1_SQL_Install.ps1 - } - } +#region Create DC VHD +if (-not $DCFilesExists){ + $vhdpath="$PSScriptRoot\LAB\$DCName\Virtual Hard Disks\$DCName.vhdx" + $VMPath="$PSScriptRoot\LAB\" + + #reuse VHD if already created + $DCVHDName=($ServerVHDs | Where-Object Edition -eq $LabConfig.DCEdition).VHDName + if ((($DCVHDName) -ne $null) -and (Test-Path -Path "$PSScriptRoot\ParentDisks\$DCVHDName")){ + WriteSuccess "`t $DCVHDName found, reusing, and copying to $vhdpath" + New-Item -Path "$VMPath\$DCName" -Name "Virtual Hard Disks" -ItemType Directory + Copy-Item -Path "$PSScriptRoot\ParentDisks\$DCVHDName" -Destination $vhdpath + }else{ + #Create Parent VHD + WriteInfoHighlighted "`t Creating VHD for DC" + if ($packages){ + Convert-WindowsImage -SourcePath "$($ServerMediaDriveLetter):\sources\install.wim" -Edition $LabConfig.DCEdition -VHDPath $vhdpath -SizeBytes 60GB -VHDFormat VHDX -DiskLayout UEFI -package $packages + }else{ + Convert-WindowsImage -SourcePath "$($ServerMediaDriveLetter):\sources\install.wim" -Edition $LabConfig.DCEdition -VHDPath $vhdpath -SizeBytes 60GB -VHDFormat VHDX -DiskLayout UEFI + } + } - if ($LabConfig.InstallSCVMM -eq "ADK"){ - WriteInfoHighlighted "Installing ADK" - Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { - Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force - d:\scvmm\2_ADK_Install.ps1 - } - } + #Get VM Version + [System.Version]$VMVersion=(Get-WindowsImage -ImagePath $VHDPath -Index 1).Version + WriteInfo "`t VM Version is $($VMVersion.Build).$($VMVersion.Revision)" +} +#endregion - if ($LabConfig.InstallSCVMM -eq "Prereqs"){ - WriteInfoHighlighted "Installing System Center VMM Prereqs" - Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { - Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force - d:\scvmm\1_SQL_Install.ps1 - d:\scvmm\2_ADK_Install.ps1 - } - } - if (($LabConfig.InstallSCVMM -eq "Yes") -or ($LabConfig.InstallSCVMM -eq "SQL") -or ($LabConfig.InstallSCVMM -eq "ADK") -or ($LabConfig.InstallSCVMM -eq "Prereqs")){ - $DC | Get-VMHardDiskDrive | Where-Object path -eq $toolsVHD.Path | Remove-VMHardDiskDrive +#region create DC if it does not exist + if (-not $DCFilesExists) { + if (-not $LabConfig.NoDehydrateDC){ + $DC = Hydrate-DC -DCName $DCName -VhdPath $vhdpath -VmPath $VmPath -SwitchName $HydrationSwitchname -TimeZone $TimeZone -DhcpScope $LabConfig.DHCPscope -AdminPassword $AdminPassword + if ($DC -eq $null){ + WriteErrorAndExit "DC was not created successfully Press any key to continue ..." + } else { + WriteInfo "`t`t Virtual Machine $($DC.name) located in folder $($DC.Path) hydrated" } - - $dcHydrationEndTime = Get-Date + } else { + WriteInfoHighlighted "Skipping creation of dehydrated Domain Controller" + } } #endregion #region backup DC and cleanup #cleanup DC - if (-not $DCFilesExists){ + if (-not $DCFilesExists -and -not $LabConfig.NoDehydrateDC){ WriteInfoHighlighted "Backup DC and cleanup" #shutdown DC - WriteInfo "`t Disconnecting VMNetwork Adapter from DC" - $DC | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter WriteInfo "`t Shutting down DC" $DC | Stop-VM $DC | Set-VM -MemoryMinimumBytes 512MB diff --git a/Scripts/3_Deploy.ps1 b/Scripts/3_Deploy.ps1 index dfef20eb..af8e5c72 100644 --- a/Scripts/3_Deploy.ps1 +++ b/Scripts/3_Deploy.ps1 @@ -14,6 +14,7 @@ If (-not $isAdmin) { #region Functions . $PSScriptRoot\0_Shared.ps1 # [!build-include-inline] +. $PSScriptRoot\0_DCHydrate.ps1 # [!build-include-inline] Function CreateUnattendFileBlob{ #Create Unattend (parameter is Blob) @@ -880,6 +881,10 @@ If (-not $isAdmin) { $LabConfig.SwitchName = 'LabSwitch' } + If (!$LabConfig.DHCPscope){ + $LabConfig.DHCPscope="10.0.0.0" + } + WriteInfoHighlighted "List of variables used" WriteInfo "`t Prefix used in lab is $($labconfig.prefix)" @@ -1196,27 +1201,45 @@ If (-not $isAdmin) { } if (!(get-vm -Name ($labconfig.prefix+"DC") -ErrorAction SilentlyContinue)){ - #import DC - WriteInfo "`t Looking for DC to be imported" - $dcCandidates = [array](Get-ChildItem $LABFolder -Recurse | Where-Object {($_.extension -eq '.vmcx' -and $_.directory -like '*Virtual Machines*') -or ($_.extension -eq '.xml' -and $_.directory -like '*Virtual Machines*')}) - $dcCandidates | ForEach-Object -Process { - # If the VM ID is already used create a copy of the DC VM configuration instead of in-place registration - $vm = Get-VM -Id $_.BaseName -ErrorAction SilentlyContinue - if($vm -and $dcCandidates.Length -eq 1) { # allow duplicating of the DC VM only if it is the only one VM in lab folder (as if more than one exists, probably just labprefix was changed after the deployment) - WriteInfoHighlighted "You are trying to deploy a previously deployed lab from a different location as there is another DC VM with a same VM ID (is this a copied lab folder?) -> this DC VM will be registered with new VM ID." - $directory = $_.Directory.FullName.replace("\Virtual Machines", "") - $DC = Import-VM -Path $_.FullName -GenerateNewId -Copy -VirtualMachinePath $directory -VhdDestinationPath "$directory\Virtual Hard Disks" - WriteInfo "`t`t Virtual Machine $($DC.Name) registered with a new VM ID $($DC.Id)" - } else { - $DC = Import-VM -Path $_.FullName - } - } + if ($LabConfig.NoDehydrateDC) { + $DCName = "DC" + $vhdpath="$PSScriptRoot\LAB\$DCName\Virtual Hard Disks\$DCName.vhdx" + $VMPath="$PSScriptRoot\LAB\" + $HydrationSwitchname="DC_HydrationSwitch_$([guid]::NewGuid())" + + $DC = Hydrate-DC -DCName $DCName -VhdPath $vhdpath -VMPath $VMPath -Switchname $HydrationSwitchname -TimeZone $TimeZone -DHCPScope $LabConfig.DHCPscope -AdminPassword $LabConfig.AdminPassword if ($DC -eq $null){ - WriteErrorAndExit "DC was not imported successfully Press any key to continue ..." - }else{ - WriteInfo "`t`t Virtual Machine $($DC.name) located in folder $($DC.Path) imported" + WriteErrorAndExit "DC was not created successfully Press any key to continue ..." + } else { + WriteInfo "`t`t Virtual Machine $($DC.name) located in folder $($DC.Path) hydrated" } + # Clean up hydration switch + WriteInfo "`t Removing switch $HydrationSwitchname" + Remove-VMSwitch -Name $HydrationSwitchname -Force -ErrorAction SilentlyContinue + } else { + #import DC + WriteInfo "`t Looking for DC to be imported" + $dcCandidates = [array](Get-ChildItem $LABFolder -Recurse | Where-Object {($_.extension -eq '.vmcx' -and $_.directory -like '*Virtual Machines*') -or ($_.extension -eq '.xml' -and $_.directory -like '*Virtual Machines*')}) + $dcCandidates | ForEach-Object -Process { + # If the VM ID is already used create a copy of the DC VM configuration instead of in-place registration + $vm = Get-VM -Id $_.BaseName -ErrorAction SilentlyContinue + if($vm -and $dcCandidates.Length -eq 1) { # allow duplicating of the DC VM only if it is the only one VM in lab folder (as if more than one exists, probably just labprefix was changed after the deployment) + WriteInfoHighlighted "You are trying to deploy a previously deployed lab from a different location as there is another DC VM with a same VM ID (is this a copied lab folder?) -> this DC VM will be registered with new VM ID." + $directory = $_.Directory.FullName.replace("\Virtual Machines", "") + $DC = Import-VM -Path $_.FullName -GenerateNewId -Copy -VirtualMachinePath $directory -VhdDestinationPath "$directory\Virtual Hard Disks" + WriteInfo "`t`t Virtual Machine $($DC.Name) registered with a new VM ID $($DC.Id)" + } else { + $DC = Import-VM -Path $_.FullName + } + } + if ($DC -eq $null){ + WriteErrorAndExit "DC was not imported successfully Press any key to continue ..." + }else{ + WriteInfo "`t`t Virtual Machine $($DC.name) located in folder $($DC.Path) imported" + } + } + #create checkpoint to be able to return to consistent state when cleaned with cleanup.ps1 $DC | Checkpoint-VM -SnapshotName Initial WriteInfo "`t Virtual Machine $($DC.name) checkpoint created" diff --git a/build.ps1 b/build.ps1 index 73be65f8..633119c6 100644 --- a/build.ps1 +++ b/build.ps1 @@ -22,7 +22,7 @@ $signedToolsOutputDir = Join-Path $outputBaseDir "Tools" $scriptsOutputFile = "Release.zip" # Files that would be skipped by Build function (no replacements) -[array]$scriptsBuildIgnoredFiles = "0_Shared.ps1" +[array]$scriptsBuildIgnoredFiles = @("0_Shared.ps1", "0_DCHydrate.ps1") [array]$toolsBuildIgnoredFiles = @() # Files that won't be signed after build function From 6db3293411b88add228259fe3c9beb03c73c0733 Mon Sep 17 00:00:00 2001 From: "Michael Bernstein (DPLAT)" <Michael.Bernstein@microsoft.com> Date: Wed, 12 Jun 2024 16:17:36 -0700 Subject: [PATCH 2/7] Update LabConfig.ps1 with support for WS2025 --- Scripts/LabConfig.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Scripts/LabConfig.ps1 b/Scripts/LabConfig.ps1 index 8adbb3a9..0cefd31e 100644 --- a/Scripts/LabConfig.ps1 +++ b/Scripts/LabConfig.ps1 @@ -7,6 +7,8 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M #1..4 | ForEach-Object {$VMNames="AzSHCI"; $LABConfig.VMs += @{ VMName = "$VMNames$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI21H2_G2.vhdx'; SSDNumber = 0; SSDSize=800GB ; HDDNumber = 12; HDDSize= 4TB ; MemoryStartupBytes= 1GB }} # Or Windows Server 2019 #1..4 | ForEach-Object {$VMNames="S2D"; $LABConfig.VMs += @{ VMName = "$VMNames$_" ; Configuration = 'S2D' ; ParentVHD = 'Win2019Core_G2.vhdx'; SSDNumber = 0; SSDSize=800GB ; HDDNumber = 12; HDDSize= 4TB ; MemoryStartupBytes= 512MB }} +# Or Windows Server 2025 +#1..4 | ForEach-Object {$VMNames="S2D"; $LABConfig.VMs += @{ VMName = "$VMNames$_" ; Configuration = 'S2D' ; ParentVHD = 'Win2025Core_G2.vhdx'; SSDNumber = 0; SSDSize=800GB ; HDDNumber = 12; HDDSize= 4TB ; MemoryStartupBytes= 512MB }} ### HELP ### @@ -47,6 +49,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M SshKeyPath="$($env:USERPROFILE)\.ssh\id_rsa" # (Optional) If set, specified SSH key will be used to build and access Linux images. AutoClosePSWindows=$false; # (Optional) If set, the PowerShell console windows will automatically close once the script has completed successfully. Best suited for use in automated deployments. AutoCleanUp=$false; # (Optional) If set, after creating initial parent disks, files that are no longer necessary will be cleaned up. Best suited for use in automated deployments. + NoDehydrateDC=$false; # (Optional) If set, do not attempt to create a dehydrated DC. AdditionalNetworksConfig=@(); # Just empty array for config below VMs=@(); # Just empty array for config below } From f20e9a751ebe99c54f2df107821536ffc5755b6f Mon Sep 17 00:00:00 2001 From: "Michael Bernstein (DPLAT)" <Michael.Bernstein@microsoft.com> Date: Thu, 13 Jun 2024 16:22:59 -0700 Subject: [PATCH 3/7] Fix issue with extra objects returning from Hydrate-DC --- Scripts/0_DCHydrate.ps1 | 1 - Scripts/2_CreateParentDisks.ps1 | 3 ++- Scripts/3_Deploy.ps1 | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Scripts/0_DCHydrate.ps1 b/Scripts/0_DCHydrate.ps1 index e335f28f..e3b70fa2 100644 --- a/Scripts/0_DCHydrate.ps1 +++ b/Scripts/0_DCHydrate.ps1 @@ -626,6 +626,5 @@ function Hydrate-DC { $DC | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter $dcHydrationEndTime = Get-Date - Return $DC } #endregion diff --git a/Scripts/2_CreateParentDisks.ps1 b/Scripts/2_CreateParentDisks.ps1 index 3c9cef94..62cd3bec 100644 --- a/Scripts/2_CreateParentDisks.ps1 +++ b/Scripts/2_CreateParentDisks.ps1 @@ -525,7 +525,8 @@ if (-not $DCFilesExists){ #region create DC if it does not exist if (-not $DCFilesExists) { if (-not $LabConfig.NoDehydrateDC){ - $DC = Hydrate-DC -DCName $DCName -VhdPath $vhdpath -VmPath $VmPath -SwitchName $HydrationSwitchname -TimeZone $TimeZone -DhcpScope $LabConfig.DHCPscope -AdminPassword $AdminPassword + Hydrate-DC -DCName $DCName -VhdPath $vhdpath -VmPath $VmPath -SwitchName $Switchname -TimeZone $TimeZone -DhcpScope $LabConfig.DHCPscope -AdminPassword $AdminPassword + $DC=Get-VM -Name $DCName if ($DC -eq $null){ WriteErrorAndExit "DC was not created successfully Press any key to continue ..." } else { diff --git a/Scripts/3_Deploy.ps1 b/Scripts/3_Deploy.ps1 index af8e5c72..b29bd440 100644 --- a/Scripts/3_Deploy.ps1 +++ b/Scripts/3_Deploy.ps1 @@ -1207,7 +1207,8 @@ If (-not $isAdmin) { $VMPath="$PSScriptRoot\LAB\" $HydrationSwitchname="DC_HydrationSwitch_$([guid]::NewGuid())" - $DC = Hydrate-DC -DCName $DCName -VhdPath $vhdpath -VMPath $VMPath -Switchname $HydrationSwitchname -TimeZone $TimeZone -DHCPScope $LabConfig.DHCPscope -AdminPassword $LabConfig.AdminPassword + Hydrate-DC -DCName $DCName -VhdPath $vhdpath -VMPath $VMPath -Switchname $HydrationSwitchname -TimeZone $TimeZone -DHCPScope $LabConfig.DHCPscope -AdminPassword $LabConfig.AdminPassword + $DC=Get-VM -Name $DCName if ($DC -eq $null){ WriteErrorAndExit "DC was not created successfully Press any key to continue ..." } else { From 646fc37d62b0772d724a91b609e9ff48f0a063b4 Mon Sep 17 00:00:00 2001 From: Kaustubh Sharma <55542518+kaustubh93-dev@users.noreply.github.com> Date: Fri, 9 Aug 2024 11:22:29 +0530 Subject: [PATCH 4/7] Update LabConfig.ps1 removed "$" sign correct format should be VMName = "S2D$_" --- Scripts/LabConfig.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/LabConfig.ps1 b/Scripts/LabConfig.ps1 index abb600a6..ad5dfb54 100644 --- a/Scripts/LabConfig.ps1 +++ b/Scripts/LabConfig.ps1 @@ -2,7 +2,7 @@ $LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'MSLab-' ; DCEdition='4'; Internet=$true ; AdditionalNetworksConfig=@(); VMs=@()} # Windows Server 2022 -1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "$S2D$_" ; Configuration = 'S2D' ; ParentVHD = 'Win2022Core_G2.vhdx'; SSDNumber = 0; SSDSize=800GB ; HDDNumber = 12; HDDSize= 4TB ; MemoryStartupBytes= 512MB }} +1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "S2D$_" ; Configuration = 'S2D' ; ParentVHD = 'Win2022Core_G2.vhdx'; SSDNumber = 0; SSDSize=800GB ; HDDNumber = 12; HDDSize= 4TB ; MemoryStartupBytes= 512MB }} # Or Azure Stack HCI 23H2 (non-domain joined) https://github.com/DellGEOS/AzureStackHOLs/tree/main/lab-guides/01a-DeployAzureStackHCICluster-CloudBasedDeployment #1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ASNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI23H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 20GB; VMProcessorCount=16 ; vTPM=$true ; Unattend="NoDjoin" ; NestedVirt=$true }} # Or Windows Server 2025 https://github.com/DellGEOS/AzureStackHOLs/tree/main/lab-guides/03-TestingWindowsServerInsider From 3ba705ca9431d773a9384221085f1e2d71b63757 Mon Sep 17 00:00:00 2001 From: Karl Wester-Ebbinghaus <45657752+Karl-WE@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:04:36 +0200 Subject: [PATCH 5/7] Update 1_Prereq.ps1 - updated diskspd Link mslab will download an outdated diskspd version, which is crucial being updated for scenarios like VMfleet. The latest 2.2 offers significant improvements. --- Scripts/1_Prereq.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/1_Prereq.ps1 b/Scripts/1_Prereq.ps1 index 87823565..a0a74fef 100644 --- a/Scripts/1_Prereq.ps1 +++ b/Scripts/1_Prereq.ps1 @@ -178,7 +178,7 @@ function Get-WindowsBuildNumber { $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") } #> - $downloadurl="https://github.com/microsoft/diskspd/releases/download/v2.1/DiskSpd.ZIP" + $downloadurl="https://github.com/microsoft/diskspd/releases/download/v2.2/DiskSpd.ZIP" Invoke-WebRequest -Uri $downloadurl -OutFile "$PSScriptRoot\Temp\ToolsVHD\DiskSpd\diskspd.zip" }catch{ WriteError "`t Failed to download Diskspd!" From 6434960394700da61738b5afdf26e50170278c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr?= <machv@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:26:22 +0100 Subject: [PATCH 6/7] Do not sign scripts. (#609) Co-authored-by: Jaromir Kaspar <kaspik@hotmail.com> --- .github/workflows/create-release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index ce330a8d..fed84ad5 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -16,7 +16,7 @@ jobs: environment: release name: Bump version if: "!contains(github.event.head_commit.message, '[no release]')" - runs-on: windows-2019 + runs-on: windows-latest permissions: # needed to create a release contents: write @@ -54,7 +54,7 @@ jobs: new-release: name: Create release if: "!contains(github.event.head_commit.message, '[no release]')" - runs-on: self-hosted + runs-on: windows-latest permissions: # needed to create a release contents: write @@ -69,7 +69,7 @@ jobs: SIGN_SCRIPT_URI: ${{ secrets.SIGN_SCRIPT_URI }} CLIENT_ID: ${{ secrets.CLIENT_ID }} # just to obfusctate it in the output run: | - ./build.ps1 -Version ${{ needs.new-version.outputs.new_tag }} -SignScripts $true -SignScriptUri $env:SIGN_SCRIPT_URI -ClientId $env:CLIENT_ID + ./build.ps1 -Version ${{ needs.new-version.outputs.new_tag }} -SignScripts $false -SignScriptUri $env:SIGN_SCRIPT_URI -ClientId $env:CLIENT_ID Move-Item ./Release.zip mslab_${{ needs.new-version.outputs.new_tag }}.zip - name: Create changelog id: changelog From 3c8a06ae50802421bdedf52dd197227a356f52a7 Mon Sep 17 00:00:00 2001 From: Jaromir Kaspar <kaspik@hotmail.com> Date: Wed, 27 Nov 2024 14:47:49 +0100 Subject: [PATCH 7/7] Win2025 --- Scripts/2_CreateParentDisks.ps1 | 4 ++-- Scripts/LabConfig.ps1 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Scripts/2_CreateParentDisks.ps1 b/Scripts/2_CreateParentDisks.ps1 index 15e1526b..2d97c101 100644 --- a/Scripts/2_CreateParentDisks.ps1 +++ b/Scripts/2_CreateParentDisks.ps1 @@ -274,10 +274,10 @@ If (-not $isAdmin) { } $ISOServer = Mount-DiskImage -ImagePath $ServerISOItem.FullName -PassThru }else{ - WriteInfoHighlighted "Please select ISO image with Windows Server 2016, 2019, 2022 or Server Insider" + WriteInfoHighlighted "Please select ISO image with Windows Server 2016, 2019, 2022, 2025 or Server Insider" [reflection.assembly]::loadwithpartialname("System.Windows.Forms") $openFile = New-Object System.Windows.Forms.OpenFileDialog -Property @{ - Title="Please select ISO image with Windows Server 2016, 2019, 2022 or Server Insider" + Title="Please select ISO image with Windows Server 2016, 2019, 2022, 2025 or Server Insider" } $openFile.Filter = "iso files (*.iso)|*.iso|All files (*.*)|*.*" If($openFile.ShowDialog() -eq "OK"){ diff --git a/Scripts/LabConfig.ps1 b/Scripts/LabConfig.ps1 index bdb2ec7e..e2d0a447 100644 --- a/Scripts/LabConfig.ps1 +++ b/Scripts/LabConfig.ps1 @@ -5,7 +5,7 @@ $LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPass 1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "$S2D$_" ; Configuration = 'S2D' ; ParentVHD = 'Win2022Core_G2.vhdx'; SSDNumber = 0; SSDSize=800GB ; HDDNumber = 12; HDDSize= 4TB ; MemoryStartupBytes= 512MB }} # Or Azure Stack HCI 23H2 (non-domain joined) https://github.com/DellGEOS/AzureStackHOLs/tree/main/lab-guides/01a-DeployAzureStackHCICluster-CloudBasedDeployment #1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ASNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI23H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 24GB; VMProcessorCount=16 ; vTPM=$true ; Unattend="NoDjoin" ; NestedVirt=$true }} -# Or Windows Server 2025 https://github.com/DellGEOS/AzureStackHOLs/tree/main/lab-guides/03-TestingWindowsServerInsider +# Or Windows Server 2025 https://github.com/DellGEOS/AzureLocalHOLs/tree/main/lab-guides/03-TestingWindowsServer2025 #1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName="S2D$_" ; Configuration='S2D' ; ParentVHD='Win2025Core_G2.vhdx' ; HDDNumber=4 ; HDDSize=2TB ; MemoryStartupBytes=1GB; VMProcessorCount=4 ; vTPM=$true}} ### HELP ###