github.com/bradfeehan/terraform@v0.7.0-rc3.0.20170529055808-34b45c5ad841/builtin/providers/aws/resource_aws_waf_rule.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/aws/aws-sdk-go/aws" 8 "github.com/aws/aws-sdk-go/aws/awserr" 9 "github.com/aws/aws-sdk-go/service/waf" 10 "github.com/hashicorp/terraform/helper/schema" 11 ) 12 13 func resourceAwsWafRule() *schema.Resource { 14 return &schema.Resource{ 15 Create: resourceAwsWafRuleCreate, 16 Read: resourceAwsWafRuleRead, 17 Update: resourceAwsWafRuleUpdate, 18 Delete: resourceAwsWafRuleDelete, 19 20 Schema: map[string]*schema.Schema{ 21 "name": &schema.Schema{ 22 Type: schema.TypeString, 23 Required: true, 24 ForceNew: true, 25 }, 26 "metric_name": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 ForceNew: true, 30 ValidateFunc: validateWafMetricName, 31 }, 32 "predicates": &schema.Schema{ 33 Type: schema.TypeSet, 34 Optional: true, 35 Elem: &schema.Resource{ 36 Schema: map[string]*schema.Schema{ 37 "negated": &schema.Schema{ 38 Type: schema.TypeBool, 39 Required: true, 40 }, 41 "data_id": &schema.Schema{ 42 Type: schema.TypeString, 43 Optional: true, 44 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 45 value := v.(string) 46 if len(value) > 128 { 47 errors = append(errors, fmt.Errorf( 48 "%q cannot be longer than 128 characters", k)) 49 } 50 return 51 }, 52 }, 53 "type": &schema.Schema{ 54 Type: schema.TypeString, 55 Required: true, 56 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 57 value := v.(string) 58 if value != "IPMatch" && value != "ByteMatch" && value != "SqlInjectionMatch" && value != "SizeConstraint" && value != "XssMatch" { 59 errors = append(errors, fmt.Errorf( 60 "%q must be one of IPMatch | ByteMatch | SqlInjectionMatch | SizeConstraint | XssMatch", k)) 61 } 62 return 63 }, 64 }, 65 }, 66 }, 67 }, 68 }, 69 } 70 } 71 72 func resourceAwsWafRuleCreate(d *schema.ResourceData, meta interface{}) error { 73 conn := meta.(*AWSClient).wafconn 74 75 wr := newWafRetryer(conn, "global") 76 out, err := wr.RetryWithToken(func(token *string) (interface{}, error) { 77 params := &waf.CreateRuleInput{ 78 ChangeToken: token, 79 MetricName: aws.String(d.Get("metric_name").(string)), 80 Name: aws.String(d.Get("name").(string)), 81 } 82 83 return conn.CreateRule(params) 84 }) 85 if err != nil { 86 return err 87 } 88 resp := out.(*waf.CreateRuleOutput) 89 d.SetId(*resp.Rule.RuleId) 90 return resourceAwsWafRuleUpdate(d, meta) 91 } 92 93 func resourceAwsWafRuleRead(d *schema.ResourceData, meta interface{}) error { 94 conn := meta.(*AWSClient).wafconn 95 96 params := &waf.GetRuleInput{ 97 RuleId: aws.String(d.Id()), 98 } 99 100 resp, err := conn.GetRule(params) 101 if err != nil { 102 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "WAFNonexistentItemException" { 103 log.Printf("[WARN] WAF Rule (%s) not found, error code (404)", d.Id()) 104 d.SetId("") 105 return nil 106 } 107 108 return err 109 } 110 111 var predicates []map[string]interface{} 112 113 for _, predicateSet := range resp.Rule.Predicates { 114 predicate := map[string]interface{}{ 115 "negated": *predicateSet.Negated, 116 "type": *predicateSet.Type, 117 "data_id": *predicateSet.DataId, 118 } 119 predicates = append(predicates, predicate) 120 } 121 122 d.Set("predicates", predicates) 123 d.Set("name", resp.Rule.Name) 124 d.Set("metric_name", resp.Rule.MetricName) 125 126 return nil 127 } 128 129 func resourceAwsWafRuleUpdate(d *schema.ResourceData, meta interface{}) error { 130 conn := meta.(*AWSClient).wafconn 131 132 if d.HasChange("predicates") { 133 o, n := d.GetChange("predicates") 134 oldP, newP := o.(*schema.Set).List(), n.(*schema.Set).List() 135 136 err := updateWafRuleResource(d.Id(), oldP, newP, conn) 137 if err != nil { 138 return fmt.Errorf("Error Updating WAF Rule: %s", err) 139 } 140 } 141 142 return resourceAwsWafRuleRead(d, meta) 143 } 144 145 func resourceAwsWafRuleDelete(d *schema.ResourceData, meta interface{}) error { 146 conn := meta.(*AWSClient).wafconn 147 148 oldPredicates := d.Get("predicates").(*schema.Set).List() 149 if len(oldPredicates) > 0 { 150 noPredicates := []interface{}{} 151 err := updateWafRuleResource(d.Id(), oldPredicates, noPredicates, conn) 152 if err != nil { 153 return fmt.Errorf("Error updating WAF Rule Predicates: %s", err) 154 } 155 } 156 157 wr := newWafRetryer(conn, "global") 158 _, err := wr.RetryWithToken(func(token *string) (interface{}, error) { 159 req := &waf.DeleteRuleInput{ 160 ChangeToken: token, 161 RuleId: aws.String(d.Id()), 162 } 163 log.Printf("[INFO] Deleting WAF Rule") 164 return conn.DeleteRule(req) 165 }) 166 if err != nil { 167 return fmt.Errorf("Error deleting WAF Rule: %s", err) 168 } 169 170 return nil 171 } 172 173 func updateWafRuleResource(id string, oldP, newP []interface{}, conn *waf.WAF) error { 174 wr := newWafRetryer(conn, "global") 175 _, err := wr.RetryWithToken(func(token *string) (interface{}, error) { 176 req := &waf.UpdateRuleInput{ 177 ChangeToken: token, 178 RuleId: aws.String(id), 179 Updates: diffWafRulePredicates(oldP, newP), 180 } 181 182 return conn.UpdateRule(req) 183 }) 184 if err != nil { 185 return fmt.Errorf("Error Updating WAF Rule: %s", err) 186 } 187 188 return nil 189 } 190 191 func diffWafRulePredicates(oldP, newP []interface{}) []*waf.RuleUpdate { 192 updates := make([]*waf.RuleUpdate, 0) 193 194 for _, op := range oldP { 195 predicate := op.(map[string]interface{}) 196 197 if idx, contains := sliceContainsMap(newP, predicate); contains { 198 newP = append(newP[:idx], newP[idx+1:]...) 199 continue 200 } 201 202 updates = append(updates, &waf.RuleUpdate{ 203 Action: aws.String(waf.ChangeActionDelete), 204 Predicate: &waf.Predicate{ 205 Negated: aws.Bool(predicate["negated"].(bool)), 206 Type: aws.String(predicate["type"].(string)), 207 DataId: aws.String(predicate["data_id"].(string)), 208 }, 209 }) 210 } 211 212 for _, np := range newP { 213 predicate := np.(map[string]interface{}) 214 215 updates = append(updates, &waf.RuleUpdate{ 216 Action: aws.String(waf.ChangeActionInsert), 217 Predicate: &waf.Predicate{ 218 Negated: aws.Bool(predicate["negated"].(bool)), 219 Type: aws.String(predicate["type"].(string)), 220 DataId: aws.String(predicate["data_id"].(string)), 221 }, 222 }) 223 } 224 return updates 225 }