From ccf26c7ac5754a9ef6eabd76c0860e2b73d660f1 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 13 Nov 2017 15:55:46 +0000 Subject: [PATCH] New Data Source: aws_instances --- aws/data_source_aws_instances.go | 112 ++++++++++++++++++++++++ aws/data_source_aws_instances_test.go | 115 +++++++++++++++++++++++++ aws/provider.go | 1 + website/aws.erb | 3 + website/docs/d/instances.html.markdown | 55 ++++++++++++ 5 files changed, 286 insertions(+) create mode 100644 aws/data_source_aws_instances.go create mode 100644 aws/data_source_aws_instances_test.go create mode 100644 website/docs/d/instances.html.markdown diff --git a/aws/data_source_aws_instances.go b/aws/data_source_aws_instances.go new file mode 100644 index 000000000000..52bff321a025 --- /dev/null +++ b/aws/data_source_aws_instances.go @@ -0,0 +1,112 @@ +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/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAwsInstances() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsInstancesRead, + + Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), + "instance_tags": tagsSchemaComputed(), + + "ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "private_ips": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "public_ips": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func dataSourceAwsInstancesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + filters, filtersOk := d.GetOk("filter") + tags, tagsOk := d.GetOk("instance_tags") + + if !filtersOk && !tagsOk { + return fmt.Errorf("One of filters or instance_tags must be assigned") + } + + params := &ec2.DescribeInstancesInput{ + Filters: []*ec2.Filter{ + &ec2.Filter{ + Name: aws.String("instance-state-name"), + Values: []*string{aws.String("running")}, + }, + }, + } + if filtersOk { + params.Filters = append(params.Filters, + buildAwsDataSourceFilters(filters.(*schema.Set))...) + } + if tagsOk { + params.Filters = append(params.Filters, buildEC2TagFilterList( + tagsFromMap(tags.(map[string]interface{})), + )...) + } + + log.Printf("[INFO] Describing EC2 instances: %s", params) + + var instanceIds, privateIps, publicIps []string + err := conn.DescribeInstancesPages(params, func(resp *ec2.DescribeInstancesOutput, isLast bool) bool { + for _, res := range resp.Reservations { + for _, instance := range res.Instances { + instanceIds = append(instanceIds, *instance.InstanceId) + if instance.PrivateIpAddress != nil { + privateIps = append(privateIps, *instance.PrivateIpAddress) + } + if instance.PublicIpAddress != nil { + publicIps = append(publicIps, *instance.PublicIpAddress) + } + } + } + return !isLast + }) + if err != nil { + return err + } + + if len(instanceIds) < 1 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + + log.Printf("[DEBUG] Found %d instances via given filter", len(instanceIds)) + + d.SetId(resource.UniqueId()) + err = d.Set("ids", instanceIds) + if err != nil { + return err + } + + err = d.Set("private_ips", privateIps) + if err != nil { + return err + } + + err = d.Set("public_ips", publicIps) + if err != nil { + return err + } + + return nil +} diff --git a/aws/data_source_aws_instances_test.go b/aws/data_source_aws_instances_test.go new file mode 100644 index 000000000000..2dd92bfa5b10 --- /dev/null +++ b/aws/data_source_aws_instances_test.go @@ -0,0 +1,115 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAWSInstancesDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccInstancesDataSourceConfig_ids, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.aws_instances.test", "ids.#", "3"), + resource.TestCheckResourceAttr("data.aws_instances.test", "private_ips.#", "3"), + resource.TestCheckResourceAttr("data.aws_instances.test", "public_ips.#", "3"), + ), + }, + }, + }) +} + +func TestAccAWSInstancesDataSource_tags(t *testing.T) { + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccInstancesDataSourceConfig_tags(rInt), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.aws_instances.test", "ids.#", "5"), + resource.TestCheckResourceAttr("data.aws_instances.test", "private_ips.#", "5"), + resource.TestCheckResourceAttr("data.aws_instances.test", "public_ips.#", "5"), + ), + }, + }, + }) +} + +const testAccInstancesDataSourceConfig_ids = ` +data "aws_ami" "ubuntu" { + most_recent = true + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + owners = ["099720109477"] # Canonical +} + +resource "aws_instance" "test" { + count = 3 + ami = "${data.aws_ami.ubuntu.id}" + instance_type = "t2.micro" + tags { + Name = "TfAccTest" + } +} + +data "aws_instances" "test" { + filter { + name = "instance-id" + values = ["${aws_instance.test.*.id}"] + } +} +` + +func testAccInstancesDataSourceConfig_tags(rInt int) string { + return fmt.Sprintf(` +data "aws_ami" "ubuntu" { + most_recent = true + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + owners = ["099720109477"] # Canonical +} + +resource "aws_instance" "test" { + count = 5 + ami = "${data.aws_ami.ubuntu.id}" + instance_type = "t2.micro" + tags { + Name = "TfAccTest-HelloWorld" + TestSeed = "%[1]d" + } +} + +data "aws_instances" "test" { + instance_tags { + Name = "${aws_instance.test.0.tags["Name"]}" + TestSeed = "%[1]d" + } +} +`, rInt) +} diff --git a/aws/provider.go b/aws/provider.go index 219efe2fe332..2f75284e220b 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -198,6 +198,7 @@ func Provider() terraform.ResourceProvider { "aws_iam_user": dataSourceAwsIAMUser(), "aws_internet_gateway": dataSourceAwsInternetGateway(), "aws_instance": dataSourceAwsInstance(), + "aws_instances": dataSourceAwsInstances(), "aws_ip_ranges": dataSourceAwsIPRanges(), "aws_kinesis_stream": dataSourceAwsKinesisStream(), "aws_kms_alias": dataSourceAwsKmsAlias(), diff --git a/website/aws.erb b/website/aws.erb index 71453affcc69..7c085d7b6467 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -134,6 +134,9 @@ > aws_instance + > + aws_instances + > aws_internet_gateway diff --git a/website/docs/d/instances.html.markdown b/website/docs/d/instances.html.markdown new file mode 100644 index 000000000000..a3ccb0b9f58c --- /dev/null +++ b/website/docs/d/instances.html.markdown @@ -0,0 +1,55 @@ +--- +layout: "aws" +page_title: "AWS: aws_instances" +sidebar_current: "docs-aws-datasource-instances" +description: |- + Get information on an Amazon EC2 instances. +--- + +# aws_instances + +Use this data source to get IDs or IPs of Amazon EC2 instances to be referenced elsewhere, +e.g. to allow easier migration from another management solution +or to make it easier for an operator to connect through bastion host(s). + +-> **Note:** It's a best practice to expose instance details via [outputs](https://www.terraform.io/docs/configuration/outputs.html) +and [remote state](https://www.terraform.io/docs/state/remote.html) and +**use [`terraform_remote_state`](https://www.terraform.io/docs/providers/terraform/d/remote_state.html) +data source instead** if you manage referenced instances via Terraform. + +~> **Note:** It's strongly discouraged to use this data source for querying ephemeral +instances (e.g. managed via autoscaling group), as the output may change at any time +and you'd need to re-run `apply` every time an instance comes up or dies. + +## Example Usage + +```hcl +data "aws_instances" "test" { + instance_tags { + Role = "HardWorker" + } +} + +resource "aws_eip" "test" { + count = "${length(data.aws_instances.test.ids)}" + instance = "${data.aws_instances.test.ids[count.index]}" +} +``` + +## Argument Reference + +* `instance_tags` - (Optional) A mapping of tags, each pair of which must +exactly match a pair on desired instances. + +* `filter` - (Optional) One or more name/value pairs to use as filters. There are +several valid keys, for a full reference, check out +[describe-instances in the AWS CLI reference][1]. + +## Attributes Reference + +* `ids` - IDs of instances found through the filter +* `private_ips` - Private IP addresses of instances found through the filter +* `public_ips` - Public IP addresses of instances found through the filter + + +[1]: http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html