2
votes

I'm having some trouble whilst importing existing AWS routing tables into Terraform. They import, and their routes are recorded in the state file, but running plan or apply afterwards always wants to delete those routes, even if they are also defined in Terraform.

I define an existing AWS routing table in Terraform like this:

resource "aws_route_table" "public_staging" {
  vpc_id = "${aws_vpc.staging.id}"

  route {
    cidr_block = "${aws_vpc.management.cidr_block}"
    vpc_peering_connection_id = "${aws_vpc_peering_connection.management_to_staging.id}"
  }
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = "${aws_internet_gateway.staging.id}"
  }

  tags {
    Name = "public staging (igw)"
    environment = "staging"
  }
}

Then import it like this; terraform import aws_route_table.public_management rtb-abc123.

Which outputs:

aws_route_table.public_staging: Importing from ID "rtb-abc123"...
aws_route_table.public_staging: Import complete!
  Imported aws_route_table (ID: rtb-abc123)
  Imported aws_route (ID: r-rtb-abc123123456)
  Imported aws_route (ID: r-rtb-abc123654321)
  Imported aws_route_table_association (ID: rtbassoc-qwert765)
  Imported aws_main_route_table_association (ID: rtbassoc-asdf9876)
aws_route.public_staging: Refreshing state... (ID: r-rtb-abc123123456)
aws_route_table.public_staging: Refreshing state... (ID: rtb-abc123)
aws_route.public_staging-1: Refreshing state... (ID: r-rtb-abc123654321)
aws_route_table_association.public_staging: Refreshing state... (ID: rtbassoc-qwert765)
aws_main_route_table_association.public_staging: Refreshing state... (ID: rtbassoc-asdf9876)

When then running terraform plan, Terraform wants to delete all the aws_route resource states it generated in the state file and create the route table we just imported:

Terraform will perform the following actions:

  - aws_route.public_staging

  - aws_route.public_staging-1

  + aws_route_table.public_management
    ...

I've also tried defining the routes separately, outside of the aws_route_table resource and attaching them to the routing table by ID, like this:

resource "aws_route" "management_to_staging" {
  route_table_id = "${aws_route_table.public_management.id}"
  cidr_block = "${aws_vpc.staging.cidr_block}"
  vpc_peering_connection_id = "${aws_vpc_peering_connection.management_to_staging.id}"
}

The only thing that will result in a no-change state is if I run the import on the routing table, also define the routes outside of the routing table (as aws_route resources), and then go in and manually change the generated names in the state file to those I've defined the the tf file. However, I believe this would not actually work on a fresh run, since the routes defined in the aws_route_table, and those as separate aws_route resources, would conflict.

EDIT:

Most likely explanation as far as I can see, is that on importing, Terraform is quite happily imports the routes inside the route table, but then on plan, it expects them to be declared explicitly using aws_route resources.

Problem with that is; you can't import aws_route resources, so you can never have your current infrastructure state match your terraform state.

I think the reason explicitly declaring them afterwards doesn't work either is that the state file records imported routes differently if it got them from an import aws_route_table ... command to if it generates them from an apply with explicit aws_route definitions.

And now I'm out of breath.

2
Are you sure the rules are in correct order?Jakub Kania
Well I'm running them in the order that makes sense. i.e. create vpc before trying to import anything that relies on it, etc.Afraz
But isn't TF just trying to put route 1 into route 0's place and the other way around? Order matters with TFJakub Kania

2 Answers

0
votes

Running in to this problem too. Since it does not look like it is feasible to fully import routing tables, I am going to create new ones via terrform and then change the associations to point at the new tables. This seems the easiest way forward towards having Terraform manage all resources.

0
votes

You should try declaring your routes on a separate resource aws_route. A nested route object inside aws_route_table resource works the same as a separate aws_route, but the latter may be imported. Check the documentation.

Try it like this.

resource "aws_route_table" "public_staging" {
  vpc_id = "${aws_vpc.staging.id}"

  tags {
    Name = "public staging (igw)"
    environment = "staging"
  }
}
 
resource "aws_route" "public_staging_r0" {
    route_table_id = aws_route_table.public_staging.id
    cidr_block = "${aws_vpc.management.cidr_block}"
    vpc_peering_connection_id = "${aws_vpc_peering_connection.management_to_staging.id}"
}

resource "aws_route" "public_staging_r1" {
    route_table_id = aws_route_table.public_staging.id
    cidr_block = "0.0.0.0/0"
    gateway_id = "${aws_internet_gateway.staging.id}"
}

Then, import the aws_route_table normally and each aws_route as below

terraform import aws_route.<route_id> <route_table_id>_<cidr_block>
terraform import aws_route.public_staging_r0 rtb-abc123_0.0.0.0/0

Important: as per the documentation, do not use a separate aws_route resource together with nested routes

NOTE on Route Tables and Routes: Terraform currently provides both a standalone Route resource and a Route Table resource with routes defined in-line. At this time you cannot use a Route Table with in-line routes in conjunction with any Route resources. Doing so will cause a conflict of rule settings and will overwrite rules.