diff --git a/.changelog/33767.txt b/.changelog/33767.txt new file mode 100644 index 00000000000..4a6fba51206 --- /dev/null +++ b/.changelog/33767.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_lb: Add `enforce_security_group_inbound_rules_on_private_link_traffic` attribute +``` diff --git a/internal/service/elbv2/load_balancer.go b/internal/service/elbv2/load_balancer.go index e753f1eedf3..f958a1fbc9e 100644 --- a/internal/service/elbv2/load_balancer.go +++ b/internal/service/elbv2/load_balancer.go @@ -160,6 +160,13 @@ func ResourceLoadBalancer() *schema.Resource { Default: false, DiffSuppressFunc: suppressIfLBTypeNot(elbv2.LoadBalancerTypeEnumApplication), }, + "enforce_security_group_inbound_rules_on_private_link_traffic": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(elbv2.EnforceSecurityGroupInboundRulesOnPrivateLinkTrafficEnum_Values(), false), + DiffSuppressFunc: suppressIfLBTypeNot(elbv2.LoadBalancerTypeEnumNetwork), + }, "idle_timeout": { Type: schema.TypeInt, Optional: true, @@ -574,13 +581,20 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met } } - if d.HasChange("security_groups") { + if d.HasChanges("security_groups", "enforce_security_group_inbound_rules_on_private_link_traffic") { sgs := flex.ExpandStringSet(d.Get("security_groups").(*schema.Set)) params := &elbv2.SetSecurityGroupsInput{ LoadBalancerArn: aws.String(d.Id()), SecurityGroups: sgs, } + + if v := d.Get("load_balancer_type"); v == elbv2.LoadBalancerTypeEnumNetwork { + if v, ok := d.GetOk("enforce_security_group_inbound_rules_on_private_link_traffic"); ok { + params.EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic = aws.String(v.(string)) + } + } + _, err := conn.SetSecurityGroupsWithContext(ctx, params) if err != nil { return sdkdiag.AppendErrorf(diags, "failure Setting LB Security Groups: %s", err) @@ -918,6 +932,7 @@ func flattenResource(ctx context.Context, d *schema.ResourceData, meta interface d.Set("arn_suffix", SuffixFromARN(lb.LoadBalancerArn)) d.Set("customer_owned_ipv4_pool", lb.CustomerOwnedIpv4Pool) d.Set("dns_name", lb.DNSName) + d.Set("enforce_security_group_inbound_rules_on_private_link_traffic", lb.EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic) d.Set("internal", aws.StringValue(lb.Scheme) == elbv2.LoadBalancerSchemeEnumInternal) d.Set("ip_address_type", lb.IpAddressType) d.Set("load_balancer_type", lb.Type) diff --git a/internal/service/elbv2/load_balancer_data_source.go b/internal/service/elbv2/load_balancer_data_source.go index 77e1b5cbd73..baadc8dee24 100644 --- a/internal/service/elbv2/load_balancer_data_source.go +++ b/internal/service/elbv2/load_balancer_data_source.go @@ -102,6 +102,10 @@ func DataSourceLoadBalancer() *schema.Resource { Type: schema.TypeBool, Computed: true, }, + "enforce_security_group_inbound_rules_on_private_link_traffic": { + Type: schema.TypeString, + Computed: true, + }, "idle_timeout": { Type: schema.TypeInt, Computed: true, @@ -247,6 +251,7 @@ func dataSourceLoadBalancerRead(ctx context.Context, d *schema.ResourceData, met d.Set("ip_address_type", lb.IpAddressType) d.Set("load_balancer_type", lb.Type) d.Set("customer_owned_ipv4_pool", lb.CustomerOwnedIpv4Pool) + d.Set("enforce_security_group_inbound_rules_on_private_link_traffic", lb.EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic) if err := d.Set("subnets", flattenSubnetsFromAvailabilityZones(lb.AvailabilityZones)); err != nil { return sdkdiag.AppendErrorf(diags, "setting subnets: %s", err) diff --git a/internal/service/elbv2/load_balancer_test.go b/internal/service/elbv2/load_balancer_test.go index b8b5a3d7ba4..39fe710eb46 100644 --- a/internal/service/elbv2/load_balancer_test.go +++ b/internal/service/elbv2/load_balancer_test.go @@ -889,6 +889,7 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_updatedSecurityGroups(t *t Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &pre), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", ""), ), }, { @@ -896,6 +897,7 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_updatedSecurityGroups(t *t Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &post), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "2"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", ""), testAccCheckLoadBalancerNotRecreated(&pre, &post), ), }, @@ -1321,6 +1323,7 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups(t *testin Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &lb1), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "0"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", ""), ), }, { @@ -1329,6 +1332,7 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups(t *testin testAccCheckLoadBalancerExists(ctx, resourceName, &lb2), testAccCheckLoadBalancerRecreated(&lb2, &lb1), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", ""), ), }, { @@ -1337,6 +1341,7 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups(t *testin testAccCheckLoadBalancerExists(ctx, resourceName, &lb3), testAccCheckLoadBalancerNotRecreated(&lb3, &lb2), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "2"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", ""), ), }, { @@ -1345,6 +1350,67 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups(t *testin testAccCheckLoadBalancerExists(ctx, resourceName, &lb4), testAccCheckLoadBalancerRecreated(&lb4, &lb3), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "0"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", ""), + ), + }, + }, + }) +} + +func TestAccELBV2LoadBalancer_NetworkLoadBalancer_enforcePrivateLink(t *testing.T) { + ctx := acctest.Context(t) + var lb1 elbv2.LoadBalancer + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lb.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckLoadBalancerDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccLoadBalancerConfig_nlbSecurityGroupsEnforcePrivateLink(rName, 1, "off"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLoadBalancerExists(ctx, resourceName, &lb1), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", "off")), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccLoadBalancerConfig_nlbSecurityGroups(rName, 1), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLoadBalancerExists(ctx, resourceName, &lb1), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", "off"), + ), + }, + { + Config: testAccLoadBalancerConfig_nlbSecurityGroupsEnforcePrivateLink(rName, 1, "on"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLoadBalancerExists(ctx, resourceName, &lb1), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", "on"), + ), + }, + { + Config: testAccLoadBalancerConfig_nlbSecurityGroupsEnforcePrivateLink(rName, 1, "off"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLoadBalancerExists(ctx, resourceName, &lb1), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", "off"), + ), + }, + { + Config: testAccLoadBalancerConfig_nlbSecurityGroups(rName, 1), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLoadBalancerExists(ctx, resourceName, &lb1), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", "off"), ), }, }, @@ -2531,6 +2597,45 @@ resource "aws_lb" "test" { `, rName, n)) } +func testAccLoadBalancerConfig_nlbSecurityGroupsEnforcePrivateLink(rName string, n int, enforcePrivateLink string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` +resource "aws_security_group" "test" { + count = 3 + + name = "%[1]s-${count.index}" + vpc_id = aws_vpc.test.id + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = %[1]q + } +} + +resource "aws_lb" "test" { + internal = true + load_balancer_type = "network" + name = %[1]q + subnets = aws_subnet.test[*].id + security_groups = slice(aws_security_group.test[*].id, 0, %[2]d) + + enforce_security_group_inbound_rules_on_private_link_traffic = %[3]q +} +`, rName, n, enforcePrivateLink)) +} + func testAccLoadBalancerConfig_nlbSubnets(rName string, subnetCount int) string { return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, subnetCount), fmt.Sprintf(` resource "aws_lb" "test" { diff --git a/website/docs/r/lb.html.markdown b/website/docs/r/lb.html.markdown index c10368302c6..3bab7f265bb 100644 --- a/website/docs/r/lb.html.markdown +++ b/website/docs/r/lb.html.markdown @@ -111,6 +111,7 @@ This argument supports the following arguments: * `enable_tls_version_and_cipher_suite_headers` - (Optional) Indicates whether the two headers (`x-amzn-tls-version` and `x-amzn-tls-cipher-suite`), which contain information about the negotiated TLS version and cipher suite, are added to the client request before sending it to the target. Only valid for Load Balancers of type `application`. Defaults to `false` * `enable_xff_client_port` - (Optional) Indicates whether the X-Forwarded-For header should preserve the source port that the client used to connect to the load balancer in `application` load balancers. Defaults to `false`. * `enable_waf_fail_open` - (Optional) Indicates whether to allow a WAF-enabled load balancer to route requests to targets if it is unable to forward the request to AWS WAF. Defaults to `false`. +* `enforce_security_group_inbound_rules_on_private_link_traffic` - (Optional) Indicates whether inbound security group rules are enforced for traffic originating from a PrivateLink. Only valid for Load Balancers of type `network`. The possible values are `on` and `off`. * `idle_timeout` - (Optional) The time in seconds that the connection is allowed to be idle. Only valid for Load Balancers of type `application`. Default: 60. * `internal` - (Optional) If true, the LB will be internal. Defaults to `false`. * `ip_address_type` - (Optional) The type of IP addresses used by the subnets for your load balancer. The possible values are `ipv4` and `dualstack`.