Unable to create ec2 instances when using modules -Incorrect attribute value type

I will really appreciate some help here as I don’t think it could be so complicated.

terraform -v
Terraform v0.12.6

  • provider.aws v2.25.0

I have a root which contains 3 other modules ( VPC, subnet and instances).
The VPC and 2 subnets are getting created just fine . But when I add the instances module I get the following error -

Error: Invalid value for module argument

on main.tf line 60, in module “instances”:
60: subnet_id = data.terraform_remote_state.network.outputs.pub_sub_id

The given value is not suitable for child module variable “subnet_id” defined
at …/mod/instances/ec2.tf:2,1-21: string required.

Here are my files. For the sake of simplicity I have merged the output and variables in one file.
Thanks !

main.tf

variable "vpc_region" {
  description = "AWS region"
  default     = "us-east-1"
}

# VPC Config
variable "vpc_name" {
  description = "VPC for building demos"
  default     = "Test"
}

variable "vpc_cidr_block" {
  description = "IP addressing for demo Network"
  default     = "10.0.0.0/16"
}




provider "aws" {
  region = "us-east-1"
  profile = "work"
  shared_credentials_file="/Users/jim/.aws/credentials"
}

terraform {
  backend "s3" {
    bucket = "terraform"
    key = "terraform/terraform.tfstate"
    region = "us-east-1"
    profile="work"
    shared_credentials_file="/Users/jim/.aws/credentials"
  }
}

data "terraform_remote_state" "network" {
  backend = "s3"
  config = { 
    bucket = "terraform"
    key = "terraform/terraform.tfstate"
    region = "us-east-1"
    profile="work"
    shared_credentials_file="/Users/jim/.aws/credentials"
  }
}

module "vpc" {
  source = "../mod/vpc"
  vpc_region     = "${var.vpc_region}"
  vpc_name       = "${var.vpc_name}"
  vpc_cidr_block = "${var.vpc_cidr_block}"
} 
module "public_subnet" {
source = "../mod/pub_sub"
vpc_id = "${module.vpc.id}"
}

 module "instances" {
source = "../mod/instances"
subnet_id = data.terraform_remote_state.network.outputs.pub_sub_id
vpc_id = "${module.vpc.id}"
}  


output "id" {
  value = "${module.vpc.id}"
}
 output "pub_sub_id" {
  value = "${module.public_subnet.*}"
} 

vpc.tf

variable "vpc_region" {}
variable "vpc_name" {}
variable "vpc_cidr_block" {}



resource "aws_vpc" "primary_vpc" {
  cidr_block = "${var.vpc_cidr_block}"
  enable_dns_hostnames = true
  enable_dns_support  =true
  tags = {
    Name = "${var.vpc_name}"
  }
}

 output "id" {
  value = "${aws_vpc.primary_vpc.id}"
} 

output "region" {
  value = "${var.vpc_region}"
}

**pub_subnet.tf** 


variable "sub_cdr" {

type="list"

default=["10.0.0.0/28", "10.0.0.16/28"]

}

variable "azs" {

type="list"

default=["us-east-1a","us-east-1b"]

}

data "aws_availability_zones" azs {}

variable "vpc_id" { }

resource aws_subnet "public_subnet"{

count=2

vpc_id="${var.vpc_id}"

cidr_block="${var.sub_cdr[count.index]}"

availability_zone="${data.aws_availability_zones.azs.names[count.index]}"

tags= {Name = "pub-sub-${count.index + 1}"}

}

output "pub_sub_id" {

value = "${aws_subnet.public_subnet.*.id}"

}

ec2.tf
variable “vpc_id” { }
variable “subnet_id” {
type = “string”
}

variable "instance_count" {
  default=1
}

