As you read in Part 15 of this blog Series we are able to create some great Gold Images with Big Demo.
How do we go about building the actual application Servers from the Gold Images?
Building your Servers with Big_Demo
Whenever you are working with logic and automation it is critical to control the steps that happen.
For example, you can’t join a domain without a functional domain controller?
To work around this there is a wonderful PowerShell Function that Dave grabbed from Ben Armstrong @VirtualPCGUy.
It’s called Wait-PSDirect and it uses PowerShell Direct to determine if a VM is ready to proceed by checking credentials.
As the script progresses you will notice that there are a few different switches Wait-PSDirect uses.
One is using $LocalCred which is the Local Administrator Account and one is $DomainCred which uses the Domain Admin Account.
This is important to understand because as the lab is building there are times when the machine hasn’t joined the domain yet and we are waiting on a pending reboot.
We want to let the machine finish the reboot before the script continues.
Yes it is a glorified Wait function but the logic behind it is brilliant because it just works. Kind of like Veeam Software
You will see this Function called a lot in the script:
function Wait-PSDirect { param ( [string] $VMName, [Object] $cred ) Write-Log $VMName "Waiting for PowerShell Direct (using $($cred.username))" while ((Invoke-Command -VMName $VMName -Credential $cred { 'Test' } -ea SilentlyContinue) -ne 'Test') { Start-Sleep -Seconds 1 } }
Next, we have Restart-VM which helps us be simply automating a reboot of the VM’s as they are servicing.
function Restart-DemoVM { param ( [string] $VMName ) Write-Log $VMName 'Rebooting' stop-vm $VMName start-vm $VMName }
So, what actually builds the HyperV VM’s for us? Well it is the next few functions that do all the heavy lifting for us.
Invoke-DemoPrepVM is the function that will build all of the base VM’s in the Lab for us. We use it to build AD,DHCP,MGMT and other core VMS.
Remember that these are all built from the Gold VHDx file that we learned about in Part 15 of this blog post.
Each of these new VM’s are created using differencing disks, configured with a set amount of RAM, vCPUS, etc.
function Invoke-DemoVMPrep { param ( [string] $VMName, [string] $GuestOSName, [switch] $FullServer ) Write-Log $VMName 'Removing old VM' get-vm $VMName -ErrorAction SilentlyContinue | stop-vm -TurnOff -Force -Passthru | remove-vm -Force Clear-File "$($VMPath)\$($GuestOSName).vhdx" Write-Log $VMName 'Creating new differencing disk' if ($FullServer) { $null = New-VHD -Path "$($VMPath)\$($GuestOSName).vhdx" -ParentPath "$($BaseVHDPath)\VMServerBase.vhdx" -Differencing } else { $null = New-VHD -Path "$($VMPath)\$($GuestOSName).vhdx" -ParentPath "$($BaseVHDPath)\VMServerBaseCore.vhdx" -Differencing } Write-Log $VMName 'Creating virtual machine' new-vm -Name $VMName -MemoryStartupBytes 16GB -SwitchName $virtualSwitchName ` -Generation 2 -Path "$($VMPath)\" | Set-VM -ProcessorCount 2 Set-VMFirmware -VMName $VMName -SecureBootTemplate MicrosoftUEFICertificateAuthority Set-VMFirmware -Vmname $VMName -EnableSecureBoot off Add-VMHardDiskDrive -VMName $VMName -Path "$($VMPath)\$($GuestOSName).vhdx" -ControllerType SCSI Write-Log $VMName 'Starting virtual machine' Enable-VMIntegrationService -Name 'Guest Service Interface' -VMName $VMName start-vm $VMName }
The build process for these VM’s takes about 2 minutes as they are just spinning up from a Sysprepped Master Gold VHDx file.
After the core VM’s have been provisioned we use Create-DemoVM to name the VM, Set the IP Address, and then use the Wait-PSDirect function to give it time to finish before continuing.
function Create-DemoVM { param ( [string] $VMName, [string] $GuestOSName, [string] $IPNumber = '0' ) Wait-PSDirect $VMName -cred $localCred Invoke-Command -VMName $VMName -Credential $localCred { param($IPNumber, $GuestOSName, $VMName, $domainName, $Subnet) if ($IPNumber -ne '0') { Write-Output -InputObject "[$($VMName)]:: Setting IP Address to $($Subnet)$($IPNumber)" $null = New-NetIPAddress -IPAddress "$($Subnet)$($IPNumber)" -InterfaceAlias 'Ethernet' -PrefixLength 24 Write-Output -InputObject "[$($VMName)]:: Setting DNS Address" Get-DnsClientServerAddress | ForEach-Object -Process { Set-DnsClientServerAddress -InterfaceIndex $_.InterfaceIndex -ServerAddresses "$($Subnet)1" } } Write-Output -InputObject "[$($VMName)]:: Renaming OS to `"$($GuestOSName)`"" Rename-Computer -NewName $GuestOSName Write-Output -InputObject "[$($VMName)]:: Configuring WSMAN Trusted hosts" Set-Item -Path WSMan:\localhost\Client\TrustedHosts -Value "*.$($domainName)" -Force Set-Item WSMan:\localhost\client\trustedhosts "$($Subnet)*" -Force -concatenate Enable-WSManCredSSP -Role Client -DelegateComputer "*.$($domainName)" -Force } -ArgumentList $IPNumber, $GuestOSName, $VMName, $domainName, $Subnet Restart-DemoVM $VMName Wait-PSDirect $VMName -cred $localCred }
Now because our Storage Nodes for Storage Spaces Direct require a bit of a customized configuration like: Additional VHDx files that serve as our JBOD disks, Core Networking, etc.
A customized version of the Create-DemoVM function has been built for this purpose. It is called Invoke-NodeStorageBuild.
function Invoke-NodeStorageBuild { param($VMName, $GuestOSName) Create-DemoVM $VMName $GuestOSName Clear-File "$($VMPath)\$($GuestOSName) - Data 1.vhdx" Clear-File "$($VMPath)\$($GuestOSName) - Data 2.vhdx" Clear-File "$($VMPath)\$($GuestOSName) - Data 3.vhdx" Clear-File "$($VMPath)\$($GuestOSName) - Data 4.vhdx" Clear-File "$($VMPath)\$($GuestOSName) - Data 5.vhdx" Clear-File "$($VMPath)\$($GuestOSName) - Data 6.vhdx" Get-VM $VMName | Stop-VM Add-VMNetworkAdapter -VMName $VMName -SwitchName $virtualSwitchName new-vhd -Path "$($VMPath)\$($GuestOSName) - Data 1.vhdx" -Dynamic -SizeBytes 200GB Add-VMHardDiskDrive -VMName $VMName -Path "$($VMPath)\$($GuestOSName) - Data 1.vhdx" -ControllerType SCSI new-vhd -Path "$($VMPath)\$($GuestOSName) - Data 2.vhdx" -Dynamic -SizeBytes 200GB Add-VMHardDiskDrive -VMName $VMName -Path "$($VMPath)\$($GuestOSName) - Data 2.vhdx" -ControllerType SCSI new-vhd -Path "$($VMPath)\$($GuestOSName) - Data 3.vhdx" -Dynamic -SizeBytes 200GB Add-VMHardDiskDrive -VMName $VMName -Path "$($VMPath)\$($GuestOSName) - Data 3.vhdx" -ControllerType SCSI new-vhd -Path "$($VMPath)\$($GuestOSName) - Data 4.vhdx" -Dynamic -SizeBytes 200GB Add-VMHardDiskDrive -VMName $VMName -Path "$($VMPath)\$($GuestOSName) - Data 4.vhdx" -ControllerType SCSI new-vhd -Path "$($VMPath)\$($GuestOSName) - Data 5.vhdx" -Dynamic -SizeBytes 200GB Add-VMHardDiskDrive -VMName $VMName -Path "$($VMPath)\$($GuestOSName) - Data 5.vhdx" -ControllerType SCSI new-vhd -Path "$($VMPath)\$($GuestOSName) - Data 6.vhdx" -Dynamic -SizeBytes 200GB Add-VMHardDiskDrive -VMName $VMName -Path "$($VMPath)\$($GuestOSName) - Data 6.vhdx" -ControllerType SCSI Set-VMProcessor -VMName $VMName -Count 2 -ExposeVirtualizationExtensions $true Add-VMNetworkAdapter -VMName $VMName -SwitchName $virtualSwitchName Add-VMNetworkAdapter -VMName $VMName -SwitchName $virtualSwitchName Add-VMNetworkAdapter -VMName $VMName -SwitchName $virtualSwitchName Get-VMNetworkAdapter -VMName $VMName | Set-VMNetworkAdapter -AllowTeaming On Get-VMNetworkAdapter -VMName $VMName | Set-VMNetworkAdapter -MacAddressSpoofing on Start-VM $VMName Wait-PSDirect $VMName -cred $localCred Invoke-Command -VMName $VMName -Credential $localCred { param($VMName, $domainCred, $domainName) Write-Output -InputObject "[$($VMName)]:: Installing Clustering" $null = Install-WindowsFeature -Name File-Services, Failover-Clustering, Hyper-V -IncludeManagementTools Write-Output -InputObject "[$($VMName)]:: Joining domain as `"$($env:computername)`"" while (!(Test-Connection -ComputerName $domainName -BufferSize 16 -Count 1 -Quiet -ea SilentlyContinue)) { Start-Sleep -Seconds 1 } do { Add-Computer -DomainName $domainName -Credential $domainCred -ea SilentlyContinue } until ($?) } -ArgumentList $VMName, $domainCred, $domainName Wait-PSDirect $VMName -cred $domainCred Invoke-Command -VMName $VMName -Credential $domainCred { Rename-NetAdapter -Name 'Ethernet' -NewName 'LOM-P0' } Invoke-Command -VMName $VMName -Credential $DomainCred { Rename-NetAdapter -Name 'Ethernet 2' -NewName 'LOM-P1' } Invoke-Command -VMName $VMName -Credential $DomainCred { Rename-NetAdapter -Name 'Ethernet 3' -NewName 'Riser-P0' } Invoke-Command -VMName $VMName -Credential $DomainCred{ Get-NetAdapter -Name 'Ethernet 5' | Rename-NetAdapter -NewName 'Riser-P1' } Invoke-Command -VMName $VMName -Credential $DomainCred { New-NetLbfoTeam -Name HyperVTeam -TeamMembers 'LOM-P0' -verbose -confirm:$false } Invoke-Command -VMName $VMName -Credential $DomainCred { Add-NetLbfoTeamMember 'LOM-P1' -team HyperVTeam -confirm:$false } Invoke-Command -VMName $VMName -Credential $DomainCred { New-NetLbfoTeam -Name StorageTeam -TeamMembers 'Riser-P0' -verbose -confirm:$false } Invoke-Command -VMName $VMName -Credential $DomainCred { Add-NetLbfoTeamMember 'Riser-P1' -team StorageTeam -confirm:$false } Restart-DemoVM $VMName Wait-PSDirect $VMName -cred $domainCred ping localhost -n 20 Invoke-Command -VMName $VMName -Credential $domainCred { New-VMSwitch -Name 'VSW01' -NetAdapterName 'HyperVTeam' -AllowManagementOS $false } Invoke-Command -VMName $VMName -Credential $domainCred { Add-VMNetworkAdapter -ManagementOS -Name ClusterCSV-VLAN204 -Switchname VSW01 -verbose } Invoke-Command -VMName $VMName -Credential $domainCred { Add-VMNetworkAdapter -ManagementOS -Name LM-VLAN203 -Switchname VSW01 -verbose } Invoke-Command -VMName $VMName -Credential $domainCred { Add-VMNetworkAdapter -ManagementOS -Name Servers-VLAN201 -Switchname VSW01 -verbose } Invoke-Command -VMName $VMName -Credential $domainCred { Add-VMNetworkAdapter -ManagementOS -Name MGMT-VLAN200 -Switchname VSW01 -verbose } # Restart-DemoVM $VMName }
Here is an example of the Script in action:
First, we run the Invoke-DemoPrepVM which builds the VM, then the Create-DemoVM to give it a name and IP Address.
The last step is to configure something inside the VM. In this case we will install Active Directory using PowerShell Direct for the Lab.
Invoke-DemoVMPrep 'DC1-RDS' 'DC1-RDS' -FullServer $VMName = 'DC1-RDS' $GuestOSName = 'DC1-RDS' $IPNumber = '1' Create-DemoVM $VMName $GuestOSName $IPNumber Invoke-Command -VMName $VMName -Credential $localCred { param($VMName, $domainName, $domainAdminPassword) Write-Output -InputObject "[$($VMName)]:: Installing AD" $null = Install-WindowsFeature AD-Domain-Services -IncludeManagementTools Write-Output -InputObject "[$($VMName)]:: Enabling Active Directory and promoting to domain controller" Install-ADDSForest -DomainName $domainName -InstallDNS -NoDNSonNetwork -NoRebootOnCompletion ` -SafeModeAdministratorPassword (ConvertTo-SecureString -String $domainAdminPassword -AsPlainText -Force) -confirm:$false } -ArgumentList $VMName, $domainName, $domainAdminPassword Restart-DemoVM $VMName
Remember the base code for this can be downloaded from http://www.github.com/dkawula
Let’s have a look at the Script executing to build my lab which includes my nested Storage Spaces Direct Farm.
Write-Log 'Host' 'Getting started...' Confirm-Path $BaseVHDPath Confirm-Path $VMPath Write-Log 'Host' 'Building Base Images' if (!(Test-Path -Path "$($BaseVHDPath)\VMServerBase.vhdx")) { . Initialize-BaseImage } if ((Get-VMSwitch | Where-Object -Property name -EQ -Value $virtualSwitchName) -eq $null) { New-VMSwitch -Name $virtualSwitchName -SwitchType Private } Invoke-DemoVMPrep 'DHCP1-RDS' 'DHCP1-RDS' -FullServer Invoke-DemoVMPrep 'MGMT1-RDS' 'MDT01-RDS' -FullServer Invoke-DemoVMPrep 'DC1-RDS' 'DC1-RDS' -FullServer $VMName = 'DC1-RDS' $GuestOSName = 'DC1-RDS' $IPNumber = '1' Create-DemoVM $VMName $GuestOSName $IPNumber Invoke-Command -VMName $VMName -Credential $localCred { param($VMName, $domainName, $domainAdminPassword) Write-Output -InputObject "[$($VMName)]:: Installing AD" $null = Install-WindowsFeature AD-Domain-Services -IncludeManagementTools Write-Output -InputObject "[$($VMName)]:: Enabling Active Directory and promoting to domain controller" Install-ADDSForest -DomainName $domainName -InstallDNS -NoDNSonNetwork -NoRebootOnCompletion ` -SafeModeAdministratorPassword (ConvertTo-SecureString -String $domainAdminPassword -AsPlainText -Force) -confirm:$false } -ArgumentList $VMName, $domainName, $domainAdminPassword Restart-DemoVM $VMName $VMName = 'DHCP1-RDS' $GuestOSName = 'DHCP1-RDS' $IPNumber = '3' Create-DemoVM $VMName $GuestOSName $IPNumber Invoke-Command -VMName $VMName -Credential $localCred { param($VMName, $domainCred, $domainName) Write-Output -InputObject "[$($VMName)]:: Installing DHCP" $null = Install-WindowsFeature DHCP -IncludeManagementTools Write-Output -InputObject "[$($VMName)]:: Joining domain as `"$($env:computername)`"" while (!(Test-Connection -ComputerName $domainName -BufferSize 16 -Count 1 -Quiet -ea SilentlyContinue)) { Start-Sleep -Seconds 1 } do { Add-Computer -DomainName $domainName -Credential $domainCred -ea SilentlyContinue } until ($?) } -ArgumentList $VMName, $domainCred, $domainName Restart-DemoVM $VMName Wait-PSDirect $VMName -cred $domainCred Invoke-Command -VMName $VMName -Credential $domainCred { param($VMName, $domainName, $Subnet, $IPNumber) Write-Output -InputObject "[$($VMName)]:: Waiting for name resolution" while ((Test-NetConnection -ComputerName $domainName).PingSucceeded -eq $false) { Start-Sleep -Seconds 1 } Write-Output -InputObject "[$($VMName)]:: Configuring DHCP Server" Set-DhcpServerv4Binding -BindingState $true -InterfaceAlias Ethernet Add-DhcpServerv4Scope -Name 'IPv4 Network' -StartRange "$($Subnet)10" -EndRange "$($Subnet)200" -SubnetMask 255.255.255.0 Set-DhcpServerv4OptionValue -OptionId 6 -value "$($Subnet)1" Add-DhcpServerInDC -DnsName "$($env:computername).$($domainName)" foreach($i in 1..99) { $mac = '00-b5-5d-fe-f6-' + ($i % 100).ToString('00') $ip = $Subnet + '1' + ($i % 100).ToString('00') $desc = 'Container ' + $i.ToString() $scopeID = $Subnet + '0' Add-DhcpServerv4Reservation -IPAddress $ip -ClientId $mac -Description $desc -ScopeId $scopeID } } -ArgumentList $VMName, $domainName, $Subnet, $IPNumber Restart-DemoVM $VMName $VMName = 'DC1-RDS' $GuestOSName = 'DC1-RDS' $IPNumber = '1' Wait-PSDirect $VMName -cred $domainCred Invoke-Command -VMName $VMName -Credential $domainCred { param($VMName, $password) Write-Output -InputObject "[$($VMName)]:: Creating user account for Dave" do { Start-Sleep -Seconds 5 New-ADUser ` -Name 'Dave' ` -SamAccountName 'Dave' ` -DisplayName 'Dave' ` -AccountPassword (ConvertTo-SecureString -String $password -AsPlainText -Force) ` -ChangePasswordAtLogon $false ` -Enabled $true -ea 0 } until ($?) Add-ADGroupMember -Identity 'Domain Admins' -Members 'Dave' } -ArgumentList $VMName, $domainAdminPassword $VMName = 'MGMT1-RDS' $GuestOSName = 'MGMT1-RDS' Create-DemoVM $VMName $GuestOSName Invoke-Command -VMName $VMName -Credential $localCred { param($VMName, $domainCred, $domainName) Write-Output -InputObject "[$($VMName)]:: Management tools" $null = Install-WindowsFeature RSAT-Clustering, RSAT-Hyper-V-Tools Write-Output -InputObject "[$($VMName)]:: Joining domain as `"$($env:computername)`"" while (!(Test-Connection -ComputerName $domainName -BufferSize 16 -Count 1 -Quiet -ea SilentlyContinue)) { Start-Sleep -Seconds 1 } do { Add-Computer -DomainName $domainName -Credential $domainCred -ea SilentlyContinue } until ($?) } -ArgumentList $VMName, $domainCred, $domainName Restart-DemoVM $VMName Invoke-DemoVMPrep 'S2D1' 'S2D1' #-FullServer Invoke-DemoVMPrep 'S2D2' 'S2D2' #-FullServer Invoke-DemoVMPrep 'S2D3' 'S2D3' #-FullServer Invoke-DemoVMPrep 'S2D4' 'S2D4' #-FullServer Invoke-DemoVMPrep 'S2D5' 'S2D5' #-FullServer Wait-PSDirect 'S2D5' -cred $localCred $VMName = 'S2D1' $GuestOSName = 'S2D1' Invoke-NodeStorageBuild 'S2D1' 'S2D1' Invoke-NodeStorageBuild 'S2D2' 'S2D2' Invoke-NodeStorageBuild 'S2D3' 'S2D3' Invoke-NodeStorageBuild 'S2D4' 'S2D4' Invoke-NodeStorageBuild 'S2D5' 'S2D5' Wait-PSDirect 'S2D5' -cred $domainCred Invoke-Command -VMName 'MGMT1' -Credential $domainCred { param ($domainName) do { New-Cluster -Name S2DCluster -Node S2D1, S2D2, S2D3, S2D4,S2D5 -NoStorage } until ($?) while (!(Test-Connection -ComputerName "S2DCluster.$($domainName)" -BufferSize 16 -Count 1 -Quiet -ea SilentlyContinue)) { ipconfig.exe /flushdns Start-Sleep -Seconds 1 } { (Get-Cluster).S2DBusTypes=4294967295 } #Enable-ClusterStorageSpacesDirect -Cluster "S2DCluster.$($domainName)" #Add-ClusterScaleOutFileServerRole -name S2DFileServer -cluster "S2DCluster.$($domainName)" } -ArgumentList $domainName Invoke-Command -VMName 'S2D1' -Credential $domainCred { param ($domainName) #New-StoragePool -StorageSubSystemName "S2DCluster.$($domainName)" -FriendlyName S2DPool -WriteCacheSizeDefault 0 -ProvisioningTypeDefault Fixed -ResiliencySettingNameDefault Mirror -PhysicalDisk (Get-StorageSubSystem -Name "S2DCluster.$($domainName)" | Get-PhysicalDisk) #New-Volume -StoragePoolFriendlyName S2DPool -FriendlyName S2DDisk -PhysicalDiskRedundancy 2 -FileSystem CSVFS_REFS -Size 500GB #updated from MSFT TP5 notes # The Cmdlet changed for RTM - Enable-ClusterS2D -CacheMode Disabled -AutoConfig:0 -SkipEligibilityChecks -confirm:$false #This step can take a little while espeically if you are adding a lot of nodes and disks (Get-Cluster).S2DBusTypes=4294967295 Enable-ClusterStorageSpacesDirect -PoolFriendlyName S2DPool -confirm:$False #Create storage pool and set media type to HDD #New-StoragePool -StorageSubSystemFriendlyName *Cluster* -FriendlyName S2D -ProvisioningTypeDefault Fixed -PhysicalDisk (Get-PhysicalDisk | Where-Object -Property CanPool -EQ -Value $true) #Create a volume #This will match the configuration that was done in the book New-Volume -StoragePoolFriendlyName S2DPool -FriendlyName Mirror-2Way -FileSystem CSVFS_REFS -Size 200GB -PhysicalDiskRedundancy 1 New-Volume -StoragePoolFriendlyName S2DPool -FriendlyName Mirror-3Way -FileSystem CSVFS_REFS -Size 200GB -PhysicalDiskRedundancy 2 } -ArgumentList $domainName Write-Log 'Done' 'Done!'
Hope you enjoyed this post and have fun learning more about HyperV, Storage Spaces Direct, and PowerShell.
Happy learning….
Thanks,
Cristal
Cristal Kawula
Cristal Kawula is the co-founder of MVPDays Community Roadshow and #MVPHour live Twitter Chat. She was also a member of the Gridstore Technical Advisory board and is the President of TriCon Elite Consulting. Cristal is also only the 2nd woman in the world to receive the prestigious Veeam Vanguard award.
BLOG: http://www.checkyourlogs.net
Twitter: @supercristal1 / @mvpdays / #mvphour
Check out www.mvpdays.com to see where the MVPDays Roadshow will be next. Maybe it will be in a city near you.