diff --git a/.changelog/40459.txt b/.changelog/40459.txt new file mode 100644 index 000000000000..89070e4913d5 --- /dev/null +++ b/.changelog/40459.txt @@ -0,0 +1,7 @@ +```release-note:new-data-source +aws_vpc_ipam +``` + +```release-note:new-data-source +aws_vpc_ipams +``` \ No newline at end of file diff --git a/internal/service/ec2/service_package_gen.go b/internal/service/ec2/service_package_gen.go index 9efe167be706..aa4cdc672184 100644 --- a/internal/service/ec2/service_package_gen.go +++ b/internal/service/ec2/service_package_gen.go @@ -24,6 +24,17 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv TypeName: "aws_spot_datafeed_subscription", Name: "Spot Data Feed Subscription Data Source", }, + { + Factory: newVPCIPAMDataSource, + TypeName: "aws_vpc_ipam", + Name: "IPAM", + Tags: &types.ServicePackageResourceTags{}, + }, + { + Factory: newVPCIPAMsDataSource, + TypeName: "aws_vpc_ipams", + Name: "IPAMs", + }, { Factory: newSecurityGroupRuleDataSource, TypeName: "aws_vpc_security_group_rule", diff --git a/internal/service/ec2/vpc_ipam_data_source.go b/internal/service/ec2/vpc_ipam_data_source.go new file mode 100644 index 000000000000..4ada1ff7b766 --- /dev/null +++ b/internal/service/ec2/vpc_ipam_data_source.go @@ -0,0 +1,148 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ec2 + +import ( + "context" + + awstypes "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkDataSource("aws_vpc_ipam", name="IPAM") +// @Tags +// @Testing(tagsTest=false) +func newVPCIPAMDataSource(context.Context) (datasource.DataSourceWithConfigure, error) { + return &dataSourceVPCIPAM{}, nil +} + +const ( + DSNameVPCIPAM = "IPAM Data Source" +) + +type dataSourceVPCIPAM struct { + framework.DataSourceWithConfigure +} + +func (d *dataSourceVPCIPAM) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { // nosemgrep:ci.meta-in-func-name + resp.TypeName = "aws_vpc_ipam" +} + +func (d *dataSourceVPCIPAM) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + names.AttrARN: framework.ARNAttributeComputedOnly(), + "default_resource_discovery_association_id": schema.StringAttribute{ + Computed: true, + }, + "default_resource_discovery_id": schema.StringAttribute{ + Computed: true, + }, + names.AttrDescription: schema.StringAttribute{ + Computed: true, + }, + "enable_private_gua": schema.BoolAttribute{ + Computed: true, + }, + names.AttrID: schema.StringAttribute{ + Required: true, + }, + "ipam_region": schema.StringAttribute{ + Computed: true, + }, + "operating_regions": framework.DataSourceComputedListOfObjectAttribute[ipamOperatingRegionModel](ctx), + names.AttrOwnerID: schema.StringAttribute{ + Computed: true, + }, + "private_default_scope_id": schema.StringAttribute{ + Computed: true, + }, + "public_default_scope_id": schema.StringAttribute{ + Computed: true, + }, + "resource_discovery_association_count": schema.Int32Attribute{ + Computed: true, + }, + "scope_count": schema.Int32Attribute{ + Computed: true, + }, + names.AttrState: schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.IpamState](), + Computed: true, + }, + "state_message": schema.StringAttribute{ + Computed: true, + }, + names.AttrTags: tftags.TagsAttributeComputedOnly(), + "tier": schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.IpamTier](), + Computed: true, + }, + }, + } +} + +func (d *dataSourceVPCIPAM) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + conn := d.Meta().EC2Client(ctx) + + var data dataSourceVPCIPAMModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + ipam, err := findIPAMByID(ctx, conn, data.IpamId.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionReading, DSNameVPCIPAM, data.IpamId.String(), err), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, ipam, &data)...) + if resp.Diagnostics.HasError() { + return + } + + setTagsOut(ctx, ipam.Tags) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +type dataSourceVPCIPAMSummaryModel struct { + Description types.String `tfsdk:"description"` + DefaultResourceDiscoveryAssociationId types.String `tfsdk:"default_resource_discovery_association_id"` + DefaultResourceDiscoveryId types.String `tfsdk:"default_resource_discovery_id"` + EnablePrivateGua types.Bool `tfsdk:"enable_private_gua"` + IpamArn types.String `tfsdk:"arn"` + IpamId types.String `tfsdk:"id"` + IpamRegion types.String `tfsdk:"ipam_region"` + OperatingRegions fwtypes.ListNestedObjectValueOf[ipamOperatingRegionModel] `tfsdk:"operating_regions"` + OwnerID types.String `tfsdk:"owner_id"` + PrivateDefaultScopeId types.String `tfsdk:"private_default_scope_id"` + PublicDefaultScopeId types.String `tfsdk:"public_default_scope_id"` + ResourceDiscoveryAssociationCount types.Int32 `tfsdk:"resource_discovery_association_count"` + ScopeCount types.Int32 `tfsdk:"scope_count"` + State fwtypes.StringEnum[awstypes.IpamState] `tfsdk:"state"` + StateMessage types.String `tfsdk:"state_message"` + Tier fwtypes.StringEnum[awstypes.IpamTier] `tfsdk:"tier"` +} + +type dataSourceVPCIPAMModel struct { + dataSourceVPCIPAMSummaryModel + Tags tftags.Map `tfsdk:"tags"` +} + +type ipamOperatingRegionModel struct { + RegionName types.String `tfsdk:"region_name"` +} diff --git a/internal/service/ec2/vpc_ipam_data_source_test.go b/internal/service/ec2/vpc_ipam_data_source_test.go new file mode 100644 index 000000000000..09ff3eceafae --- /dev/null +++ b/internal/service/ec2/vpc_ipam_data_source_test.go @@ -0,0 +1,69 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ec2_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccVPCIPAMDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_vpc_ipam.test" + dataSourceName := "data.aws_vpc_ipam.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccVPCIPAMDataSourceConfig_basic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrARN, resourceName, names.AttrARN), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrDescription, resourceName, names.AttrDescription), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrID, resourceName, names.AttrID), + resource.TestCheckResourceAttrPair(dataSourceName, "default_resource_discovery_id", resourceName, "default_resource_discovery_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "default_resource_discovery_association_id", resourceName, "default_resource_discovery_association_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "enable_private_gua", resourceName, "enable_private_gua"), + resource.TestCheckResourceAttrPair(dataSourceName, "operating_regions.0.region_name", resourceName, "operating_regions.0.region_name"), + resource.TestCheckResourceAttrPair(dataSourceName, "private_default_scope_id", resourceName, "private_default_scope_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "public_default_scope_id", resourceName, "public_default_scope_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "scope_count", resourceName, "scope_count"), + resource.TestCheckResourceAttrPair(dataSourceName, "tier", resourceName, "tier"), + resource.TestCheckResourceAttrPair(dataSourceName, acctest.CtTagsPercent, resourceName, acctest.CtTagsPercent), + resource.TestCheckResourceAttrPair(dataSourceName, "tags.Test", resourceName, "tags.Test"), + ), + }, + }, + }) +} + +func testAccVPCIPAMDataSourceConfig_basic() string { + return ` +data "aws_region" "current" {} + +resource "aws_vpc_ipam" "test" { + description = "My IPAM" + operating_regions { + region_name = data.aws_region.current.name + } + + tags = { + Test = "Test" + } +} + +data "aws_vpc_ipam" "test" { + id = aws_vpc_ipam.test.id +} +` +} diff --git a/internal/service/ec2/vpc_ipams_data_source.go b/internal/service/ec2/vpc_ipams_data_source.go new file mode 100644 index 000000000000..57183c8b7017 --- /dev/null +++ b/internal/service/ec2/vpc_ipams_data_source.go @@ -0,0 +1,121 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ec2 + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/service/ec2" + awstypes "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkDataSource("aws_vpc_ipams", name="IPAMs") +func newVPCIPAMsDataSource(context.Context) (datasource.DataSourceWithConfigure, error) { + return &dataSourceVPCIPAMs{}, nil +} + +const ( + DSNameVPCIPAMs = "IPAMs Data Source" +) + +type dataSourceVPCIPAMs struct { + framework.DataSourceWithConfigure +} + +func (d *dataSourceVPCIPAMs) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { // nosemgrep:ci.meta-in-func-name + resp.TypeName = "aws_vpc_ipams" +} + +func (d *dataSourceVPCIPAMs) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "ipams": framework.DataSourceComputedListOfObjectAttribute[dataSourceVPCIPAMSummaryModel](ctx), + "ipam_ids": schema.ListAttribute{ + Optional: true, + ElementType: types.StringType, + }, + }, + Blocks: map[string]schema.Block{ + names.AttrFilter: schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[filterModel](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + names.AttrName: schema.StringAttribute{ + Required: true, + }, + names.AttrValues: schema.SetAttribute{ + CustomType: fwtypes.SetOfStringType, + Required: true, + }, + }, + }, + }, + }, + } +} + +func findVPCIPAMs(ctx context.Context, conn *ec2.Client, input *ec2.DescribeIpamsInput) ([]awstypes.Ipam, error) { + var output []awstypes.Ipam + + pages := ec2.NewDescribeIpamsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + if err != nil { + return nil, err + } + output = append(output, page.Ipams...) + } + return output, nil +} + +func (d *dataSourceVPCIPAMs) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + conn := d.Meta().EC2Client(ctx) + + var data dataSourceVPCIPAMsModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + input := ec2.DescribeIpamsInput{} + resp.Diagnostics.Append(fwflex.Expand(ctx, data, &input)...) + if resp.Diagnostics.HasError() { + return + } + + output, err := findVPCIPAMs(ctx, conn, &input) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionReading, DSNameVPCIPAMs, "", err), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(fwflex.Flatten(ctx, output, &data.Ipams, fwflex.WithFieldNamePrefix("ipam"))...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +type filterModel struct { + Name types.String `tfsdk:"name"` + Values fwtypes.SetOfString `tfsdk:"values"` +} + +type dataSourceVPCIPAMsModel struct { + Ipams fwtypes.ListNestedObjectValueOf[dataSourceVPCIPAMSummaryModel] `tfsdk:"ipams"` + Filters fwtypes.ListNestedObjectValueOf[filterModel] `tfsdk:"filter"` + IpamIds types.List `tfsdk:"ipam_ids"` +} diff --git a/internal/service/ec2/vpc_ipams_data_source_test.go b/internal/service/ec2/vpc_ipams_data_source_test.go new file mode 100644 index 000000000000..707061ba10fa --- /dev/null +++ b/internal/service/ec2/vpc_ipams_data_source_test.go @@ -0,0 +1,97 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ec2_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccVPCIPAMsDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_vpc_ipam.test" + dataSourceName := "data.aws_vpc_ipams.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccVPCIPAMsDataSourceConfig_basic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "ipams.#", "1"), + resource.TestCheckResourceAttrPair(dataSourceName, "ipams.0.id", resourceName, names.AttrID), + resource.TestCheckResourceAttrPair(dataSourceName, "ipams.0.operating_regions.0.region_name", resourceName, "operating_regions.0.region_name"), + resource.TestCheckResourceAttrPair(dataSourceName, "ipams.0.tags.#", resourceName, "tags.#"), + ), + }, + }, + }) +} + +func TestAccVPCIPAMsDataSource_filter(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_vpc_ipam.test" + dataSourceNameAdvanced := "data.aws_vpc_ipams.advanced" + dataSourceNameFree := "data.aws_vpc_ipams.free" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccVPCIPAMsDataSourceConfig_filter(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceNameAdvanced, "ipams.#", "1"), + resource.TestCheckResourceAttrPair(dataSourceNameAdvanced, "ipams.0.id", resourceName, names.AttrID), + resource.TestCheckResourceAttrPair(dataSourceNameAdvanced, "ipams.0.tier", resourceName, "tier"), + resource.TestCheckResourceAttr(dataSourceNameFree, "ipams.#", "0"), + ), + }, + }, + }) +} + +func testAccVPCIPAMsDataSourceConfig_basic() string { + return acctest.ConfigCompose(testAccIPAMConfig_tags("Some", "Value"), ` +data "aws_vpc_ipams" "test" { + ipam_ids = [aws_vpc_ipam.test.id] +} +`) +} + +func testAccVPCIPAMsDataSourceConfig_filter() string { + return acctest.ConfigCompose(testAccIPAMConfig_tags("Some", "Value"), ` +data "aws_vpc_ipams" "advanced" { + ipam_ids = [aws_vpc_ipam.test.id] + filter { + name = "tier" + values = ["advanced"] + } +} + +data "aws_vpc_ipams" "free" { + ipam_ids = [aws_vpc_ipam.test.id] + filter { + name = "tier" + values = ["free"] + } +} +`) +} diff --git a/website/docs/d/vpc_ipam.html.markdown b/website/docs/d/vpc_ipam.html.markdown new file mode 100644 index 000000000000..85ceb0431573 --- /dev/null +++ b/website/docs/d/vpc_ipam.html.markdown @@ -0,0 +1,49 @@ +--- +subcategory: "VPC IPAM (IP Address Manager)" +layout: "aws" +page_title: "AWS: aws_vpc_ipam" +description: |- + Terraform data source for managing a VPC IPAM. +--- + +# Data Source: aws_vpc_ipam + +Terraform data source for managing a VPC IPAM. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_vpc_ipam" "example" { + id = "ipam-abcd1234" +} +``` + +## Argument Reference + +The following arguments are required: + +* `id` - (Required) ID of the IPAM. + +## Attribute Reference + +This data source exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the IPAM. +* `default_resource_discovery_association_id` - The default resource discovery association ID. +* `default_resource_discovery_id` - The default resource discovery ID. +* `description` - Description for the IPAM. +* `enable_private_gua` - If private GUA is enabled. +* `id` - ID of the IPAM resource. +* `ipam_region` - Region that the IPAM exists in. +* `operating_regions` - Regions that the IPAM is configured to operate in. +* `owner_id` - ID of the account that owns this IPAM. +* `private_default_scope_id` - ID of the default private scope. +* `public_default_scope_id` - ID of the default public scope. +* `resource_discovery_association_count` - Number of resource discovery associations. +* `scope_count` - Number of scopes on this IPAM. +* `state` - Current state of the IPAM. +* `state_message` - State message of the IPAM. +* `tier` - IPAM Tier. +* `tags` - Tags of the IPAM resource. diff --git a/website/docs/d/vpc_ipams.html.markdown b/website/docs/d/vpc_ipams.html.markdown new file mode 100644 index 000000000000..c7da416bf13d --- /dev/null +++ b/website/docs/d/vpc_ipams.html.markdown @@ -0,0 +1,84 @@ +--- +subcategory: "VPC IPAM (IP Address Manager)" +layout: "aws" +page_title: "AWS: aws_vpc_ipams" +description: |- + Terraform data source for managing VPC IPAMs. +--- + +# Data Source: aws_vpc_ipams + +Terraform data source for managing VPC IPAMs. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_vpc_ipams" "example" { + ipam_ids = ["ipam-abcd1234"] +} +``` + +### Filter by `tags` + +```terraform +data "aws_vpc_ipams" "example" { + filter { + name = "tags.Some" + values = ["Value"] + } +} +``` + +### Filter by `tier` + +```terraform +data "aws_vpc_ipams" "example" { + filter { + name = "tier" + values = ["free"] + } +} +``` + +## Argument Reference + +The arguments of this data source act as filters for querying the available IPAMs. + +* `ipam_ids` - (Optional) IDs of the IPAM resources to query for. +* `filter` - (Optional) Custom filter block as described below. + +More complex filters can be expressed using one or more `filter` sub-blocks, +which take the following arguments: + +* `name` - (Required) Name of the field to filter by, as defined by + [the underlying AWS API](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeIpams.html). + +* `values` - (Required) Set of values that are accepted for the given field. + An IPAM resource will be selected if any one of the given values matches. + +## Attribute Reference + +All of the argument attributes except `filter` are also exported as result attributes. + +* `ipams` - List of IPAM resources matching the provided arguments. + +### ipams + +* `arn` - ARN of the IPAM. +* `default_resource_discovery_association_id` - The default resource discovery association ID. +* `default_resource_discovery_id` - The default resource discovery ID. +* `description` - Description for the IPAM. +* `enable_private_gua` - If private GUA is enabled. +* `id` - ID of the IPAM resource. +* `ipam_region` - Region that the IPAM exists in. +* `operating_regions` - Regions that the IPAM is configured to operate in. +* `owner_id` - ID of the account that owns this IPAM. +* `private_default_scope_id` - ID of the default private scope. +* `public_default_scope_id` - ID of the default public scope. +* `resource_discovery_association_count` - Number of resource discovery associations. +* `scope_count` - Number of scopes on this IPAM. +* `state` - Current state of the IPAM. +* `state_message` - State message of the IPAM. +* `tier` - IPAM Tier.