resource "aws_security_group" "pub_sg" {
  vpc_id = "${var.vpc_id}"
  name = "public-sg"
  ingress {
    from_port = 22
    protocol = "tcp"
    to_port = 22
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port = 0
    protocol = "-1"
    to_port = 0
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "web-srvs" {
count="${var.instance_count == "0" ? "1" : var.instance_count}"
ami = "ami-035b3c7efe6d061d5"
instance_type = "t2.nano"
key_name="XXXXX"
subnet_id = "${var.subnet_id}"
vpc_security_group_ids = ["${aws_security_group.pub_sg.id}"]
associate_public_ip_address=true
tags = { 
 #   Name = "${var.name}${var.instance_count == "0" ? "" : format("%02d", count.index + 1)}-${var.environment}" 
  #   Application = "${var.environment}" 
   #  Project = "${var.project}"
     CountIndex = "${var.instance_count == "0" ? "" : format("%02d", count.index + 1)}"
   }
}

I made a small modification.
As it is obvious, I am getting 2 subnet_id as output -

Outputs:

id = vpc-00531…
pub_sub_id = [
{
“pub_sub_id” = [
“subnet-…”,
“subnet-…”,
]
},
]

I changed the following to pick only one of the two subnets -

subnet_id = element(data.terraform_remote_state.network.outputs.pub_sub_id, 0)

But, still getting the following error -

Invalid value for module argument

on main.tf line 60, in module “instances”:
** 60: subnet_id = element(data.terraform_remote_state.network.outputs.pub_sub_id, 0)**

The given value is not suitable for child module variable “subnet_id” defined
at …/mod/instances/ec2.tf:6,1-21: string required.

Any help will be highly appreciated.

I made some progress -

output "id" {
  value = module.vpc.id
}
 output "pub_sub_id" {
  value = module.public_subnet.*
} 

 output "test" {
  value = element(data.terraform_remote_state.network.outputs.pub_sub_id, 0)
}  

The output I am getting is –

Outputs:

id = vpc-07123456.......51
pub_sub_id = [
  {
    "pub_sub_id" = [
      "subnet-04abc97e82051f370",
      "subnet-04abcde275ccba15e",
    ]
  },
]
test = {
  "pub_sub_id" = [
    "subnet-04abc97e82051f370",
    "subnet-04abcde275ccba15e",
  ]
}

My question – Why is element returning more than one subnet ?

if I do

output “test” {
value = length(data.terraform_remote_state.network.outputs.pub_sub_id)
}

I get

test=1

I think this is the reason but I don’t know how to fix this

Any help (please) ??

Hi @apparentlymart,

Can you please help ?

Thanks

I was finally able to find a solution using Data Source but was wondering if someone can help me with a solution to the above problem using tfstate.

Thanks !

Hi @Jim420,

I’m a little confused as to what you are trying to do here. It looks like your backend "s3" block has an identical config to your data "terraform_remote_state" "network" block, and so this configuration is effectively reading its own remote state. That doesn’t work because at the time Terraform reads it the state hasn’t updated yet.

I think perhaps you’ve created a situation where each terraform plan is obtaining the result of the previous terraform apply, and so each time you run Terraform you get one more level of nesting around those outputs, creating a map of lists rather than a single list, or something else I can’t quite determine.

You’ll need to either remove that data "terraform_remote_state" "network" block or to change it to not be the same as backend "s3", depending on whether your goal here was to have one configuration or two).

If you can clarify what your goals were here (in real-world terms, rather than in Terraform terms) then I might be able to give a more concrete suggestion on how to achieve it.

Thanks @apparentlymart.
I really appreciate your reply. I am sure I will have a solution.

Here is what I am trying to do -
Create plenty of modules then call the required modules using the main.tf.
The objective here - create one VPN, then create a public subnet in the created VPN, finally create an ec2 instance in one of the public subnets.

main.tf
→ vpc.tf
→ public_subnet.tf
→ ec2.tf
The problem is - I am unable to pass the public subnet id to my ec2.tf while initiating the module from main.tf

I could not a solution so added the following lines in ec2.tf.

data “aws_vpc” “selected” {
tags = {
Name = “${var.vpc_name}”
}
}

data “aws_subnet_ids” “pub” {
vpc_id = “${data.aws_vpc.selected.id}”
tags = {
Name = "pub*"
}
}

I hope I was able to convey my objective here.

Thanks again,