diff --git a/builtin/providers/aws/data_source_aws_security_group.go b/builtin/providers/aws/data_source_aws_security_group.go new file mode 100644 index 000000000..1ff1f17a4 --- /dev/null +++ b/builtin/providers/aws/data_source_aws_security_group.go @@ -0,0 +1,86 @@ +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 dataSourceAwsSecurityGroup() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsSecurityGroupRead, + + Schema: map[string]*schema.Schema{ + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "filter": ec2CustomFiltersSchema(), + + "id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "tags": tagsSchemaComputed(), + }, + } +} + +func dataSourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + req := &ec2.DescribeSecurityGroupsInput{} + + if id, idExists := d.GetOk("id"); idExists { + req.GroupIds = []*string{aws.String(id.(string))} + } + + req.Filters = buildEC2AttributeFilterList( + map[string]string{ + "group-name": d.Get("name").(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] Describe Security Groups %v\n", req) + resp, err := conn.DescribeSecurityGroups(req) + if err != nil { + return err + } + if resp == nil || len(resp.SecurityGroups) == 0 { + return fmt.Errorf("no matching SecurityGroup found") + } + if len(resp.SecurityGroups) > 1 { + return fmt.Errorf("multiple Security Groups matched; use additional constraints to reduce matches to a single Security Group") + } + + sg := resp.SecurityGroups[0] + + d.SetId(*sg.GroupId) + d.Set("id", sg.VpcId) + d.Set("name", sg.GroupName) + d.Set("description", sg.Description) + d.Set("vpc_id", sg.VpcId) + d.Set("tags", tagsToMap(sg.Tags)) + + return nil +} diff --git a/builtin/providers/aws/data_source_aws_security_group_test.go b/builtin/providers/aws/data_source_aws_security_group_test.go new file mode 100644 index 000000000..3454e6120 --- /dev/null +++ b/builtin/providers/aws/data_source_aws_security_group_test.go @@ -0,0 +1,109 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceAwsSecurityGroup(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceAwsSecurityGroupConfig, + Check: resource.ComposeTestCheckFunc( + testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_id"), + testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_tag"), + testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_filter"), + testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_name"), + ), + }, + }, + }) +} + +func testAccDataSourceAwsSecurityGroupCheck(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) + } + + SGRs, ok := s.RootModule().Resources["aws_security_group.test"] + if !ok { + return fmt.Errorf("can't find aws_security_group.test in state") + } + 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"] != SGRs.Primary.Attributes["id"] { + return fmt.Errorf( + "id is %s; want %s", + attr["id"], + SGRs.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["tags.Name"] != "terraform-testacc-security-group-data-source" { + return fmt.Errorf("bad Name tag %s", attr["tags.Name"]) + } + + return nil + } +} + +const testAccDataSourceAwsSecurityGroupConfig = ` +provider "aws" { + region = "eu-west-1" +} +resource "aws_vpc" "test" { + cidr_block = "172.16.0.0/16" + + tags { + Name = "terraform-testacc-subnet-data-source" + } +} + +resource "aws_security_group" "test" { + vpc_id = "${aws_vpc.test.id}" + name = "security-groupe-name-test" + tags { + Name = "terraform-testacc-security-group-data-source" + } +} + +data "aws_security_group" "by_id" { + id = "${aws_security_group.test.id}" +} + +data "aws_security_group" "by_name" { + name = "${aws_security_group.test.name}" +} +data "aws_security_group" "by_tag" { + tags { + Name = "${aws_security_group.test.tags["Name"]}" + } +} + +data "aws_security_group" "by_filter" { + filter { + name = "group-name" + values = ["${aws_security_group.test.name}"] + } +} +` diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index f552c1341..b7388453c 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -158,6 +158,7 @@ func Provider() terraform.ResourceProvider { "aws_region": dataSourceAwsRegion(), "aws_s3_bucket_object": dataSourceAwsS3BucketObject(), "aws_subnet": dataSourceAwsSubnet(), + "aws_security_group": dataSourceAwsSecurityGroup(), "aws_vpc": dataSourceAwsVpc(), }, diff --git a/website/source/docs/providers/aws/d/security_group.html.markdown b/website/source/docs/providers/aws/d/security_group.html.markdown new file mode 100644 index 000000000..d9662744b --- /dev/null +++ b/website/source/docs/providers/aws/d/security_group.html.markdown @@ -0,0 +1,69 @@ +--- +layout: "aws" +page_title: "AWS: aws_security_group" +sidebar_current: "docs-aws-datasource-security-group" +description: |- + Provides details about a specific Security Group +--- + +# aws\_security\_group + +`aws_security_group` provides details about a specific Security Group. + +This resource can prove useful when a module accepts a Security Group id as +an input variable and needs to, for example, determine the id of the +VPC that the security group belongs to. + +## Example Usage + +The following example shows how one might accept a Security Group id as a variable +and use this data source to obtain the data necessary to create a subnet. + +``` +variable "security_group_id" {} + +data "aws_security_group" "selected" { + id = "${var.security_group}" +} + +resource "aws_subnet" "subnet" { + vpc_id = "${data.aws_security_group.selected.vpc_id}" + cidr_block = "10.0.1.0/24" +} +``` + +## Argument Reference + +The arguments of this data source act as filters for querying the available +security group in the current region. The given filters must match exactly one +security group whose data will be exported as attributes. + + +* `filter` - (Optional) Custom filter block as described below. + +* `id` - (Optional) The id of the specific security group to retrieve. + +* `name` - (Optional) The name that the desired security group must have. + +* `tags` - (Optional) A mapping of tags, each pair of which must exactly match + a pair on the desired security group. + +* `vpc_id` - (Optional) The id of the VPC that the desired security group 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_DescribeSecurityGroups.html). + +* `values` - (Required) Set of values that are accepted for the given field. + A Security Group 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 Security Group. +Additionnaly `description` field is enable. + diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index a0fb0539d..0bb37f2ec 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -53,6 +53,9 @@