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

Terraform部署Azure VM时remote-exec提示host为空问题求助

问题分析与修复方案

出现host for provisioner cannot be empty错误的核心原因及修复方式如下:

错误根源

  1. 冗余的data "azurerm_public_ip"数据源完全没必要,反而可能因依赖时序问题导致IP地址未被正确解析
  2. null_resource中的Timeouts拼写错误(应为小写timeouts),且使用了中文引号,导致配置失效
  3. file provisioner未指定连接参数,无法复用SSH配置
  4. 虚拟机未启用wait_for_address,可能在IP尚未分配完成时就触发远程执行操作

修复步骤

  1. 移除冗余数据源:直接通过azurerm_public_ip.test-publicIP-VM[count.index].ip_address获取公网IP,无需通过data二次查询
  2. 修正timeouts配置:改为小写timeouts,并使用英文引号
  3. 为file provisioner绑定连接:通过connection = self.connection继承SSH配置
  4. 启用VM地址等待:打开wait_for_address = true,确保Terraform等待VM网络就绪
  5. 简化依赖声明:利用Terraform隐式依赖,删除不必要的depends_on

修正后的完整代码

# Resource Group
resource "azurerm_resource_group" "test-rg-nginx" {
  name     = var.rgname
  location = var.rg_location
}

# Virtual Network
resource "azurerm_virtual_network" "test-vnet-nginx" {
  name                = var.vnetname
  address_space       = ["10.66.0.0/16"]
  location            = azurerm_resource_group.test-rg-nginx.location
  resource_group_name = azurerm_resource_group.test-rg-nginx.name
}

# Subnet
resource "azurerm_subnet" "test-subnet-nginx" {
  name                 = var.subnetname
  resource_group_name  = azurerm_resource_group.test-rg-nginx.name
  virtual_network_name = azurerm_virtual_network.test-vnet-nginx.name
  address_prefixes     = ["10.66.1.0/24"]
}

# Public IPs
resource "azurerm_public_ip" "test-publicIP-VM" {
  count = var.vm_count
  name  = "${var.publicIP-VMname}${count.index}"

  location            = azurerm_resource_group.test-rg-nginx.location
  resource_group_name = azurerm_resource_group.test-rg-nginx.name
  allocation_method   = "Dynamic"
}

resource "azurerm_public_ip" "test-publicIP-LB" {
  name                = var.publicIP-LBname
  location            = azurerm_resource_group.test-rg-nginx.location
  resource_group_name = azurerm_resource_group.test-rg-nginx.name
  allocation_method   = "Static"
}

# Create Load Balancer
resource "azurerm_lb" "test-lb-nginx" {
  name                = var.lbname
  location            = azurerm_resource_group.test-rg-nginx.location
  resource_group_name = azurerm_resource_group.test-rg-nginx.name

  frontend_ip_configuration {
    name                 = "${var.publicIP-LBname}-frontend"
    public_ip_address_id = azurerm_public_ip.test-publicIP-LB.id
  }
}

resource "azurerm_lb_backend_address_pool" "lb_backendIPpool" {
  loadbalancer_id = azurerm_lb.test-lb-nginx.id
  name            = "BackEndAddressPool"
}

resource "azurerm_lb_probe" "http_probe" {
  name            = "http_probe"
  protocol        = "Tcp"
  port            = 80
  loadbalancer_id = azurerm_lb.test-lb-nginx.id
}

resource "azurerm_lb_rule" "http_rule" {
  name                           = "http-rule"
  protocol                       = "Tcp"
  frontend_port                  = 80
  backend_port                   = 80
  frontend_ip_configuration_name = "${var.publicIP-LBname}-frontend"
  probe_id        = azurerm_lb_probe.http_probe.id
  loadbalancer_id = azurerm_lb.test-lb-nginx.id
}

resource "azurerm_network_interface_backend_address_pool_association" "backendpoolAssoc" {
  count                   = var.vm_count
  network_interface_id    = azurerm_network_interface.test-nic[count.index].id
  ip_configuration_name   = "${var.nic-ip-configname}${count.index}"
  backend_address_pool_id = azurerm_lb_backend_address_pool.lb_backendIPpool.id
}

