Terraform Error : Can't access attributes on a primitive-typed value (number)

I am getting Error in terraform execution with “Can’t access attributes on a primitive-typed value (number).”

Getting Error on ebs_block_mapping variables , I have requirement to add the ebs volumes with different size & type so i have included with mappings .

However ebs_block_mapping variable doesn’t get substituted properly.

Could you please help on this ?

main.tf :

### EC2 Instance creation
resource "aws_instance" "instance" {
  ami                    = var.ami_id
  subnet_id              = var.subnet_id
  instance_type          = var.instance_type
  key_name               = var.key_pair
  iam_instance_profile   = var.iam_instance_profile
  vpc_security_group_ids = var.security_group_id
  root_block_device {
    volume_type           = var.root_volume_type
    volume_size           = var.root_volume_size
    delete_on_termination = var.delete_on_termination
    encrypted             = "true"
    tags = {
      "Name" = "${var.instance_name}-root"
    }
  }
  dynamic "ebs_block_device" {
    for_each = [for s in var.ebs_device_mappings != null ? [0] : [] : {
      name = s.name
      size = s.size
      type = s.type
    }]
    content {
      device_name           = ebs_block_device.value["name"]
      volume_size           = ebs_block_device.value["size"]
      volume_type           = ebs_block_device.value["type"]
      delete_on_termination = true
      encrypted             = true
      tags = {
        "Name" = "${var.instance_name}-ebs"
      }
    }
  }
}

test.tfvars :

instance_type         = "t2.xlarge"
ami_id                = "ami-05de6de"
ebs_device_mappings   = [{ name = "/dev/sdd", size = 50, type = "gp3" }]
security_group_id     = ["sg-xxxxxxxxxxx"]
root_volume_type      = "gp3"
root_volume_size      = "60"
delete_on_termination = "true"
subnet_id             = "subnet-xxxxxxxxxxxx"
iam_instance_profile  = "test-role"
instance_name         = "test"

variables.tf :

variable "instance_name" {
  description = "Name tag for EC2 instance"
}
variable "ami_id" {
  description = "AMI ID for EC2 instance"
}
variable "subnet_id" {
  description = "Subnet ID for EC2 instance"
}
variable "instance_type" {
  description = "Instance type for EC2 instance"
}
variable "key_pair" {
  description = "Key pair name for EC2 instance"
  default     = null
}
variable "iam_instance_profile" {
  description = "IAM Instance Profile to launch the instance with"
}
variable "security_group_id" {
  description = "Security group ID for EC2"
}
variable "root_volume_type" {
  description = "EBS root volume type"
}
variable "root_volume_size" {
  description = "EBS root volume size"
}
variable "delete_on_termination" {
  description = "EBS volume deletion on EC2 termination"
}
variable "ebs_device_mappings" {
  default     = null
  description = "Need to provide the device name,volume size and volume type"
}

Hi @d.udhayashankar,

In your dynamic block you’ve defined for_each like this:

    for_each = [for s in var.ebs_device_mappings != null ? [0] : [] : {
      name = s.name
      size = s.size
      type = s.type
    }]

This causes s to be 0, and so s.name is invalid: a number doesn’t have any attributes.

I assume your intention was to generate a block when var.ebs_device_mappings is non-null and to skip generating it when it is null. If so, you can achieve that concisely using a “splat” expression:

  for_each = [
    for s in var.ebs_device_mappings[*] : {
      name = s.name
      size = s.size
      type = s.type
    }
  }

The [*] operator has different behavior depending on the type of value it’s applied to. When applied to a value that isn’t a sequence (list, set, or tuple) it has two possible outcomes:

  • If the value is anything other than null then it returns a single-element tuple containing the value.
  • If the value is null then it returns an empty tuple, [].

That means that s – the iteration symbol of the for loop – would refer to the non-null object value, allowing s.name etc to work.

Since your var.ebs_device_mappings elements are already objects with name, size, and type attributes you could also potentially simplify this further, if it isn’t important to reduce the objects to just those three attributes:

  for_each = var.ebs_device_mappings[*]

You can learn more about this operator in Splat Expressions. The behavior when applied to something that isn’t a sequence is described in Single Values as Lists.

@apparentlymart ,

Thanks for your suggestion :+1: , i will check on this & get back to you !

It is like you’re encountering a challenge with the ebs_block_mapping variables in Terraform. Have you checked your mappings syntax to ensure prope no text substitution? Double-checking that might help resolve the issue.