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

使用Node.js API创建Azure VM实例后公网IP未显示的问题排查

解决Azure VM创建后公网IP不显示的问题

问题概述

通过Node.js Azure SDK基于自定义镜像创建VM实例,已成功创建公网IP和NIC,但VM实例创建后无法显示公网IP。检查发现,创建的公网IP在Azure门户JSON视图中缺少ipConfigurations-->id属性,而正常关联VM的公网IP具备该属性。核心矛盾在于:公网IP的ipConfigurations.id需要关联NIC,但创建NIC又依赖已存在的公网IP。

代码问题分析

  1. NIC配置结构错误:在createNIC函数中,公网IP的引用被错误嵌套在properties字段下。Azure SDK的NetworkInterfaceIPConfiguration模型中,publicIPAddress是顶级属性,而非嵌套在properties内,这会导致NIC与公网IP的关联未正确建立。
  2. 不必要的公网IP预配置:创建公网IP时尝试提前指定ipConfigurations是无效的,该属性由Azure自动维护,当NIC关联公网IP后会自动填充,无需手动设置。
  3. 固定延迟不可靠:使用setTimeout等待VM创建完成,无法适配Azure后台的实际处理时长,可能导致后续操作执行过早。

解决方案

  1. 修正NIC的IP配置结构:将publicIPAddress从properties中移出,作为ipConfigurations数组元素的直接属性。
  2. 移除公网IP的ipConfigurations预配置:删除创建公网IP时注释的相关代码,由Azure自动维护关联关系。
  3. 使用pollUntilDone替代固定延迟:利用Azure SDK的异步操作轮询机制,确保VM完全创建完成后再执行启动操作。
  4. 增加错误校验:在获取公网IP和NIC ID时添加空值校验,避免后续操作因资源创建失败而报错。

修改后的完整代码

// pages/api/azure-create-instances.ts

import { ComputeManagementClient, VirtualMachine } from '@azure/arm-compute';
import { NetworkManagementClient } from '@azure/arm-network';
import { DefaultAzureCredential } from '@azure/identity';
import { NextApiRequest, NextApiResponse } from 'next';

function generateVMname(): string {
    const myDatetime = new Date();
    const generalName = 'c24cVM';
    return generalName + myDatetime.toISOString().replace(/[^\d]/g, '');
}
function generateNICname(): string {
    const myDatetime = new Date();
    const generalName = 'NIC';
    return generalName + myDatetime.toISOString().replace(/[^\d]/g, '');
}
function generateIPname(): string {
    const myDatetime = new Date();
    const generalName = 'IP';
    return generalName + myDatetime.toISOString().replace(/[^\d]/g, '');
}

const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID!;
const resourceGroupName = 'Azure-test_group';
const vmName = generateVMname();
const location = 'westeurope';
const vmSize = 'Standard_B1s';
const imageId = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.Compute/galleries/imgs_gallery1/images/test_img/versions/0.0.1`;

const createPublicIP = async () => {
    const creds = new DefaultAzureCredential();
    const networkClient = new NetworkManagementClient(creds, subscriptionId);

    const publicIpParams = {
        location: location,
        zones: ['3'],
        publicIPAddressVersion: 'IPv4',
        publicIPAllocationMethod: 'Static',
        sku: {
            name: 'Standard',
        },
    };

    const publicIpName = generateIPname();
    console.log('Creating Public IP...');
    try {
        const poller = await networkClient.publicIPAddresses.beginCreateOrUpdate(
            resourceGroupName,
            publicIpName,
            publicIpParams
        );
        const publicIp = await poller.pollUntilDone();
        console.log('Public IP created:', publicIp);
        return publicIp.id;
    } catch (error) {
        console.error('Error creating Public IP:', error);
        return undefined;
    }
};

const createNIC = async () => {
    const creds = new DefaultAzureCredential();
    const networkClient = new NetworkManagementClient(creds, subscriptionId);
    const publicIpId = await createPublicIP();
    if (!publicIpId) {
        throw new Error('Failed to get public IP ID');
    }
    console.log('Public IP ID:', publicIpId);
    
    const nicParams = {
        location: location,
        ipConfigurations: [
            {
                name: 'MyIpConfig',
                subnet: {
                    id: `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.Network/virtualNetworks/Azure-test-vnet/subnets/default`,
                },
                publicIPAddress: {
                    id: publicIpId,
                    properties: {
                        deleteOption: 'Detach',
                    },
                },
                privateIPAllocationMethod: 'Dynamic',
            },
        ],
    };
    
    const nicName = generateNICname();
    const poller = await networkClient.networkInterfaces.beginCreateOrUpdate(resourceGroupName, nicName, nicParams);
    const nic = await poller.pollUntilDone();
    console.log('NIC created:', nic);
    return nic.id;
};

const createVM = async () => {
    try {
        const networkID = await createNIC();
        if (!networkID) {
            throw new Error('Failed to get NIC ID');
        }
        
        const creds = new DefaultAzureCredential();
        const computeClient = new ComputeManagementClient(creds, subscriptionId);

        const vmParams: VirtualMachine = {
            location,
            zones: ['3'],
            hardwareProfile: {
                vmSize,
            },
            diagnosticsProfile: {
                bootDiagnostics: {
                    enabled: true,
                },
            },
            networkProfile: {
                networkInterfaces: [
                    {
                        id: networkID,
                        primary: true, // 明确标记为主网卡,避免多网卡场景下的问题
                    },
                ],
            },
            storageProfile: {
                imageReference: {
                    id: imageId,
                },
            },
            osProfile: {
                computerName: vmName,
                adminUsername: 'testuser',
                adminPassword: 'myPassword123!',
                linuxConfiguration: {
                    disablePasswordAuthentication: true,
                    ssh: {
                        publicKeys: [
                            {
                                keyData: process.env.SSHKEY!,
                                path: '/home/testuser/.ssh/authorized_keys',
                            },
                        ],
                    },
                },
            },
        };

        console.log('Starting VM creation...');
        const poller = await computeClient.virtualMachines.beginCreateOrUpdate(resourceGroupName, vmName, vmParams);
        const vm = await poller.pollUntilDone();
        console.log('VM created successfully:', vm);
        return vm;
    } catch (err) {
        console.error('Error creating VM:', err);
        throw err;
    }
};

const startVM = async () => {
    try {
        const computeClient = new ComputeManagementClient(new DefaultAzureCredential(), subscriptionId);
        const poller = await computeClient.virtualMachines.beginStart(resourceGroupName, vmName);
        const result = await poller.pollUntilDone();
        console.log('VM started successfully:', result);
    } catch (err) {
        console.error('Error starting VM:', err);
    }
};

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
    try {
        await createVM();
        await startVM();
        res.status(200).send('VM created and started successfully');
    } catch (err) {
        res.status(500).send(`Failed to create VM: ${(err as Error).message}`);
    }
}

关键修改点说明

  • NIC配置修正:将publicIPAddress从ipConfigurations[0].properties移至ipConfigurations[0]的顶级属性,符合Azure SDK的数据模型要求,确保NIC与公网IP正确关联。
  • 异步操作轮询:所有资源创建操作均使用pollUntilDone()替代固定延迟,确保资源完全创建完成后再执行后续步骤,避免因Azure后台处理延迟导致的问题。
  • 标记主网卡:在VM的networkInterfaces配置中添加primary: true,明确指定主网卡,确保公网IP正确关联到VM的默认网络接口。

内容的提问来源于stack exchange,提问作者ArieAI

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 05:45:55