provider/aws: availability zone data source

This adds a singular data source in addition to the existing plural one.
This allows retrieving data about a specific AZ.

As a helper for writing reusable modules, the AZ letter (without its
usual region name prefix) is exposed so that it can be used in
region-agnostic mappings where a different value is used per AZ, such as
for subnet numbering schemes.
This commit is contained in:
Martin Atkins 2016-05-22 10:21:02 -07:00
parent aa0b6019f8
commit fca9216f53
6 changed files with 251 additions and 0 deletions

View File

@ -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
}

View File

@ -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"
}
`

View File

@ -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_caller_identity": dataSourceAwsCallerIdentity(),
"aws_cloudformation_stack": dataSourceAwsCloudFormationStack(),

View File

@ -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.

View File

@ -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
```

View File

@ -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>