Add state filter to aws_availability_zones data source. (#7965)

* Add state filter to aws_availability_zones data source.

This commit adds an ability to filter Availability Zones based on state, where
by default it would only list available zones.

Be advised that this does not always works reliably for an older accounts which
have been created in the pre-VPC era of EC2. These accounts tends to retrieve
availability zones that are not VPC-enabled, thus creation of a custom subnet
within such Availability Zone would result in a failure.

Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>

* Update documentation for aws_availability_zones data source.

Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>

* Do not filter on state by default.

This commit makes the state filter applicable only when set.

Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>
This commit is contained in:
Krzysztof Wilczynski 2016-08-05 09:14:05 +09:00 committed by Paul Stack
parent 256190a84c
commit 19800b8e26
3 changed files with 89 additions and 15 deletions

View File

@ -21,6 +21,11 @@ func dataSourceAwsAvailabilityZones() *schema.Resource {
Computed: true, Computed: true,
Elem: &schema.Schema{Type: schema.TypeString}, Elem: &schema.Schema{Type: schema.TypeString},
}, },
"state": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateStateType,
},
}, },
} }
} }
@ -28,25 +33,55 @@ func dataSourceAwsAvailabilityZones() *schema.Resource {
func dataSourceAwsAvailabilityZonesRead(d *schema.ResourceData, meta interface{}) error { func dataSourceAwsAvailabilityZonesRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn conn := meta.(*AWSClient).ec2conn
log.Printf("[DEBUG] Reading availability zones") log.Printf("[DEBUG] Reading Availability Zones.")
d.SetId(time.Now().UTC().String()) d.SetId(time.Now().UTC().String())
req := &ec2.DescribeAvailabilityZonesInput{DryRun: aws.Bool(false)} request := &ec2.DescribeAvailabilityZonesInput{}
azresp, err := conn.DescribeAvailabilityZones(req)
if err != nil { if v, ok := d.GetOk("state"); ok {
return fmt.Errorf("Error listing availability zones: %s", err) request.Filters = []*ec2.Filter{
&ec2.Filter{
Name: aws.String("state"),
Values: []*string{aws.String(v.(string))},
},
}
} }
raw := make([]string, len(azresp.AvailabilityZones)) log.Printf("[DEBUG] Availability Zones request options: %#v", *request)
for i, v := range azresp.AvailabilityZones {
resp, err := conn.DescribeAvailabilityZones(request)
if err != nil {
return fmt.Errorf("Error fetching Availability Zones: %s", err)
}
raw := make([]string, len(resp.AvailabilityZones))
for i, v := range resp.AvailabilityZones {
raw[i] = *v.ZoneName raw[i] = *v.ZoneName
} }
sort.Strings(raw) sort.Strings(raw)
if err := d.Set("names", raw); err != nil { if err := d.Set("names", raw); err != nil {
return fmt.Errorf("[WARN] Error setting availability zones") return fmt.Errorf("[WARN] Error setting Availability Zones: %s", err)
} }
return nil return nil
} }
func validateStateType(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
validState := map[string]bool{
"available": true,
"information": true,
"impaired": true,
"unavailable": true,
}
if !validState[value] {
errors = append(errors, fmt.Errorf(
"%q contains an invalid Availability Zone state %q. Valid states are: %q, %q, %q and %q.",
k, value, "available", "information", "impaired", "unavailable"))
}
return
}

View File

