Azure现有VNet中查询可用IP/CIDR的PowerShell脚本报错排查
修复Azure VNet可用IP及CIDR查询的PowerShell脚本
问题诊断
- 循环逻辑失效:原脚本遍历前缀长度的循环使用
for ($i=32; $i -le 29; $i++),初始值32大于目标值29,递增循环条件永远不成立,导致循环体完全不执行。 - IPAddress类型错误:未将VNet基础IP字符串正确转换为
[System.Net.IPAddress]对象,后续子网计算时出现类型不匹配错误。
修复后的完整脚本
# 配置目标VNet信息 $vnetName = "Your-VNet-Name" $resourceGroupName = "Your-Resource-Group-Name" $targetSubnetPrefix = "/25" # 示例新增子网前缀 # 获取VNet详情 $vnet = Get-AzVirtualNetwork -Name $vnetName -ResourceGroupName $resourceGroupName $vnetPrefix = $vnet.AddressSpace.AddressPrefixes[0] $vnetIp = [System.Net.IPAddress]::Parse($vnetPrefix.Split('/')[0]) $vnetPrefixLength = [int]$vnetPrefix.Split('/')[1] # 计算VNet的网络地址字节数组 $vnetBytes = $vnetIp.GetAddressBytes() [Array]::Reverse($vnetBytes) $vnetInt = [System.BitConverter]::ToUInt32($vnetBytes, 0) $subnetMask = [System.Net.IPAddress]::Parse(([System.Net.IPAddress]::HostToNetworkOrder(-1 -shl (32 - $vnetPrefixLength)))).GetAddressBytes() [Array]::Reverse($subnetMask) $maskInt = [System.BitConverter]::ToUInt32($subnetMask, 0) $networkInt = $vnetInt -band $maskInt # 查询已存在的子网前缀 $existingSubnets = $vnet.Subnets.AddressPrefixes | ForEach-Object { $ipPart = $_.Split('/')[0] $prefix = [int]$_.Split('/')[1] $ip = [System.Net.IPAddress]::Parse($ipPart) $bytes = $ip.GetAddressBytes() [Array]::Reverse($bytes) $int = [System.BitConverter]::ToUInt32($bytes, 0) [PSCustomObject]@{ StartInt = $int -band ([System.BitConverter]::ToUInt32(([System.Net.IPAddress]::Parse(([System.Net.IPAddress]::HostToNetworkOrder(-1 -shl (32 - $prefix)))).GetAddressBytes(), 0)) EndInt = $int -bor (-bnot ([System.BitConverter]::ToUInt32(([System.Net.IPAddress]::Parse(([System.Net.IPAddress]::HostToNetworkOrder(-1 -shl (32 - $prefix)))).GetAddressBytes(), 0))) Prefix = $prefix } } # 遍历目标前缀长度(示例:从32到29,可按需调整) for ($prefixLength = 32; $prefixLength -ge 29; $prefixLength--) { $subnetSize = [Math]::Pow(2, (32 - $prefixLength)) $currentNetworkInt = $networkInt while ($currentNetworkInt -le ($networkInt -bor (-bnot $maskInt))) { # 检查当前子网是否与已存在子网重叠 $isOverlapping = $false foreach ($subnet in $existingSubnets) { if (($currentNetworkInt -ge $subnet.StartInt -and $currentNetworkInt -le $subnet.EndInt) -or (($currentNetworkInt + $subnetSize - 1) -ge $subnet.StartInt -and ($currentNetworkInt + $subnetSize - 1) -le $subnet.EndInt)) { $isOverlapping = $true break } } if (-not $isOverlapping) { # 转换回IP地址格式 $bytes = [System.BitConverter]::GetBytes($currentNetworkInt) [Array]::Reverse($bytes) $subnetIp = [System.Net.IPAddress]$bytes $availablePrefix = "$subnetIp/$prefixLength" # 计算可用IP范围(排除网络地址和广播地址) $startIpInt = $currentNetworkInt + 1 $endIpInt = $currentNetworkInt + $subnetSize - 2 $startBytes = [System.BitConverter]::GetBytes($startIpInt) [Array]::Reverse($startBytes) $startIp = [System.Net.IPAddress]$startBytes $endBytes = [System.BitConverter]::GetBytes($endIpInt) [Array]::Reverse($endBytes) $endIp = [System.Net.IPAddress]$endBytes Write-Host "可用CIDR: $availablePrefix" Write-Host "可用IP范围: $startIp 至 $endIp`n" } $currentNetworkInt += $subnetSize } } # 针对指定前缀(如/25)的快速查询 $targetPrefixLength = [int]$targetSubnetPrefix.TrimStart('/') $targetSubnetSize = [Math]::Pow(2, (32 - $targetPrefixLength)) $currentTargetInt = $networkInt Write-Host "`n=== 目标前缀$targetSubnetPrefix的可用子网 ===" while ($currentTargetInt -le ($networkInt -bor (-bnot $maskInt))) { $isOverlapping = $false foreach ($subnet in $existingSubnets) { if (($currentTargetInt -ge $subnet.StartInt -and $currentTargetInt -le $subnet.EndInt) -or (($currentTargetInt + $targetSubnetSize - 1) -ge $subnet.StartInt -and ($currentTargetInt + $targetSubnetSize - 1) -le $subnet.EndInt)) { $isOverlapping = $true break } } if (-not $isOverlapping) { $bytes = [System.BitConverter]::GetBytes($currentTargetInt) [Array]::Reverse($bytes) $subnetIp = [System.Net.IPAddress]$bytes $availablePrefix = "$subnetIp/$targetPrefixLength" $startIpInt = $currentTargetInt + 1 $endIpInt = $currentTargetInt + $targetSubnetSize - 2 $startBytes = [System.BitConverter]::GetBytes($startIpInt) [Array]::Reverse($startBytes) $startIp = [System.Net.IPAddress]$startBytes $endBytes = [System.BitConverter]::GetBytes($endIpInt) [Array]::Reverse($endBytes) $endIp = [System.Net.IPAddress]$endBytes Write-Host "可用CIDR: $availablePrefix" Write-Host "可用IP范围: $startIp 至 $endIp`n" } $currentTargetInt += $targetSubnetSize }
关键修复点
- 修正循环逻辑:将原无效递增循环替换为
for ($prefixLength = 32; $prefixLength -ge 29; $prefixLength--),确保32到29的所有前缀长度都能被遍历处理。 - IPAddress对象正确初始化:使用
[System.Net.IPAddress]::Parse()将VNet和子网IP字符串转换为强类型对象,避免后续计算的类型不匹配错误。 - 完善子网重叠检测:通过将IP地址转换为无符号整数做数值比较,准确判断子网是否与已存在子网重叠,避免逻辑错误。
- 添加指定前缀快速查询:增加针对目标前缀(如/25)的单独查询模块,直接输出符合需求的可用子网信息,简化IP规划流程。
内容的提问来源于stack exchange,提问作者Mohammad Amir
相关产品推荐
相关产品推荐

