diff --git a/internal/service/elbv2/load_balancer.go b/internal/service/elbv2/load_balancer.go index a67f19064066..b26651ba2435 100644 --- a/internal/service/elbv2/load_balancer.go +++ b/internal/service/elbv2/load_balancer.go @@ -1281,24 +1281,19 @@ func suffixFromARN(arn *string) string { return "" } -// Load balancers of type 'network' cannot have their subnets updated, -// cannot have security groups added if none are present, and cannot have -// all security groups removed. If the type is 'network' and any of these -// conditions are met, mark the diff as a ForceNew operation. +// Load balancers of type 'network' cannot have security groups added if none +// are present, and cannot have all security groups removed. If the type is +// 'network' and any of these conditions are met, mark the diff as a ForceNew operation. func customizeDiffLoadBalancerNLB(_ context.Context, diff *schema.ResourceDiff, v any) error { // The current criteria for determining if the operation should be ForceNew: // - lb of type "network" // - existing resource (id is not "") - // - there are subnet removals - // OR security groups are being added where none currently exist + // - security groups are being added where none currently exist // OR all security groups are being removed // OR secondary IPv4 addresses are being decreased // - // Any other combination should be treated as normal. At this time, subnet - // handling is the only known difference between Network Load Balancers and - // Application Load Balancers, so the logic below is simple individual checks. - // If other differences arise we'll want to refactor to check other - // conditions in combinations, but for now all we handle is subnets + // Note: As of February 2025, AWS supports full subnet management for NLBs, + // including adding and removing subnets, matching ALB capabilities. if lbType := awstypes.LoadBalancerTypeEnum(diff.Get("load_balancer_type").(string)); lbType != awstypes.LoadBalancerTypeEnumNetwork { return nil } @@ -1321,12 +1316,16 @@ func customizeDiffLoadBalancerNLB(_ context.Context, diff *schema.ResourceDiff, switch { case deltaN == 0: // No change in number of subnet mappings, but one of the mappings did change. - fallthrough - case deltaN < 0: - // Subnet mappings removed. if err := diff.ForceNew("subnet_mapping"); err != nil { return err } + case deltaN < 0: + // Subnet mappings removed. Ensure that the remaining mappings didn't change. + if os.Intersection(ns).Len() != ns.Len() { + if err := diff.ForceNew("subnet_mapping"); err != nil { + return err + } + } case deltaN > 0: // Subnet mappings added. Ensure that the previous mappings didn't change. if ns.Intersection(os).Len() != os.Len() { @@ -1342,18 +1341,6 @@ func customizeDiffLoadBalancerNLB(_ context.Context, diff *schema.ResourceDiff, } } if hasSubnetsChanges { - if v := config.GetAttr(names.AttrSubnets); v.IsWhollyKnown() { - o, n := diff.GetChange(names.AttrSubnets) - os, ns := o.(*schema.Set), n.(*schema.Set) - - // In-place increase in number of subnets only. - if deltaN := ns.Len() - os.Len(); deltaN <= 0 { - if err := diff.ForceNew(names.AttrSubnets); err != nil { - return err - } - } - } - if err := diff.SetNewComputed("subnet_mapping"); err != nil { return err } diff --git a/internal/service/elbv2/load_balancer_test.go b/internal/service/elbv2/load_balancer_test.go index 211a2e7bbbed..7d9215a4a1f3 100644 --- a/internal/service/elbv2/load_balancer_test.go +++ b/internal/service/elbv2/load_balancer_test.go @@ -1977,7 +1977,7 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_deleteSubnet(t *testing.T) { Config: testAccLoadBalancerConfig_nlbSubnetCount(rName, 2), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &post), - testAccCheckLoadBalancerRecreated(&post, &pre), + testAccCheckLoadBalancerNotRecreated(&pre, &post), resource.TestCheckResourceAttr(resourceName, "subnets.#", "2"), ), }, @@ -2050,7 +2050,7 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_deleteSubnetMapping(t *testing Config: testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, false, 2), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &post), - testAccCheckLoadBalancerRecreated(&pre, &post), + testAccCheckLoadBalancerNotRecreated(&pre, &post), resource.TestCheckResourceAttr(resourceName, "subnet_mapping.#", "2"), resource.TestCheckResourceAttr(resourceName, "subnets.#", "2"), ), diff --git a/website/docs/cdktf/python/r/lb.html.markdown b/website/docs/cdktf/python/r/lb.html.markdown index 79d9549c8645..4036b337c7b9 100644 --- a/website/docs/cdktf/python/r/lb.html.markdown +++ b/website/docs/cdktf/python/r/lb.html.markdown @@ -161,8 +161,8 @@ This resource supports the following arguments: * `security_groups` - (Optional) List of security group IDs to assign to the LB. Only valid for Load Balancers of type `application` or `network`. For load balancers of type `network` security groups cannot be added if none are currently present, and cannot all be removed once added. If either of these conditions are met, this will force a recreation of the resource. * `preserve_host_header` - (Optional) Whether the Application Load Balancer should preserve the Host header in the HTTP request and send it to the target without any change. Defaults to `false`. * `secondary_ips_auto_assigned_per_subnet` - (Optional) The number of secondary IP addresses to configure for your load balancer nodes. Only valid for Load Balancers of type `network`. The valid range is 0-7. When decreased, this will force a recreation of the resource. Default: `0`. -* `subnet_mapping` - (Optional) Subnet mapping block. See below. For Load Balancers of type `network` subnet mappings can only be added. -* `subnets` - (Optional) List of subnet IDs to attach to the LB. For Load Balancers of type `network` subnets can only be added (see [Availability Zones](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#availability-zones)), deleting a subnet for load balancers of type `network` will force a recreation of the resource. +* `subnet_mapping` - (Optional) Subnet mapping block. See below. +* `subnets` - (Optional) List of subnet IDs to attach to the LB. Subnets can be added or removed for Load Balancers of type `network` (see [Availability Zones](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#availability-zones)). * `tags` - (Optional) 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. * `xff_header_processing_mode` - (Optional) Determines how the load balancer modifies the `X-Forwarded-For` header in the HTTP request before sending the request to the target. The possible values are `append`, `preserve`, and `remove`. Only valid for Load Balancers of type `application`. The default is `append`. diff --git a/website/docs/cdktf/typescript/r/lb.html.markdown b/website/docs/cdktf/typescript/r/lb.html.markdown index a7971610b53b..b90237eafc9e 100644 --- a/website/docs/cdktf/typescript/r/lb.html.markdown +++ b/website/docs/cdktf/typescript/r/lb.html.markdown @@ -181,8 +181,8 @@ This resource supports the following arguments: * `securityGroups` - (Optional) List of security group IDs to assign to the LB. Only valid for Load Balancers of type `application` or `network`. For load balancers of type `network` security groups cannot be added if none are currently present, and cannot all be removed once added. If either of these conditions are met, this will force a recreation of the resource. * `preserveHostHeader` - (Optional) Whether the Application Load Balancer should preserve the Host header in the HTTP request and send it to the target without any change. Defaults to `false`. * `secondaryIpsAutoAssignedPerSubnet` - (Optional) The number of secondary IP addresses to configure for your load balancer nodes. Only valid for Load Balancers of type `network`. The valid range is 0-7. When decreased, this will force a recreation of the resource. Default: `0`. -* `subnetMapping` - (Optional) Subnet mapping block. See below. For Load Balancers of type `network` subnet mappings can only be added. -* `subnets` - (Optional) List of subnet IDs to attach to the LB. For Load Balancers of type `network` subnets can only be added (see [Availability Zones](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#availability-zones)), deleting a subnet for load balancers of type `network` will force a recreation of the resource. +* `subnetMapping` - (Optional) Subnet mapping block. See below. +* `subnets` - (Optional) List of subnet IDs to attach to the LB. Subnets can be added or removed for Load Balancers of type `network` (see [Availability Zones](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#availability-zones)). * `tags` - (Optional) Map of tags to assign to the resource. If configured with a provider [`defaultTags` 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. * `xffHeaderProcessingMode` - (Optional) Determines how the load balancer modifies the `X-Forwarded-For` header in the HTTP request before sending the request to the target. The possible values are `append`, `preserve`, and `remove`. Only valid for Load Balancers of type `application`. The default is `append`. diff --git a/website/docs/r/lb.html.markdown b/website/docs/r/lb.html.markdown index 973308e17517..0b8c61ce2e0b 100644 --- a/website/docs/r/lb.html.markdown +++ b/website/docs/r/lb.html.markdown @@ -124,8 +124,8 @@ This resource supports the following arguments: * `security_groups` - (Optional) List of security group IDs to assign to the LB. Only valid for Load Balancers of type `application` or `network`. For load balancers of type `network` security groups cannot be added if none are currently present, and cannot all be removed once added. If either of these conditions are met, this will force a recreation of the resource. * `preserve_host_header` - (Optional) Whether the Application Load Balancer should preserve the Host header in the HTTP request and send it to the target without any change. Defaults to `false`. * `secondary_ips_auto_assigned_per_subnet` - (Optional) The number of secondary IP addresses to configure for your load balancer nodes. Only valid for Load Balancers of type `network`. The valid range is 0-7. When decreased, this will force a recreation of the resource. Default: `0`. -* `subnet_mapping` - (Optional) Subnet mapping block. See below. For Load Balancers of type `network` subnet mappings can only be added. -* `subnets` - (Optional) List of subnet IDs to attach to the LB. For Load Balancers of type `network` subnets can only be added (see [Availability Zones](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#availability-zones)), deleting a subnet for load balancers of type `network` will force a recreation of the resource. +* `subnet_mapping` - (Optional) Subnet mapping block. See below. +* `subnets` - (Optional) List of subnet IDs to attach to the LB. Subnets can be added or removed for Load Balancers of type `network` (see [Availability Zones](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#availability-zones)). * `tags` - (Optional) 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. * `xff_header_processing_mode` - (Optional) Determines how the load balancer modifies the `X-Forwarded-For` header in the HTTP request before sending the request to the target. The possible values are `append`, `preserve`, and `remove`. Only valid for Load Balancers of type `application`. The default is `append`.