diff --git a/aws/data_source_aws_ec2_managed_prefix_list.go b/aws/data_source_aws_ec2_managed_prefix_list.go new file mode 100644 index 00000000000..cbee0c9e144 --- /dev/null +++ b/aws/data_source_aws_ec2_managed_prefix_list.go @@ -0,0 +1,145 @@ +package aws + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func dataSourceAwsEc2ManagedPrefixList() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceAwsEc2ManagedPrefixListRead, + Schema: map[string]*schema.Schema{ + "address_family": { + Type: schema.TypeString, + Computed: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "entries": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "filter": dataSourceFiltersSchema(), + "id": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "max_entries": { + Type: schema.TypeInt, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "owner_id": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tagsSchemaComputed(), + "version": { + Type: schema.TypeInt, + Computed: true, + }, + }, + } +} + +func dataSourceAwsEc2ManagedPrefixListRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).ec2conn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + input := ec2.DescribeManagedPrefixListsInput{} + + if filters, ok := d.GetOk("filter"); ok { + input.Filters = buildAwsDataSourceFilters(filters.(*schema.Set)) + } + + if prefixListId, ok := d.GetOk("id"); ok { + input.PrefixListIds = aws.StringSlice([]string{prefixListId.(string)}) + } + + if prefixListName, ok := d.GetOk("name"); ok { + input.Filters = append(input.Filters, &ec2.Filter{ + Name: aws.String("prefix-list-name"), + Values: aws.StringSlice([]string{prefixListName.(string)}), + }) + } + + out, err := conn.DescribeManagedPrefixListsWithContext(ctx, &input) + + if err != nil { + return diag.Errorf("error describing EC2 Managed Prefix Lists: %s", err) + } + + if len(out.PrefixLists) < 1 { + return diag.Errorf("no managed prefix lists matched the given criteria") + } + + if len(out.PrefixLists) > 1 { + return diag.Errorf("more than 1 prefix list matched the given criteria") + } + + pl := *out.PrefixLists[0] + + d.SetId(aws.StringValue(pl.PrefixListId)) + d.Set("name", pl.PrefixListName) + d.Set("owner_id", pl.OwnerId) + d.Set("address_family", pl.AddressFamily) + d.Set("arn", pl.PrefixListArn) + d.Set("max_entries", pl.MaxEntries) + d.Set("version", pl.Version) + + if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(pl.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return diag.Errorf("error setting tags attribute: %s", err) + } + + var entries []interface{} + + err = conn.GetManagedPrefixListEntriesPages( + &ec2.GetManagedPrefixListEntriesInput{ + PrefixListId: pl.PrefixListId, + }, + func(output *ec2.GetManagedPrefixListEntriesOutput, last bool) bool { + for _, entry := range output.Entries { + entries = append(entries, map[string]interface{}{ + "cidr": aws.StringValue(entry.Cidr), + "description": aws.StringValue(entry.Description), + }) + } + + return true + }, + ) + + if err != nil { + return diag.Errorf("error listing EC2 Managed Prefix List (%s) entries: %s", d.Id(), err) + } + + if err := d.Set("entries", entries); err != nil { + return diag.FromErr(err) + } + + return nil +} diff --git a/aws/data_source_aws_ec2_managed_prefix_list_test.go b/aws/data_source_aws_ec2_managed_prefix_list_test.go new file mode 100644 index 00000000000..332e1259c45 --- /dev/null +++ b/aws/data_source_aws_ec2_managed_prefix_list_test.go @@ -0,0 +1,165 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func testAccDataSourceAwsEc2ManagedPrefixListGetIdByName(name string, id *string, arn *string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + output, err := conn.DescribeManagedPrefixLists(&ec2.DescribeManagedPrefixListsInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("prefix-list-name"), + Values: aws.StringSlice([]string{name}), + }, + }, + }) + + if err != nil { + return err + } + + *id = *output.PrefixLists[0].PrefixListId + *arn = *output.PrefixLists[0].PrefixListArn + return nil + } +} + +func TestAccDataSourceAwsEc2ManagedPrefixList_basic(t *testing.T) { + prefixListName := fmt.Sprintf("com.amazonaws.%s.s3", testAccGetRegion()) + prefixListId := "" + prefixListArn := "" + + resourceByName := "data.aws_ec2_managed_prefix_list.s3_by_name" + resourceById := "data.aws_ec2_managed_prefix_list.s3_by_id" + prefixListResourceName := "data.aws_prefix_list.s3_by_id" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsEc2ManagedPrefixListConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccDataSourceAwsEc2ManagedPrefixListGetIdByName(prefixListName, &prefixListId, &prefixListArn), + + resource.TestCheckResourceAttrPtr(resourceByName, "id", &prefixListId), + resource.TestCheckResourceAttr(resourceByName, "name", prefixListName), + resource.TestCheckResourceAttr(resourceByName, "owner_id", "AWS"), + resource.TestCheckResourceAttr(resourceByName, "address_family", "IPv4"), + resource.TestCheckResourceAttrPtr(resourceByName, "arn", &prefixListArn), + resource.TestCheckResourceAttr(resourceByName, "max_entries", "0"), + resource.TestCheckResourceAttr(resourceByName, "version", "0"), + resource.TestCheckResourceAttr(resourceByName, "tags.%", "0"), + + resource.TestCheckResourceAttrPtr(resourceById, "id", &prefixListId), + resource.TestCheckResourceAttr(resourceById, "name", prefixListName), + + resource.TestCheckResourceAttrPair(resourceByName, "id", prefixListResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceByName, "name", prefixListResourceName, "name"), + resource.TestCheckResourceAttrPair(resourceByName, "entries.#", prefixListResourceName, "cidr_blocks.#"), + ), + }, + }, + }) +} + +const testAccDataSourceAwsEc2ManagedPrefixListConfig_basic = ` +data "aws_region" "current" {} + +data "aws_ec2_managed_prefix_list" "s3_by_name" { + name = "com.amazonaws.${data.aws_region.current.name}.s3" +} + +data "aws_ec2_managed_prefix_list" "s3_by_id" { + id = data.aws_ec2_managed_prefix_list.s3_by_name.id +} + +data "aws_prefix_list" "s3_by_id" { + prefix_list_id = data.aws_ec2_managed_prefix_list.s3_by_name.id +} +` + +func TestAccDataSourceAwsEc2ManagedPrefixList_filter(t *testing.T) { + prefixListName := fmt.Sprintf("com.amazonaws.%s.s3", testAccGetRegion()) + prefixListId := "" + prefixListArn := "" + + resourceByName := "data.aws_ec2_managed_prefix_list.s3_by_name" + resourceById := "data.aws_ec2_managed_prefix_list.s3_by_id" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsEc2ManagedPrefixListConfig_filter, + Check: resource.ComposeTestCheckFunc( + testAccDataSourceAwsEc2ManagedPrefixListGetIdByName(prefixListName, &prefixListId, &prefixListArn), + resource.TestCheckResourceAttrPtr(resourceByName, "id", &prefixListId), + resource.TestCheckResourceAttr(resourceByName, "name", prefixListName), + resource.TestCheckResourceAttr(resourceByName, "owner_id", "AWS"), + resource.TestCheckResourceAttr(resourceByName, "address_family", "IPv4"), + resource.TestCheckResourceAttrPtr(resourceByName, "arn", &prefixListArn), + resource.TestCheckResourceAttr(resourceByName, "max_entries", "0"), + resource.TestCheckResourceAttr(resourceByName, "version", "0"), + resource.TestCheckResourceAttr(resourceByName, "tags.%", "0"), + + resource.TestCheckResourceAttrPair(resourceByName, "id", resourceById, "id"), + resource.TestCheckResourceAttrPair(resourceByName, "name", resourceById, "name"), + resource.TestCheckResourceAttrPair(resourceByName, "entries", resourceById, "entries"), + resource.TestCheckResourceAttrPair(resourceByName, "owner_id", resourceById, "owner_id"), + resource.TestCheckResourceAttrPair(resourceByName, "address_family", resourceById, "address_family"), + resource.TestCheckResourceAttrPair(resourceByName, "arn", resourceById, "arn"), + resource.TestCheckResourceAttrPair(resourceByName, "max_entries", resourceById, "max_entries"), + resource.TestCheckResourceAttrPair(resourceByName, "tags", resourceById, "tags"), + resource.TestCheckResourceAttrPair(resourceByName, "version", resourceById, "version"), + ), + }, + }, + }) +} + +const testAccDataSourceAwsEc2ManagedPrefixListConfig_filter = ` +data "aws_region" "current" {} + +data "aws_ec2_managed_prefix_list" "s3_by_name" { + filter { + name = "prefix-list-name" + values = ["com.amazonaws.${data.aws_region.current.name}.s3"] + } +} + +data "aws_ec2_managed_prefix_list" "s3_by_id" { + filter { + name = "prefix-list-id" + values = [data.aws_ec2_managed_prefix_list.s3_by_name.id] + } +} +` + +func TestAccDataSourceAwsEc2ManagedPrefixList_matchesTooMany(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsPrefixListConfig_matchesTooMany, + ExpectError: regexp.MustCompile(`more than 1 prefix list matched the given criteria`), + }, + }, + }) +} + +const testAccDataSourceAwsPrefixListConfig_matchesTooMany = ` +data "aws_ec2_managed_prefix_list" "test" {} +` diff --git a/aws/provider.go b/aws/provider.go index 79d050c6a0a..f8bcf7751e6 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -225,6 +225,7 @@ func Provider() *schema.Provider { "aws_ec2_local_gateway_virtual_interface": dataSourceAwsEc2LocalGatewayVirtualInterface(), "aws_ec2_local_gateway_virtual_interface_group": dataSourceAwsEc2LocalGatewayVirtualInterfaceGroup(), "aws_ec2_local_gateway_virtual_interface_groups": dataSourceAwsEc2LocalGatewayVirtualInterfaceGroups(), + "aws_ec2_managed_prefix_list": dataSourceAwsEc2ManagedPrefixList(), "aws_ec2_spot_price": dataSourceAwsEc2SpotPrice(), "aws_ec2_transit_gateway": dataSourceAwsEc2TransitGateway(), "aws_ec2_transit_gateway_dx_gateway_attachment": dataSourceAwsEc2TransitGatewayDxGatewayAttachment(), diff --git a/website/docs/d/ec2_managed_prefix_list.html.markdown b/website/docs/d/ec2_managed_prefix_list.html.markdown new file mode 100644 index 00000000000..63986d99d47 --- /dev/null +++ b/website/docs/d/ec2_managed_prefix_list.html.markdown @@ -0,0 +1,65 @@ +--- +subcategory: "VPC" +layout: "aws" +page_title: "AWS: aws_ec2_managed_prefix_list" +description: |- + Provides details about a specific managed prefix list +--- + +# Data Source: aws_ec2_managed_prefix_list + +`aws_ec2_managed_prefix_list` provides details about a specific AWS prefix list or +customer-managed prefix list in the current region. + +## Example Usage + +### Find the regional DynamoDB prefix list + +```hcl +data "aws_region" "current" {} + +data "aws_ec2_managed_prefix_list" "example" { + name = "com.amazonaws.${data.aws_region.current.name}.dynamodb" +} +``` + +### Find a managed prefix list using filters + +```hcl +data "aws_ec2_managed_prefix_list" "example" { + filter { + name = "prefix-list-name" + values = ["my-prefix-list"] + } +} +``` + +## Argument Reference + +The arguments of this data source act as filters for querying the available +prefix lists. The given filters must match exactly one prefix list +whose data will be exported as attributes. + +* `id` - (Optional) The ID of the prefix list to select. +* `name` - (Optional) The name of the prefix list to select. +* `filter` - (Optional) Configuration block(s) for filtering. Detailed below. + +### filter Configuration Block + +The following arguments are supported by the `filter` configuration block: + +* `name` - (Required) The name of the filter field. Valid values can be found in the EC2 [DescribeManagedPrefixLists](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeManagedPrefixLists.html) API Reference. +* `values` - (Required) Set of values that are accepted for the given filter field. Results will be selected if any given value matches. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the selected prefix list. +* `arn` - The ARN of the selected prefix list. +* `name` - The name of the selected prefix list. +* `entries` - The set of entries in this prefix list. Each entry is an object with `cidr` and `description`. +* `owner_id` - The Account ID of the owner of a customer-managed prefix list, or `AWS` otherwise. +* `address_family` - The address family of the prefix list. Valid values are `IPv4` and `IPv6`. +* `max_entries` - When then prefix list is managed, the maximum number of entries it supports, or null otherwise. +* `tags` - A map of tags assigned to the resource.