diff --git a/builtin/providers/aws/data_source_aws_route_table.go b/builtin/providers/aws/data_source_aws_route_table.go new file mode 100644 index 000000000..df0dc7eea --- /dev/null +++ b/builtin/providers/aws/data_source_aws_route_table.go @@ -0,0 +1,205 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAwsRouteTable() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsRouteTableRead, + + Schema: map[string]*schema.Schema{ + "subnet_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "vpc_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "filter": ec2CustomFiltersSchema(), + "tags": tagsSchemaComputed(), + "routes": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr_block": { + Type: schema.TypeString, + Computed: true, + }, + + "gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + + "instance_id": { + Type: schema.TypeString, + Computed: true, + }, + + "nat_gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + + "vpc_peering_connection_id": { + Type: schema.TypeString, + Computed: true, + }, + + "network_interface_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "associations": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "route_table_association_id": { + Type: schema.TypeString, + Computed: true, + }, + + "route_table_id": { + Type: schema.TypeString, + Computed: true, + }, + + "subnet_id": { + Type: schema.TypeString, + Computed: true, + }, + + "main": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAwsRouteTableRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + req := &ec2.DescribeRouteTablesInput{} + vpcId, vpcIdOk := d.GetOk("vpc_id") + subnetId, subnetIdOk := d.GetOk("subnet_id") + tags, tagsOk := d.GetOk("tags") + filter, filterOk := d.GetOk("filter") + + if !vpcIdOk && !subnetIdOk && !tagsOk && !filterOk { + return fmt.Errorf("One of vpc_id, subnet_id, filters, or tags must be assigned") + } + req.Filters = buildEC2AttributeFilterList( + map[string]string{ + "vpc-id": vpcId.(string), + "association.subnet-id": subnetId.(string), + }, + ) + req.Filters = append(req.Filters, buildEC2TagFilterList( + tagsFromMap(tags.(map[string]interface{})), + )...) + req.Filters = append(req.Filters, buildEC2CustomFilterList( + filter.(*schema.Set), + )...) + + log.Printf("[DEBUG] Describe Route Tables %v\n", req) + resp, err := conn.DescribeRouteTables(req) + if err != nil { + return err + } + if resp == nil || len(resp.RouteTables) == 0 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + if len(resp.RouteTables) > 1 { + return fmt.Errorf("Multiple Route Table matched; use additional constraints to reduce matches to a single Route Table") + } + + rt := resp.RouteTables[0] + + d.SetId(*rt.RouteTableId) + d.Set("vpc_id", rt.VpcId) + d.Set("tags", tagsToMap(rt.Tags)) + if err := d.Set("routes", dataSourceRoutesRead(rt.Routes)); err != nil { + return err + } + + if err := d.Set("associations", dataSourceAssociationsRead(rt.Associations)); err != nil { + return err + } + + return nil +} + +func dataSourceRoutesRead(ec2Routes []*ec2.Route) []map[string]interface{} { + routes := make([]map[string]interface{}, 0, len(ec2Routes)) + // Loop through the routes and add them to the set + for _, r := range ec2Routes { + if r.GatewayId != nil && *r.GatewayId == "local" { + continue + } + + if r.Origin != nil && *r.Origin == "EnableVgwRoutePropagation" { + continue + } + + if r.DestinationPrefixListId != nil { + // Skipping because VPC endpoint routes are handled separately + // See aws_vpc_endpoint + continue + } + + m := make(map[string]interface{}) + + if r.DestinationCidrBlock != nil { + m["cidr_block"] = *r.DestinationCidrBlock + } + if r.GatewayId != nil { + m["gateway_id"] = *r.GatewayId + } + if r.NatGatewayId != nil { + m["nat_gateway_id"] = *r.NatGatewayId + } + if r.InstanceId != nil { + m["instance_id"] = *r.InstanceId + } + if r.VpcPeeringConnectionId != nil { + m["vpc_peering_connection_id"] = *r.VpcPeeringConnectionId + } + if r.NetworkInterfaceId != nil { + m["network_interface_id"] = *r.NetworkInterfaceId + } + + routes = append(routes, m) + } + return routes +} + +func dataSourceAssociationsRead(ec2Assocations []*ec2.RouteTableAssociation) []map[string]interface{} { + associations := make([]map[string]interface{}, 0, len(ec2Assocations)) + // Loop through the routes and add them to the set + for _, a := range ec2Assocations { + + m := make(map[string]interface{}) + m["route_table_id"] = *a.RouteTableId + m["route_table_association_id"] = *a.RouteTableAssociationId + m["subnet_id"] = *a.SubnetId + m["main"] = *a.Main + associations = append(associations, m) + } + return associations +} diff --git a/builtin/providers/aws/data_source_aws_route_table_test.go b/builtin/providers/aws/data_source_aws_route_table_test.go new file mode 100644 index 000000000..743f5b175 --- /dev/null +++ b/builtin/providers/aws/data_source_aws_route_table_test.go @@ -0,0 +1,132 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceAwsRouteTable(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceAwsRouteTableGroupConfig, + Check: resource.ComposeTestCheckFunc( + testAccDataSourceAwsRouteTableCheck("data.aws_route_table.by_tag"), + testAccDataSourceAwsRouteTableCheck("data.aws_route_table.by_filter"), + testAccDataSourceAwsRouteTableCheck("data.aws_route_table.by_subnet"), + ), + }, + }, + }) +} + +func testAccDataSourceAwsRouteTableCheck(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + + if !ok { + return fmt.Errorf("root module has no resource called %s", name) + } + + rts, ok := s.RootModule().Resources["aws_route_table.test"] + if !ok { + return fmt.Errorf("can't find aws_route_table.test in state") + } + vpcRs, ok := s.RootModule().Resources["aws_vpc.test"] + if !ok { + return fmt.Errorf("can't find aws_vpc.test in state") + } + subnetRs, ok := s.RootModule().Resources["aws_subnet.test"] + if !ok { + return fmt.Errorf("can't find aws_subnet.test in state") + } + attr := rs.Primary.Attributes + + if attr["id"] != rts.Primary.Attributes["id"] { + return fmt.Errorf( + "id is %s; want %s", + attr["id"], + rts.Primary.Attributes["id"], + ) + } + + if attr["vpc_id"] != vpcRs.Primary.Attributes["id"] { + return fmt.Errorf( + "vpc_id is %s; want %s", + attr["vpc_id"], + vpcRs.Primary.Attributes["id"], + ) + } + + if attr["tags.Name"] != "terraform-testacc-routetable-data-source" { + return fmt.Errorf("bad Name tag %s", attr["tags.Name"]) + } + if attr["associations.0.subnet_id"] != subnetRs.Primary.Attributes["id"] { + return fmt.Errorf( + "subnet_id is %v; want %s", + attr["associations.0.subnet_id"], + subnetRs.Primary.Attributes["id"], + ) + } + + return nil + } +} + +const testAccDataSourceAwsRouteTableGroupConfig = ` +provider "aws" { + region = "eu-central-1" +} +resource "aws_vpc" "test" { + cidr_block = "172.16.0.0/16" + + tags { + Name = "terraform-testacc-data-source" + } +} + +resource "aws_subnet" "test" { + cidr_block = "172.16.0.0/24" + vpc_id = "${aws_vpc.test.id}" + tags { + Name = "terraform-testacc-data-source" + } +} + +resource "aws_route_table" "test" { + vpc_id = "${aws_vpc.test.id}" + tags { + Name = "terraform-testacc-routetable-data-source" + } +} + +resource "aws_route_table_association" "a" { + subnet_id = "${aws_subnet.test.id}" + route_table_id = "${aws_route_table.test.id}" +} + +data "aws_route_table" "by_filter" { + filter { + name = "association.route-table-association-id" + values = ["${aws_route_table_association.a.id}"] + } + depends_on = ["aws_route_table_association.a"] +} + +data "aws_route_table" "by_tag" { + tags { + Name = "${aws_route_table.test.tags["Name"]}" + } + depends_on = ["aws_route_table_association.a"] +} +data "aws_route_table" "by_subnet" { + subnet_id = "${aws_subnet.test.id}" + depends_on = ["aws_route_table_association.a"] +} + +` diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 2f0d8d23e..8b0e8c518 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -161,6 +161,7 @@ func Provider() terraform.ResourceProvider { "aws_prefix_list": dataSourceAwsPrefixList(), "aws_redshift_service_account": dataSourceAwsRedshiftServiceAccount(), "aws_region": dataSourceAwsRegion(), + "aws_route_table": dataSourceAwsRouteTable(), "aws_s3_bucket_object": dataSourceAwsS3BucketObject(), "aws_subnet": dataSourceAwsSubnet(), "aws_security_group": dataSourceAwsSecurityGroup(), diff --git a/website/source/docs/providers/aws/d/route_table.html.markdown b/website/source/docs/providers/aws/d/route_table.html.markdown new file mode 100644 index 000000000..9b11d8d1d --- /dev/null +++ b/website/source/docs/providers/aws/d/route_table.html.markdown @@ -0,0 +1,86 @@ +--- +layout: "aws" +page_title: "AWS: aws_route_table" +sidebar_current: "docs-aws-datasource-route-table" +description: |- + Provides details about a specific Route Table +--- + +# aws\_route\_table + +`aws_route_table` provides details about a specific Route Table. + +This resource can prove useful when a module accepts a Subnet id as +an input variable and needs to, for example, add a route in +the Route Table. + +## Example Usage + +The following example shows how one might accept a Route Table id as a variable +and use this data source to obtain the data necessary to create a route. + +``` +variable "subnet_id" {} + +data "aws_route_table" "selected" { + subnet_id = "${var.subnet_id}" +} + +resource "aws_route" "route" { + route_table_id = "${data.aws_route_table.selected.id}" + destination_cidr_block = "10.0.1.0/22" + vpc_peering_connection_id = "pcx-45ff3dc1" +} +``` + +## Argument Reference + +The arguments of this data source act as filters for querying the available +Route Table in the current region. The given filters must match exactly one +Route Table whose data will be exported as attributes. + + +* `filter` - (Optional) Custom filter block as described below. + +* `id` - (Optional) The id of the specific Route Table to retrieve. + +* `tags` - (Optional) A mapping of tags, each pair of which must exactly match + a pair on the desired Route Table. + +* `vpc_id` - (Optional) The id of the VPC that the desired Route Table belongs to. + +* `subnet_id` - (Optional) The id of a Subnet which is connected to the Route Table (not be exported if not given in parameter). + +More complex filters can be expressed using one or more `filter` sub-blocks, +which take the following arguments: + +* `name` - (Required) The name of the field to filter by, as defined by + [the underlying AWS API](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html). + +* `values` - (Required) Set of values that are accepted for the given field. + A Route Table will be selected if any one of the given values matches. + +## Attributes Reference + +All of the argument attributes except `filter` and `subnet_id` blocks are also exported as +result attributes. This data source will complete the data by populating +any fields that are not included in the configuration with the data for +the selected Route Table. + +`routes` are also exported with the following attributes, when there are relevants: +Each route supports the following: + +* `cidr_block` - The CIDR block of the route. +* `gateway_id` - The Internet Gateway ID. +* `nat_gateway_id` - The NAT Gateway ID. +* `instance_id` - The EC2 instance ID. +* `vpc_peering_connection_id` - The VPC Peering ID. +* `network_interface_id` - The ID of the elastic network interface (eni) to use. + + +`associations` are also exported with the following attributes: + +* `route_table_association_id` - The Association ID . +* `route_table_id` - The Route Table ID. +* `subnet_id` - The Subnet ID. +* `main` - If the Association due to the Main Route Table. diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index 7a5a42ff8..cc569b434 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -66,6 +66,9 @@