Skip to content

Commit

Permalink
Add aws_ami_ids and aws_ebs_snapshot_ids resources (#13844)
Browse files Browse the repository at this point in the history
Fixes #12081. Adds new `aws_ami_ids` and `aws_ebs_snapshot_ids` resources.
  • Loading branch information
joshuaspence authored and stack72 committed Apr 21, 2017
1 parent c2a1e68 commit 2aac8fb
Show file tree
Hide file tree
Showing 9 changed files with 411 additions and 2 deletions.
112 changes: 112 additions & 0 deletions builtin/providers/aws/data_source_aws_ami_ids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package aws

import (
"fmt"
"log"
"regexp"

"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceAwsAmiIds() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsAmiIdsRead,

Schema: map[string]*schema.Schema{
"filter": dataSourceFiltersSchema(),
"executable_users": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"name_regex": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateNameRegex,
},
"owners": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"tags": dataSourceTagsSchema(),
"ids": &schema.Schema{
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
}
}

func dataSourceAwsAmiIdsRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

executableUsers, executableUsersOk := d.GetOk("executable_users")
filters, filtersOk := d.GetOk("filter")
nameRegex, nameRegexOk := d.GetOk("name_regex")
owners, ownersOk := d.GetOk("owners")

if executableUsersOk == false && filtersOk == false && nameRegexOk == false && ownersOk == false {
return fmt.Errorf("One of executable_users, filters, name_regex, or owners must be assigned")
}

params := &ec2.DescribeImagesInput{}

if executableUsersOk {
params.ExecutableUsers = expandStringList(executableUsers.([]interface{}))
}
if filtersOk {
params.Filters = buildAwsDataSourceFilters(filters.(*schema.Set))
}
if ownersOk {
o := expandStringList(owners.([]interface{}))

if len(o) > 0 {
params.Owners = o
}
}

resp, err := conn.DescribeImages(params)
if err != nil {
return err
}

var filteredImages []*ec2.Image
imageIds := make([]string, 0)

if nameRegexOk {
r := regexp.MustCompile(nameRegex.(string))
for _, image := range resp.Images {
// Check for a very rare case where the response would include no
// image name. No name means nothing to attempt a match against,
// therefore we are skipping such image.
if image.Name == nil || *image.Name == "" {
log.Printf("[WARN] Unable to find AMI name to match against "+
"for image ID %q owned by %q, nothing to do.",
*image.ImageId, *image.OwnerId)
continue
}
if r.MatchString(*image.Name) {
filteredImages = append(filteredImages, image)
}
}
} else {
filteredImages = resp.Images[:]
}

for _, image := range filteredImages {
imageIds = append(imageIds, *image.ImageId)
}

d.SetId(fmt.Sprintf("%d", hashcode.String(params.String())))
d.Set("ids", imageIds)

return nil
}
58 changes: 58 additions & 0 deletions builtin/providers/aws/data_source_aws_ami_ids_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package aws

import (
"testing"

"github.com/hashicorp/terraform/helper/resource"
)

func TestAccDataSourceAwsAmiIds_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAwsAmiIdsConfig_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsAmiDataSourceID("data.aws_ami_ids.ubuntu"),
),
},
},
})
}

func TestAccDataSourceAwsAmiIds_empty(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAwsAmiIdsConfig_empty,
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsAmiDataSourceID("data.aws_ami_ids.empty"),
resource.TestCheckResourceAttr("data.aws_ami_ids.empty", "ids.#", "0"),
),
},
},
})
}

const testAccDataSourceAwsAmiIdsConfig_basic = `
data "aws_ami_ids" "ubuntu" {
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/ubuntu-*-*-amd64-server-*"]
}
}
`

const testAccDataSourceAwsAmiIdsConfig_empty = `
data "aws_ami_ids" "empty" {
filter {
name = "name"
values = []
}
}
`
78 changes: 78 additions & 0 deletions builtin/providers/aws/data_source_aws_ebs_snapshot_ids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package aws

import (
"fmt"

"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceAwsEbsSnapshotIds() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsEbsSnapshotIdsRead,

Schema: map[string]*schema.Schema{
"filter": dataSourceFiltersSchema(),
"owners": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"restorable_by_user_ids": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"tags": dataSourceTagsSchema(),
"ids": &schema.Schema{
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
}
}

func dataSourceAwsEbsSnapshotIdsRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

restorableUsers, restorableUsersOk := d.GetOk("restorable_by_user_ids")
filters, filtersOk := d.GetOk("filter")
owners, ownersOk := d.GetOk("owners")

if restorableUsers == false && filtersOk == false && ownersOk == false {
return fmt.Errorf("One of filters, restorable_by_user_ids, or owners must be assigned")
}

params := &ec2.DescribeSnapshotsInput{}

if restorableUsersOk {
params.RestorableByUserIds = expandStringList(restorableUsers.([]interface{}))
}
if filtersOk {
params.Filters = buildAwsDataSourceFilters(filters.(*schema.Set))
}
if ownersOk {
params.OwnerIds = expandStringList(owners.([]interface{}))
}

resp, err := conn.DescribeSnapshots(params)
if err != nil {
return err
}

snapshotIds := make([]string, 0)

for _, snapshot := range resp.Snapshots {
snapshotIds = append(snapshotIds, *snapshot.SnapshotId)
}

d.SetId(fmt.Sprintf("%d", hashcode.String(params.String())))
d.Set("ids", snapshotIds)

return nil
}
59 changes: 59 additions & 0 deletions builtin/providers/aws/data_source_aws_ebs_snapshot_ids_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package aws

