Search code examples
amazon-web-servicesterraformterraform-provider-aws

Terraform Inappropriate value for attribute "route"


relatively new to terraform and currently trying to build cloud infrastructure in AWS. I get an error when I use an official example (little bit changed) from the documentation for the resource aws_route_table (https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table)

resource "aws_route_table" "prod-route-table" {
  vpc_id = aws_vpc.prod-vpc.id

  route = [{
      # Route all Traffic to the internet gateway
      cidr_block = "0.0.0.0/0"
      gateway_id = aws_internet_gateway.gw.id
  },{
      ipv6_cidr_block = "::/0"
      gateway_id = aws_internet_gateway.gw.id
  }]
  
}

I get the following error message

Error: Incorrect attribute value type
│ Inappropriate value for attribute "route": element 0: attributes "carrier_gateway_id",
│ "destination_prefix_list_id", "egress_only_gateway_id", "instance_id", "ipv6_cidr_block",
│ "local_gateway_id", "nat_gateway_id", "network_interface_id", "transit_gateway_id", "vpc_endpoint_id",
│ and "vpc_peering_connection_id" are required.

Adding all these attributes solves the error however this blows up the code massively. Writing it differently (see the following) results in no errors, is the terraform AWS documentation incorrect, as they clearly state the first way of writing it?

resource "aws_route_table" "prod-route-table" {
  vpc_id = aws_vpc.prod-vpc.id

  route {
      # Route all Traffic to the internet gateway
      cidr_block = "0.0.0.0/0"
      gateway_id = aws_internet_gateway.gw.id
  }
  route{
      ipv6_cidr_block = "::/0"
      gateway_id = aws_internet_gateway.gw.id
  }
  
}

I am using terraform v1.0.10 and aws provider version = "3.63.0"
Thanks in advance


Solution

  • The documentation for that argument mentions that it's using the legacy attributes as blocks mode, which is a holdover from Terraform v0.12 for some situations where providers were depending on being able to write certain arguments in both the nested block syntax (like in your second example) and in the attribute syntax (like in your first example).

    The syntax currently shown in the doc example -- and your first example in your question -- is contrary to the advice about how to write a fixed value (as opposed to a dynamic value), so indeed the second example you showed here would be the preferred way as far as the general Terraform documentation is concerned.

    resource "aws_route_table" "example" {
      vpc_id = aws_vpc.example.id
    
      route {
        cidr_block = "10.0.1.0/24"
        gateway_id = aws_internet_gateway.example.id
      }
      route {
        ipv6_cidr_block        = "::/0"
        egress_only_gateway_id = aws_egress_only_internet_gateway.example.id
      }
    
      tags = {
        Name = "example"
      }
    }
    

    Possibly the AWS provider documentation author here used the attribute syntax in order to show symmetry with the special case of setting route = [] to explicitly state that there should be no routes at all, because entirely omitting this argument unfortunately (for historical reasons) means to ignore any existing routes in the remote API, rather than to remove all existing routes in the remote API.

    There's a little more about the behavior you saw in the subsequent section arbitrary expressions with argument syntax:

    Because of the rule that argument declarations like this fully override any default value, when creating a list-of-objects expression directly the usual handling of optional arguments does not apply, so all of the arguments must be assigned a value, even if it's an explicit null:

    example = [
      {
        # Cannot omit foo in this case, even though it would be optional in the
        # nested block syntax.
        foo = null
      },
    ]
    

    Over time providers will gradually phase out this legacy mode, but must do so cautiously because it can be a breaking change for some existing configurations. Until then this is unfortunately a confusing rough edge for some particular provider attributes, though they should at least all link to the relevant documentation page I linked above to note that their behavior doesn't match the normal Terraform argument-handling behaviors.