@ -22,6 +22,12 @@ func TestAccAWSAvailabilityZones_basic(t *testing.T) {
testAccCheckAwsAvailabilityZonesMeta("data.aws_availability_zones.availability_zones"), testAccCheckAwsAvailabilityZonesMeta("data.aws_availability_zones.availability_zones"),
), ),
}, },
resource.TestStep{
Config: testAccCheckAwsAvailabilityZonesStateConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsAvailabilityZoneState("data.aws_availability_zones.state_filter"),
),
},
}, },
}) })
} }
@ -34,7 +40,7 @@ func testAccCheckAwsAvailabilityZonesMeta(n string) resource.TestCheckFunc {
} }
if rs.Primary.ID == "" { if rs.Primary.ID == "" {
return fmt.Errorf("AZ resource ID not set") return fmt.Errorf("AZ resource ID not set.")
} }
actual, err := testAccCheckAwsAvailabilityZonesBuildAvailable(rs.Primary.Attributes) actual, err := testAccCheckAwsAvailabilityZonesBuildAvailable(rs.Primary.Attributes)
@ -51,10 +57,33 @@ func testAccCheckAwsAvailabilityZonesMeta(n string) resource.TestCheckFunc {
} }
} }
func testAccCheckAwsAvailabilityZoneState(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Can't find AZ resource: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("AZ resource ID not set.")
}
if _, ok := rs.Primary.Attributes["state"]; !ok {
return fmt.Errorf("AZs state filter is missing, should be set.")
}
_, err := testAccCheckAwsAvailabilityZonesBuildAvailable(rs.Primary.Attributes)
if err != nil {
return err
}
return nil
}
}
func testAccCheckAwsAvailabilityZonesBuildAvailable(attrs map[string]string) ([]string, error) { func testAccCheckAwsAvailabilityZonesBuildAvailable(attrs map[string]string) ([]string, error) {
v, ok := attrs["names.#"] v, ok := attrs["names.#"]
if !ok { if !ok {
return nil, fmt.Errorf("Available AZ list is missing") return nil, fmt.Errorf("Available AZ list is missing.")
} }
qty, err := strconv.Atoi(v) qty, err := strconv.Atoi(v)
if err != nil { if err != nil {
@ -67,7 +96,7 @@ func testAccCheckAwsAvailabilityZonesBuildAvailable(attrs map[string]string) ([]
for n := range zones { for n := range zones {
zone, ok := attrs["names."+strconv.Itoa(n)] zone, ok := attrs["names."+strconv.Itoa(n)]
if !ok { if !ok {
return nil, fmt.Errorf("AZ list corrupt, this is definitely a bug") return nil, fmt.Errorf("AZ list corrupt, this is definitely a bug.")
} }
zones[n] = zone zones[n] = zone
} }
@ -75,6 +104,11 @@ func testAccCheckAwsAvailabilityZonesBuildAvailable(attrs map[string]string) ([]
} }
const testAccCheckAwsAvailabilityZonesConfig = ` const testAccCheckAwsAvailabilityZonesConfig = `
data "aws_availability_zones" "availability_zones" { data "aws_availability_zones" "availability_zones" { }
`
const testAccCheckAwsAvailabilityZonesStateConfig = `
data "aws_availability_zones" "state_filter" {
state = "available"
} }
` `

View File

@ -3,7 +3,7 @@ layout: "aws"
page_title: "AWS: aws_availability_zones" page_title: "AWS: aws_availability_zones"
sidebar_current: "docs-aws-datasource-availability-zones" sidebar_current: "docs-aws-datasource-availability-zones"
description: |- description: |-
Provides a list of availability zones which can be used by an AWS account Provides a list of Availability Zones which can be used by an AWS account.
--- ---
# aws\_availability\_zones # aws\_availability\_zones
@ -35,10 +35,15 @@ resource "aws_subnet" "secondary" {
## Argument Reference ## Argument Reference
There are no arguments for this data source. The following arguments are supported:
* `state` - (Optional) Allows to filter list of Availability Zones based on their
current state. Can be either `"available"`, `"information"`, `"impaired"` or
`"unavailable"`. By default the list includes a complete set of Availability Zones
to which the underlying AWS account has access, regardless of their state.
## Attributes Reference ## Attributes Reference
The following attributes are exported: The following attributes are exported:
* `names` - A list of the availability zone names available to the account. * `names` - A list of the Availability Zone names available to the account.