Public/New-DSCPullServerLab.ps1
function New-DSCPullServerLab { [CmdletBinding(SupportsShouldProcess = $true)] Param( [Parameter (Mandatory = $true)] [PSCredential]$Credential, [Parameter (Mandatory = $true)] [string]$ISODestinationPath, [Parameter (Mandatory = $true)] [string]$ParentVHDDestinationPath, [Parameter (Mandatory = $true)] [string]$ParentVHDLocale, [Parameter (Mandatory = $true)] [string]$ParentVHDTimeZone, [Parameter (Mandatory = $true)] [string]$DSCPullServerName, [Parameter (ParameterSetName = 'TargetNodes')] [string[]]$TargetNodeNames, [Parameter (Mandatory = $true)] [int]$DSCPullServerRamMB, [Parameter (Mandatory = $true, ParameterSetName = 'TargetNodes')] [Parameter (Mandatory = $false, ParameterSetName = 'Default')] [int]$TargetNodeRamMB, [Parameter (Mandatory = $true)] [string]$VMRootPath, [Parameter (Mandatory = $true)] [string]$VMIPAddress, [Parameter (Mandatory = $true)] [byte]$VMSubnetBits, [Parameter (Mandatory = $true)] [string[]]$VMDNSServers, [Parameter (Mandatory = $true)] [string]$VMDefaultGateway, [Parameter (Mandatory = $true)] [string]$VirtualSwitchName, [switch]$StartVMs, [switch]$Force, [string]$Uri = 'https://software-download.microsoft.com/download/pr/Windows_Server_2016_Datacenter_EVAL_en-us_14393_refresh.ISO', [string]$OSEdition = '4', [string]$XPSDesiredStateConfigurationVersion = '3.13.0.0' ) try { $ResourcesNeeded = @() if ($Force) { $ConfirmPreference = 'Low' Write-Warning 'The force parameter will overwrite existing components if required. The ISO will also be re-downloaded.' $ResourcesNeeded = 'ISO', 'ParentVHD', 'VSwitch', 'VM', 'VMConfig', 'DSCServiceConfig', 'TestVMs' } else { if ((Test-Path $ISODestinationPath) -eq $false) { $ResourcesNeeded += 'ISO' } if ((Test-Path $ParentVHDDestinationPath) -eq $false) { $ResourcesNeeded += 'ParentVHD' } if (-not(Get-VMSwitch $VirtualSwitchName -ErrorAction SilentlyContinue)) { $ResourcesNeeded += 'VSwitch' } if (-not($VM = Get-VM -Name $DSCPullServerName -ErrorAction SilentlyContinue)) { $ResourcesNeeded += 'VM' $ResourcesNeeded += 'VMConfig' $ResourcesNeeded += 'DSCServiceConfig' } else { # check whether the existing vm has already been configured as a pull server by Install-DSCPullServer $ErrorActionPreference = 'Stop' if ($VM.State -ne 'Running') { Start-VM -VM $VM } $error.Clear() $i = 0 do { $i++ Write-Verbose "Waiting for $($VM.Name)..." Wait-VM -Name $VM.Name -For IPAddress -Timeout 1800 -ErrorAction Stop try { # compare-Object will return null if pull server is already configured $ConfigurationDrift = Invoke-Command -VMId $VM.Id -Credential $Credential -ScriptBlock {$ResourcesInDesiredState = (Get-DscConfigurationStatus).resourcesindesiredstate.resourceid $DesiredResources = '[WindowsFeature]DSCServiceFeature', '[xDSCWebService]PSDSCPullServer', '[File]RegistrationKeyFile', '[WindowsFeature]DHCPServerFeature', '[WindowsFeature]DHCPRSAT', '[xDhcpServerScope]DHCPScope', '[xDhcpServerOption]DHCPScopeDNSServer', '[WindowsFeature]DNSServerFeature', '[xDnsServerPrimaryZone]DNSZone', '[WindowsFeature]DNSRSAT', '[xDnsServerPrimaryZone]DNSZone', '[xDnsRecord]DSCPullServerARecord' Compare-Object -ReferenceObject $DesiredResources -DifferenceObject $ResourcesInDesiredState} if ($ConfigurationDrift) { $ResourcesNeeded += 'DSCServiceConfig' } else { Write-Warning "The VM 'DSCPullServer' already exists and is configured as a pull server." } $error.Clear() } catch { if ($_.Exception.Message -like "*The Consistency Check or Pull cmdlet is in progress and must return before Get-DscConfigurationStatus can be invoked*") { Start-Sleep -Seconds 20 $error.Add($_) | Out-Null } # check whether Get-DSCConfigurationStatus reported that there is no current configuration (by means of an error) elseif ($_.Exception.Message -like "No status information*" -or $_.Exception.Message -eq "Cannot bind argument to parameter 'DifferenceObject' because it is null.") { $ResourcesNeeded += 'DSCServiceConfig' $error.Clear() } else { if ($i -gt 9) { throw "Timed out while trying to determine whether $($VM.name) is already configured as a pull server" } $error.Add($_) | Out-Null } } } # keep trying if there is any other error (i.e. vm decides to reboot midway through the remote command) while ($error) $ErrorActionPreference = 'Continue' } if ($PSBoundParameters.ContainsKey('TargetNodeNames')) { # no need to check for existing test vms with colliding names as New-WindowsServerLabVM will warn and skip $ResourcesNeeded += 'TestVMs' } if ($ResourcesNeeded.Length -eq 0) { $ResourcesNeeded = 'None' } } if ($PSCmdlet.ShouldProcess("$env:COMPUTERNAME")) { $ConfirmPreference = 'High' if ($Force) { if (Get-VM -Name $DSCPullServerName -ErrorAction 'SilentlyContinue' | Where-Object state -EQ 'running' -ErrorAction 'SilentlyContinue') { Remove-WindowsServerLabVM -Name $DSCPullServerName -RemoveDisk } } switch ($ResourcesNeeded) { 'ISO' {New-WindowsServerISO -ISODestinationPath $ISODestinationPath -Uri $Uri -Force:$Force} 'ParentVHD' {New-WindowsServerParentVHD -ParentVHDDestinationPath $ParentVHDDestinationPath -ISOPath $ISODestinationPath -OSEdition $OSEdition -Force:$Force; Set-VHDUnattendFile -VHDPath $ParentVHDDestinationPath -UnattendAdminPassword $Credential.Password -Locale $ParentVHDLocale -TimeZone $ParentVHDTimeZone} 'VSwitch' {New-LabVirtualSwitch -SwitchName $VirtualSwitchName -Force:$Force} 'VM' {New-WindowsServerLabVM -Name $DSCPullServerName -ParentVHDPath $ParentVHDDestinationPath -RootPath $VMRootPath -RamMB $DSCPullServerRamMB -VirtualSwitchName $VirtualSwitchName -Start:$StartVMs -Force:$Force} 'VMConfig' {Set-WindowsServerLabVM -Name $DSCPullServerName -Credential $Credential -IPAddress $VMIPAddress -SubnetBits $VMSubnetBits -DefaultGateway $VMDefaultGateway -DNSServers $VMDNSServers -EthernetAdapterName 'Ethernet' -Attempts 10} 'DSCServiceConfig' {Install-DSCPullServer -VMName $DSCPullServerName -Credential $Credential -XPSDesiredStateConfigurationVersion $XPSDesiredStateConfigurationVersion -Attempts 10} 'TestVMs' {New-WindowsServerLabVM -Name $TargetNodeNames -ParentVHDPath $ParentVHDDestinationPath -RootPath $VMRootPath -RamMB $TargetNodeRamMB -VirtualSwitchName $VirtualSwitchName -Start:$StartVMs -Force:$Force -PassThru | Set-WindowsServerLabVM -Credential $Credential -Attempts 20} Default {Write-Warning 'All lab components already exist. Use the force switch to replace them or delete the components you want to replace then run the command again.'} } } } catch { $PSCmdlet.ThrowTerminatingError($_) } } |