Public/Deploy-AGInfrastructure.ps1
function Deploy-AGInfrastructure { <# TODO: Create 'PAW' as a Custom Role that includes RSAT. TODO: Accept Roles as Parameter. #> [CmdletBinding()] param ( [PsfValidatePattern('^\w{1,11}$', ErrorMessage = 'Lab name must be no longer than 11 characters and only contain letters and numbers.')] $Name = 'Locksmith', [PsfValidatePattern('\.', ErrorMessage = 'Domain must contain at least one dot.')] $Domain = 'adcs.goat', $ExternalSwitch = 'External Switch', $Sources = (Get-LabSourcesLocation), $LabsRoot = "$((Get-PSFConfig -Module AutomatedLab -Name LabAppDataRoot).Value)\Labs", # Not currently needed, but I like it., [switch]$Confirm ) <# #requires -Modules Hyper-V, AutomatedLab -Version 7 -RunAsAdministrator #> Write-Verbose -Message @" ---------------------------------------------------- | Initial Configuration | ---------------------------------------------------- Name = $Name Domain = $Domain ExternalSwitch = $ExternalSwitch Sources = $Sources LabRoot = $LabsRoot "@ # Confirm lab name is unique on this host. while ((Get-Lab -List) -contains $Name) { Write-Host Write-Warning -Message "A lab named `"$Name`" already exists on this host." Write-Host "Please select a new lab name: " -NoNewline $Name = Read-Host } # Import existing labs and add their domains to an array. Write-Host "`nImporting existing labs to confirm the new root domain name `"$Domain`" is unique." $ExistingDomains = Get-Lab -List | ForEach-Object { Import-Lab -Name $_ Get-LabVM | Select-Object DomainName } $ExistingDomains = $ExistingDomains | Sort-Object -Property DomainName -Unique if ($ExistingDomains) { Write-Verbose "Existing Domains: $($ExistingDomains.DomainName)" } # Confirm root domain name is unique on this host. while ($ExistingDomains.DomainName -contains $Domain) { Write-Host Write-Warning -Message "A lab using the domain `"$Domain`" already exists on this host." Write-Host "Please select a new root domain name: " -NoNewline $Domain = Read-Host } # Create a Hyper-V External Switch if none exists. while (-not (Get-VMSwitch | Where-Object Name -EQ $ExternalSwitch)) { #region Select NetAdapter for Use in Lab $netIPAddressCollection = Get-NetIPAddress | Where-Object { $_.IPAddress -notmatch '^169.254|^127.0.0' -and $_.InterfaceAlias -notmatch 'VMware' -and $_.AddressFamily -eq 'IPv4' -and $_.PrefixLength -eq 24 -and $_.PrefixOrigin -eq 'Dhcp' } Write-Host @" This script is designed to use a single network adapter with the following configuration: - has an IPv4 address - does not have an IP address in a link-local block - is configured for DHCP - has a subnet mask of /24 Only network adapters meeting this configuration are shown below. Select the network adapter you'd like to use in your lab. "@ # Enumerate network adapters on the host. $i = 0 $netIPAddressCollection | ForEach-Object { $i++ Write-Host " ${i}: $($_.InterfaceAlias) ($($_.IPAddress))" } [int]$adapterIndex = Read-Host -Prompt "Please enter a number `[1-$i`]" $adapterIndex = $adapterIndex - 1 $NetAdapterName = $netIPAddressCollection[$($adapterIndex)].InterfaceAlias #endregion Select NetAdapter for Use in Lab # Create a new External Switch named $ExternalSwitch aka 'vEthernet ($ExternalSwitch)' try { New-VMSwitch -Name $ExternalSwitch -NetAdapterName $NetAdapterName -ErrorAction Stop Start-Sleep -Seconds 5 } catch { throw $_ } } # Get IP Address of External Switch [string]$NetAdapterIP = (Get-NetIPConfiguration -InterfaceAlias "vEthernet ($ExternalSwitch)").IPv4Address # Create required addresses if ($NetAdapterIP -match '(?:\d{1,3}\.){3}') { $BaseAddress = $matches[0] $NetworkAddress = $BaseAddress + '0' $Gateway = $BaseAddress + '1' } # Get IP Address of other machines in subnet $ExistingIPs = Get-VM | Where-Object State -EQ Running | Select-Object -ExpandProperty NetworkAdapters | Select-Object -ExpandProperty IPAddresses | ForEach-Object { if ($_ -match '(?:\d{1,3}\.){3}') { $_ } } # Pick IP Addresses for new VMs $NewIPs = @{} $Roles = @('DC', 'CA', 'PAW') $RoleIndex = 0 for ($i = 3; $i -lt 255 -and $RoleIndex -lt $Roles.Count; $i++) { $CandidateIP = "$BaseAddress$i" if ($ExistingIPs -notcontains $CandidateIP) { $NewIPs[$Roles[$RoleIndex]] = $CandidateIP $RoleIndex++ } } # Create IPs for each role. $Roles | ForEach-Object { New-Variable -Name "${_}IP" -Value $NewIPs[$_] } if (-not $Confirm) { Write-PSFHostColor @" ---------------------------------------------------- | Lab Configuration | ---------------------------------------------------- Name: <c='em'>$Name</c> Root Domain: <c='em'>$Domain</c> Network Address: <c='em'>$NetworkAddress</c> Gateway: <c='em'>$Gateway</c> Domain Controller IP: <c='em'>$DCIP</c> Certification Authority IP: <c='em'>$CAIP</c> Privileged Access Workstation IP: <c='em'>$PAWIP</c> "@ $Answer = Get-PSFUserChoice -Caption 'Continue with deployment?' -Options Yes, No if ($Answer -eq 1) { exit } } # Define the lab + hypervisor New-LabDefinition -Name $Name -DefaultVirtualizationEngine HyperV # Use existing External Switch created or discovered above Add-LabVirtualNetworkDefinition -Name $ExternalSwitch -AddressSpace "$NetAdapterIP/24" # Set default parameters for all machines in the lab $PSDefaultParameterValues = @{ 'Add-LabMachineDefinition:Network' = $ExternalSwitch 'Add-LabMachineDefinition:ToolsPath' = "$Sources\Tools" 'Add-LabMachineDefinition:MinMemory' = 512MB 'Add-LabMachineDefinition:Memory' = 1GB 'Add-LabMachineDefinition:MaxMemory' = 4GB 'Add-LabMachineDefinition:Processors' = 2 'Add-LabMachineDefinition:DomainName' = $Domain 'Add-LabMachineDefinition:Gateway' = $Gateway 'Add-LabMachineDefinition:DnsServer1' = $DCIP 'Add-LabMachineDefinition:OperatingSystem' = 'Windows Server 2022 Datacenter (Desktop Experience)' } Add-LabMachineDefinition -Name "$Name-DC" -Roles RootDC -IpAddress $DCIP Add-LabMachineDefinition -Name "$Name-CA" -Roles CaRoot -IpAddress $CAIP Add-LabMachineDefinition -Name "$Name-PAW" -IpAddress $PAWIP Install-Lab Install-LabWindowsFeature -FeatureName RSAT -ComputerName "$Name-PAW" -IncludeAllSubFeature Show-LabDeploymentSummary } |