You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure现有VNet中查询可用IP/CIDR的PowerShell脚本报错排查

修复Azure VNet可用IP及CIDR查询的PowerShell脚本

问题诊断

  1. 循环逻辑失效:原脚本遍历前缀长度的循环使用for ($i=32; $i -le 29; $i++),初始值32大于目标值29,递增循环条件永远不成立,导致循环体完全不执行。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 00:23:17