-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
New Resource: aws_wafregional_rule #3756
Changes from 2 commits
f95d112
5a42045
cc2d84c
61478f2
40b0a08
210fef9
1f23227
61cf64a
81e084a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"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/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceAwsWafRegionalRule() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsWafRegionalRuleCreate, | ||
Read: resourceAwsWafRegionalRuleRead, | ||
Update: resourceAwsWafRegionalRuleUpdate, | ||
Delete: resourceAwsWafRegionalRuleDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"metric_name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
Sorry, something went wrong. |
||
ForceNew: true, | ||
}, | ||
"predicates": &schema.Schema{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Due to the way this may (and likely is going to be) expressed in HCL: predicates {
negated = true
data_id = "..."
}
predicates {
data_id = "..."
} we tend to prefer singular names for TypeSet/TypeList fields with non-primitive nested types. i.e. |
||
Type: schema.TypeSet, | ||
Optional: true, | ||
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
Sorry, something went wrong. |
||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"negated": &schema.Schema{ | ||
Type: schema.TypeBool, | ||
Required: true, | ||
}, | ||
"data_id": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems this field is actually required per docs: |
||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { | ||
value := v.(string) | ||
if len(value) > 128 { | ||
errors = append(errors, fmt.Errorf( | ||
"%q cannot be longer than 128 characters", k)) | ||
} | ||
return | ||
}, | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nipick: We have a validation helper for this: ValidateFunc: validation.StringLenBetween(1, 128), |
||
"type": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { | ||
value := v.(string) | ||
if value != "IPMatch" && value != "ByteMatch" && value != "SqlInjectionMatch" && value != "SizeConstraint" && value != "XssMatch" { | ||
errors = append(errors, fmt.Errorf( | ||
"%q must be one of IPMatch | ByteMatch | SqlInjectionMatch | SizeConstraint | XssMatch", k)) | ||
} | ||
return | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: We have a handy validation helper for this: ValidateFunc: validation.StringInSlice([]string{
"IPMatch",
"ByteMatch",
"SqlInjectionMatch",
"SizeConstraint",
"XssMatch",
}, false), |
||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsWafRegionalRuleCreate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).wafregionalconn | ||
region := meta.(*AWSClient).region | ||
|
||
wr := newWafRegionalRetryer(conn, region) | ||
out, err := wr.RetryWithToken(func(token *string) (interface{}, error) { | ||
params := &waf.CreateRuleInput{ | ||
ChangeToken: token, | ||
MetricName: aws.String(d.Get("metric_name").(string)), | ||
Name: aws.String(d.Get("name").(string)), | ||
} | ||
|
||
return conn.CreateRule(params) | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
resp := out.(*waf.CreateRuleOutput) | ||
d.SetId(*resp.Rule.RuleId) | ||
return resourceAwsWafRegionalRuleUpdate(d, meta) | ||
} | ||
|
||
func resourceAwsWafRegionalRuleRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).wafregionalconn | ||
|
||
params := &waf.GetRuleInput{ | ||
RuleId: aws.String(d.Id()), | ||
} | ||
|
||
resp, err := conn.GetRule(params) | ||
if err != nil { | ||
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "WAFNonexistentItemException" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mind using a helper to simplify the code here? if isAWSErr(err, wafregional.ErrCodeWAFNonexistentItemException, "") { |
||
log.Printf("[WARN] WAF Rule (%s) not found, error code (404)", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return err | ||
} | ||
|
||
var predicates []map[string]interface{} | ||
|
||
for _, predicateSet := range resp.Rule.Predicates { | ||
predicate := map[string]interface{}{ | ||
"negated": *predicateSet.Negated, | ||
"type": *predicateSet.Type, | ||
"data_id": *predicateSet.DataId, | ||
} | ||
predicates = append(predicates, predicate) | ||
} | ||
|
||
d.Set("predicates", predicates) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: We tend to decouple logic like this (anything converting SDK structures to schema-friendly structures and vice-versa) to functions which are usually called expanders (schema -> SDK) or flatteners (SDK -> schema). d.Set("predicates", flattenWafPredicates(resp.Rule.Predicates)) |
||
d.Set("name", resp.Rule.Name) | ||
d.Set("metric_name", resp.Rule.MetricName) | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsWafRegionalRuleUpdate(d *schema.ResourceData, meta interface{}) error { | ||
err := updateWafRegionalRuleResource(d, meta, waf.ChangeActionInsert) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in all previous reviews of regional WAF attempts we cannot just assume that all operations during updates are Insert, because we may be removing predicates as part of the update. See 99e75ad - specifically |
||
if err != nil { | ||
return fmt.Errorf("Error Updating WAF Rule: %s", err) | ||
} | ||
return resourceAwsWafRegionalRuleRead(d, meta) | ||
} | ||
|
||
func resourceAwsWafRegionalRuleDelete(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).wafregionalconn | ||
region := meta.(*AWSClient).region | ||
|
||
err := updateWafRegionalRuleResource(d, meta, waf.ChangeActionDelete) | ||
if err != nil { | ||
return fmt.Errorf("Error Removing WAF Rule Predicates: %s", err) | ||
} | ||
wr := newWafRegionalRetryer(conn, region) | ||
_, err = wr.RetryWithToken(func(token *string) (interface{}, error) { | ||
req := &waf.DeleteRuleInput{ | ||
ChangeToken: token, | ||
RuleId: aws.String(d.Id()), | ||
} | ||
log.Printf("[INFO] Deleting WAF Rule") | ||
return conn.DeleteRule(req) | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("Error deleting WAF Rule: %s", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func updateWafRegionalRuleResource(d *schema.ResourceData, meta interface{}, ChangeAction string) error { | ||
conn := meta.(*AWSClient).wafregionalconn | ||
region := meta.(*AWSClient).region | ||
|
||
wr := newWafRegionalRetryer(conn, region) | ||
_, err := wr.RetryWithToken(func(token *string) (interface{}, error) { | ||
req := &waf.UpdateRuleInput{ | ||
ChangeToken: token, | ||
RuleId: aws.String(d.Id()), | ||
} | ||
|
||
predicatesSet := d.Get("predicates").(*schema.Set) | ||
for _, predicateI := range predicatesSet.List() { | ||
predicate := predicateI.(map[string]interface{}) | ||
updatePredicate := &waf.RuleUpdate{ | ||
Action: aws.String(ChangeAction), | ||
Predicate: &waf.Predicate{ | ||
Negated: aws.Bool(predicate["negated"].(bool)), | ||
Type: aws.String(predicate["type"].(string)), | ||
DataId: aws.String(predicate["data_id"].(string)), | ||
}, | ||
} | ||
req.Updates = append(req.Updates, updatePredicate) | ||
} | ||
|
||
return conn.UpdateRule(req) | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("Error Updating WAF Rule: %s", err) | ||
} | ||
|
||
return nil | ||
} |
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
Sorry, something went wrong.