Terraform_remote_state - object with no attributes

Hi there,

I don’t seem to make terraform_remote_state data lookup work at all. I have this simple configuration to create a subnet and output that value:

// Create Public subnet(s)
resource "aws_subnet" "public" {
  count                   = length(local.s_count)
  vpc_id                  = local.vpc_ids[index(var.s_zones, substr(local.s_count[count.index], 0, 1))]
  cidr_block              = cidrsubnet(cidrsubnet(var.vpc_cidrs[index(var.s_zones, substr(local.s_count[count.index], 0, 1))], 2, 2), 2, count.index)
  availability_zone       = "${var.aws_region}${substr(local.s_count[count.index], 1, 0)}"
  map_public_ip_on_launch = false
}
// Output value
output "snet_pub_ids" {
  value = aws_subnet.public.*.id
}

and then look it up for it:

// Remote state lookup
data "terraform_remote_state" "infra" {
  backend   = "s3"
  workspace = terraform.workspace
  config = {
    key     = var.tf_state_key
    bucket  = var.tf_state_bucket
    region  = var.aws_region
    encrypt = true
  }
}

// Public subnet-ids
output "snet_pub_ids" {
  value = aws_subnet.public.*.id
}
// Transit Gateway VPC Attachments
resource "aws_ec2_transit_gateway_vpc_attachment" "public" {
  count              = length(var.s_zones)
  transit_gateway_id = data.aws_ec2_transit_gateway.tgwz.id
  vpc_id             = element(aws_vpc.vpcs.*.id, count.index)
  subnet_ids         = data.terraform_remote_state.infra.outputs.snet_pub_ids[count.index].ids
  #subnet_ids        = element(data.terraform_remote_state.infra.outputs.snet_pub_ids.*.id, count.index)
   ransit_gateway_default_route_table_association = false
   transit_gateway_default_route_table_propagation = false
}

But I’m getting object with no attributes error:

Error: Unsupported attribute

  on ../../modules/vpc/tgw.tf line 9, in resource "aws_ec2_transit_gateway_vpc_attachment" "public":
   9:   subnet_ids         = data.terraform_remote_state.infra.outputs.snet_pub_ids[count.index].ids
    |----------------
    | data.terraform_remote_state.infra.outputs is object with no attributes

This object does not have an attribute named "snet_pub_ids".

What am I doing wrong here? I’m using v0.12.20 atm. Any help will be greatly appreciated.

-S

Do you have an S3 backend declaration in the first confguration where you create your subnets? If you don’t store your state in S3, there’s nothing to read from.

What happens in the second configuration if you just make an output with value = data.terraform_remote_state.infra?

I usually start out like that so I know for sure what I’m working against.

//Bent

Yeah, using S3 as the backend.
but I found something interesting, if I look into the state-file, downloaded from the S3 bucket, I see the output is empty:

