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

Terraform Azurerm部署Azure VM时SSH认证失败求助

Terraform AzureRM remote-exec SSH认证失败排查与修复

错误信息

Error: remote-exec provisioner error
│
│   with azurerm_linux_virtual_machine.tm_vm,
│   on main.tf line 162, in resource "azurerm_linux_virtual_machine" "tm_vm":
│  162:   provisioner "remote-exec" {
│
│ timeout - last error: SSH authentication failed (linxuser@172.173.252.191:22): ssh: handshake failed: ssh: unable to authenticate, attempted methods [none 
│ publickey], no supported methods remain

问题背景

我是Terraform AzureRM新手,在Windows环境下尝试在Azure创建虚拟机并执行配置脚本,一直被上述SSH认证错误困扰。

配置代码

# find public ip via $terraform state show azurerm_linux_virtual_machine.tm_vm

# We strongly recommend using the required_providers block to set the
# Azure Provider source and version being used
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=3.0.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {
  }
}

# Create a resource group
resource "azurerm_resource_group" "tm_resources" {
  name     = "triplem_resources"
  location = "East Us"
  tags = {
    environment = "dev"
  }
}

# Create virtual network
resource "azurerm_virtual_network" "tm_virtualnet" {
  name                = "triplem_network"
  resource_group_name = azurerm_resource_group.tm_resources.name
  location            = azurerm_resource_group.tm_resources.location
  address_space       = ["10.123.0.0/16"]

  tags = {
    environment = "dev"
  }
}

# Create Subnet
resource "azurerm_subnet" "tm_subnet" {
  name                 = "tm_subnet"
  resource_group_name  = azurerm_resource_group.tm_resources.name
  virtual_network_name = azurerm_virtual_network.tm_virtualnet.name
  address_prefixes     = ["10.123.1.0/24"]
}

# Create security group
resource "azurerm_network_security_group" "tm_security_group" {
  name                = "tm_security_group"
  location            = azurerm_resource_group.tm_resources.location
  resource_group_name = azurerm_resource_group.tm_resources.name

  tags = {
    environment = "dev"
  }
}

# Security group rules
resource "azurerm_network_security_rule" "tm_dev_rule" {
  name                        = "tm_dev_rule"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "*"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*" #<-- add my ip
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.tm_resources.name
  network_security_group_name = azurerm_network_security_group.tm_security_group.name
}

resource "azurerm_subnet_network_security_group_association" "tm_security_group_association" {
  subnet_id                 = azurerm_subnet.tm_subnet.id
  network_security_group_id = azurerm_network_security_group.tm_security_group.id
}

resource "azurerm_public_ip" "tm_ip" {
  name                    = "tm_ip"
  location                = azurerm_resource_group.tm_resources.location
  resource_group_name     = azurerm_resource_group.tm_resources.name
  allocation_method       = "Static"
  idle_timeout_in_minutes = 30

  tags = {
    environment = "dev"
  }
}

resource "azurerm_network_interface" "tm_net_interface" {
  name                = "tm_net_interface"
  location            = azurerm_resource_group.tm_resources.location
  resource_group_name = azurerm_resource_group.tm_resources.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.tm_subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.tm_ip.id
  }

  tags = {
    environment = "dev"
  }
}

# RSA key of size 4096 bits
resource "tls_private_key" "key_generation" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

# Save key locally
resource "local_file" "tm_key" {
  filename = "${var.compute_name}_key.pem"
  content  = tls_private_key.key_generation.private_key_pem
}

# template provision file
data "template_file" "provision_file" {
  template = file("provision.tpl")
  vars     = {}
}

resource "null_resource" "test" {
  provisioner "local-exec" {
    command = "echo ${data.azurerm_public_ip.data_public_ip.ip_address}"
  }
}

