Skip to content

Commit

Permalink
Merge pull request #35678 from hashicorp/b-elbv2-unexpected-diff
Browse files Browse the repository at this point in the history
Fixes unexpected diff for `aws_lb_listener` and `aws_lb_listener_rule`
  • Loading branch information
gdavison authored Feb 7, 2024
2 parents e6a44f2 + 7a3920f commit bfcae6a
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 35 deletions.
7 changes: 7 additions & 0 deletions .changelog/35678.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:bug
resource/aws_lb_listener: Fixes unexpected diff when using `default_action` parameters which don't match the `type`.
```

```release-note:bug
resource/aws_lb_listener_rule: Fixes unexpected diff when using `action` parameters which don't match the `type`.
```
59 changes: 39 additions & 20 deletions internal/service/elbv2/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ func ResourceListener() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"authenticate_cognito": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: suppressIfDefaultActionTypeNot(awstypes.ActionTypeEnumAuthenticateCognito),
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"authentication_request_extra_params": {
Expand Down Expand Up @@ -123,9 +124,10 @@ func ResourceListener() *schema.Resource {
},
},
"authenticate_oidc": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: suppressIfDefaultActionTypeNot(awstypes.ActionTypeEnumAuthenticateOidc),
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"authentication_request_extra_params": {
Expand Down Expand Up @@ -183,9 +185,10 @@ func ResourceListener() *schema.Resource {
},
},
"fixed_response": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: suppressIfDefaultActionTypeNot(awstypes.ActionTypeEnumFixedResponse),
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"content_type": {
Expand Down Expand Up @@ -214,11 +217,10 @@ func ResourceListener() *schema.Resource {
},
},
"forward": {
Type: schema.TypeList,
Optional: true,
DiffSuppressOnRefresh: true,
DiffSuppressFunc: diffSuppressMissingForward("default_action"),
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: suppressIfDefaultActionTypeNot(awstypes.ActionTypeEnumForward),
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"target_group": {
Expand Down Expand Up @@ -272,9 +274,10 @@ func ResourceListener() *schema.Resource {
ValidateFunc: validation.IntBetween(listenerActionOrderMin, listenerActionOrderMax),
},
"redirect": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: suppressIfDefaultActionTypeNot(awstypes.ActionTypeEnumRedirect),
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"host": {
Expand Down Expand Up @@ -319,9 +322,10 @@ func ResourceListener() *schema.Resource {
},
},
"target_group_arn": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidARN,
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: suppressIfDefaultActionTypeNot(awstypes.ActionTypeEnumForward),
ValidateFunc: verify.ValidARN,
},
"type": {
Type: schema.TypeString,
Expand Down Expand Up @@ -393,6 +397,21 @@ func ResourceListener() *schema.Resource {
}
}

func suppressIfDefaultActionTypeNot(t awstypes.ActionTypeEnum) schema.SchemaDiffSuppressFunc {
return func(k, old, new string, d *schema.ResourceData) bool {
take := 2
i := strings.IndexFunc(k, func(r rune) bool {
if r == '.' {
take -= 1
return take == 0
}
return false
})
at := k[:i+1] + "type"
return awstypes.ActionTypeEnum(d.Get(at).(string)) != t
}
}

func resourceListenerCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).ELBV2Client(ctx)
Expand Down
51 changes: 36 additions & 15 deletions internal/service/elbv2/listener_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"log"
"sort"
"strconv"
"strings"
"time"

"github.com/YakDriver/regexache"
Expand Down Expand Up @@ -92,9 +93,10 @@ func ResourceListenerRule() *schema.Resource {
},

"target_group_arn": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidARN,
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: suppressIfActionTypeNot(awstypes.ActionTypeEnumForward),
ValidateFunc: verify.ValidARN,
},

"forward": {
Expand Down Expand Up @@ -151,9 +153,10 @@ func ResourceListenerRule() *schema.Resource {
},

"redirect": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: suppressIfActionTypeNot(awstypes.ActionTypeEnumRedirect),
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"host": {
Expand Down Expand Up @@ -204,9 +207,10 @@ func ResourceListenerRule() *schema.Resource {
},

"fixed_response": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: suppressIfActionTypeNot(awstypes.ActionTypeEnumFixedResponse),
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"content_type": {
Expand Down Expand Up @@ -238,9 +242,10 @@ func ResourceListenerRule() *schema.Resource {
},

"authenticate_cognito": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: suppressIfActionTypeNot(awstypes.ActionTypeEnumAuthenticateCognito),
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"authentication_request_extra_params": {
Expand Down Expand Up @@ -287,9 +292,10 @@ func ResourceListenerRule() *schema.Resource {
},

"authenticate_oidc": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: suppressIfActionTypeNot(awstypes.ActionTypeEnumAuthenticateOidc),
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"authentication_request_extra_params": {
Expand Down Expand Up @@ -481,6 +487,21 @@ func ResourceListenerRule() *schema.Resource {
}
}

func suppressIfActionTypeNot(t awstypes.ActionTypeEnum) schema.SchemaDiffSuppressFunc {
return func(k, old, new string, d *schema.ResourceData) bool {
take := 2
i := strings.IndexFunc(k, func(r rune) bool {
if r == '.' {
take -= 1
return take == 0
}
return false
})
at := k[:i+1] + "type"
return awstypes.ActionTypeEnum(d.Get(at).(string)) != t
}
}

func resourceListenerRuleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).ELBV2Client(ctx)
Expand Down
155 changes: 155 additions & 0 deletions internal/service/elbv2/listener_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1510,6 +1510,48 @@ func TestAccELBV2ListenerRule_EmptyAction(t *testing.T) {
}
}

func TestAccELBV2ListenerRule_redirectWithTargetGroupARN(t *testing.T) {
ctx := acctest.Context(t)
var conf awstypes.Rule
lbName := fmt.Sprintf("testrule-redirect-%s", sdkacctest.RandString(14))

resourceName := "aws_lb_listener_rule.static"
frontEndListenerResourceName := "aws_lb_listener.front_end"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, tfelbv2.AwsSdkId),
CheckDestroy: testAccCheckListenerRuleDestroy(ctx),
Steps: []resource.TestStep{
{
ExternalProviders: map[string]resource.ExternalProvider{
"aws": {
Source: "hashicorp/aws",
VersionConstraint: "5.34.0",
},
},
Config: testAccListenerRuleConfig_redirectWithTargetGroupARN(lbName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckListenerRuleExists(ctx, resourceName, &conf),
acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "elasticloadbalancing", regexache.MustCompile(fmt.Sprintf(`listener-rule/app/%s/.+$`, lbName))),
resource.TestCheckResourceAttrPair(resourceName, "listener_arn", frontEndListenerResourceName, "arn"),
resource.TestCheckResourceAttr(resourceName, "action.#", "1"),
resource.TestCheckResourceAttr(resourceName, "action.0.type", "redirect"),
resource.TestCheckResourceAttr(resourceName, "action.0.redirect.#", "1"),
resource.TestCheckResourceAttr(resourceName, "action.0.forward.#", "0"),
resource.TestCheckResourceAttr(resourceName, "action.0.target_group_arn", ""),
),
},
{
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Config: testAccListenerRuleConfig_redirectWithTargetGroupARN(lbName),
PlanOnly: true,
ExpectNonEmptyPlan: false,
},
},
})
}

func TestAccELBV2ListenerRule_conditionAttributesCount(t *testing.T) {
ctx := acctest.Context(t)
err_many := regexache.MustCompile("Only one of host_header, http_header, http_request_method, path_pattern, query_string or source_ip can be set in a condition block")
Expand Down Expand Up @@ -4318,6 +4360,119 @@ resource "aws_security_group" "test" {
`, rName, action)
}

