From 267d011304306ea1d00df84cbdc4cd912e726cd0 Mon Sep 17 00:00:00 2001 From: tabito Date: Tue, 28 Oct 2025 02:37:00 +0900 Subject: [PATCH 01/21] Implement transform block --- internal/service/elbv2/listener_rule.go | 214 ++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/internal/service/elbv2/listener_rule.go b/internal/service/elbv2/listener_rule.go index e51fff04dbe9..d6b7b11b00df 100644 --- a/internal/service/elbv2/listener_rule.go +++ b/internal/service/elbv2/listener_rule.go @@ -486,6 +486,76 @@ func resourceListenerRule() *schema.Resource { }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), + "transform": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 2, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrType: { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[awstypes.TransformTypeEnum](), + }, + "host_header_rewrite_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rewrite": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, // This argument is an array, but the current AWS API accepts exactly only one `rewrite` + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "regex": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, + "replace": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 1024), + }, + }, + }, + }, + }, + }, + }, + "url_rewrite_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rewrite": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, // This argument is an array, but the current AWS API accepts exactly only one `rewrite` + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "regex": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 1024), + }, + "replace": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 1024), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, }, CustomizeDiff: customdiff.All( @@ -531,6 +601,10 @@ func resourceListenerRuleCreate(ctx context.Context, d *schema.ResourceData, met return sdkdiag.AppendFromErr(diags, err) } + if v, ok := d.GetOk("transform"); ok && len(v.(*schema.Set).List()) > 0 { + input.Transforms = expandRuleTransforms(v.(*schema.Set).List()) + } + output, err := retryListenerRuleCreate(ctx, conn, d, input, listenerARN) // Some partitions (e.g. ISO) may not support tag-on-create. @@ -648,6 +722,10 @@ func resourceListenerRuleRead(ctx context.Context, d *schema.ResourceData, meta return sdkdiag.AppendErrorf(diags, "setting condition: %s", err) } + if err := d.Set("transform", flattenRuleTransforms(rule.Transforms)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting transform: %s", err) + } + return diags } @@ -695,6 +773,11 @@ func resourceListenerRuleUpdate(ctx context.Context, d *schema.ResourceData, met requestUpdate = true } + if d.HasChange("transform") { + input.Transforms = expandRuleTransforms(d.Get("transform").(*schema.Set).List()) + requestUpdate = true + } + if requestUpdate { resp, err := conn.ModifyRule(ctx, input) if err != nil { @@ -993,6 +1076,74 @@ func expandPathPatternConditionConfig(tfMap map[string]any) *awstypes.PathPatter return apiObject } +func expandRuleTransforms(tfList []any) []awstypes.RuleTransform { + var apiObjects []awstypes.RuleTransform + + for _, tfMapRaw := range tfList { + if tfMapRaw == nil { + continue + } + tfMap := tfMapRaw.(map[string]any) + apiObject := awstypes.RuleTransform{} + + if v, ok := tfMap[names.AttrType]; ok && v.(string) != "" { + apiObject.Type = awstypes.TransformTypeEnum(v.(string)) + } + if v, ok := tfMap["host_header_rewrite_config"].([]any); ok && len(v) > 0 { + apiObject.HostHeaderRewriteConfig = expandHostHeaderRewriteConfig(v[0].(map[string]any)) + } + if v, ok := tfMap["url_rewrite_config"].([]any); ok && len(v) > 0 { + apiObject.UrlRewriteConfig = expandURLRewriteConfig(v[0].(map[string]any)) + } + apiObjects = append(apiObjects, apiObject) + } + return apiObjects +} + +func expandHostHeaderRewriteConfig(tfMap map[string]any) *awstypes.HostHeaderRewriteConfig { + if tfMap == nil { + return &awstypes.HostHeaderRewriteConfig{} + } + + apiObject := &awstypes.HostHeaderRewriteConfig{} + if v, ok := tfMap["rewrite"].([]any); ok && len(v) > 0 { + apiObject.Rewrites = expandRewriteConfig(v) + } + return apiObject +} + +func expandURLRewriteConfig(tfMap map[string]any) *awstypes.UrlRewriteConfig { + if tfMap == nil { + return &awstypes.UrlRewriteConfig{} + } + + apiObject := &awstypes.UrlRewriteConfig{} + if v, ok := tfMap["rewrite"].([]any); ok && len(v) > 0 { + apiObject.Rewrites = expandRewriteConfig(v) + } + return apiObject +} + +func expandRewriteConfig(tfList []any) []awstypes.RewriteConfig { + if len(tfList) == 0 { + return nil + } + var apiObjects []awstypes.RewriteConfig + + for _, tfMapRaw := range tfList { + if tfMapRaw == nil { + continue + } + tfMap := tfMapRaw.(map[string]any) + apiObject := awstypes.RewriteConfig{ + Regex: aws.String(tfMap["regex"].(string)), + Replace: aws.String(tfMap["replace"].(string)), + } + apiObjects = append(apiObjects, apiObject) + } + return apiObjects +} + func flattenHostHeaderConditionConfig(apiObject *awstypes.HostHeaderConditionConfig) map[string]any { if apiObject == nil { return nil @@ -1039,3 +1190,66 @@ func flattenPathPatternConditionConfig(apiObject *awstypes.PathPatternConditionC } return tfMap } + +func flattenRuleTransforms(apiObjects []awstypes.RuleTransform) []any { + if len(apiObjects) == 0 { + return nil + } + var tfList []any + + for _, apiObject := range apiObjects { + tfMap := make(map[string]any) + + if v := string(apiObject.Type); v != "" { + tfMap[names.AttrType] = v + } + if v := flattenHostHeaderRewriteConfig(apiObject.HostHeaderRewriteConfig); v != nil { + tfMap["host_header_rewrite_config"] = []any{v} + } + if v := flattenURLRewriteConfig(apiObject.UrlRewriteConfig); v != nil { + tfMap["url_rewrite_config"] = []any{v} + } + tfList = append(tfList, tfMap) + } + return tfList +} + +func flattenHostHeaderRewriteConfig(apiObject *awstypes.HostHeaderRewriteConfig) map[string]any { + if apiObject == nil { + return nil + } + tfMap := make(map[string]any) + + if v := flattenRewriteConfig(apiObject.Rewrites); v != nil { + tfMap["rewrite"] = v + } + return tfMap +} + +func flattenURLRewriteConfig(apiObject *awstypes.UrlRewriteConfig) map[string]any { + if apiObject == nil { + return nil + } + tfMap := make(map[string]any) + + if v := flattenRewriteConfig(apiObject.Rewrites); v != nil { + tfMap["rewrite"] = v + } + return tfMap +} + +func flattenRewriteConfig(apiObjects []awstypes.RewriteConfig) []any { + if len(apiObjects) == 0 { + return nil + } + var tfList []any + + for _, apiObject := range apiObjects { + tfMap := map[string]any{ + "regex": aws.ToString(apiObject.Regex), + "replace": aws.ToString(apiObject.Replace), + } + tfList = append(tfList, tfMap) + } + return tfList +} From 110e9d08e5738e243175941052b41c6429911396 Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 06:17:05 +0900 Subject: [PATCH 02/21] Add an acceptance test for transform block --- internal/service/elbv2/listener_rule_test.go | 140 +++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/internal/service/elbv2/listener_rule_test.go b/internal/service/elbv2/listener_rule_test.go index 76cbe995e26c..51411d2d8315 100644 --- a/internal/service/elbv2/listener_rule_test.go +++ b/internal/service/elbv2/listener_rule_test.go @@ -2227,6 +2227,68 @@ func TestAccELBV2ListenerRule_conditionUpdateMultiple(t *testing.T) { }) } +func TestAccELBV2ListenerRule_transform(t *testing.T) { + ctx := acctest.Context(t) + var conf awstypes.Rule + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lb_listener_rule.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ELBV2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckListenerRuleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccListenerRuleConfig_transform(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckListenerRuleExists(ctx, resourceName, &conf), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "transform.*", map[string]string{ + names.AttrType: string(awstypes.TransformTypeEnumHostHeaderRewrite), + "host_header_rewrite_config.#": "1", + "host_header_rewrite_config.0.rewrite.#": "1", + "host_header_rewrite_config.0.rewrite.0.regex": "^mywebsite-(.+).com$", + "host_header_rewrite_config.0.rewrite.0.replace": "internal.dev.$1.myweb.com", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "transform.*", map[string]string{ + names.AttrType: string(awstypes.TransformTypeEnumUrlRewrite), + "url_rewrite_config.#": "1", + "url_rewrite_config.0.rewrite.#": "1", + "url_rewrite_config.0.rewrite.0.regex": "^/dp/([A-Za-z0-9]+)/?$", + "url_rewrite_config.0.rewrite.0.replace": "/product.php?id=$1", + }), + ), + }, + { + Config: testAccListenerRuleConfig_transformUpdated(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckListenerRuleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccListenerRuleConfig_transformUpdated(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckListenerRuleExists(ctx, resourceName, &conf), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "transform.*", map[string]string{ + names.AttrType: string(awstypes.TransformTypeEnumHostHeaderRewrite), + "host_header_rewrite_config.#": "1", + "host_header_rewrite_config.0.rewrite.#": "1", + "host_header_rewrite_config.0.rewrite.0.regex": "^mywebsite2-(.+).com$", + "host_header_rewrite_config.0.rewrite.0.replace": "internal.dev.$1.myweb.com", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "transform.*", map[string]string{ + names.AttrType: string(awstypes.TransformTypeEnumUrlRewrite), + "url_rewrite_config.#": "1", + "url_rewrite_config.0.rewrite.#": "1", + "url_rewrite_config.0.rewrite.0.regex": "^/dp2/([A-Za-z0-9]+)/?$", + "url_rewrite_config.0.rewrite.0.replace": "/product.php?id=$1", + }), + ), + }, + }, + }) +} + func testAccCheckListenerRuleActionOrderDisappears(ctx context.Context, rule *awstypes.Rule, actionOrderToDelete int) resource.TestCheckFunc { return func(s *terraform.State) error { var newActions []awstypes.Action @@ -4892,3 +4954,81 @@ condition { } `, lbName) } + +func testAccListenerRuleConfig_transform(rName string) string { + return acctest.ConfigCompose(testAccListenerRuleConfig_baseWithHTTPListener(rName), ` +resource "aws_lb_listener_rule" "test" { + listener_arn = aws_lb_listener.test.arn + + action { + type = "forward" + target_group_arn = aws_lb_target_group.test.arn + } + + condition { + path_pattern { + values = ["*"] + } + } + + transform { + type = "host-header-rewrite" + host_header_rewrite_config { + rewrite { + regex = "^mywebsite-(.+).com$" + replace = "internal.dev.$1.myweb.com" + } + } + } + + transform { + type = "url-rewrite" + url_rewrite_config { + rewrite { + regex = "^/dp/([A-Za-z0-9]+)/?$" + replace = "/product.php?id=$1" + } + } + } +} +`) +} + +func testAccListenerRuleConfig_transformUpdated(rName string) string { + return acctest.ConfigCompose(testAccListenerRuleConfig_baseWithHTTPListener(rName), ` +resource "aws_lb_listener_rule" "test" { + listener_arn = aws_lb_listener.test.arn + + action { + type = "forward" + target_group_arn = aws_lb_target_group.test.arn + } + + condition { + path_pattern { + values = ["*"] + } + } + + transform { + type = "url-rewrite" + url_rewrite_config { + rewrite { + regex = "^/dp2/([A-Za-z0-9]+)/?$" + replace = "/product.php?id=$1" + } + } + } + + transform { + type = "host-header-rewrite" + host_header_rewrite_config { + rewrite { + regex = "^mywebsite2-(.+).com$" + replace = "internal.dev.$1.myweb.com" + } + } + } +} +`) +} From 652fa7d4895f90a227f259cf9dacfe78cef1ef1e Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 06:17:26 +0900 Subject: [PATCH 03/21] Implement transform block for the data source --- .../elbv2/listener_rule_data_source.go | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/internal/service/elbv2/listener_rule_data_source.go b/internal/service/elbv2/listener_rule_data_source.go index 3974d4a46411..4ed8be2c15b6 100644 --- a/internal/service/elbv2/listener_rule_data_source.go +++ b/internal/service/elbv2/listener_rule_data_source.go @@ -313,10 +313,61 @@ func (d *listenerRuleDataSource) Schema(ctx context.Context, req datasource.Sche }, }, }, + "transform": schema.SetNestedBlock{ + CustomType: fwtypes.NewSetNestedObjectTypeOf[transformModel](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + names.AttrType: schema.StringAttribute{ + Computed: true, + }, + }, + Blocks: map[string]schema.Block{ + "host_header_rewrite_config": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[hostHeaderRewriteConfigModel](ctx), + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "rewrite": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[rewriteConfigModel](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "regex": schema.StringAttribute{ + Computed: true, + }, + "replace": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "url_rewrite_config": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[urlRewriteConfigModel](ctx), + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "rewrite": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[rewriteConfigModel](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "regex": schema.StringAttribute{ + Computed: true, + }, + "replace": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, }, } } - func (d *listenerRuleDataSource) ConfigValidators(_ context.Context) []datasource.ConfigValidator { return []datasource.ConfigValidator{ datasourcevalidator.ExactlyOneOf( @@ -397,6 +448,7 @@ type listenerRuleDataSourceModel struct { ListenerARN fwtypes.ARN `tfsdk:"listener_arn"` Priority types.Int32 `tfsdk:"priority" autoflex:"-"` Tags tftags.Map `tfsdk:"tags"` + Transform fwtypes.SetNestedObjectValueOf[transformModel] `tfsdk:"transform"` } // The API includes a TargetGroupArn field at the root level of the Action. This only applies when Type == "forward" @@ -507,3 +559,22 @@ type queryStringKeyValuePairModel struct { type sourceIPConfigModel struct { Values fwtypes.SetValueOf[types.String] `tfsdk:"values"` } + +type transformModel struct { + Type types.String `tfsdk:"type"` + HostHeaderRewriteConfig fwtypes.ListNestedObjectValueOf[hostHeaderRewriteConfigModel] `tfsdk:"host_header_rewrite_config"` + URLRewriteConfig fwtypes.ListNestedObjectValueOf[urlRewriteConfigModel] `tfsdk:"url_rewrite_config"` +} + +type hostHeaderRewriteConfigModel struct { + Rewrites fwtypes.ListNestedObjectValueOf[rewriteConfigModel] `tfsdk:"rewrite"` +} + +type urlRewriteConfigModel struct { + Rewrites fwtypes.ListNestedObjectValueOf[rewriteConfigModel] `tfsdk:"rewrite"` +} + +type rewriteConfigModel struct { + Regex types.String `tfsdk:"regex"` + Replace types.String `tfsdk:"replace"` +} From b8733f00729f308474a6fe08d4483d012e63bb87 Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 06:17:47 +0900 Subject: [PATCH 04/21] Add an acceptance test for transform block in the data source --- .../elbv2/listener_rule_data_source_test.go | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/internal/service/elbv2/listener_rule_data_source_test.go b/internal/service/elbv2/listener_rule_data_source_test.go index c9986317b19d..407989ee42af 100644 --- a/internal/service/elbv2/listener_rule_data_source_test.go +++ b/internal/service/elbv2/listener_rule_data_source_test.go @@ -921,6 +921,78 @@ func TestAccELBV2ListenerRuleDataSource_conditionSourceIP(t *testing.T) { }) } +func TestAccELBV2ListenerRuleDataSource_transform(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var listenerRule awstypes.Rule + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_lb_listener_rule.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ELBV2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckListenerRuleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccListenerRuleDataSourceConfig_transform(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckListenerRuleExists(ctx, dataSourceName, &listenerRule), + resource.TestCheckTypeSetElemNestedAttrs(dataSourceName, "transform.*", map[string]string{ + names.AttrType: "host-header-rewrite", + "host_header_rewrite_config.#": "1", + "host_header_rewrite_config.0.rewrite.#": "1", + "host_header_rewrite_config.0.rewrite.0.regex": "^mywebsite-(.+).com$", + "host_header_rewrite_config.0.rewrite.0.replace": "internal.dev.$1.myweb.com", + }), + resource.TestCheckTypeSetElemNestedAttrs(dataSourceName, "transform.*", map[string]string{ + names.AttrType: "url-rewrite", + "url_rewrite_config.#": "1", + "url_rewrite_config.0.rewrite.#": "1", + "url_rewrite_config.0.rewrite.0.regex": "^/dp/([A-Za-z0-9]+)/?$", + "url_rewrite_config.0.rewrite.0.replace": "/product.php?id=$1", + }), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("transform"), knownvalue.SetExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + names.AttrType: knownvalue.StringExact(string(awstypes.TransformTypeEnumHostHeaderRewrite)), + "host_header_rewrite_config": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "rewrite": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "regex": knownvalue.StringExact("^mywebsite-(.+).com$"), + "replace": knownvalue.StringExact("internal.dev.$1.myweb.com"), + }), + }), + }), + }), + "url_rewrite_config": knownvalue.ListExact([]knownvalue.Check{}), + }), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + names.AttrType: knownvalue.StringExact(string(awstypes.TransformTypeEnumUrlRewrite)), + "host_header_rewrite_config": knownvalue.ListExact([]knownvalue.Check{}), + "url_rewrite_config": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "rewrite": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "regex": knownvalue.StringExact("^/dp/([A-Za-z0-9]+)/?$"), + "replace": knownvalue.StringExact("/product.php?id=$1"), + }), + }), + }), + }), + }), + })), + }, + }, + }, + }) +} + func expectKnownCondition(key string, check knownvalue.Check) knownvalue.Check { checks := map[string]knownvalue.Check{ "host_header": knownvalue.ListExact([]knownvalue.Check{}), @@ -1508,3 +1580,46 @@ resource "aws_lb_listener_rule" "test" { } `) } + +func testAccListenerRuleDataSourceConfig_transform(rName string) string { + return acctest.ConfigCompose(testAccListenerRuleConfig_baseWithHTTPListener(rName), ` +data "aws_lb_listener_rule" "test" { + arn = aws_lb_listener_rule.test.arn +} + +resource "aws_lb_listener_rule" "test" { + listener_arn = aws_lb_listener.test.arn + + action { + type = "forward" + target_group_arn = aws_lb_target_group.test.arn + } + + condition { + path_pattern { + values = ["*"] + } + } + + transform { + type = "host-header-rewrite" + host_header_rewrite_config { + rewrite { + regex = "^mywebsite-(.+).com$" + replace = "internal.dev.$1.myweb.com" + } + } + } + + transform { + type = "url-rewrite" + url_rewrite_config { + rewrite { + regex = "^/dp/([A-Za-z0-9]+)/?$" + replace = "/product.php?id=$1" + } + } + } +} +`) +} From 4acd1b81337d254472934dd2e8f6dd8dc6df146d Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 06:18:11 +0900 Subject: [PATCH 05/21] Update the documentation for the resource to include transform block --- website/docs/r/lb_listener_rule.html.markdown | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/website/docs/r/lb_listener_rule.html.markdown b/website/docs/r/lb_listener_rule.html.markdown index 262b599cea3a..717fe623ed50 100644 --- a/website/docs/r/lb_listener_rule.html.markdown +++ b/website/docs/r/lb_listener_rule.html.markdown @@ -202,6 +202,43 @@ resource "aws_lb_listener_rule" "oidc" { target_group_arn = aws_lb_target_group.static.arn } } + +# With transform + +resource "aws_lb_listener_rule" "transform" { + listener_arn = aws_lb_listener.front_end.arn + + action { + type = "forward" + target_group_arn = aws_lb_target_group.static.arn + } + + condition { + path_pattern { + values = ["*"] + } + } + + transform { + type = "host-header-rewrite" + host_header_rewrite_config { + rewrite { + regex = "^mywebsite-(.+).com$" + replace = "internal.dev.$1.myweb.com" + } + } + } + + transform { + type = "url-rewrite" + url_rewrite_config { + rewrite { + regex = "^/dp/([A-Za-z0-9]+)/?$" + replace = "/product.php?id=$1" + } + } + } +} ``` ## Argument Reference @@ -214,6 +251,7 @@ This resource supports the following arguments: * `action` - (Required) An Action block. Action blocks are documented below. * `condition` - (Required) A Condition block. Multiple condition blocks of different types can be set and all must be satisfied for the rule to match. Condition blocks are documented below. * `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. +* `transform` - (Optional) Configuration block for transform to apply to requests that match this rule. See [Transform Blocks](#transform-blocks) below. ### Action Blocks @@ -346,6 +384,33 @@ Query String Value Blocks (for `query_string.values`) support the following: * `key` - (Optional) Query string key pattern to match. * `value` - (Required) Query string value pattern to match. +#### Transform Blocks + +Transform Blocks (for `transform`) support the following: + +* `type` - (Required) Type of transform. Valid values are `host-header-rewrite` and `url-rewrite`. +* `host_header_rewrite_config` - (Optional) Configuration block for host header rewrite. Required if `type` is `host-header-rewrite`. See [Host Header Rewrite Config Blocks](#host-header-rewrite-config-blocks) below. +* `url_rewrite_config` - (Optional) Configuration block for URL rewrite. Required if `type` is `url-rewrite`. See [URL Rewrite Config Blocks](#url-rewrite-config-blocks) below. + +### Host Header Rewrite Config Blocks + +Host Header Rewrite Config Blocks (for `host_header_rewrite_config`) support the following: + +* `rewrite` - (Optional) Block for host header rewrite configuration. Only one block is accepted. See [Rewrite Blocks](#rewrite-blocks) below. + +### URL Rewrite Config Blocks + +URL Rewrite Config Blocks (for `url_rewrite_config`) support the following: + +* `rewrite` - (Optional) Block for URL rewrite configuration. Only one block is accepted. See [Rewrite Blocks](#rewrite-blocks) below. + +### Rewrite Blocks + +Rewrite Blocks (for `rewrite`) support the following: + +* `regex` - (Required) Regular expression to match in the input string. +* `replace` - (Required) Replacement string to use when rewriting the matched input. Capture groups in the regular expression (for example, `$1` and `$2`) can be specified. + ## Attribute Reference This resource exports the following attributes in addition to the arguments above: From 8988f69103ceb3c08edf411010cae8f1b4aae85c Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 06:25:07 +0900 Subject: [PATCH 06/21] Update the documentation for the data source to include transform block --- website/docs/d/lb_listener_rule.html.markdown | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/website/docs/d/lb_listener_rule.html.markdown b/website/docs/d/lb_listener_rule.html.markdown index 7c4428da4c8b..2578f4de13b7 100644 --- a/website/docs/d/lb_listener_rule.html.markdown +++ b/website/docs/d/lb_listener_rule.html.markdown @@ -62,6 +62,7 @@ This data source exports the following attributes in addition to the arguments a * `condition` - Set of conditions associated with the rule. [Detailed below](#condition). * `tags` - Tags assigned to the Listener Rule. +* `transform` Block for transform to apply to requests that match this rule. [Detailed below](#transform). ### `action` @@ -173,3 +174,22 @@ This data source exports the following attributes in addition to the arguments a #### `query_string` * `values` - Set of `key`-`value` pairs indicating the query string parameters to match. + +### `transform` + +* `type` - Type of transform. +* `host_header_rewrite_config` - Block for host header rewrite. [Detailed below](#host_header_rewrite_config). +* `url_rewrite_config` - Block for URL rewrite. [Detailed below](#url_rewrite_config). + +#### `host_header_rewrite_config` + +* `rewrite` - Block for host header rewrite configuration. [Detailed below](#rewrite). + +#### `url_rewrite_config` + +* `rewrite` - Block for URL rewrite configuration. [Detailed below](#rewrite). + +#### `rewrite` + +* `regex` - Regular expression to match in the input string. +* `replace` - Replacement string to use when rewriting the matched input. From 2d5e0fe00791aa9a3afe897ca72fb4a0533045c1 Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 06:34:49 +0900 Subject: [PATCH 07/21] terraform fmt --- .../elbv2/listener_rule_data_source_test.go | 22 +++++----- internal/service/elbv2/listener_rule_test.go | 44 +++++++++---------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/internal/service/elbv2/listener_rule_data_source_test.go b/internal/service/elbv2/listener_rule_data_source_test.go index 407989ee42af..26f2716703df 100644 --- a/internal/service/elbv2/listener_rule_data_source_test.go +++ b/internal/service/elbv2/listener_rule_data_source_test.go @@ -1596,29 +1596,29 @@ resource "aws_lb_listener_rule" "test" { } condition { - path_pattern { - values = ["*"] - } + path_pattern { + values = ["*"] + } } transform { type = "host-header-rewrite" host_header_rewrite_config { - rewrite { - regex = "^mywebsite-(.+).com$" - replace = "internal.dev.$1.myweb.com" + rewrite { + regex = "^mywebsite-(.+).com$" + replace = "internal.dev.$1.myweb.com" } - } + } } transform { type = "url-rewrite" url_rewrite_config { - rewrite { - regex = "^/dp/([A-Za-z0-9]+)/?$" - replace = "/product.php?id=$1" + rewrite { + regex = "^/dp/([A-Za-z0-9]+)/?$" + replace = "/product.php?id=$1" } - } + } } } `) diff --git a/internal/service/elbv2/listener_rule_test.go b/internal/service/elbv2/listener_rule_test.go index 51411d2d8315..7421d59a0844 100644 --- a/internal/service/elbv2/listener_rule_test.go +++ b/internal/service/elbv2/listener_rule_test.go @@ -4966,29 +4966,29 @@ resource "aws_lb_listener_rule" "test" { } condition { - path_pattern { - values = ["*"] - } + path_pattern { + values = ["*"] + } } transform { type = "host-header-rewrite" host_header_rewrite_config { - rewrite { - regex = "^mywebsite-(.+).com$" - replace = "internal.dev.$1.myweb.com" + rewrite { + regex = "^mywebsite-(.+).com$" + replace = "internal.dev.$1.myweb.com" } - } + } } transform { type = "url-rewrite" url_rewrite_config { - rewrite { - regex = "^/dp/([A-Za-z0-9]+)/?$" - replace = "/product.php?id=$1" + rewrite { + regex = "^/dp/([A-Za-z0-9]+)/?$" + replace = "/product.php?id=$1" } - } + } } } `) @@ -5005,29 +5005,29 @@ resource "aws_lb_listener_rule" "test" { } condition { - path_pattern { - values = ["*"] - } + path_pattern { + values = ["*"] + } } transform { type = "url-rewrite" url_rewrite_config { - rewrite { - regex = "^/dp2/([A-Za-z0-9]+)/?$" - replace = "/product.php?id=$1" + rewrite { + regex = "^/dp2/([A-Za-z0-9]+)/?$" + replace = "/product.php?id=$1" } - } + } } transform { type = "host-header-rewrite" host_header_rewrite_config { - rewrite { - regex = "^mywebsite2-(.+).com$" - replace = "internal.dev.$1.myweb.com" + rewrite { + regex = "^mywebsite2-(.+).com$" + replace = "internal.dev.$1.myweb.com" } - } + } } } `) From bc7f2c2da1c9b307344c9152daa16241cbca4942 Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 06:36:22 +0900 Subject: [PATCH 08/21] add changelog --- .changelog/44702.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/44702.txt diff --git a/.changelog/44702.txt b/.changelog/44702.txt new file mode 100644 index 000000000000..3aa8d95e9aa0 --- /dev/null +++ b/.changelog/44702.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_lb_listener_rule: Add `transform` configuration block +``` From 54715f3acf12283d9226d07a38332fe84e1a568a Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 06:38:50 +0900 Subject: [PATCH 09/21] restore a removed empty line --- internal/service/elbv2/listener_rule_data_source.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/elbv2/listener_rule_data_source.go b/internal/service/elbv2/listener_rule_data_source.go index 4ed8be2c15b6..8510570b7729 100644 --- a/internal/service/elbv2/listener_rule_data_source.go +++ b/internal/service/elbv2/listener_rule_data_source.go @@ -368,6 +368,7 @@ func (d *listenerRuleDataSource) Schema(ctx context.Context, req datasource.Sche }, } } + func (d *listenerRuleDataSource) ConfigValidators(_ context.Context) []datasource.ConfigValidator { return []datasource.ConfigValidator{ datasourcevalidator.ExactlyOneOf( From 048231247e500c6e9fd3484a40724be0e943093f Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 07:27:24 +0900 Subject: [PATCH 10/21] acctest: remove duplicated checks --- .../elbv2/listener_rule_data_source_test.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/internal/service/elbv2/listener_rule_data_source_test.go b/internal/service/elbv2/listener_rule_data_source_test.go index 26f2716703df..b30adb5c2c68 100644 --- a/internal/service/elbv2/listener_rule_data_source_test.go +++ b/internal/service/elbv2/listener_rule_data_source_test.go @@ -941,20 +941,6 @@ func TestAccELBV2ListenerRuleDataSource_transform(t *testing.T) { Config: testAccListenerRuleDataSourceConfig_transform(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckListenerRuleExists(ctx, dataSourceName, &listenerRule), - resource.TestCheckTypeSetElemNestedAttrs(dataSourceName, "transform.*", map[string]string{ - names.AttrType: "host-header-rewrite", - "host_header_rewrite_config.#": "1", - "host_header_rewrite_config.0.rewrite.#": "1", - "host_header_rewrite_config.0.rewrite.0.regex": "^mywebsite-(.+).com$", - "host_header_rewrite_config.0.rewrite.0.replace": "internal.dev.$1.myweb.com", - }), - resource.TestCheckTypeSetElemNestedAttrs(dataSourceName, "transform.*", map[string]string{ - names.AttrType: "url-rewrite", - "url_rewrite_config.#": "1", - "url_rewrite_config.0.rewrite.#": "1", - "url_rewrite_config.0.rewrite.0.regex": "^/dp/([A-Za-z0-9]+)/?$", - "url_rewrite_config.0.rewrite.0.replace": "/product.php?id=$1", - }), ), ConfigStateChecks: []statecheck.StateCheck{ statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("transform"), knownvalue.SetExact([]knownvalue.Check{ From 2b5495a92ee3cccd113e387db021dd21cb223568 Mon Sep 17 00:00:00 2001 From: tabito Date: Sat, 18 Oct 2025 07:31:44 +0900 Subject: [PATCH 11/21] fix validations for character length --- internal/service/elbv2/listener_rule.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/elbv2/listener_rule.go b/internal/service/elbv2/listener_rule.go index d6b7b11b00df..05540626b4a2 100644 --- a/internal/service/elbv2/listener_rule.go +++ b/internal/service/elbv2/listener_rule.go @@ -540,7 +540,7 @@ func resourceListenerRule() *schema.Resource { "regex": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringLenBetween(0, 1024), + ValidateFunc: validation.StringLenBetween(1, 1024), }, "replace": { Type: schema.TypeString, From 169c1e2efe1550f32b94e74fd618dfecafa081cb Mon Sep 17 00:00:00 2001 From: tabito Date: Sun, 19 Oct 2025 08:12:21 +0900 Subject: [PATCH 12/21] Add operation to remove transform --- internal/service/elbv2/listener_rule.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/service/elbv2/listener_rule.go b/internal/service/elbv2/listener_rule.go index 05540626b4a2..81aa643b2a5c 100644 --- a/internal/service/elbv2/listener_rule.go +++ b/internal/service/elbv2/listener_rule.go @@ -774,7 +774,11 @@ func resourceListenerRuleUpdate(ctx context.Context, d *schema.ResourceData, met } if d.HasChange("transform") { - input.Transforms = expandRuleTransforms(d.Get("transform").(*schema.Set).List()) + if v, ok := d.GetOk("transform"); ok && len(v.(*schema.Set).List()) > 0 { + input.Transforms = expandRuleTransforms(d.Get("transform").(*schema.Set).List()) + } else { + input.ResetTransforms = aws.Bool(true) + } requestUpdate = true } From 5d2005abe4ed8e8a639f982767ab88d266c50d75 Mon Sep 17 00:00:00 2001 From: tabito Date: Sun, 19 Oct 2025 08:13:11 +0900 Subject: [PATCH 13/21] acctest: Fix to cover removing transform --- internal/service/elbv2/listener_rule_test.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/internal/service/elbv2/listener_rule_test.go b/internal/service/elbv2/listener_rule_test.go index 7421d59a0844..56d4c1819753 100644 --- a/internal/service/elbv2/listener_rule_test.go +++ b/internal/service/elbv2/listener_rule_test.go @@ -2259,12 +2259,6 @@ func TestAccELBV2ListenerRule_transform(t *testing.T) { }), ), }, - { - Config: testAccListenerRuleConfig_transformUpdated(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckListenerRuleExists(ctx, resourceName, &conf), - ), - }, { Config: testAccListenerRuleConfig_transformUpdated(rName), Check: resource.ComposeAggregateTestCheckFunc( @@ -2285,6 +2279,14 @@ func TestAccELBV2ListenerRule_transform(t *testing.T) { }), ), }, + { + // Remove all transforms + Config: testAccListenerRuleConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckListenerRuleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "transform.#", "0"), + ), + }, }, }) } @@ -4959,6 +4961,7 @@ func testAccListenerRuleConfig_transform(rName string) string { return acctest.ConfigCompose(testAccListenerRuleConfig_baseWithHTTPListener(rName), ` resource "aws_lb_listener_rule" "test" { listener_arn = aws_lb_listener.test.arn + priority = 100 action { type = "forward" @@ -4967,7 +4970,7 @@ resource "aws_lb_listener_rule" "test" { condition { path_pattern { - values = ["*"] + values = ["/static/*"] } } @@ -4998,6 +5001,7 @@ func testAccListenerRuleConfig_transformUpdated(rName string) string { return acctest.ConfigCompose(testAccListenerRuleConfig_baseWithHTTPListener(rName), ` resource "aws_lb_listener_rule" "test" { listener_arn = aws_lb_listener.test.arn + priority = 100 action { type = "forward" @@ -5006,7 +5010,7 @@ resource "aws_lb_listener_rule" "test" { condition { path_pattern { - values = ["*"] + values = ["/static/*"] } } From 2dc0a71bea2fe05df76f7c36c59e2d1015c6aa57 Mon Sep 17 00:00:00 2001 From: tabito Date: Sun, 19 Oct 2025 08:25:56 +0900 Subject: [PATCH 14/21] docs: Add description of character length constraints --- website/docs/r/lb_listener_rule.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/lb_listener_rule.html.markdown b/website/docs/r/lb_listener_rule.html.markdown index 717fe623ed50..29f67dcb4eb3 100644 --- a/website/docs/r/lb_listener_rule.html.markdown +++ b/website/docs/r/lb_listener_rule.html.markdown @@ -408,8 +408,8 @@ URL Rewrite Config Blocks (for `url_rewrite_config`) support the following: Rewrite Blocks (for `rewrite`) support the following: -* `regex` - (Required) Regular expression to match in the input string. -* `replace` - (Required) Replacement string to use when rewriting the matched input. Capture groups in the regular expression (for example, `$1` and `$2`) can be specified. +* `regex` - (Required) Regular expression to match in the input string. Length constraints: Between 1 and 1024 characters. +* `replace` - (Required) Replacement string to use when rewriting the matched input. Capture groups in the regular expression (for example, `$1` and `$2`) can be specified. Length constraints: Between 0 and 1024 characters. ## Attribute Reference From cf1ad2c2b4104465a2160f64a3bcf16d2350472e Mon Sep 17 00:00:00 2001 From: tabito Date: Sun, 19 Oct 2025 08:27:04 +0900 Subject: [PATCH 15/21] refactor: introduce a function to make rewrite schema for the resource --- internal/service/elbv2/listener_rule.go | 62 ++++++++++--------------- 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/internal/service/elbv2/listener_rule.go b/internal/service/elbv2/listener_rule.go index 81aa643b2a5c..58f6918aa5d4 100644 --- a/internal/service/elbv2/listener_rule.go +++ b/internal/service/elbv2/listener_rule.go @@ -503,25 +503,7 @@ func resourceListenerRule() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "rewrite": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, // This argument is an array, but the current AWS API accepts exactly only one `rewrite` - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "regex": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 1024), - }, - "replace": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(0, 1024), - }, - }, - }, - }, + "rewrite": transformRewriteConfigSchema(), }, }, }, @@ -531,25 +513,7 @@ func resourceListenerRule() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "rewrite": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, // This argument is an array, but the current AWS API accepts exactly only one `rewrite` - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "regex": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 1024), - }, - "replace": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(0, 1024), - }, - }, - }, - }, + "rewrite": transformRewriteConfigSchema(), }, }, }, @@ -564,6 +528,28 @@ func resourceListenerRule() *schema.Resource { } } +func transformRewriteConfigSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, // This argument is an array, but the current AWS API accepts exactly only one `rewrite` + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "regex": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, + "replace": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 1024), + }, + }, + }, + } +} + func suppressIfActionTypeNot(t awstypes.ActionTypeEnum) schema.SchemaDiffSuppressFunc { return func(k, old, new string, d *schema.ResourceData) bool { take := 2 From 5a2da6e812256f8616d5ed1436871d7d9cab0095 Mon Sep 17 00:00:00 2001 From: tabito Date: Sun, 19 Oct 2025 08:27:59 +0900 Subject: [PATCH 16/21] refactor: introduce a function to make rewrite schema for the data resource --- .../elbv2/listener_rule_data_source.go | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/internal/service/elbv2/listener_rule_data_source.go b/internal/service/elbv2/listener_rule_data_source.go index 8510570b7729..46b5fdea596c 100644 --- a/internal/service/elbv2/listener_rule_data_source.go +++ b/internal/service/elbv2/listener_rule_data_source.go @@ -326,19 +326,7 @@ func (d *listenerRuleDataSource) Schema(ctx context.Context, req datasource.Sche CustomType: fwtypes.NewListNestedObjectTypeOf[hostHeaderRewriteConfigModel](ctx), NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ - "rewrite": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[rewriteConfigModel](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "regex": schema.StringAttribute{ - Computed: true, - }, - "replace": schema.StringAttribute{ - Computed: true, - }, - }, - }, - }, + "rewrite": transformRewriteConfigDataSourceSchema(), }, }, }, @@ -346,19 +334,7 @@ func (d *listenerRuleDataSource) Schema(ctx context.Context, req datasource.Sche CustomType: fwtypes.NewListNestedObjectTypeOf[urlRewriteConfigModel](ctx), NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ - "rewrite": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[rewriteConfigModel](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "regex": schema.StringAttribute{ - Computed: true, - }, - "replace": schema.StringAttribute{ - Computed: true, - }, - }, - }, - }, + "rewrite": transformRewriteConfigDataSourceSchema(), }, }, }, @@ -369,6 +345,22 @@ func (d *listenerRuleDataSource) Schema(ctx context.Context, req datasource.Sche } } +func transformRewriteConfigDataSourceSchema() schema.Block { + return schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[rewriteConfigModel](context.Background()), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "regex": schema.StringAttribute{ + Computed: true, + }, + "replace": schema.StringAttribute{ + Computed: true, + }, + }, + }, + } +} + func (d *listenerRuleDataSource) ConfigValidators(_ context.Context) []datasource.ConfigValidator { return []datasource.ConfigValidator{ datasourcevalidator.ExactlyOneOf( From 31541736b45f74432a49f9ac42042926eaa32ce7 Mon Sep 17 00:00:00 2001 From: tabito Date: Sun, 19 Oct 2025 09:27:08 +0900 Subject: [PATCH 17/21] docs: Add description when removing the transform --- website/docs/r/lb_listener_rule.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lb_listener_rule.html.markdown b/website/docs/r/lb_listener_rule.html.markdown index 29f67dcb4eb3..bea63266cdf1 100644 --- a/website/docs/r/lb_listener_rule.html.markdown +++ b/website/docs/r/lb_listener_rule.html.markdown @@ -251,7 +251,7 @@ This resource supports the following arguments: * `action` - (Required) An Action block. Action blocks are documented below. * `condition` - (Required) A Condition block. Multiple condition blocks of different types can be set and all must be satisfied for the rule to match. Condition blocks are documented below. * `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. -* `transform` - (Optional) Configuration block for transform to apply to requests that match this rule. See [Transform Blocks](#transform-blocks) below. +* `transform` - (Optional) Configuration block that defines the transform to apply to requests matching this rule. See [Transform Blocks](#transform-blocks) below for more details. Once specified, to remove the transform from the rule, remove the `transform` block from the configuration. ### Action Blocks From 05a59f41e1ce5276c45215e8a735566dc736382e Mon Sep 17 00:00:00 2001 From: tabito Date: Sun, 19 Oct 2025 09:29:54 +0900 Subject: [PATCH 18/21] fix format --- website/docs/d/lb_listener_rule.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/d/lb_listener_rule.html.markdown b/website/docs/d/lb_listener_rule.html.markdown index 2578f4de13b7..0a7e05573313 100644 --- a/website/docs/d/lb_listener_rule.html.markdown +++ b/website/docs/d/lb_listener_rule.html.markdown @@ -62,7 +62,7 @@ This data source exports the following attributes in addition to the arguments a * `condition` - Set of conditions associated with the rule. [Detailed below](#condition). * `tags` - Tags assigned to the Listener Rule. -* `transform` Block for transform to apply to requests that match this rule. [Detailed below](#transform). +* `transform` - Block for transform to apply to requests that match this rule. [Detailed below](#transform). ### `action` From cd5065ead0ee6c013129a3b7e999dbe44e7ee554 Mon Sep 17 00:00:00 2001 From: tabito Date: Sun, 19 Oct 2025 10:06:57 +0900 Subject: [PATCH 19/21] use context --- internal/service/elbv2/listener_rule_data_source.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/elbv2/listener_rule_data_source.go b/internal/service/elbv2/listener_rule_data_source.go index 46b5fdea596c..a49ac76f511f 100644 --- a/internal/service/elbv2/listener_rule_data_source.go +++ b/internal/service/elbv2/listener_rule_data_source.go @@ -326,7 +326,7 @@ func (d *listenerRuleDataSource) Schema(ctx context.Context, req datasource.Sche CustomType: fwtypes.NewListNestedObjectTypeOf[hostHeaderRewriteConfigModel](ctx), NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ - "rewrite": transformRewriteConfigDataSourceSchema(), + "rewrite": transformRewriteConfigDataSourceSchema(ctx), }, }, }, @@ -334,7 +334,7 @@ func (d *listenerRuleDataSource) Schema(ctx context.Context, req datasource.Sche CustomType: fwtypes.NewListNestedObjectTypeOf[urlRewriteConfigModel](ctx), NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ - "rewrite": transformRewriteConfigDataSourceSchema(), + "rewrite": transformRewriteConfigDataSourceSchema(ctx), }, }, }, @@ -345,9 +345,9 @@ func (d *listenerRuleDataSource) Schema(ctx context.Context, req datasource.Sche } } -func transformRewriteConfigDataSourceSchema() schema.Block { +func transformRewriteConfigDataSourceSchema(ctx context.Context) schema.Block { return schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[rewriteConfigModel](context.Background()), + CustomType: fwtypes.NewListNestedObjectTypeOf[rewriteConfigModel](ctx), NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "regex": schema.StringAttribute{ From a653a861b43b802eea304ffb35eaea7902984b03 Mon Sep 17 00:00:00 2001 From: tabito Date: Sun, 19 Oct 2025 10:22:17 +0900 Subject: [PATCH 20/21] changelog: add an entry for the data source --- .changelog/44702.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.changelog/44702.txt b/.changelog/44702.txt index 3aa8d95e9aa0..493d4c188a50 100644 --- a/.changelog/44702.txt +++ b/.changelog/44702.txt @@ -1,3 +1,7 @@ ```release-note:enhancement resource/aws_lb_listener_rule: Add `transform` configuration block ``` + +```release-note:enhancement +data-source/aws_lb_listener_rule: Add `transform` attribute +``` From 500235b98730ee1f4a25ce97e26273a8c13a6fd4 Mon Sep 17 00:00:00 2001 From: tabito Date: Tue, 21 Oct 2025 22:22:22 +0900 Subject: [PATCH 21/21] acctest: Add a case where one of two entries is removed --- internal/service/elbv2/listener_rule_test.go | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/internal/service/elbv2/listener_rule_test.go b/internal/service/elbv2/listener_rule_test.go index 56d4c1819753..a5d9d3425a20 100644 --- a/internal/service/elbv2/listener_rule_test.go +++ b/internal/service/elbv2/listener_rule_test.go @@ -2243,6 +2243,7 @@ func TestAccELBV2ListenerRule_transform(t *testing.T) { Config: testAccListenerRuleConfig_transform(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckListenerRuleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "transform.#", "2"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "transform.*", map[string]string{ names.AttrType: string(awstypes.TransformTypeEnumHostHeaderRewrite), "host_header_rewrite_config.#": "1", @@ -2263,6 +2264,7 @@ func TestAccELBV2ListenerRule_transform(t *testing.T) { Config: testAccListenerRuleConfig_transformUpdated(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckListenerRuleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "transform.#", "2"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "transform.*", map[string]string{ names.AttrType: string(awstypes.TransformTypeEnumHostHeaderRewrite), "host_header_rewrite_config.#": "1", @@ -2279,6 +2281,20 @@ func TestAccELBV2ListenerRule_transform(t *testing.T) { }), ), }, + { + Config: testAccListenerRuleConfig_transformRemoveOneEntry(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckListenerRuleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "transform.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "transform.*", map[string]string{ + names.AttrType: string(awstypes.TransformTypeEnumUrlRewrite), + "url_rewrite_config.#": "1", + "url_rewrite_config.0.rewrite.#": "1", + "url_rewrite_config.0.rewrite.0.regex": "^/dp2/([A-Za-z0-9]+)/?$", + "url_rewrite_config.0.rewrite.0.replace": "/product.php?id=$1", + }), + ), + }, { // Remove all transforms Config: testAccListenerRuleConfig_basic(rName), @@ -5036,3 +5052,33 @@ resource "aws_lb_listener_rule" "test" { } `) } + +func testAccListenerRuleConfig_transformRemoveOneEntry(rName string) string { + return acctest.ConfigCompose(testAccListenerRuleConfig_baseWithHTTPListener(rName), ` +resource "aws_lb_listener_rule" "test" { + listener_arn = aws_lb_listener.test.arn + priority = 100 + + action { + type = "forward" + target_group_arn = aws_lb_target_group.test.arn + } + + condition { + path_pattern { + values = ["/static/*"] + } + } + + transform { + type = "url-rewrite" + url_rewrite_config { + rewrite { + regex = "^/dp2/([A-Za-z0-9]+)/?$" + replace = "/product.php?id=$1" + } + } + } +} +`) +}