-
Notifications
You must be signed in to change notification settings - Fork 9.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
provider/aws: AWS WAF Regional IPSet + ByteMatchSet support #13705
Merged
Merged
Changes from 12 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
2b80e31
provider/aws: AWS WAF Regional ByteMatchSet support
yusukegoto 3917cca
providor/aws: AWS WAF Regional IPSet support
yusukegoto 71907d9
fix typo
yusukegoto 87899f4
Nested ByteMatchTuples of WAF Regional support
yusukegoto 6f44bd2
fix name byte_match_tuple singular WAF Regional
yusukegoto 8be3e3c
update wafregional_byte_match_set doc
yusukegoto cbf5afb
Add wafregional_byte_match_set test
yusukegoto 46339cf
fix wafregional ipset to look descriptor
yusukegoto cb32fc9
fix wafregional_ipset_descriptor to be singular
yusukegoto 465cf4a
fix newWafRegionalRetryer to receive region
yusukegoto 69326b6
fix updateSetWR argument
yusukegoto 5a73a6f
fix wafregional_ipset_descriptor doc
yusukegoto f1313c3
Separate out logic into flatteners
radeksimko d30580e
Fix failing test
radeksimko File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
261 changes: 261 additions & 0 deletions
261
builtin/providers/aws/resource_aws_wafregional_byte_match_set.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,261 @@ | ||
package aws | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/awserr" | ||
"github.com/aws/aws-sdk-go/service/waf" | ||
"github.com/aws/aws-sdk-go/service/wafregional" | ||
"github.com/hashicorp/errwrap" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceAwsWafRegionalByteMatchSet() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsWafRegionalByteMatchSetCreate, | ||
Read: resourceAwsWafRegionalByteMatchSetRead, | ||
Update: resourceAwsWafRegionalByteMatchSetUpdate, | ||
Delete: resourceAwsWafRegionalByteMatchSetDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"byte_match_tuple": &schema.Schema{ | ||
Type: schema.TypeSet, | ||
Optional: true, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"field_to_match": { | ||
Type: schema.TypeSet, | ||
Required: true, | ||
MaxItems: 1, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"data": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
"positional_constraint": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"target_string": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"text_transformation": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsWafRegionalByteMatchSetCreate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).wafregionalconn | ||
region := meta.(*AWSClient).region | ||
|
||
log.Printf("[INFO] Creating ByteMatchSet: %s", d.Get("name").(string)) | ||
|
||
wr := newWafRegionalRetryer(conn, region) | ||
out, err := wr.RetryWithToken(func(token *string) (interface{}, error) { | ||
params := &waf.CreateByteMatchSetInput{ | ||
ChangeToken: token, | ||
Name: aws.String(d.Get("name").(string)), | ||
} | ||
return conn.CreateByteMatchSet(params) | ||
}) | ||
|
||
if err != nil { | ||
return errwrap.Wrapf("[ERROR] Error creating ByteMatchSet: {{err}}", err) | ||
} | ||
resp := out.(*waf.CreateByteMatchSetOutput) | ||
|
||
d.SetId(*resp.ByteMatchSet.ByteMatchSetId) | ||
|
||
return resourceAwsWafRegionalByteMatchSetUpdate(d, meta) | ||
} | ||
|
||
func resourceAwsWafRegionalByteMatchSetRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).wafregionalconn | ||
|
||
log.Printf("[INFO] Reading ByteMatchSet: %s", d.Get("name").(string)) | ||
|
||
params := &waf.GetByteMatchSetInput{ | ||
ByteMatchSetId: aws.String(d.Id()), | ||
} | ||
|
||
resp, err := conn.GetByteMatchSet(params) | ||
if err != nil { | ||
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "WAFNonexistentItemException" { | ||
log.Printf("[WARN] WAF IPSet (%s) not found, error code (404)", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return err | ||
} | ||
|
||
var tuples []interface{} | ||
|
||
for _, tuple := range resp.ByteMatchSet.ByteMatchTuples { | ||
field_to_match := tuple.FieldToMatch | ||
m := map[string]interface{}{ | ||
"type": *field_to_match.Type, | ||
} | ||
|
||
if field_to_match.Data == nil { | ||
m["data"] = "" | ||
} else { | ||
m["data"] = *field_to_match.Data | ||
} | ||
|
||
var ms []map[string]interface{} | ||
ms = append(ms, m) | ||
|
||
tuple := map[string]interface{}{ | ||
"field_to_match": ms, | ||
"positional_constraint": *tuple.PositionalConstraint, | ||
"target_string": tuple.TargetString, | ||
"text_transformation": *tuple.TextTransformation, | ||
} | ||
tuples = append(tuples, tuple) | ||
} | ||
d.Set("byte_match_tuple", tuples) | ||
d.Set("name", resp.ByteMatchSet.Name) | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsWafRegionalByteMatchSetUpdate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).wafregionalconn | ||
region := meta.(*AWSClient).region | ||
log.Printf("[INFO] Updating ByteMatchSet: %s", d.Get("name").(string)) | ||
|
||
if d.HasChange("byte_match_tuple") { | ||
o, n := d.GetChange("byte_match_tuple") | ||
oldT, newT := o.(*schema.Set).List(), n.(*schema.Set).List() | ||
|
||
err := updateByteMatchSetResourceWR(d, oldT, newT, conn, region) | ||
if err != nil { | ||
return errwrap.Wrapf("[ERROR] Error updating ByteMatchSet: {{err}}", err) | ||
} | ||
} | ||
return resourceAwsWafRegionalByteMatchSetRead(d, meta) | ||
} | ||
|
||
func resourceAwsWafRegionalByteMatchSetDelete(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).wafregionalconn | ||
region := meta.(*AWSClient).region | ||
|
||
log.Printf("[INFO] Deleting ByteMatchSet: %s", d.Get("name").(string)) | ||
|
||
oldT := d.Get("byte_match_tuple").(*schema.Set).List() | ||
|
||
if len(oldT) > 0 { | ||
var newT []interface{} | ||
|
||
err := updateByteMatchSetResourceWR(d, oldT, newT, conn, region) | ||
if err != nil { | ||
return errwrap.Wrapf("[ERROR] Error deleting ByteMatchSet: {{err}}", err) | ||
} | ||
} | ||
|
||
wr := newWafRegionalRetryer(conn, region) | ||
_, err := wr.RetryWithToken(func(token *string) (interface{}, error) { | ||
req := &waf.DeleteByteMatchSetInput{ | ||
ChangeToken: token, | ||
ByteMatchSetId: aws.String(d.Id()), | ||
} | ||
return conn.DeleteByteMatchSet(req) | ||
}) | ||
if err != nil { | ||
return errwrap.Wrapf("[ERROR] Error deleting ByteMatchSet: {{err}}", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func updateByteMatchSetResourceWR(d *schema.ResourceData, oldT, newT []interface{}, conn *wafregional.WAFRegional, region string) error { | ||
wr := newWafRegionalRetryer(conn, region) | ||
_, err := wr.RetryWithToken(func(token *string) (interface{}, error) { | ||
req := &waf.UpdateByteMatchSetInput{ | ||
ChangeToken: token, | ||
ByteMatchSetId: aws.String(d.Id()), | ||
Updates: diffByteMatchSetTuple(oldT, newT), | ||
} | ||
|
||
return conn.UpdateByteMatchSet(req) | ||
}) | ||
if err != nil { | ||
return errwrap.Wrapf("[ERROR] Error updating ByteMatchSet: {{err}}", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func expandFieldToMatchWR(d map[string]interface{}) *waf.FieldToMatch { | ||
return &waf.FieldToMatch{ | ||
Type: aws.String(d["type"].(string)), | ||
Data: aws.String(d["data"].(string)), | ||
} | ||
} | ||
|
||
func flattenFieldToMatchWR(fm *waf.FieldToMatch) map[string]interface{} { | ||
m := make(map[string]interface{}) | ||
m["data"] = *fm.Data | ||
m["type"] = *fm.Type | ||
return m | ||
} | ||
|
||
func diffByteMatchSetTuple(oldT, newT []interface{}) []*waf.ByteMatchSetUpdate { | ||
updates := make([]*waf.ByteMatchSetUpdate, 0) | ||
|
||
for _, ot := range oldT { | ||
tuple := ot.(map[string]interface{}) | ||
|
||
if idx, contains := sliceContainsMap(newT, tuple); contains { | ||
newT = append(newT[:idx], newT[idx+1:]...) | ||
continue | ||
} | ||
|
||
updates = append(updates, &waf.ByteMatchSetUpdate{ | ||
Action: aws.String(waf.ChangeActionDelete), | ||
ByteMatchTuple: &waf.ByteMatchTuple{ | ||
FieldToMatch: expandFieldToMatch(tuple["field_to_match"].(*schema.Set).List()[0].(map[string]interface{})), | ||
PositionalConstraint: aws.String(tuple["positional_constraint"].(string)), | ||
TargetString: []byte(tuple["target_string"].(string)), | ||
TextTransformation: aws.String(tuple["text_transformation"].(string)), | ||
}, | ||
}) | ||
} | ||
|
||
for _, nt := range newT { | ||
tuple := nt.(map[string]interface{}) | ||
|
||
updates = append(updates, &waf.ByteMatchSetUpdate{ | ||
Action: aws.String(waf.ChangeActionInsert), | ||
ByteMatchTuple: &waf.ByteMatchTuple{ | ||
FieldToMatch: expandFieldToMatch(tuple["field_to_match"].(*schema.Set).List()[0].(map[string]interface{})), | ||
PositionalConstraint: aws.String(tuple["positional_constraint"].(string)), | ||
TargetString: []byte(tuple["target_string"].(string)), | ||
TextTransformation: aws.String(tuple["text_transformation"].(string)), | ||
}, | ||
}) | ||
} | ||
return updates | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're missing some fields here, specifically
byte_match_tuples
and all nested fields under it. The expectation from the Terraform user is that for any resource Terraform will detect drifts from the configuration. In order to do that we need to set all the available data from the API viad.Set()
here in Read func.Sets can be slightly trickier to work with, I will try and fix the WAF resources first and point you there for reference instead of trying to explain how this can be done. 😅
It's probably the cause of some of the reported WAF bugs I saw earlier, so it's worth fixing there either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw. this would also break import functionality when it's actually implemented as it leverages
Read()
(at least the default implementation)