Terraform Apply Error: Code="InvalidResourceName" Message="Resource name is invalid" When creating VPN-GW Module

Terraform Apply Error: Code="InvalidResourceName" Message="Resource name is invalid" When creating VPN-GW Module

我在执行 Terraform 应用命令时收到以下错误。


│ Error: Creating/Updating Virtual Network Gateway: (Name "VPN-GW-HUB-dev" / Resource Group "RG-HUB-dev"): network.VirtualNetworkGatewaysClient#CreateOrUpdate: Failure sending request: StatusCode=400 -- Original Error: Code="InvalidResourceName" Message="Resource name  is invalid. The name can be up to 80 characters long. It must begin with a word character, and it must end with a word character or with '_'. The name may contain word characters or '.', '-', '_'." Details=[]
│ 
│   with module.MOD-VPN-GW.azurerm_virtual_network_gateway.vpngw,
│   on Modules/10.VPN-GW/main.tf line 31, in resource "azurerm_virtual_network_gateway" "vpngw":
│   31: resource "azurerm_virtual_network_gateway" "vpngw" {


我想做什么?

我的 Terraform 代码在 Azure 中创建了一个完整的 Hub-and-Spoke 基础结构。在我决定模块化 VPN GW 部分之前,它工作得很好。

我的Terraform代码结构是通常的格式:

--Modules/{module-name}/main.tf
--main.tf

所以在 {module-name} 中 - 我有一个名为的文件夹:

10.VPN-GW\main.tf

我尝试了什么?

好吧,我已经尝试重新格式化代码,我针对所有其他工作正常的模块仔细检查了它。我不知道为什么会出现这个错误,现在已经 3 天不停地调试了。

模块文件在这里:

#------------------------------
# VPN GW MODULE FILE - ALso contains GW SUBNET
#-------------------------------

resource "azurerm_subnet" "gwSubnet" {
  name                 =    var.subnet_name
  resource_group_name  =    var.subnet_resource_group_name
  virtual_network_name =    var.subnet_virtual_network_name
  address_prefixes     =    var.subnet_address_prefixes

}

variable "subnet_name" {
    type = string
}

variable "subnet_resource_group_name" {
    type = string
}

variable "subnet_virtual_network_name" {
    type = string
}

variable "subnet_address_prefixes" {
    type = list(string)
}


output "outSubnetIDVPNGW" {
    value = azurerm_subnet.gwSubnet.id
}
 

resource "azurerm_virtual_network_gateway" "vpngw" {
  name                = "${var.vpn-gw-name}"
  location            = "${var.vpn-gw-location}"
  resource_group_name = "${var.vpngw_resource_group_name}"

  type     = "Vpn"
  vpn_type = "RouteBased"
  
  active_active = false
  enable_bgp    = false
  sku           = "VpnGw1"

  ip_configuration {
    name                          = "${var.ip_configuration_name}"
    public_ip_address_id          = "${var.ip_configuration_public_ip_address_id}"
    private_ip_address_allocation = "Dynamic"
    subnet_id                     = azurerm_subnet.gwSubnet.id
  }

    vpn_client_configuration {
        address_space = "${var.vpn_client_configuration_address_space}"

        root_certificate {
        name = "${var.root_certificate_name}"
        public_cert_data = "${var.cert}"
    }

    revoked_certificate {
      name       = "${var.revoked_certificate_name}"
      thumbprint = "${var.revoked_certificate_thumbprint}"
    }
  }
}

variable "vpn-gw-name" {
    type = string
    //default = ""
}

variable "vpn-gw-location" {
    //default = ""
    type = string
}

variable "vpngw_resource_group_name" {
    default = ""
}

variable "ip_configuration_name" {
    default = ""
}

variable "ip_configuration_public_ip_address_id" {
    default = ""
}

/*
variable "ip_configuration_private_ip_address_allocation" {
    //default = ""
   // type = string
}
*/

variable "vpn_client_configuration_address_space" {
    default = []
}

variable "root_certificate_name" {
    default = ""
}

variable "cert" {  
    default = ""
}

variable "revoked_certificate_name" {
  default = ""
}

variable "revoked_certificate_thumbprint" {
    default = ""
}

variable "ip_configuration" {
    type = map(string)
}


variable "vpn_client_configuration" {
  type = object({
    address_space           = list(string)
    root_certificate        = map(string)
    revoked_certificate     = map(string)
  })
} 

/*
variable "revoked_certificate" {
  type = map(string)
}
*/

main.tf 文件——调用每个模块,如下所示——(我只是为导致错误的部分添加代码片段)——即创建 VPN GW


#-----------------------------------------
# Create GW SUBNET & VPN GW
#-----------------------------------------

module "MOD-VPN-GW" {
  source              = "./Modules/10.VPN-GW"

  subnet_name                 = "GatewaySubnet"
  subnet_resource_group_name  = "RG-HUB-${var.environmentCode}"
  subnet_virtual_network_name =  "VNET-HUB-${var.environmentCode}"
  subnet_address_prefixes     = ["10.0.1.0/27"]

  vpn-gw-name                 = "VPN-GW-HUB-${var.environmentCode}"
  vpn-gw-location               = "westeurope"
  vpngw_resource_group_name   = "RG-HUB-${var.environmentCode}"
  
  ip_configuration = {
    name        = "VNetGatewayConfig"
    public_ip_address_id                    = "${module.MOD-VPNGW-PIP.id}"
    private_ip_address_allocation  = "Dynamic"
    subnet_id                   = module.MOD-VPN-GW.outSubnetIDVPNGW
  }

  vpn_client_configuration = {
    address_space = ["172.16.10.0/24"]

    root_certificate = {
      name = "dev.vpn.macos.com"
      public_cert_data  = data.azurerm_key_vault_certificate.akv-certificate.certificate_data_base64

    }

    revoked_certificate  = {
      name       = "Verizon-Global-Root-CA"
      thumbprint = "912198EEF23DCAC40939312FEE97DD560BAE49B1"
    }
  }

   depends_on = [
     module.MOD-RG-HUB, module.MOD-VNET-HUB, azurerm_linux_virtual_machine.label-vm-spoke-01
  ]

}

我搜索了什么?

我似乎无法在 google 搜索中找到这个特定的错误代码。它没有告诉我给我的 VPN GW 的名称不正确 - 它给我名称(在错误消息中)并告诉我没有给出名称。这很奇怪。还是我误解了错误信息?

terraform init 命令成功。 terraform 验证命令成功。 terraform plan 命令成功。

申请时失败

root 模块和 VPN GW 模块之间存在问题。 Terraform 中模块的工作方式类似于其他编程语言中的函数:它们需要特定的输入变量,然后这些变量会被提供给它们的值替换。 [1].

中有关模块的更多信息

要重构此代码,我将在 root 模块中执行以下操作:

  source = "./Modules/10.VPN-GW"

  subnet_name                 = "GatewaySubnet"
  subnet_resource_group_name  = "RG-HUB-${var.environmentCode}"
  subnet_virtual_network_name = "VNET-HUB-${var.environmentCode}"
  subnet_address_prefixes     = ["10.0.1.0/27"]

  vpn-gw-name               = "VPN-GW-HUB-${var.environmentCode}"
  vpn-gw-location           = "westeurope"
  vpngw_resource_group_name = "RG-HUB-${var.environmentCode}"

  ip_configuration_name                 = "VNetGatewayConfig"
  ip_configuration_public_ip_address_id = module.MOD-VPNGW-PIP.id

  vpn_client_configuration_address_space = ["172.16.10.0/24"]
  root_certificate_name                  = "dev.vpn.macos.com"
  cert                                   = data.azurerm_key_vault_certificate.akv-certificate.certificate_data_base64

  revoked_certificate_name       = "Verizon-Global-Root-CA"
  revoked_certificate_thumbprint = "912198EEF23DCAC40939312FEE97DD560BAE49B1"

}

