Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c1ad631
Implement transform block
tabito-hara Oct 17, 2025
a4c936b
Add an acceptance test for transform block
tabito-hara Oct 17, 2025
df6cdcc
Implement transform block for the data source
tabito-hara Oct 17, 2025
110141e
Add an acceptance test for transform block in the data source
tabito-hara Oct 17, 2025
a73d71c
Update the documentation for the resource to include transform block
tabito-hara Oct 17, 2025
3b2228a
Update the documentation for the data source to include transform block
tabito-hara Oct 17, 2025
53ea18a
terraform fmt
tabito-hara Oct 17, 2025
383321c
add changelog
tabito-hara Oct 17, 2025
0310aa0
restore a removed empty line
tabito-hara Oct 17, 2025
42c4964
acctest: remove duplicated checks
tabito-hara Oct 17, 2025
167d7e6
fix validations for character length
tabito-hara Oct 17, 2025
64e2a1e
Add operation to remove transform
tabito-hara Oct 18, 2025
8460af3
acctest: Fix to cover removing transform
tabito-hara Oct 18, 2025
8fa09b6
docs: Add description of character length constraints
tabito-hara Oct 18, 2025
84e8a1d
refactor: introduce a function to make rewrite schema for the resource
tabito-hara Oct 18, 2025
80a2907
refactor: introduce a function to make rewrite schema for the data re…
tabito-hara Oct 18, 2025
03657ae
docs: Add description when removing the transform
tabito-hara Oct 19, 2025
7022653
fix format
tabito-hara Oct 19, 2025
cccdcda
use context
tabito-hara Oct 19, 2025
ee0d3d6
changelog: add an entry for the data source
tabito-hara Oct 19, 2025
a07d792
acctest: Add a case where one of two entries is removed
tabito-hara Oct 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/44702.txt
Original file line number Diff line number Diff line change
@@ -0,0 +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
```
202 changes: 202 additions & 0 deletions internal/service/elbv2/listener_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,40 @@ 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": transformRewriteConfigSchema(),
},
},
},
"url_rewrite_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"rewrite": transformRewriteConfigSchema(),
},
},
},
},
},
},
},

CustomizeDiff: customdiff.All(
Expand All @@ -470,6 +504,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
Expand Down Expand Up @@ -507,6 +563,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.
Expand Down Expand Up @@ -637,6 +697,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
}

Expand Down Expand Up @@ -684,6 +748,15 @@ func resourceListenerRuleUpdate(ctx context.Context, d *schema.ResourceData, met
requestUpdate = true
}

if d.HasChange("transform") {
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
}

if requestUpdate {
resp, err := conn.ModifyRule(ctx, input)
if err != nil {
Expand Down Expand Up @@ -943,3 +1016,132 @@ func expandRuleConditions(tfList []any) ([]awstypes.RuleCondition, error) {

return apiObjects, nil
}

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 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
}
64 changes: 64 additions & 0 deletions internal/service/elbv2/listener_rule_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,50 @@ 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": transformRewriteConfigDataSourceSchema(ctx),
},
},
},
"url_rewrite_config": schema.ListNestedBlock{
CustomType: fwtypes.NewListNestedObjectTypeOf[urlRewriteConfigModel](ctx),
NestedObject: schema.NestedBlockObject{
Blocks: map[string]schema.Block{
"rewrite": transformRewriteConfigDataSourceSchema(ctx),
},
},
},
},
},
},
},
}
}

func transformRewriteConfigDataSourceSchema(ctx context.Context) schema.Block {
return 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,
},
},
},
}
}
Expand Down Expand Up @@ -385,6 +429,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"
Expand Down Expand Up @@ -492,3 +537,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"`
}
Loading
Loading