6e86c481a4
Credentials: - The stock appliance password is retained by default. It is typed at a console before being changed, and the appliance prompts for a change on first sign in regardless - GenerateRootPassword opts into a random password, now sixteen characters without symbols rather than twenty with them Preboot execution environment: - Get-HyperVNextAvailableMACAddress reads the next dynamic address the hypervisor will issue, treating the six bytes as one integer so an offset carries correctly across octets - That address is reserved within Kea for a PXE server on the Infrastructure network, which allows the reservation to exist before the virtual machine that will carry the address does - The reserved address populates a PXE_SERVERS_001 alias, becomes next_server and tftp_server_name within every scope, and is reachable through a floating rule from RFC1918 - The reservation is skipped with a warning on a host that does not run the hypervisor, so the document still generates Rules: - The management, jump host, name resolution and PXE rules are now floating. They were identical on every interface, so a sixteen network policy falls from 87 rules to 28 - Descriptions no longer name the network they were generated for, since a floating rule applies everywhere - The RFC1918 alias now has a purpose as the source of the internal permits Kea: - The socket type defaults to udp and is exposed as DHCPSocketType. Raw remains the safer choice for a client that holds no address yet - Option data auto collection is enabled Elsewhere: - Outbound network address translation moves to hybrid, with no manual rules - Zone roles carry three digit indexes, so Servers_Zone_001 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
128 lines
6.6 KiB
PowerShell
128 lines
6.6 KiB
PowerShell
#region Get-HyperVNextAvailableMACAddress
|
|
Function Get-HyperVNextAvailableMACAddress
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Retrieves the next media access control address that the hypervisor will hand to a virtual machine.
|
|
|
|
.DESCRIPTION
|
|
The hypervisor allocates dynamic media access control addresses out of a pool and records the next value it will issue within the registry.
|
|
Reading that value allows an address to be reserved within the appliance before the virtual machine that will carry it has been created, which is how a preboot execution environment server is given a predictable address ahead of time.
|
|
|
|
.PARAMETER Offset
|
|
The number of addresses to advance beyond the next available address. Specify a value greater than zero when the next address is expected to be consumed by another virtual machine first.
|
|
|
|
.PARAMETER ContinueOnError
|
|
Ignore failures.
|
|
|
|
.EXAMPLE
|
|
$GetHyperVNextAvailableMACAddressResult = Get-HyperVNextAvailableMACAddress -Verbose
|
|
|
|
Write-Output -InputObject ($GetHyperVNextAvailableMACAddressResult.Address)
|
|
|
|
.NOTES
|
|
The registry value only exists once the hypervisor has been installed. The function returns a null address rather than throwing when the value cannot be read, so that a configuration document can still be generated on a device that does not host the hypervisor.
|
|
|
|
The address is a prediction rather than a guarantee. The hypervisor issues it to whichever virtual machine requests an address next, so a reservation that is built from it should be verified once the intended virtual machine exists.
|
|
|
|
.LINK
|
|
https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/deploy/configure-mac-address-ranges
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
Param
|
|
(
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateRange(0, 65535)]
|
|
[Alias('O')]
|
|
[System.Int32]$Offset,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('COE')]
|
|
[Switch]$ContinueOnError
|
|
)
|
|
|
|
Try
|
|
{
|
|
[System.String]$CmdletName = $MyInvocation.MyCommand.Name
|
|
|
|
$WriteLogMessage.Invoke(0, @("Function `'$($CmdletName)`' is beginning. Please Wait..."))
|
|
|
|
$OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$OutputObjectProperties.Address = $Null
|
|
$OutputObjectProperties.AddressWithoutSeparator = $Null
|
|
$OutputObjectProperties.WasResolved = $False
|
|
|
|
[System.String]$VirtualizationWorkerRegistryPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Worker'
|
|
|
|
$WriteLogMessage.Invoke(0, @("Attempting to read the next available media access control address from the hypervisor. Please Wait... [Path: $($VirtualizationWorkerRegistryPath)]"))
|
|
|
|
[Byte[]]$AddressBytes = Try {(Get-ItemProperty -Path ($VirtualizationWorkerRegistryPath) -Name 'CurrentMacAddress' -ErrorAction SilentlyContinue).CurrentMacAddress} Catch {$Null}
|
|
|
|
Switch (($Null -ine $AddressBytes) -and ($AddressBytes.Count -eq 6))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
#region Advance the address by the requested offset
|
|
# The address is treated as a single unsigned integer so that a carry across the final octets is handled correctly.
|
|
|
|
[System.Int64]$AddressValue = 0
|
|
|
|
For ($AddressByteIndex = 0; $AddressByteIndex -lt $AddressBytes.Count; $AddressByteIndex++)
|
|
{
|
|
[System.Int64]$AddressValue = ($AddressValue * 256) + $AddressBytes[$AddressByteIndex]
|
|
}
|
|
|
|
[System.Int64]$AddressValue = $AddressValue + $Offset
|
|
|
|
$ResolvedAddressBytes = New-Object -TypeName 'System.Byte[]' -ArgumentList (6)
|
|
|
|
For ($AddressByteIndex = 5; $AddressByteIndex -ge 0; $AddressByteIndex--)
|
|
{
|
|
$ResolvedAddressBytes[$AddressByteIndex] = [System.Byte]($AddressValue % 256)
|
|
|
|
[System.Int64]$AddressValue = [System.Math]::Floor($AddressValue / 256)
|
|
}
|
|
#endregion
|
|
|
|
$OutputObjectProperties.Address = ([System.BitConverter]::ToString($ResolvedAddressBytes)).Replace('-', ':').ToLower()
|
|
$OutputObjectProperties.AddressWithoutSeparator = ([System.BitConverter]::ToString($ResolvedAddressBytes)).Replace('-', '').ToLower()
|
|
$OutputObjectProperties.WasResolved = $True
|
|
|
|
$WriteLogMessage.Invoke(0, @("The next available media access control address is `"$($OutputObjectProperties.Address)`". [Offset: $($Offset)]"))
|
|
}
|
|
|
|
{($_ -eq $False)}
|
|
{
|
|
$WriteLogMessage.Invoke(2, @("The next available media access control address could not be read. This is expected on a device that does not host the hypervisor."))
|
|
}
|
|
}
|
|
|
|
$OutputObject = New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($OutputObjectProperties)
|
|
|
|
Write-Output -InputObject ($OutputObject)
|
|
}
|
|
Catch
|
|
{
|
|
$ErrorRecord = $_
|
|
|
|
Switch ($ContinueOnError.IsPresent)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(2, @("[Message: $($ErrorRecord.Exception.Message)] [LineNumber: $($ErrorRecord.InvocationInfo.ScriptLineNumber)] [Code: $($ErrorRecord.InvocationInfo.Line.Trim())]"))
|
|
}
|
|
|
|
{($_ -eq $False)}
|
|
{
|
|
Throw
|
|
}
|
|
}
|
|
}
|
|
Finally
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Function `'$($CmdletName)`' is completed."))
|
|
}
|
|
}
|
|
#endregion
|