Merge pull request #6819 from hashicorp/f-aws-vpc-data-sources
provider/aws: data sources for AWS network planning
This commit is contained in:
commit
46ee2ef51a
|
@ -0,0 +1,89 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceAwsAvailabilityZone() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceAwsAvailabilityZoneRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"region": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"name_suffix": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"state": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAwsAvailabilityZoneRead(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).ec2conn
|
||||
|
||||
req := &ec2.DescribeAvailabilityZonesInput{}
|
||||
|
||||
if name := d.Get("name"); name != "" {
|
||||
req.ZoneNames = []*string{aws.String(name.(string))}
|
||||
}
|
||||
|
||||
req.Filters = buildEC2AttributeFilterList(
|
||||
map[string]string{
|
||||
"state": d.Get("state").(string),
|
||||
},
|
||||
)
|
||||
if len(req.Filters) == 0 {
|
||||
// Don't send an empty filters list; the EC2 API won't accept it.
|
||||
req.Filters = nil
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] DescribeAvailabilityZones %s\n", req)
|
||||
resp, err := conn.DescribeAvailabilityZones(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp == nil || len(resp.AvailabilityZones) == 0 {
|
||||
return fmt.Errorf("no matching AZ found")
|
||||
}
|
||||
if len(resp.AvailabilityZones) > 1 {
|
||||
return fmt.Errorf("multiple AZs matched; use additional constraints to reduce matches to a single AZ")
|
||||
}
|
||||
|
||||
az := resp.AvailabilityZones[0]
|
||||
|
||||
// As a convenience when working with AZs generically, we expose
|
||||
// the AZ suffix alone, without the region name.
|
||||
// This can be used e.g. to create lookup tables by AZ letter that
|
||||
// work regardless of region.
|
||||
nameSuffix := (*az.ZoneName)[len(*az.RegionName):]
|
||||
|
||||
d.SetId(*az.ZoneName)
|
||||
d.Set("id", az.ZoneName)
|
||||
d.Set("name", az.ZoneName)
|
||||
d.Set("name_suffix", nameSuffix)
|
||||
d.Set("region", az.RegionName)
|
||||
d.Set("state", az.State)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDataSourceAwsAvailabilityZone(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDataSourceAwsAvailabilityZoneConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccDataSourceAwsAvailabilityZoneCheck("data.aws_availability_zone.by_name"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccDataSourceAwsAvailabilityZoneCheck(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)
|
||||
}
|
||||
|
||||
attr := rs.Primary.Attributes
|
||||
|
||||
if attr["name"] != "us-west-2a" {
|
||||
return fmt.Errorf("bad name %s", attr["name"])
|
||||
}
|
||||
if attr["name_suffix"] != "a" {
|
||||
return fmt.Errorf("bad name_suffix %s", attr["name_suffix"])
|
||||
}
|
||||
if attr["region"] != "us-west-2" {
|
||||
return fmt.Errorf("bad region %s", attr["region"])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccDataSourceAwsAvailabilityZoneConfig = `
|
||||
provider "aws" {
|
||||
region = "us-west-2"
|
||||
}
|
||||
|
||||
data "aws_availability_zone" "by_name" {
|
||||
name = "us-west-2a"
|
||||
}
|
||||
`
|
|
@ -0,0 +1,84 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceAwsRegion() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceAwsRegionRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"current": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"endpoint": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAwsRegionRead(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).ec2conn
|
||||
currentRegion := meta.(*AWSClient).region
|
||||
|
||||
req := &ec2.DescribeRegionsInput{}
|
||||
|
||||
req.RegionNames = make([]*string, 0, 2)
|
||||
if name := d.Get("name").(string); name != "" {
|
||||
req.RegionNames = append(req.RegionNames, aws.String(name))
|
||||
}
|
||||
|
||||
if d.Get("current").(bool) {
|
||||
req.RegionNames = append(req.RegionNames, aws.String(currentRegion))
|
||||
}
|
||||
|
||||
req.Filters = buildEC2AttributeFilterList(
|
||||
map[string]string{
|
||||
"endpoint": d.Get("endpoint").(string),
|
||||
},
|
||||
)
|
||||
if len(req.Filters) == 0 {
|
||||
// Don't send an empty filters list; the EC2 API won't accept it.
|
||||
req.Filters = nil
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] DescribeRegions %s\n", req)
|
||||
resp, err := conn.DescribeRegions(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp == nil || len(resp.Regions) == 0 {
|
||||
return fmt.Errorf("no matching regions found")
|
||||
}
|
||||
if len(resp.Regions) > 1 {
|
||||
return fmt.Errorf("multiple regions matched; use additional constraints to reduce matches to a single region")
|
||||
}
|
||||
|
||||
region := resp.Regions[0]
|
||||
|
||||
d.SetId(*region.RegionName)
|
||||
d.Set("id", region.RegionName)
|
||||
d.Set("name", region.RegionName)
|
||||
d.Set("endpoint", region.Endpoint)
|
||||
d.Set("current", *region.RegionName == currentRegion)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDataSourceAwsRegion(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDataSourceAwsRegionConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccDataSourceAwsRegionCheck("data.aws_region.by_name_current", "us-west-2", "true"),
|
||||
testAccDataSourceAwsRegionCheck("data.aws_region.by_name_other", "us-west-1", "false"),
|
||||
testAccDataSourceAwsRegionCheck("data.aws_region.by_current", "us-west-2", "true"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccDataSourceAwsRegionCheck(name, region, current 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)
|
||||
}
|
||||
|
||||
attr := rs.Primary.Attributes
|
||||
|
||||
if attr["name"] != region {
|
||||
return fmt.Errorf("bad name %s", attr["name"])
|
||||
}
|
||||
if attr["current"] != current {
|
||||
return fmt.Errorf("bad current %s; want %s", attr["current"], current)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccDataSourceAwsRegionConfig = `
|
||||
provider "aws" {
|
||||
region = "us-west-2"
|
||||
}
|
||||
|
||||
data "aws_region" "by_name_current" {
|
||||
name = "us-west-2"
|
||||
}
|
||||
|
||||
data "aws_region" "by_name_other" {
|
||||
name = "us-west-1"
|
||||
}
|
||||
|
||||
data "aws_region" "by_current" {
|
||||
current = true
|
||||
}
|
||||
`
|
|
@ -0,0 +1,123 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceAwsSubnet() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceAwsSubnetRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"availability_zone": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"cidr_block": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"default_for_az": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"filter": ec2CustomFiltersSchema(),
|
||||
|
||||
"id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"state": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"tags": tagsSchemaComputed(),
|
||||
|
||||
"vpc_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).ec2conn
|
||||
|
||||
req := &ec2.DescribeSubnetsInput{}
|
||||
|
||||
if id := d.Get("id"); id != "" {
|
||||
req.SubnetIds = []*string{aws.String(id.(string))}
|
||||
}
|
||||
|
||||
// We specify default_for_az as boolean, but EC2 filters want
|
||||
// it to be serialized as a string. Note that setting it to
|
||||
// "false" here does not actually filter by it *not* being
|
||||
// the default, because Terraform can't distinguish between
|
||||
// "false" and "not set".
|
||||
defaultForAzStr := ""
|
||||
if d.Get("default_for_az").(bool) {
|
||||
defaultForAzStr = "true"
|
||||
}
|
||||
|
||||
req.Filters = buildEC2AttributeFilterList(
|
||||
map[string]string{
|
||||
"availabilityZone": d.Get("availability_zone").(string),
|
||||
"cidrBlock": d.Get("cidr_block").(string),
|
||||
"defaultForAz": defaultForAzStr,
|
||||
"state": d.Get("state").(string),
|
||||
"vpc-id": d.Get("vpc_id").(string),
|
||||
},
|
||||
)
|
||||
req.Filters = append(req.Filters, buildEC2TagFilterList(
|
||||
tagsFromMap(d.Get("tags").(map[string]interface{})),
|
||||
)...)
|
||||
req.Filters = append(req.Filters, buildEC2CustomFilterList(
|
||||
d.Get("filter").(*schema.Set),
|
||||
)...)
|
||||
if len(req.Filters) == 0 {
|
||||
// Don't send an empty filters list; the EC2 API won't accept it.
|
||||
req.Filters = nil
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] DescribeSubnets %s\n", req)
|
||||
resp, err := conn.DescribeSubnets(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp == nil || len(resp.Subnets) == 0 {
|
||||
return fmt.Errorf("no matching subnet found")
|
||||
}
|
||||
if len(resp.Subnets) > 1 {
|
||||
return fmt.Errorf("multiple subnets matched; use additional constraints to reduce matches to a single subnet")
|
||||
}
|
||||
|
||||
subnet := resp.Subnets[0]
|
||||
|
||||
d.SetId(*subnet.SubnetId)
|
||||
d.Set("id", subnet.SubnetId)
|
||||
d.Set("vpc_id", subnet.VpcId)
|
||||
d.Set("availability_zone", subnet.AvailabilityZone)
|
||||
d.Set("cidr_block", subnet.CidrBlock)
|
||||
d.Set("default_for_az", subnet.DefaultForAz)
|
||||
d.Set("state", subnet.State)
|
||||
d.Set("tags", tagsToMap(subnet.Tags))
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDataSourceAwsSubnet(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDataSourceAwsSubnetConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_id"),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_cidr"),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_tag"),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_vpc"),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_filter"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccDataSourceAwsSubnetCheck(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)
|
||||
}
|
||||
|
||||
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"] != subnetRs.Primary.Attributes["id"] {
|
||||
return fmt.Errorf(
|
||||
"id is %s; want %s",
|
||||
attr["id"],
|
||||
subnetRs.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["cidr_block"] != "172.16.123.0/24" {
|
||||
return fmt.Errorf("bad cidr_block %s", attr["cidr_block"])
|
||||
}
|
||||
if attr["availability_zone"] != "us-west-2a" {
|
||||
return fmt.Errorf("bad availability_zone %s", attr["availability_zone"])
|
||||
}
|
||||
if attr["tags.Name"] != "terraform-testacc-subnet-data-source" {
|
||||
return fmt.Errorf("bad Name tag %s", attr["tags.Name"])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccDataSourceAwsSubnetConfig = `
|
||||
provider "aws" {
|
||||
region = "us-west-2"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "172.16.0.0/16"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-data-source"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "test" {
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
cidr_block = "172.16.123.0/24"
|
||||
availability_zone = "us-west-2a"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-data-source"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_id" {
|
||||
id = "${aws_subnet.test.id}"
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_cidr" {
|
||||
cidr_block = "${aws_subnet.test.cidr_block}"
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_tag" {
|
||||
tags {
|
||||
Name = "${aws_subnet.test.tags["Name"]}"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_vpc" {
|
||||
vpc_id = "${aws_subnet.test.vpc_id}"
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_filter" {
|
||||
filter {
|
||||
name = "vpc-id"
|
||||
values = ["${aws_subnet.test.vpc_id}"]
|
||||
}
|
||||
}
|
||||
`
|
|
@ -0,0 +1,121 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceAwsVpc() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceAwsVpcRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"cidr_block": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"dhcp_options_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"default": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"filter": ec2CustomFiltersSchema(),
|
||||
|
||||
"id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"instance_tenancy": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"state": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"tags": tagsSchemaComputed(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).ec2conn
|
||||
|
||||
req := &ec2.DescribeVpcsInput{}
|
||||
|
||||
if id := d.Get("id"); id != "" {
|
||||
req.VpcIds = []*string{aws.String(id.(string))}
|
||||
}
|
||||
|
||||
// We specify "default" as boolean, but EC2 filters want
|
||||
// it to be serialized as a string. Note that setting it to
|
||||
// "false" here does not actually filter by it *not* being
|
||||
// the default, because Terraform can't distinguish between
|
||||
// "false" and "not set".
|
||||
isDefaultStr := ""
|
||||
if d.Get("default").(bool) {
|
||||
isDefaultStr = "true"
|
||||
}
|
||||
|
||||
req.Filters = buildEC2AttributeFilterList(
|
||||
map[string]string{
|
||||
"cidr": d.Get("cidr_block").(string),
|
||||
"dhcp-options-id": d.Get("dhcp_options_id").(string),
|
||||
"isDefault": isDefaultStr,
|
||||
"state": d.Get("state").(string),
|
||||
},
|
||||
)
|
||||
req.Filters = append(req.Filters, buildEC2TagFilterList(
|
||||
tagsFromMap(d.Get("tags").(map[string]interface{})),
|
||||
)...)
|
||||
req.Filters = append(req.Filters, buildEC2CustomFilterList(
|
||||
d.Get("filter").(*schema.Set),
|
||||
)...)
|
||||
if len(req.Filters) == 0 {
|
||||
// Don't send an empty filters list; the EC2 API won't accept it.
|
||||
req.Filters = nil
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] DescribeVpcs %s\n", req)
|
||||
resp, err := conn.DescribeVpcs(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp == nil || len(resp.Vpcs) == 0 {
|
||||
return fmt.Errorf("no matching VPC found")
|
||||
}
|
||||
if len(resp.Vpcs) > 1 {
|
||||
return fmt.Errorf("multiple VPCs matched; use additional constraints to reduce matches to a single VPC")
|
||||
}
|
||||
|
||||
vpc := resp.Vpcs[0]
|
||||
|
||||
d.SetId(*vpc.VpcId)
|
||||
d.Set("id", vpc.VpcId)
|
||||
d.Set("cidr_block", vpc.CidrBlock)
|
||||
d.Set("dhcp_options_id", vpc.DhcpOptionsId)
|
||||
d.Set("instance_tenancy", vpc.InstanceTenancy)
|
||||
d.Set("default", vpc.IsDefault)
|
||||
d.Set("state", vpc.State)
|
||||
d.Set("tags", tagsToMap(vpc.Tags))
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDataSourceAwsVpc(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDataSourceAwsVpcConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccDataSourceAwsVpcCheck("data.aws_vpc.by_id"),
|
||||
testAccDataSourceAwsVpcCheck("data.aws_vpc.by_cidr"),
|
||||
testAccDataSourceAwsVpcCheck("data.aws_vpc.by_tag"),
|
||||
testAccDataSourceAwsVpcCheck("data.aws_vpc.by_filter"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccDataSourceAwsVpcCheck(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)
|
||||
}
|
||||
|
||||
vpcRs, ok := s.RootModule().Resources["aws_vpc.test"]
|
||||
if !ok {
|
||||
return fmt.Errorf("can't find aws_vpc.test in state")
|
||||
}
|
||||
|
||||
attr := rs.Primary.Attributes
|
||||
|
||||
if attr["id"] != vpcRs.Primary.Attributes["id"] {
|
||||
return fmt.Errorf(
|
||||
"id is %s; want %s",
|
||||
attr["id"],
|
||||
vpcRs.Primary.Attributes["id"],
|
||||
)
|
||||
}
|
||||
|
||||
if attr["cidr_block"] != "172.16.0.0/16" {
|
||||
return fmt.Errorf("bad cidr_block %s", attr["cidr_block"])
|
||||
}
|
||||
if attr["tags.Name"] != "terraform-testacc-vpc-data-source" {
|
||||
return fmt.Errorf("bad Name tag %s", attr["tags.Name"])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccDataSourceAwsVpcConfig = `
|
||||
provider "aws" {
|
||||
region = "us-west-2"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "172.16.0.0/16"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-vpc-data-source"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_vpc" "by_id" {
|
||||
id = "${aws_vpc.test.id}"
|
||||
}
|
||||
|
||||
data "aws_vpc" "by_cidr" {
|
||||
cidr_block = "${aws_vpc.test.cidr_block}"
|
||||
}
|
||||
|
||||
data "aws_vpc" "by_tag" {
|
||||
tags {
|
||||
Name = "${aws_vpc.test.tags["Name"]}"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_vpc" "by_filter" {
|
||||
filter {
|
||||
name = "cidr"
|
||||
values = ["${aws_vpc.test.cidr_block}"]
|
||||
}
|
||||
}
|
||||
`
|
|
@ -0,0 +1,153 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
// buildEC2AttributeFilterList takes a flat map of scalar attributes (most
|
||||
// likely values extracted from a *schema.ResourceData on an EC2-querying
|
||||
// data source) and produces a []*ec2.Filter representing an exact match
|
||||
// for each of the given non-empty attributes.
|
||||
//
|
||||
// The keys of the given attributes map are the attribute names expected
|
||||
// by the EC2 API, which are usually either in camelcase or with dash-separated
|
||||
// words. We conventionally map these to underscore-separated identifiers
|
||||
// with the same words when presenting these as data source query attributes
|
||||
// in Terraform.
|
||||
//
|
||||
// It's the callers responsibility to transform any non-string values into
|
||||
// the appropriate string serialization required by the AWS API when
|
||||
// encoding the given filter. Any attributes given with empty string values
|
||||
// are ignored, assuming that the user wishes to leave that attribute
|
||||
// unconstrained while filtering.
|
||||
//
|
||||
// The purpose of this function is to create values to pass in
|
||||
// for the "Filters" attribute on most of the "Describe..." API functions in
|
||||
// the EC2 API, to aid in the implementation of Terraform data sources that
|
||||
// retrieve data about EC2 objects.
|
||||
func buildEC2AttributeFilterList(attrs map[string]string) []*ec2.Filter {
|
||||
filters := make([]*ec2.Filter, 0, len(attrs))
|
||||
|
||||
for filterName, value := range attrs {
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
filters = append(filters, &ec2.Filter{
|
||||
Name: aws.String(filterName),
|
||||
Values: []*string{aws.String(value)},
|
||||
})
|
||||
}
|
||||
|
||||
return filters
|
||||
}
|
||||
|
||||
// buildEC2TagFilterList takes a []*ec2.Tag and produces a []*ec2.Filter that
|
||||
// represents exact matches for all of the tag key/value pairs given in
|
||||
// the tag set.
|
||||
//
|
||||
// The purpose of this function is to create values to pass in for
|
||||
// the "Filters" attribute on most of the "Describe..." API functions
|
||||
// in the EC2 API, to implement filtering by tag values e.g. in Terraform
|
||||
// data sources that retrieve data about EC2 objects.
|
||||
//
|
||||
// It is conventional for an EC2 data source to include an attribute called
|
||||
// "tags" which conforms to the schema returned by the tagsSchema() function.
|
||||
// The value of this can then be converted to a tags slice using tagsFromMap,
|
||||
// and the result finally passed in to this function.
|
||||
//
|
||||
// In Terraform configuration this would then look like this, to constrain
|
||||
// results by name:
|
||||
//
|
||||
// tags {
|
||||
// Name = "my-awesome-subnet"
|
||||
// }
|
||||
func buildEC2TagFilterList(tags []*ec2.Tag) []*ec2.Filter {
|
||||
filters := make([]*ec2.Filter, len(tags))
|
||||
|
||||
for i, tag := range tags {
|
||||
filters[i] = &ec2.Filter{
|
||||
Name: aws.String(fmt.Sprintf("tag:%s", *tag.Key)),
|
||||
Values: []*string{tag.Value},
|
||||
}
|
||||
}
|
||||
|
||||
return filters
|
||||
}
|
||||
|
||||
// ec2CustomFiltersSchema returns a *schema.Schema that represents
|
||||
// a set of custom filtering criteria that a user can specify as input
|
||||
// to a data source that wraps one of the many "Describe..." API calls
|
||||
// in the EC2 API.
|
||||
//
|
||||
// It is conventional for an attribute of this type to be included
|
||||
// as a top-level attribute called "filter". This is the "catch all" for
|
||||
// filter combinations that are not possible to express using scalar
|
||||
// attributes or tags. In Terraform configuration, the custom filter blocks
|
||||
// then look like this:
|
||||
//
|
||||
// filter {
|
||||
// name = "availabilityZone"
|
||||
// values = ["us-west-2a", "us-west-2b"]
|
||||
// }
|
||||
func ec2CustomFiltersSchema() *schema.Schema {
|
||||
return &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"values": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// buildEC2CustomFilterList takes the set value extracted from a schema
|
||||
// attribute conforming to the schema returned by ec2CustomFiltersSchema,
|
||||
// and transforms it into a []*ec2.Filter representing the same filter
|
||||
// expressions which is ready to pass into the "Filters" attribute on most
|
||||
// of the "Describe..." functions in the EC2 API.
|
||||
//
|
||||
// This function is intended only to be used in conjunction with
|
||||
// ec2CustomFitlersSchema. See the docs on that function for more details
|
||||
// on the configuration pattern this is intended to support.
|
||||
func buildEC2CustomFilterList(filterSet *schema.Set) []*ec2.Filter {
|
||||
if filterSet == nil {
|
||||
return []*ec2.Filter{}
|
||||
}
|
||||
|
||||
customFilters := filterSet.List()
|
||||
filters := make([]*ec2.Filter, len(customFilters))
|
||||
|
||||
for filterIdx, customFilterI := range customFilters {
|
||||
customFilterMapI := customFilterI.(map[string]interface{})
|
||||
name := customFilterMapI["name"].(string)
|
||||
valuesI := customFilterMapI["values"].(*schema.Set).List()
|
||||
values := make([]*string, len(valuesI))
|
||||
for valueIdx, valueI := range valuesI {
|
||||
values[valueIdx] = aws.String(valueI.(string))
|
||||
}
|
||||
|
||||
filters[filterIdx] = &ec2.Filter{
|
||||
Name: &name,
|
||||
Values: values,
|
||||
}
|
||||
}
|
||||
|
||||
return filters
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func TestBuildEC2AttributeFilterList(t *testing.T) {
|
||||
type TestCase struct {
|
||||
Attrs map[string]string
|
||||
Expected []*ec2.Filter
|
||||
}
|
||||
testCases := []TestCase{
|
||||
{
|
||||
map[string]string{
|
||||
"foo": "bar",
|
||||
"baz": "boo",
|
||||
},
|
||||
[]*ec2.Filter{
|
||||
{
|
||||
Name: aws.String("foo"),
|
||||
Values: []*string{aws.String("bar")},
|
||||
},
|
||||
{
|
||||
Name: aws.String("baz"),
|
||||
Values: []*string{aws.String("boo")},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]string{
|
||||
"foo": "bar",
|
||||
"baz": "",
|
||||
},
|
||||
[]*ec2.Filter{
|
||||
{
|
||||
Name: aws.String("foo"),
|
||||
Values: []*string{aws.String("bar")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := buildEC2AttributeFilterList(testCase.Attrs)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.Expected) {
|
||||
t.Errorf(
|
||||
"test case %d: got %#v, but want %#v",
|
||||
i, result, testCase.Expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildEC2TagFilterList(t *testing.T) {
|
||||
type TestCase struct {
|
||||
Tags []*ec2.Tag
|
||||
Expected []*ec2.Filter
|
||||
}
|
||||
testCases := []TestCase{
|
||||
{
|
||||
[]*ec2.Tag{
|
||||
{
|
||||
Key: aws.String("foo"),
|
||||
Value: aws.String("bar"),
|
||||
},
|
||||
{
|
||||
Key: aws.String("baz"),
|
||||
Value: aws.String("boo"),
|
||||
},
|
||||
},
|
||||
[]*ec2.Filter{
|
||||
{
|
||||
Name: aws.String("tag:foo"),
|
||||
Values: []*string{aws.String("bar")},
|
||||
},
|
||||
{
|
||||
Name: aws.String("tag:baz"),
|
||||
Values: []*string{aws.String("boo")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := buildEC2TagFilterList(testCase.Tags)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.Expected) {
|
||||
t.Errorf(
|
||||
"test case %d: got %#v, but want %#v",
|
||||
i, result, testCase.Expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildEC2CustomFilterList(t *testing.T) {
|
||||
|
||||
// We need to get a set with the appropriate hash function,
|
||||
// so we'll use the schema to help us produce what would
|
||||
// be produced in the normal case.
|
||||
filtersSchema := ec2CustomFiltersSchema()
|
||||
|
||||
// The zero value of this schema will be an interface{}
|
||||
// referring to a new, empty *schema.Set with the
|
||||
// appropriate hash function configured.
|
||||
filters := filtersSchema.ZeroValue().(*schema.Set)
|
||||
|
||||
// We also need an appropriately-configured set for
|
||||
// the list of values.
|
||||
valuesSchema := filtersSchema.Elem.(*schema.Resource).Schema["values"]
|
||||
valuesSet := func(vals ...string) *schema.Set {
|
||||
ret := valuesSchema.ZeroValue().(*schema.Set)
|
||||
for _, val := range vals {
|
||||
ret.Add(val)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
filters.Add(map[string]interface{}{
|
||||
"name": "foo",
|
||||
"values": valuesSet("bar", "baz"),
|
||||
})
|
||||
filters.Add(map[string]interface{}{
|
||||
"name": "pizza",
|
||||
"values": valuesSet("cheese"),
|
||||
})
|
||||
|
||||
expected := []*ec2.Filter{
|
||||
// These are produced in the deterministic order guaranteed
|
||||
// by schema.Set.List(), which happens to produce them in
|
||||
// the following order for our current input. If this test
|
||||
// evolves with different input data in future then they
|
||||
// will likely be emitted in a different order, which is fine.
|
||||
{
|
||||
Name: aws.String("pizza"),
|
||||
Values: []*string{aws.String("cheese")},
|
||||
},
|
||||
{
|
||||
Name: aws.String("foo"),
|
||||
Values: []*string{aws.String("bar"), aws.String("baz")},
|
||||
},
|
||||
}
|
||||
result := buildEC2CustomFilterList(filters)
|
||||
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf(
|
||||
"got %#v, but want %#v",
|
||||
result, expected,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -144,6 +144,7 @@ func Provider() terraform.ResourceProvider {
|
|||
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"aws_ami": dataSourceAwsAmi(),
|
||||
"aws_availability_zone": dataSourceAwsAvailabilityZone(),
|
||||
"aws_availability_zones": dataSourceAwsAvailabilityZones(),
|
||||
"aws_billing_service_account": dataSourceAwsBillingServiceAccount(),
|
||||
"aws_caller_identity": dataSourceAwsCallerIdentity(),
|
||||
|
@ -153,7 +154,10 @@ func Provider() terraform.ResourceProvider {
|
|||
"aws_iam_policy_document": dataSourceAwsIamPolicyDocument(),
|
||||
"aws_ip_ranges": dataSourceAwsIPRanges(),
|
||||
"aws_redshift_service_account": dataSourceAwsRedshiftServiceAccount(),
|
||||
"aws_region": dataSourceAwsRegion(),
|
||||
"aws_s3_bucket_object": dataSourceAwsS3BucketObject(),
|
||||
"aws_subnet": dataSourceAwsSubnet(),
|
||||
"aws_vpc": dataSourceAwsVpc(),
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
|
|
|
@ -22,6 +22,14 @@ func tagsSchema() *schema.Schema {
|
|||
}
|
||||
}
|
||||
|
||||
func tagsSchemaComputed() *schema.Schema {
|
||||
return &schema.Schema{
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
}
|
||||
}
|
||||
|
||||
func setElbV2Tags(conn *elbv2.ELBV2, d *schema.ResourceData) error {
|
||||
if d.HasChange("tags") {
|
||||
oraw, nraw := d.GetChange("tags")
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
terraform.tfstate
|
||||
terraform.tfstate.backup
|
||||
.terraform/*
|
|
@ -0,0 +1,11 @@
|
|||
# AWS Networking Example
|
||||
|
||||
This example creates AWS VPC resources, making a VPC in each of two regions and
|
||||
then two subnets in each VPC in two different availability zones.
|
||||
|
||||
This example also demonstrates the use of modules to create several copies of
|
||||
the same resource set with different arguments. The child modules in this
|
||||
directory are:
|
||||
|
||||
* `region`: container module for all of the network resources within a region. This is instantiated once per region.
|
||||
* `subnet`: represents a subnet within a given availability zone. This is instantiated twice per region, using the first two availability zones supported within the target AWS account.
|
|
@ -0,0 +1,27 @@
|
|||
variable "region_numbers" {
|
||||
default = {
|
||||
us-east-1 = 1
|
||||
us-west-1 = 2
|
||||
us-west-2 = 3
|
||||
eu-west-1 = 4
|
||||
}
|
||||
}
|
||||
|
||||
variable "az_numbers" {
|
||||
default = {
|
||||
a = 1
|
||||
b = 2
|
||||
c = 3
|
||||
d = 4
|
||||
e = 5
|
||||
f = 6
|
||||
g = 7
|
||||
h = 8
|
||||
i = 9
|
||||
j = 10
|
||||
k = 11
|
||||
l = 12
|
||||
m = 13
|
||||
n = 14
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../numbering/variables.tf
|
|
@ -0,0 +1,11 @@
|
|||
output "vpc_id" {
|
||||
value = "${aws_vpc.main.id}"
|
||||
}
|
||||
|
||||
output "primary_subnet_id" {
|
||||
value = "${module.primary_subnet.subnet_id}"
|
||||
}
|
||||
|
||||
output "secondary_subnet_id" {
|
||||
value = "${module.secondary_subnet.subnet_id}"
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
resource "aws_security_group" "region" {
|
||||
name = "region"
|
||||
description = "Open access within this region"
|
||||
vpc_id = "${aws_vpc.main.id}"
|
||||
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = -1
|
||||
cidr_blocks = ["${aws_vpc.main.cidr_block}"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_security_group" "internal-all" {
|
||||
name = "internal-all"
|
||||
description = "Open access within the full internal network"
|
||||
vpc_id = "${aws_vpc.main.id}"
|
||||
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = -1
|
||||
cidr_blocks = ["${var.base_cidr_block}"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
data "aws_availability_zones" "all" {
|
||||
}
|
||||
|
||||
module "primary_subnet" {
|
||||
source = "../subnet"
|
||||
vpc_id = "${aws_vpc.main.id}"
|
||||
availability_zone = "${data.aws_availability_zones.all.names[0]}"
|
||||
}
|
||||
|
||||
module "secondary_subnet" {
|
||||
source = "../subnet"
|
||||
vpc_id = "${aws_vpc.main.id}"
|
||||
availability_zone = "${data.aws_availability_zones.all.names[1]}"
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
variable "region" {
|
||||
description = "The name of the AWS region to set up a network within"
|
||||
}
|
||||
|
||||
variable "base_cidr_block" {}
|
||||
|
||||
provider "aws" {
|
||||
region = "${var.region}"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
resource "aws_vpc" "main" {
|
||||
cidr_block = "${cidrsubnet(var.base_cidr_block, 4, lookup(var.region_numbers, var.region))}"
|
||||
}
|
||||
|
||||
resource "aws_internet_gateway" "main" {
|
||||
vpc_id = "${aws_vpc.main.id}"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
module "us-east-1" {
|
||||
source = "./region"
|
||||
region = "us-east-1"
|
||||
base_cidr_block = "${var.base_cidr_block}"
|
||||
}
|
||||
|
||||
module "us-west-2" {
|
||||
source = "./region"
|
||||
region = "us-west-2"
|
||||
base_cidr_block = "${var.base_cidr_block}"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../numbering/variables.tf
|
|
@ -0,0 +1,3 @@
|
|||
output "subnet_id" {
|
||||
value = "${aws_subnet.main.id}"
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
resource "aws_security_group" "az" {
|
||||
name = "az-${data.aws_availability_zone.target.name}"
|
||||
description = "Open access within the AZ ${data.aws_availability_zone.target.name}"
|
||||
vpc_id = "${var.vpc_id}"
|
||||
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = -1
|
||||
cidr_blocks = ["${aws_subnet.main.cidr_block}"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
resource "aws_subnet" "main" {
|
||||
cidr_block = "${cidrsubnet(data.aws_vpc.target.cidr_block, 4, lookup(var.az_numbers, data.aws_availability_zone.target.name_suffix))}"
|
||||
vpc_id = "${var.vpc_id}"
|
||||
}
|
||||
|
||||
resource "aws_route_table" "main" {
|
||||
vpc_id = "${var.vpc_id}"
|
||||
}
|
||||
|
||||
resource "aws_route_table_association" "main" {
|
||||
subnet_id = "${aws_subnet.main.id}"
|
||||
route_table_id = "${aws_route_table.main.id}"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
variable "vpc_id" {}
|
||||
|
||||
variable "availability_zone" {}
|
||||
|
||||
data "aws_availability_zone" "target" {
|
||||
name = "${var.availability_zone}"
|
||||
}
|
||||
|
||||
data "aws_vpc" "target" {
|
||||
id = "${var.vpc_id}"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
variable "base_cidr_block" {
|
||||
default = "10.0.0.0/12"
|
||||
}
|
|
@ -47,14 +47,17 @@ func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
|
|||
diff = new(InstanceDiff)
|
||||
}
|
||||
|
||||
// id is always computed, because we're always "creating a new resource"
|
||||
// if id isn't explicitly set then it's always computed, because we're
|
||||
// always "creating a new resource".
|
||||
diff.init()
|
||||
diff.SetAttribute("id", &ResourceAttrDiff{
|
||||
Old: "",
|
||||
NewComputed: true,
|
||||
RequiresNew: true,
|
||||
Type: DiffAttrOutput,
|
||||
})
|
||||
if _, ok := diff.Attributes["id"]; !ok {
|
||||
diff.SetAttribute("id", &ResourceAttrDiff{
|
||||
Old: "",
|
||||
NewComputed: true,
|
||||
RequiresNew: true,
|
||||
Type: DiffAttrOutput,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
err = ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_availability_zone"
|
||||
sidebar_current: "docs-aws-datasource-availability-zone"
|
||||
description: |-
|
||||
Provides details about a specific availability zone
|
||||
---
|
||||
|
||||
# aws\_availability\_zone
|
||||
|
||||
`aws_availability_zone` provides details about a specific availablity zone (AZ)
|
||||
in the current region.
|
||||
|
||||
This can be used both to validate an availability zone given in a variable
|
||||
and to split the AZ name into its component parts of an AWS region and an
|
||||
AZ identifier letter. The latter may be useful e.g. for implementing a
|
||||
consistent subnet numbering scheme across several regions by mapping both
|
||||
the region and the subnet letter to network numbers.
|
||||
|
||||
This is different from the `aws_availability_zones` (plural) data source,
|
||||
which provides a list of the available zones.
|
||||
|
||||
## Example Usage
|
||||
|
||||
The following example shows how this data source might be used to derive
|
||||
VPC and subnet CIDR prefixes systematically for an availability zone.
|
||||
|
||||
```
|
||||
variable "region_number" {
|
||||
# Arbitrary mapping of region name to number to use in
|
||||
# a VPC's CIDR prefix.
|
||||
default = {
|
||||
us-east-1 = 1
|
||||
us-west-1 = 2
|
||||
us-west-2 = 3
|
||||
eu-central-1 = 4
|
||||
ap-northeast-1 = 5
|
||||
}
|
||||
}
|
||||
|
||||
variable "az_number" {
|
||||
# Assign a number to each AZ letter used in our configuration
|
||||
default = {
|
||||
a = 1
|
||||
b = 2
|
||||
c = 3
|
||||
d = 4
|
||||
e = 5
|
||||
f = 6
|
||||
}
|
||||
}
|
||||
|
||||
# Retrieve the AZ where we want to create network resources
|
||||
# This must be in the region selected on the AWS provider.
|
||||
data "aws_availability_zone" "example" {
|
||||
name = "eu-central-1a"
|
||||
}
|
||||
|
||||
# Create a VPC for the region associated with the AZ
|
||||
resource "aws_vpc" "example" {
|
||||
cidr_block = "${cidrsubnet("10.0.0.0/8", 4, var.region_number[data.aws_availability_zone.example.region])}"
|
||||
}
|
||||
|
||||
# Create a subnet for the AZ within the regional VPC
|
||||
resource "aws_subnet" "example" {
|
||||
vpc_id = "${aws_vpc.example.id}"
|
||||
cidr_block = "${cidrsubnet(aws_vpc.example.cidr_block, 4, var.az_number[data.aws_availability_zone.name_suffix])}"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The arguments of this data source act as filters for querying the available
|
||||
availability zones. The given filters must match exactly one availability
|
||||
zone whose data will be exported as attributes.
|
||||
|
||||
* `name` - (Optional) The full name of the availability zone to select.
|
||||
|
||||
* `state` - (Optional) A specific availability zone state to require. May
|
||||
be any of `"available"`, `"information"`, `"impaired"` or `"available"`.
|
||||
|
||||
All reasonable uses of this data source will specify `name`, since `state`
|
||||
alone would match a single AZ only in a region that itself has only one AZ.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `name` - The name of the selected availability zone.
|
||||
|
||||
* `region` - The region where the selected availability zone resides.
|
||||
This is always the region selected on the provider, since this data source
|
||||
searches only within that region.
|
||||
|
||||
* `name_suffix` - The part of the AZ name that appears after the region name,
|
||||
uniquely identifying the AZ within its region.
|
||||
|
||||
* `state` - The current state of the AZ.
|
|
@ -12,6 +12,9 @@ The Availability Zones data source allows access to the list of AWS
|
|||
Availability Zones which can be accessed by an AWS account within the region
|
||||
configured in the provider.
|
||||
|
||||
This is different from the `aws_availability_zone` (singular) data source,
|
||||
which provides some details about a specific availability zone.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_region"
|
||||
sidebar_current: "docs-aws-datasource-region"
|
||||
description: |-
|
||||
Provides details about a specific service region
|
||||
---
|
||||
|
||||
# aws\_region
|
||||
|
||||
`aws_region` provides details about a specific AWS region.
|
||||
|
||||
As well as validating a given region name (and optionally obtaining its
|
||||
endpoint) this resource can be used to discover the name of the region
|
||||
configured within the provider. The latter can be useful in a child module
|
||||
which is inheriting an AWS provider configuration from its parent module.
|
||||
|
||||
## Example Usage
|
||||
|
||||
The following example shows how the resource might be used to obtain
|
||||
the name of the AWS region configured on the provider.
|
||||
|
||||
```
|
||||
data "aws_region" "current" {
|
||||
current = true
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The arguments of this data source act as filters for querying the available
|
||||
regions. The given filters must match exactly one region whose data will be
|
||||
exported as attributes.
|
||||
|
||||
* `name` - (Optional) The full name of the region to select.
|
||||
|
||||
* `current` - (Optional) Set to `true` to match only the region configured
|
||||
in the provider. (It is not meaningful to set this to `false`.)
|
||||
|
||||
* `endpoint` - (Optional) The endpoint of the region to select.
|
||||
|
||||
At least one of the above attributes should be provided to ensure that only
|
||||
one region is matched.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `name` - The name of the selected region.
|
||||
|
||||
* `current` - `true` if the selected region is the one configured on the
|
||||
provider, or `false` otherwise.
|
||||
|
||||
* `endpoint` - The endpoint for the selected region.
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_subnet"
|
||||
sidebar_current: "docs-aws-datasource-subnet"
|
||||
description: |-
|
||||
Provides details about a specific VPC subnet
|
||||
---
|
||||
|
||||
# aws\_subnet
|
||||
|
||||
`aws_subnet` provides details about a specific VPC subnet.
|
||||
|
||||
This resource can prove useful when a module accepts a subnet id as
|
||||
an input variable and needs to, for example, determine the id of the
|
||||
VPC that the subnet belongs to.
|
||||
|
||||
## Example Usage
|
||||
|
||||
The following example shows how one might accept a subnet id as a variable
|
||||
and use this data source to obtain the data necessary to create a security
|
||||
group that allows connections from hosts in that subnet.
|
||||
|
||||
```
|
||||
variable "subnet_id" {}
|
||||
|
||||
data "aws_subnet" "selected" {
|
||||
id = "${var.subnet_id}"
|
||||
}
|
||||
|
||||
resource "aws_security_group" "subnet" {
|
||||
vpc_id = "${aws_subnet.selected.vpc_id}"
|
||||
|
||||
ingress {
|
||||
cidr_blocks = ["${aws_subnet.selected.cidr_block}"]
|
||||
from_port = 80
|
||||
to_port = 80
|
||||
protocol = "tcp"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The arguments of this data source act as filters for querying the available
|
||||
subnets in the current region. The given filters must match exactly one
|
||||
subnet whose data will be exported as attributes.
|
||||
|
||||
* `availability_zone` - (Optional) The availability zone where the
|
||||
subnet must reside.
|
||||
|
||||
* `cidr_block` - (Optional) The cidr block of the desired subnet.
|
||||
|
||||
* `default_for_az` - (Optional) Boolean constraint for whether the desired
|
||||
subnet must be the default subnet for its associated availability zone.
|
||||
|
||||
* `filter` - (Optional) Custom filter block as described below.
|
||||
|
||||
* `id` - (Optional) The id of the specific subnet to retrieve.
|
||||
|
||||
* `state` - (Optional) The state that the desired subnet must have.
|
||||
|
||||
* `tags` - (Optional) A mapping of tags, each pair of which must exactly match
|
||||
a pair on the desired subnet.
|
||||
|
||||
* `vpc_id` - (Optional) The id of the VPC that the desired subnet belongs to.
|
||||
|
||||
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_DescribeSubnets.html).
|
||||
|
||||
* `values` - (Required) Set of values that are accepted for the given field.
|
||||
A subnet will be selected if any one of the given values matches.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
All of the argument attributes except `filter` 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 subnet.
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_vpc"
|
||||
sidebar_current: "docs-aws-datasource-vpc"
|
||||
description: |-
|
||||
Provides details about a specific VPC
|
||||
---
|
||||
|
||||
# aws\_vpc
|
||||
|
||||
`aws_vpc` provides details about a specific VPC.
|
||||
|
||||
This resource can prove useful when a module accepts a vpc id as
|
||||
an input variable and needs to, for example, determine the CIDR block of that
|
||||
VPC.
|
||||
|
||||
## Example Usage
|
||||
|
||||
The following example shows how one might accept a VPC id as a variable
|
||||
and use this data source to obtain the data necessary to create a subnet
|
||||
within it.
|
||||
|
||||
```
|
||||
variable "vpc_id" {}
|
||||
|
||||
data "aws_vpc" "selected" {
|
||||
id = "${var.vpc_id}"
|
||||
}
|
||||
|
||||
resource "aws_subnet" "example" {
|
||||
vpc_id = "${aws_vpc.selected.id}"
|
||||
availability_zone = "us-west-2a"
|
||||
cidr_block = "${cidrsubnet(aws_vpc.selected.cidr_block, 4, 1)}"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The arguments of this data source act as filters for querying the available
|
||||
VPCs in the current region. The given filters must match exactly one
|
||||
VPC whose data will be exported as attributes.
|
||||
|
||||
* `cidr_block` - (Optional) The cidr block of the desired VPC.
|
||||
|
||||
* `dhcp_options_id` - (Optional) The DHCP options id of the desired VPC.
|
||||
|
||||
* `default` - (Optional) Boolean constraint on whether the desired VPC is
|
||||
the default VPC for the region.
|
||||
|
||||
* `filter` - (Optional) Custom filter block as described below.
|
||||
|
||||
* `id` - (Optional) The id of the specific VPC to retrieve.
|
||||
|
||||
* `state` - (Optional) The current state of the desired VPC.
|
||||
Can be either `"pending"` or `"available"`.
|
||||
|
||||
* `tags` - (Optional) A mapping of tags, each pair of which must exactly match
|
||||
a pair on the desired VPC.
|
||||
|
||||
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_DescribeVpcs.html).
|
||||
|
||||
* `values` - (Required) Set of values that are accepted for the given field.
|
||||
A VPC will be selected if any one of the given values matches.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
All of the argument attributes except `filter` 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 VPC.
|
||||
|
||||
The following attribute is additionally exported:
|
||||
|
||||
* `instance_tenancy` - The allowed tenancy of instances launched into the
|
||||
selected VPC. May be any of `"default"`, `"dedicated"`, or `"host"`.
|
|
@ -17,6 +17,9 @@
|
|||
<li<%= sidebar_current("docs-aws-datasource-ami") %>>
|
||||
<a href="/docs/providers/aws/d/ami.html">aws_ami</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-availability-zone") %>>
|
||||
<a href="/docs/providers/aws/d/availability_zone.html">aws_availability_zone</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-availability-zones") %>>
|
||||
<a href="/docs/providers/aws/d/availability_zones.html">aws_availability_zones</a>
|
||||
</li>
|
||||
|
@ -38,12 +41,21 @@
|
|||
<li<%= sidebar_current("docs-aws-datasource-ip_ranges") %>>
|
||||
<a href="/docs/providers/aws/d/ip_ranges.html">aws_ip_ranges</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-redshift-service-account") %>>
|
||||
<a href="/docs/providers/aws/d/redshift_service_account.html">aws_redshift_service_account</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-redshift-service-account") %>>
|
||||
<a href="/docs/providers/aws/d/redshift_service_account.html">aws_redshift_service_account</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-region") %>>
|
||||
<a href="/docs/providers/aws/d/region.html">aws_region</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-s3-bucket-object") %>>
|
||||
<a href="/docs/providers/aws/d/s3_bucket_object.html">aws_s3_bucket_object</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-subnet") %>>
|
||||
<a href="/docs/providers/aws/d/subnet.html">aws_subnet</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-vpc") %>>
|
||||
<a href="/docs/providers/aws/d/vpc.html">aws_vpc</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
|
Loading…
Reference in New Issue