diff --git a/.changelog/40042.txt b/.changelog/40042.txt new file mode 100644 index 00000000000..bcc27c3e424 --- /dev/null +++ b/.changelog/40042.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_vpc_ipam_pool: Fix bug when `public_ip_source = "amazon"`: `The request can only contain PubliclyAdvertisable if the AddressFamily is IPv6 and PublicIpSource is byoip.` +``` diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 925ad7e64cb..7984d4ee338 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -100,4 +100,4 @@ jobs: # Trigger garbage collection more frequently to reduce the likelihood # of OOM errors. Higher values mean it runs faster but more likely to OOM, exit 137. # ref: https://golangci-lint.run/product/performance/ - GOGC: "150" # 100 is the default value + GOGC: "140" # 100 is the default value diff --git a/internal/service/ec2/ipam_pool.go b/internal/service/ec2/ipam_pool.go index 6f674203e0a..e8f813e2356 100644 --- a/internal/service/ec2/ipam_pool.go +++ b/internal/service/ec2/ipam_pool.go @@ -155,11 +155,6 @@ func resourceIPAMPoolCreate(ctx context.Context, d *schema.ResourceData, meta in conn := meta.(*conns.AWSClient).EC2Client(ctx) scopeID := d.Get("ipam_scope_id").(string) - scope, err := findIPAMScopeByID(ctx, conn, scopeID) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading IPAM Scope (%s): %s", scopeID, err) - } addressFamily := awstypes.AddressFamily(d.Get("address_family").(string)) input := &ec2.CreateIpamPoolInput{ @@ -204,9 +199,15 @@ func resourceIPAMPoolCreate(ctx context.Context, d *schema.ResourceData, meta in if v, ok := d.GetOk("public_ip_source"); ok { input.PublicIpSource = awstypes.IpamPoolPublicIpSource(v.(string)) } - // PubliclyAdvertisable must be set if if the AddressFamily is IPv6 and PublicIpSource is byoip. - // The request can only contain PubliclyAdvertisable if the AddressFamily is IPv6 and PublicIpSource is byoip. - if addressFamily == awstypes.AddressFamilyIpv6 && scope.IpamScopeType == awstypes.IpamScopeTypePublic { + + scope, err := findIPAMScopeByID(ctx, conn, scopeID) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading IPAM Scope (%s): %s", scopeID, err) + } + + // PubliclyAdvertisable must be set if if the AddressFamily is IPv6 and PublicIpSource is byoip (either '' or 'byoip'). + // The request can't contain PubliclyAdvertisable if PublicIpSource is 'amazon'. + if addressFamily == awstypes.AddressFamilyIpv6 && scope.IpamScopeType == awstypes.IpamScopeTypePublic && input.PublicIpSource != awstypes.IpamPoolPublicIpSourceAmazon { input.PubliclyAdvertisable = aws.Bool(d.Get("publicly_advertisable").(bool)) } diff --git a/internal/service/ec2/ipam_pool_test.go b/internal/service/ec2/ipam_pool_test.go index 4833936b162..b1f2852cae5 100644 --- a/internal/service/ec2/ipam_pool_test.go +++ b/internal/service/ec2/ipam_pool_test.go @@ -132,6 +132,36 @@ func TestAccIPAMPool_ipv6Basic(t *testing.T) { }) } +func TestAccIPAMPool_ipv6PublicIPAmazon(t *testing.T) { + ctx := acctest.Context(t) + var pool awstypes.IpamPool + resourceName := "aws_vpc_ipam_pool.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIPAMPoolDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIPAMPoolConfig_ipv6PublicIPAmazon, + Check: resource.ComposeTestCheckFunc( + testAccCheckIPAMPoolExists(ctx, resourceName, &pool), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv6"), + resource.TestCheckResourceAttr(resourceName, "public_ip_source", "amazon"), + resource.TestCheckResourceAttr(resourceName, "aws_service", "ec2"), + resource.TestCheckResourceAttr(resourceName, "publicly_advertisable", acctest.CtFalse), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccIPAMPool_ipv6Contiguous(t *testing.T) { ctx := acctest.Context(t) var pool awstypes.IpamPool @@ -368,6 +398,16 @@ resource "aws_vpc_ipam_pool" "test" { } `) +var testAccIPAMPoolConfig_ipv6PublicIPAmazon = acctest.ConfigCompose(testAccIPAMPoolConfig_base, ` +resource "aws_vpc_ipam_pool" "test" { + address_family = "ipv6" + ipam_scope_id = aws_vpc_ipam.test.public_default_scope_id + locale = data.aws_region.current.name + public_ip_source = "amazon" + aws_service = "ec2" +} +`) + var testAccIPAMPoolConfig_ipv6Contiguous = acctest.ConfigCompose(testAccIPAMPoolConfig_base, ` resource "aws_vpc_ipam_pool" "test" { address_family = "ipv6" diff --git a/website/docs/r/vpc_ipam_pool.html.markdown b/website/docs/r/vpc_ipam_pool.html.markdown index dd56e5c1a30..8238167aa01 100644 --- a/website/docs/r/vpc_ipam_pool.html.markdown +++ b/website/docs/r/vpc_ipam_pool.html.markdown @@ -81,7 +81,7 @@ within the CIDR range in the pool. * `description` - (Optional) A description for the IPAM pool. * `ipam_scope_id` - (Required) The ID of the scope in which you would like to create the IPAM pool. * `locale` - (Optional) The locale in which you would like to create the IPAM pool. Locale is the Region where you want to make an IPAM pool available for allocations. You can only create pools with locales that match the operating Regions of the IPAM. You can only create VPCs from a pool whose locale matches the VPC's Region. Possible values: Any AWS region, such as `us-east-1`. -* `publicly_advertisable` - (Optional) Defines whether or not IPv6 pool space is publicly advertisable over the internet. This argument is required if `address_family = "ipv6"` and `public_ip_source = "byoip"`, default is `false`. This option is not available for IPv4 pool space or if `public_ip_source = "amazon"`. +* `publicly_advertisable` - (Optional) Defines whether or not IPv6 pool space is publicly advertisable over the internet. This argument is required if `address_family = "ipv6"` and `public_ip_source = "byoip"`, default is `false`. This option is not available for IPv4 pool space or if `public_ip_source = "amazon"`. Setting this argument to `true` when it is not available may result in erroneous differences being reported. * `public_ip_source` - (Optional) The IP address source for pools in the public scope. Only used for provisioning IP address CIDRs to pools in the public scope. Valid values are `byoip` or `amazon`. Default is `byoip`. * `source_ipam_pool_id` - (Optional) The ID of the source IPAM pool. Use this argument to create a child pool within an existing pool. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.