Terraform部署Azure VM时remote-exec提示host为空问题求助
问题分析与修复方案
出现host for provisioner cannot be empty错误的核心原因及修复方式如下:
错误根源
- 冗余的
data "azurerm_public_ip"数据源完全没必要,反而可能因依赖时序问题导致IP地址未被正确解析 null_resource中的Timeouts拼写错误(应为小写timeouts),且使用了中文引号,导致配置失效fileprovisioner未指定连接参数,无法复用SSH配置- 虚拟机未启用
wait_for_address,可能在IP尚未分配完成时就触发远程执行操作
修复步骤
- 移除冗余数据源:直接通过
azurerm_public_ip.test-publicIP-VM[count.index].ip_address获取公网IP,无需通过data二次查询 - 修正timeouts配置:改为小写
timeouts,并使用英文引号 - 为file provisioner绑定连接:通过
connection = self.connection继承SSH配置 - 启用VM地址等待:打开
wait_for_address = true,确保Terraform等待VM网络就绪 - 简化依赖声明:利用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-----
相关产品推荐
相关产品推荐

