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

EC2模块动态创建EBS卷报错:无法访问字符串类型属性

Terraform创建EC2实例EBS卷参数传递失败问题

报错信息

│ Error: Unsupported attribute
│   on modules/vm-deploy/main.tf line 19, in resource "aws_instance" "ec2_instance":
│   19:         device_name      = ebs_block_device.value.ebs_name
│     │ ebs_block_device.value is "/dev/sdb"
│ 
│ Can't access attributes on a primitive-typed value (string).
╵

│ Error: Unsupported attribute
│   on modules/vm-deploy/main.tf line 19, in resource "aws_instance" "ec2_instance":
│   19:         device_name      = ebs_block_device.value.ebs_name
│     ├────────────────
│     │ ebs_block_device.value is "100"
│ 
│ Can't access attributes on a primitive-typed value (string).

问题原因

核心错误是**blockdisks变量类型定义与遍历逻辑不匹配**:

  • 根模块中blockdisks被定义为map(string),存储的是单个键值对(如ebs_name="/dev/sdb"、ebs_size="100"),每个值都是字符串类型。
  • 模块中dynamic "ebs_block_device"遍历该map时,ebs_block_device.value直接取到字符串("/dev/sdb"或"100"),而字符串是原始类型,无法通过.ebs_name访问属性。

相关代码

根模块variables.tf

variable "instances_subnet_1" {
  description = "多台虚拟机的配置对象"
  type = map(object({
    ami                    = string
    instance_type          = string
    private_ip             = string
    subnet_id              = string
    vpc_security_group_ids = list(string)
    blockdisks             = map(string)
    root_size              = string
  }))
  default = {
    "ec2-resource" = {
      ami                    = "ami-win"
      instance_type          = "t3.large"
      private_ip             = "10.0.109.4"
      subnet_id              = ""
      vpc_security_group_ids = [""]
      root_size              = "50"
      blockdisks = {
        "ebs_name" = "/dev/sdb"
        "ebs_size" = "100"
      }
    }
  }
}

VM部署模块main.tf

resource "aws_instance" "ec2_instance" {
  ami                    = var.ami
  iam_instance_profile   = var.iam_instance_role
  instance_type          = var.instance_type
  private_ip             = var.private_ip
  subnet_id              = var.subnet_id
  vpc_security_group_ids = var.vpc_security_group_ids

  dynamic "ebs_block_device" {
    for_each = var.blockdisks
    iterator = ebs_block_device
    content {
      delete_on_termination = false
      encrypted             = true
      device_name           = ebs_block_device.value.ebs_name
      volume_size           = ebs_block_device.value.ebs_size
      volume_type           = "gp2"
    }
  }

  root_block_device {
    delete_on_termination = false
    encrypted             = true
    volume_size           = var.root_size
    volume_type           = "gp2"
  }
}

根模块main.tf

module "vm-deploy-subnet-1" {
  source                 = "./modules/vm-deploy"
  for_each               = var.instances_subnet_1
  ami                    = each.value.ami
  iam_instance_role      = each.value.iam_instance_role
  instance_type          = each.value.instance_type
  private_ip             = each.value.private_ip
  subnet_id              = data.aws_subnet.subnet_1.id
  vpc_security_group_ids = [data.aws_security_group.default.id]
  root_size              = each.value.root_size
  blockdisks             = each.value.blockdisks
}

解决方案

调整blockdisks变量类型,使其能存储包含多属性的EBS卷对象,同时修正遍历逻辑:

1. 修改根模块variables.tf的类型定义

将blockdisks从map(string)改为list(object)(支持多块EBS卷),同时把root_size改为number(匹配volume_size的数字类型要求):

variable "instances_subnet_1" {
  description = "多台虚拟机的配置对象"
  type = map(object({
    ami                    = string
    instance_type          = string
    private_ip             = string
    subnet_id              = string
    vpc_security_group_ids = list(string)
    blockdisks             = list(object({
      ebs_name = string
      ebs_size = number
    }))
    root_size              = number
  }))
  default = {
    "ec2-resource" = {
      ami                    = "ami-win"
      instance_type          = "t3.large"
      private_ip             = "10.0.109.4"
      subnet_id              = ""
      vpc_security_group_ids = [""]
      root_size              = 50
      blockdisks = [
        {
          ebs_name = "/dev/sdb"
          ebs_size = 100
        }
      ]
    }
  }
}

2. 修正模块中的dynamic块遍历逻辑

var.blockdisks现在是list类型,遍历每个元素(EBS卷对象)即可直接访问属性:

dynamic "ebs_block_device" {
  for_each = var.blockdisks
  content {
    delete_on_termination = false
    encrypted             = true
    device_name           = ebs_block_device.value.ebs_name
    volume_size           = ebs_block_device.value.ebs_size
    volume_type           = "gp2"
  }
}

可选:用map管理EBS卷

如果希望给每个EBS卷命名标识,可将blockdisks定义为map(object):

# 根模块variables.tf中修改类型
blockdisks = map(object({
  ebs_name = string
  ebs_size = number
}))

# 默认值示例
blockdisks = {
  "data_disk" = {
    ebs_name = "/dev/sdb"
    ebs_size = 100
  }
}

模块中的dynamic块遍历逻辑保持不变,for_each遍历map时value为对象类型,可正常访问.ebs_name和.ebs_size。

额外注意事项

  • volume_size要求为数字类型,避免用字符串传递(如"100"改为100),防止类型转换错误。
  • 添加多块EBS卷时,只需在blockdisks的list或map中新增对应对象即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 02:07:25