func testAccListenerRuleConfig_redirectWithTargetGroupARN(lbName string) string {
return fmt.Sprintf(`
resource "aws_lb_listener_rule" "static" {
listener_arn = aws_lb_listener.front_end.arn
priority = 100
action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
condition {
path_pattern {
values = ["/static/*"]
}
}
}
resource "aws_lb_listener" "front_end" {
load_balancer_arn = aws_lb.alb_test.id
protocol = "HTTP"
port = "80"
default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}
resource "aws_lb" "alb_test" {
name = %[1]q
internal = true
security_groups = [aws_security_group.alb_test.id]
subnets = aws_subnet.alb_test[*].id
idle_timeout = 30
enable_deletion_protection = false
tags = {
Name = %[1]q
}
}
variable "subnets" {
default = ["10.0.1.0/24", "10.0.2.0/24"]
type = list(string)
}
data "aws_availability_zones" "available" {
state = "available"
filter {
name = "opt-in-status"
values = ["opt-in-not-required"]
}
}
resource "aws_vpc" "alb_test" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "terraform-testacc-lb-listener-rule-redirect"
}
}
resource "aws_subnet" "alb_test" {
count = 2
vpc_id = aws_vpc.alb_test.id
cidr_block = element(var.subnets, count.index)
map_public_ip_on_launch = true
availability_zone = element(data.aws_availability_zones.available.names, count.index)
tags = {
Name = "tf-acc-lb-listener-rule-redirect-${count.index}"
}
}
resource "aws_security_group" "alb_test" {
name = "allow_all_alb_test"
description = "Used for ALB Testing"
vpc_id = aws_vpc.alb_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
}
}
`, lbName)
}

func testAccListenerRuleConfig_condition_error(condition string) string {
return fmt.Sprintf(`
data "aws_partition" "current" {}
Expand Down
Loading

0 comments on commit bfcae6a

Please sign in to comment.