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
267d011
Implement transform block
tabito-hara Oct 27, 2025
110e9d0
Add an acceptance test for transform block
tabito-hara Oct 17, 2025
652fa7d
Implement transform block for the data source
tabito-hara Oct 17, 2025
b8733f0
Add an acceptance test for transform block in the data source
tabito-hara Oct 17, 2025
4acd1b8
Update the documentation for the resource to include transform block
tabito-hara Oct 17, 2025
8988f69
Update the documentation for the data source to include transform block
tabito-hara Oct 17, 2025
2d5e0fe
terraform fmt
tabito-hara Oct 17, 2025
bc7f2c2
add changelog
tabito-hara Oct 17, 2025
54715f3
restore a removed empty line
tabito-hara Oct 17, 2025
0482312
acctest: remove duplicated checks
tabito-hara Oct 17, 2025
2b5495a
fix validations for character length
tabito-hara Oct 17, 2025
169c1e2
Add operation to remove transform
tabito-hara Oct 18, 2025
5d2005a
acctest: Fix to cover removing transform
tabito-hara Oct 18, 2025
2dc0a71
docs: Add description of character length constraints
tabito-hara Oct 18, 2025
cf1ad2c
refactor: introduce a function to make rewrite schema for the resource
tabito-hara Oct 18, 2025
5a2da6e
refactor: introduce a function to make rewrite schema for the data re…
tabito-hara Oct 18, 2025
3154173
docs: Add description when removing the transform
tabito-hara Oct 19, 2025
05a59f4
fix format
tabito-hara Oct 19, 2025
cd5065e
use context
tabito-hara Oct 19, 2025
a653a86
changelog: add an entry for the data source
tabito-hara Oct 19, 2025
500235b
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
```
204 changes: 204 additions & 0 deletions internal/service/elbv2/listener_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,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 @@ -494,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
Expand Down Expand Up @@ -531,6 +587,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 @@ -648,6 +708,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 @@ -695,6 +759,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 @@ -993,6 +1066,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
Expand Down Expand Up @@ -1039,3 +1180,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
}
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 @@ -313,6 +313,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 @@ -397,6 +441,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 @@ -507,3 +552,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