{
  "version": 4,
  "terraform_version": "0.12.20",
  "serial": 17,
  "lineage": "e6257473-31c0-a075-7d36-ffb5e1b8823e",
  "outputs": {},
........
........ 

What am I missing here?
Looks like remote_state object changed massively from the v0.11.x?

-S

Only thing I can think of is that you’re not looking at the JSON which actually store the state of your first configuration where you define the subnets

Those two pieces of code is actually part of the same core-module which creates the VPC and the associated networking bit like main subnets, TGW attachments etc. And then consume that from the front-end module, like this:

module "vpc" {
  source          = "../../modules/vpc"
  tf_state_key    = var.key
  tf_state_bucket = var.bucket
  project         = var.my_project 
  ......
  ......
}

The data and outputs above, are all under modules/vpc. It’s works just fine if I use the standard data source, like this:

data "aws_subnet_ids" "public" {
  count  = length(var.s_zones)
  vpc_id = element(aws_vpc.vpcs.*.id, count.index)
  filter {
    name   = "tag:Name"
    values = ["${var.vpc_names[count.index]}-pub-*"]
  }
  depends_on = [aws_subnet.public]
}

and then use it like:
subnet_ids = data.aws_subnet_ids.public[count.index].ids

the problem starts if I do the similar thing with the remote_state. I’m sure I’m missing something here in V0.12 as this works perfectly well in v0.11

Another thing is I have terraform workspace enabled - should that cause any issue?

-S

But if you’re inside the same module then why do you use a remote_state data source?

Remember that remote_state reads the JSON statfile which was written previous by an apply. This is intended for one Terraform configuration to read the state of another configuration.

I think I can see why “this works perfectly in 0.11”: you create the first resource, run apply, the statefile is written with the output. Then you add the remote_state which can read the output from the state_file and use that in the next resource. But if you want to cold start and do everything at once then the statefile is empty and so there’s nothing to read…

If resource "aws_subnet" "public" is in the same module as resource "aws_ec2_transit_gateway_vpc_attachment" "public" then subnet_ids should just be aws_subnet.public.*.id.

Data sources in general should be used to obtain information about “outside stuff”, things that are not controlled by the current Terraform configuration. And the remote_state data source in particular should be used to obtain information about other Terraform configurations.

I don’t know if workspaces are involved in this problem but they might well be - seem to cause problems for a good many people…

1 Like

Thanks @bentterp! I actually tried that but started getting this error:

Error: Incorrect attribute value type
  on ../../modules/vpc/tgw.tf line 8, in resource "aws_ec2_transit_gateway_vpc_attachment" "public":
   8:   subnet_ids       = element(aws_subnet.public.*.id, count.index)
    |----------------
    | aws_subnet.public is tuple with 4 elements
    | count.index is 1

Inappropriate value for attribute "subnet_ids": set of string required. 

if I use: subnet_ids = element(aws_subnet.public.*.id, count.index or (aws_subnet.public[count.index].id) for the assignment. It only works for me if I define a data source (the one I mentioned above) and use it in the code. I couldn’t figure out why.

The depends_on in the data-source brings another problem but that’s a story for the another day.

-S

Sorry I wasn’t properly awake, the proper syntax would be subnet_ids = aws_subnet.public[*].id

https://www.terraform.io/docs/configuration/expressions.html#aws_instance-example-id is a pretty good example

1 Like

I did pretty similar thing, right? In stead of [*] (which is the full list) I used [count.index], which is targeted and that’s what I got to do to attach individual VPCs and the associated sub-nets. But even with [*] I get the same error.
Here is the code:

// Transit Gateway VPC Attachments
resource "aws_ec2_transit_gateway_vpc_attachment" "public" {
  count              = length(var.s_zones)
  transit_gateway_id = data.aws_ec2_transit_gateway.tgwz.id
  vpc_id             = element(aws_vpc.vpcs.*.id, count.index)
  subnet_ids         = element(aws_subnet.public[*].id, count.index)
  //subnet_ids       = data.aws_subnet_ids.public[count.index].ids
  depends_on         = [aws_subnet.public]
  transit_gateway_default_route_table_association = false
  transit_gateway_default_route_table_propagation = false

  tags = merge(
    local.common_tags,
    { "Name" = "${var.vpc_names[count.index]}-att" }
  )
}

and the Error:

Error: Incorrect attribute value type

  on ../../modules/vpc/tgw.tf line 8, in resource "aws_ec2_transit_gateway_vpc_attachment" "public":
   8:   subnet_ids         = element(aws_subnet.public[*].id, count.index)
    |----------------
    | aws_subnet.public is tuple with 4 elements
    | count.index is 0

Inappropriate value for attribute "subnet_ids": set of string required.

which is same as before. It’s something to do with v0.12.x upgrade, I’m sure. Any idea what might going wrong?

I also found this but never understood the outcome:

-S

1 Like

subnet_ids (note the plural ‘s’ - it is good practice to do that the parameter is for a list or map) require a set (or list)of strings, so if you only want to use one you can wrap it in [ ].

thanks @bentterp!
yeah, just figured out last-night that I was missing that [..]. The ids was definitely the indication of that, which I failed to notice.
To summarize, when I was using the data-source (to assign the value), it was already creating the list (so didn’t require the list operator):

# module.vpc.data.aws_subnet_ids.public[1]:
data "aws_subnet_ids" "public" {
    id     = "vpc-0b844a2d20b8ba2bc"
    ids    = [
        "subnet-00faace6eb15c959d",
        "subnet-0dbfc457eaee23540",
    ]
    vpc_id = "vpc-0b844a2d20b8ba2bc"
    filter {
        name   = "tag:Name"
        values = [
            "zenstgh-pub-*",
        ]
    }
}

but using direct assignment it didn’t. This seems to be working: subnet_ids = [aws_subnet.public[count.index].id] but now I’m facing a different issue, which is not directly related to this and will ask separately.