如您所见,我所做的唯一更改是将值分配给 VPN GW 模块所需的变量。它的工作方式是当模块被调用时,它会被提供变量的值并且它会知道如何处理它们。不需要在调用(root)和被调用模块(VPN GW)中定义相同的块。因此,将 root 模块视为一段调用函数的代码,例如:

MOD_VPN_GW(subnet_name, subnet_resource_group_name, subnet_virtual_network_name, subnet_address_prefixes, vpn-gw-name, vpn-gw-location, vpngw_resource_group_name, ip_configuration_name, ip_configuration_public_ip_address_id, vpn_client_configuration_address_space, root_certificate_name, cert, revoked_certificate_name, revoked_certificate_thumbprint)

至于您关于如何访问 subnet_id 的问题,这是简单的部分。在 VPN GW 模块中有一个资源块用于 azurerm_subnet:

resource "azurerm_subnet" "gwSubnet" {
  name                 = var.subnet_name
  resource_group_name  = var.subnet_resource_group_name
  virtual_network_name = var.subnet_virtual_network_name
  address_prefixes     = var.subnet_address_prefixes
}

此资源在创建后提供的属性之一是子网 ID [2]。这意味着您可以通过使用此语法 [3]:[=32= 引用结果值来分配属性值]

<resource type>.<resource_name>.<attribute>

你的情况是:

azurerm_subnet.gwSubnet.id

此值已在 azurerm_virtual_network_gateway 资源的 subnet_id 参数中引用:

subnet_id = azurerm_subnet.gwSubnet.id

这意味着它不需要作为模块的输入变量之一。好处是 VPN GW 模块会知道创建资源的顺序,因为这是隐式依赖。


[1] https://www.terraform.io/language/modules/develop

[2] https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet#id

[3]https://www.terraform.io/language/expressions/references#references-to-resource-attributes