# Create VM
resource "azurerm_linux_virtual_machine" "tm_vm" {
  name                  = var.compute_name
  resource_group_name   = azurerm_resource_group.tm_resources.name
  location              = azurerm_resource_group.tm_resources.location
  size                  = "Standard_F2"
  admin_username        = "linuxuser"
  network_interface_ids = [azurerm_network_interface.tm_net_interface.id]

  # Run Node Provision Script
  # custom_data = filebase64("provision.tpl")

  admin_ssh_key {
    username   = "linuxuser"
    public_key = tls_private_key.key_generation.public_key_openssh
  }

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "16.04-LTS"
    version   = "latest"
  }

  provisioner "remote-exec" {
    inline = ["${data.template_file.provision_file.rendered}"]

    connection {
      host        = data.azurerm_public_ip.data_public_ip.ip_address
      type        = "ssh"
      user        = "linxuser"
      private_key = tls_private_key.key_generation.private_key_pem
      timeout     = "5m"
    }
  }

  depends_on = [
    azurerm_network_interface.tm_net_interface,
  ]
}

data "azurerm_public_ip" "data_public_ip" {
  name                = azurerm_public_ip.tm_ip.name
  resource_group_name = azurerm_public_ip.tm_ip.resource_group_name
}

output "print_public_ip" {
  value = data.azurerm_public_ip.data_public_ip.ip_address
}

# Run provision
# resource "null_resource" "provision" {
#   depends_on = [azurerm_linux_virtual_machine.tm_vm, data.azurerm_public_ip.data_public_ip, null_resource.test]

#   provisioner "remote-exec" {
#     inline = ["${data.template_file.provision_file.rendered}"]
#   }

#   connection {
#     host        = "${data.azurerm_public_ip.data_public_ip.ip_address}"
#     type        = "ssh"
#     user        = "linxuser"
#     private_key = file("C:/Users/wongm/Documents/GitHub/triple-m/terraform-azure/key")#tls_private_key.key_generation.private_key_pem
#     timeout "5m"
#   }
# }

已尝试的方法

  • 动态与静态公IP分配切换(当前使用静态)
  • 手动生成密钥
  • 设置手动密钥权限为600
  • 设置remote-exec与azurerm_linux_virtual_machine的依赖关系
  • 将provision移入azurerm_linux_virtual_machine资源块

解决方案

核心修复:用户名拼写错误

在azurerm_linux_virtual_machine.tm_vm资源中,配置的admin_username是linuxuser,但remote-exec的connection块中user写成了linxuser(少了字母u)。VM上不存在linxuser账户,直接导致公钥认证失败。

修正后的connection块:

connection {
  host        = data.azurerm_public_ip.data_public_ip.ip_address
  type        = "ssh"
  user        = "linuxuser" # 修正用户名拼写
  private_key = tls_private_key.key_generation.private_key_pem
  timeout     = "5m"
}

其他优化建议

  1. Windows私钥格式处理:
    Terraform生成的PEM私钥在Windows下可能存在换行符问题,建议通过file()函数读取本地保存的密钥文件,而非直接引用资源内容:

    private_key = file("${var.compute_name}_key.pem")
    

    同时用Git Bash执行chmod 600 ${var.compute_name}_key.pem设置正确权限。

  2. 增加连接延迟:
    VM创建完成后SSH服务可能未完全启动,可在connection块中添加延迟:

    connection {
      # ... 其他参数
      delay = "30s" # 等待30秒后再尝试连接
    }
    
  3. NSG规则收紧:
    当前NSG允许所有端口入站,生产环境建议仅开放SSH端口:

    resource "azurerm_network_security_rule" "ssh_inbound" {
      name                        = "AllowSSHInbound"
      priority                    = 100
      direction                   = "Inbound"
      access                      = "Allow"
      protocol                    = "Tcp"
      source_port_range           = "*"
      destination_port_range      = "22"
      source_address_prefix       = "*" # 建议替换为你的公网IP
      destination_address_prefix  = "*"
      resource_group_name         = azurerm_resource_group.tm_resources.name
      network_security_group_name = azurerm_network_security_group.tm_security_group.name
    }
    
  4. 替代方案:使用custom_data:
    如果SSH连接问题持续,可通过custom_data在VM启动时直接执行脚本,无需SSH:

    resource "azurerm_linux_virtual_machine" "tm_vm" {
      # ... 其他配置
      custom_data = filebase64("provision.tpl")
    }
    

    注意provision.tpl需符合cloud-init格式(Ubuntu默认支持)。

内容的提问来源于stack exchange,提问作者P.R

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 00:05:04