import (
"testing"

"github.com/hashicorp/terraform/helper/resource"
)

func TestAccDataSourceAwsEbsSnapshotIds_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAwsEbsSnapshotIdsConfig_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsEbsSnapshotDataSourceID("data.aws_ebs_snapshot_ids.test"),
),
},
},
})
}

func TestAccDataSourceAwsEbsSnapshotIds_empty(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAwsEbsSnapshotIdsConfig_empty,
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsEbsSnapshotDataSourceID("data.aws_ebs_snapshot_ids.empty"),
resource.TestCheckResourceAttr("data.aws_ebs_snapshot_ids.empty", "ids.#", "0"),
),
},
},
})
}

const testAccDataSourceAwsEbsSnapshotIdsConfig_basic = `
resource "aws_ebs_volume" "test" {
availability_zone = "us-west-2a"
size = 40
}
resource "aws_ebs_snapshot" "test" {
volume_id = "${aws_ebs_volume.test.id}"
}
data "aws_ebs_snapshot_ids" "test" {
owners = ["self"]
}
`

const testAccDataSourceAwsEbsSnapshotIdsConfig_empty = `
data "aws_ebs_snapshot_ids" "empty" {
owners = ["000000000000"]
}
`
2 changes: 1 addition & 1 deletion builtin/providers/aws/data_source_aws_ebs_snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func testAccCheckAwsEbsSnapshotDataSourceID(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Can't find Volume data source: %s", n)
return fmt.Errorf("Can't find snapshot data source: %s", n)
}

if rs.Primary.ID == "" {
Expand Down
2 changes: 2 additions & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ func Provider() terraform.ResourceProvider {
"aws_alb": dataSourceAwsAlb(),
"aws_alb_listener": dataSourceAwsAlbListener(),
"aws_ami": dataSourceAwsAmi(),
"aws_ami_ids": dataSourceAwsAmiIds(),
"aws_autoscaling_groups": dataSourceAwsAutoscalingGroups(),
"aws_availability_zone": dataSourceAwsAvailabilityZone(),
"aws_availability_zones": dataSourceAwsAvailabilityZones(),
Expand All @@ -172,6 +173,7 @@ func Provider() terraform.ResourceProvider {
"aws_cloudformation_stack": dataSourceAwsCloudFormationStack(),
"aws_db_instance": dataSourceAwsDbInstance(),
"aws_ebs_snapshot": dataSourceAwsEbsSnapshot(),
"aws_ebs_snapshot_ids": dataSourceAwsEbsSnapshotIds(),
"aws_ebs_volume": dataSourceAwsEbsVolume(),
"aws_ecs_cluster": dataSourceAwsEcsCluster(),
"aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(),
Expand Down
3 changes: 2 additions & 1 deletion website/source/docs/providers/aws/d/ami.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ options to narrow down the list AWS returns.

~> **NOTE:** If more or less than a single match is returned by the search,
Terraform will fail. Ensure that your search is specific enough to return
a single AMI ID only, or use `most_recent` to choose the most recent one.
a single AMI ID only, or use `most_recent` to choose the most recent one. If
you want to match multiple AMIs, use the `aws_ami_ids` data source instead.

## Attributes Reference

Expand Down
51 changes: 51 additions & 0 deletions website/source/docs/providers/aws/d/ami_ids.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
layout: "aws"
page_title: "AWS: aws_ami_ids"
sidebar_current: "docs-aws-datasource-ami-ids"
description: |-
Provides a list of AMI IDs.
---

# aws\_ami_ids

Use this data source to get a list of AMI IDs matching the specified criteria.

## Example Usage

```hcl
data "aws_ami_ids" "ubuntu" {
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/ubuntu-*-*-amd64-server-*"]
}
}
```

## Argument Reference

* `executable_users` - (Optional) Limit search to users with *explicit* launch
permission on the image. Valid items are the numeric account ID or `self`.

* `filter` - (Optional) One or more name/value pairs to filter off of. There
are several valid keys, for a full reference, check out
[describe-images in the AWS CLI reference][1].

* `owners` - (Optional) Limit search to specific AMI owners. Valid items are
the numeric account ID, `amazon`, or `self`.

* `name_regex` - (Optional) A regex string to apply to the AMI list returned
by AWS. This allows more advanced filtering not supported from the AWS API.
This filtering is done locally on what AWS returns, and could have a performance
impact if the result is large. It is recommended to combine this with other
options to narrow down the list AWS returns.

~> **NOTE:** At least one of `executable_users`, `filter`, `owners` or
`name_regex` must be specified.

## Attributes Reference

`ids` is set to the list of AMI IDs.

[1]: http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-images.html
Loading

0 comments on commit 2aac8fb

Please sign in to comment.