internal/functions/UtilityFunctions/New-AVDMFSubnetRange.ps1
function New-AVDMFSubnetRange { [cmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "Does not change any states")] param( # Address Space for the subnet, this can be any of the address spaces created under the vNet in the format X.X.X.X/X [Parameter(Mandatory = $true)][string]$AddressSpace, # Mask bits of the new subnet, written as XX (example 27) [Parameter(Mandatory = $true)][int]$NewSubnetMaskBits ) #region: functions function ConvertFrom-DecimalIPtoBinary ([string]$DecimalIPAddress) { #Create an empty variable $Binary = $null #Extract octets from IP Address $Octets = $DecimalIPAddress.Split('.') #Convert each octet to Binary and add to the variable $Binary # Here we use ToString with '2' as the base, 2 means binary # We are also using padleft to make sure each octet is 8 bits long with leading zeros if needed $Octets | ForEach-Object { $Binary += ([convert]::ToString($_, 2)).PadLeft(8, "0") } return $Binary } function ConvertFrom-BinaryIPtoDecimal ([string]$BinaryIPAddress) { #Create an empty string $Decimal = $null #Split Binary address into 4 octets - And convert to decimal # Again, 2 is for the base. $Octets = for ($i = 0; $i -lt 4; $i++) { [convert]::ToInt32($BinaryIPAddress.Substring($i * 8, 8), 2) } # Join the octets into one string with "." as delimeter $Decimal = $Octets -join "." return $Decimal } function Find-IPAddressesInRange ($FirstIPAddress, $LastIPAddress) { # First we confirm the IP Addresses to Binary, then to int64 $Int64IP1 = [convert]::Toint64((ConvertFrom-DecimalIPtoBinary $FirstIPAddress), 2) $Int64IP2 = [convert]::Toint64((ConvertFrom-DecimalIPtoBinary $LastIPAddress), 2) # Then we just create a loop of all the values in the range of int64 $IPAddresses = for ($i = $Int64IP1; $i -le $Int64IP2; $i++) { #Finally, we convert the int64 to binary then back to a decimal IP Address ConvertFrom-BinaryIPtoDecimal ([convert]::ToString($i, 2)).padleft(32, "0") } return $IPAddresses } function Get-SubnetDetails ($IPAddress, $MaskBits) { $BinaryIPAddress = ConvertFrom-DecimalIPtoBinary $IPAddress $SubnetID = ConvertFrom-BinaryIPtoDecimal $BinaryIPAddress.Substring(0, $MaskBits).PadRight(32, '0') $BroadcastIP = ConvertFrom-BinaryIPtoDecimal $BinaryIPAddress.Substring(0, $MaskBits).PadRight(32, '1') return [PSCustomObject]@{ SubnetID = $SubnetID BroadcastIP = $BroadcastIP AddressPrefix = "$SubnetID/$MaskBits" } } function Test-OverlappingSubnets ($SubnetA, $SubnetB) { $SubnetAIDDigital = [convert]::Toint64((ConvertFrom-DecimalIPtoBinary $SubnetA.SubnetID), 2) $SubnetABCDigital = [convert]::Toint64((ConvertFrom-DecimalIPtoBinary $SubnetA.BroadcastIP), 2) $SubnetBIDDigital = [convert]::Toint64((ConvertFrom-DecimalIPtoBinary $SubnetB.SubnetID), 2) $SubnetBBCDigital = [convert]::Toint64((ConvertFrom-DecimalIPtoBinary $SubnetB.BroadcastIP), 2) if ($SubnetAIDDigital -ge $SubnetBIDDigital -and $SubnetAIDDigital -le $SubnetBBCDigital) { $Overlap = $true } elseif ($SubnetABCDigital -ge $SubnetBIDDigital -and $SubnetABCDigital -le $SubnetBBCDigital) { $Overlap = $true } else { $Overlap = $false } return $Overlap } #endregion: functions #region: Analyzing Address Space Write-Verbose -Message "Analyzing Address Space" #Find the position of '/' in the provided address space. $AddressSpaceIndexOfMaskBits = $AddressSpace.IndexOf("/") $AddressSpaceID = $AddressSpace.Substring(0, $AddressSpaceIndexOfMaskBits) $AddressSpaceMaskBits = $AddressSpace.Substring($AddressSpaceIndexOfMaskBits + 1) Write-Verbose -Message "ID: $AddressSpaceID - MaskbBits: $AddressSpaceMaskBits" $AddressSpaceSize = [math]::Pow(2, 32 - $AddressSpaceMaskBits) Write-Verbose -Message "Address Space Size: $AddressSpaceSize" Write-Verbose -Message "Finished Analyzing Address Space" #endregion: Analyzing Address Space #region: Find all possible subnets Write-Verbose -Message "ENTER: Find all possible subnets" $NewSubnetSize = [math]::Pow(2, (32 - $NewSubnetMaskBits)) Write-Verbose -Message "New Subnet Size: $NewSubnetSize" $NumberOfPossibleSubnets = $AddressSpaceSize / $NewSubnetSize Write-Verbose -Message "Number of Possible Subnets: $NumberOfPossibleSubnets" $PossibleSubnetsArray = @(Get-SubnetDetails $AddressSpaceID $NewSubnetMaskBits) for ($i = 1; $i -lt $NumberOfPossibleSubnets; $i++) { $LastSubnetInt64 = ([convert]::Toint64((ConvertFrom-DecimalIPtoBinary $PossibleSubnetsArray[$i - 1].BroadcastIP), 2)) $NextSubnetID = ConvertFrom-BinaryIPtoDecimal ([convert]::ToString($LastSubnetInt64 + 1, 2)).padleft(32, '0') $PossibleSubnetsArray += Get-SubnetDetails $NextSubnetID $NewSubnetMaskBits } Write-Verbose -Message "Calculated $($PossibleSubnetsArray.Count) possible subnets." Write-Verbose -Message "Exit: Find all possible subnets" #endregion: Find all possible subnets #region: Collect vNet information Write-Verbose -Message "ENTER: Collect vNet information" $vNetSubnets = foreach ($key in $script:Subnets.Keys) { if ((ConvertFrom-DecimalIPtoBinary ($script:Subnets[$key].Properties.AddressPrefix.Substring(0, $script:Subnets[$key].Properties.AddressPrefix.IndexOf("/")))).Substring(0, $AddressSpaceMaskBits) ` -eq (ConvertFrom-DecimalIPtoBinary ($AddressSpaceID)).Substring(0, $AddressSpaceMaskBits)) { $script:Subnets[$key] } } Write-Verbose -Message "Found $($vNetSubnets.count) subnets in the vNet belonging to the address space $AddressSpace" $UtilizedAddressesArray = foreach ($Subnet in $vNetSubnets) { $IndexOfSubnetMask = $Subnet.Properties.AddressPrefix.indexOf("/") $SubnetID = $Subnet.Properties.AddressPrefix.Substring(0, $IndexOfSubnetMask) $MaskBits = $Subnet.Properties.AddressPrefix.Substring($IndexOfSubnetMask + 1) Get-SubnetDetails -IPAddress $SubnetID -MaskBits $MaskBits } Write-Verbose -Message "Calculated utilized addresses" Write-Verbose -Message "Exit: Collect vNet information" #endregion: Collect vNet information #region: Return the first free subnet Write-Verbose -Message "ENTER: Return the first free subnet" foreach ($PossibleSubnet in $PossibleSubnetsArray) { foreach ($ExistingSubnet in $UtilizedAddressesArray) { $Overlap = $false if (Test-OverlappingSubnets $PossibleSubnet $ExistingSubnet) { $Overlap = $true break } } if (!($Overlap)) { return $PossibleSubnet } } Write-Verbose -Message "Exit: Return the first free subnet" #endregion: Return the first free subnet # if we did not return any subnet, throw an error throw "Could not find any free subnets" } |