# Create network interface
resource "azurerm_network_interface" "test-nic" {
  count = var.vm_count
  name  = "${var.nicname}-${count.index}"
  location            = azurerm_resource_group.test-rg-nginx.location
  resource_group_name = azurerm_resource_group.test-rg-nginx.name

  ip_configuration {
    name                          = "${var.nic-ip-configname}${count.index}"
    subnet_id                     = azurerm_subnet.test-subnet-nginx.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.test-publicIP-VM[count.index].id
  }
}

# Network Security Group and Security rules
resource "azurerm_network_security_group" "test-nsg-nginx" {
  name                = var.nsgname
  location            = azurerm_resource_group.test-rg-nginx.location
  resource_group_name = azurerm_resource_group.test-rg-nginx.name

  security_rule {
    name                       = "HTTPS"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "443"
    source_address_prefixes    = var.address-list
    destination_address_prefix = "*"
  }
  security_rule {
    name                       = "HTTP"
    priority                   = 101
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "80"
    source_address_prefixes    = var.address-list
    destination_address_prefix = "*"
  }
  security_rule {
    name                       = "SSH-Inbound"
    priority                   = 102
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefixes    = var.address-list
    destination_address_prefix = "*"
  }
}

# Connect the security group to the network interface
resource "azurerm_network_interface_security_group_association" "test-sgConnect" {
  count                     = var.vm_count
  network_interface_id      = azurerm_network_interface.test-nic[count.index].id
  network_security_group_id = azurerm_network_security_group.test-nsg-nginx.id
}

resource "azurerm_availability_set" "avset" {
  name                         = "avset"
  location                     = azurerm_resource_group.test-rg-nginx.location
  resource_group_name          = azurerm_resource_group.test-rg-nginx.name
  platform_fault_domain_count  = 2
  platform_update_domain_count = 2
  managed                      = true
}

# Create VM
resource "azurerm_linux_virtual_machine" "test-linux" {
  count                 = var.vm_count
  name                  = "${var.vm-linux}-${count.index}"
  location              = azurerm_resource_group.test-rg-nginx.location
  resource_group_name   = azurerm_resource_group.test-rg-nginx.name
  availability_set_id   = azurerm_availability_set.avset.id
  network_interface_ids = [azurerm_network_interface.test-nic[count.index].id]
  size                  = var.vm-size

  os_disk {
    name                 = "myOsdisk-${count.index}"
    caching              = "ReadWrite"
    storage_account_type = "Premium_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts-gen2"
    version   = "latest"
  }
  computer_name                   = var.admin-user
  admin_username                  = var.admin-user
  disable_password_authentication = true
  wait_for_address = true # 启用等待地址分配完成

  admin_ssh_key {
    username   = var.admin-user
    public_key = file("${path.module}/connection/linux-pub0.pub")
  }
}

resource "null_resource" "default-web-server" {
  count = var.vm_count

  provisioner "remote-exec" {
    inline = [
      "sudo apt-get update",
      "sudo apt-get install -y nginx",
      "sudo mkdir -p /var/www/html/",
      "sudo chown -R ${var.admin-user}:${var.admin-user} /var/www/html/",
      "sudo chmod 644 /var/www/html/index.nginx-debian.html"
    ]
  }

  timeouts {
    read = "5m" # 修正拼写和引号
  }

  connection {
    type        = "ssh"
    user        = var.admin-user
    private_key = file("${path.module}/connection/linux-priv0.pem")
    host        = azurerm_public_ip.test-publicIP-VM[count.index].ip_address # 直接引用资源属性
    timeout     = "2m"
  }

  provisioner "file" {
    connection = self.connection # 继承当前资源的连接配置
    source      = "${path.module}/WebContents/server-contents.html"
    destination = "/var/www/html/index.nginx-debian.html"
  }

  triggers = {
    timestamp = timestamp()
  }
}

额外提示

  • 动态公网IP在Azure中会在NIC关联后立即分配,启用wait_for_address能确保Terraform等待VM的网络完全就绪
  • 避免使用冗余的data数据源,直接引用资源属性可以减少依赖链的复杂性
  • 确保SSH密钥文件路径正确,且权限设置为600(Linux系统下),否则可能导致SSH连接失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 04:27:02