github.com/jrperritt/terraform@v0.1.1-0.20170525065507-96f391dafc38/builtin/providers/aws/resource_aws_waf_byte_match_set.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/errwrap" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceAwsWafByteMatchSet() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceAwsWafByteMatchSetCreate, 17 Read: resourceAwsWafByteMatchSetRead, 18 Update: resourceAwsWafByteMatchSetUpdate, 19 Delete: resourceAwsWafByteMatchSetDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "name": &schema.Schema{ 23 Type: schema.TypeString, 24 Required: true, 25 ForceNew: true, 26 }, 27 "byte_match_tuples": &schema.Schema{ 28 Type: schema.TypeSet, 29 Optional: true, 30 Elem: &schema.Resource{ 31 Schema: map[string]*schema.Schema{ 32 "field_to_match": { 33 Type: schema.TypeSet, 34 Required: true, 35 MaxItems: 1, 36 Elem: &schema.Resource{ 37 Schema: map[string]*schema.Schema{ 38 "data": { 39 Type: schema.TypeString, 40 Optional: true, 41 }, 42 "type": { 43 Type: schema.TypeString, 44 Required: true, 45 }, 46 }, 47 }, 48 }, 49 "positional_constraint": &schema.Schema{ 50 Type: schema.TypeString, 51 Required: true, 52 }, 53 "target_string": &schema.Schema{ 54 Type: schema.TypeString, 55 Optional: true, 56 }, 57 "text_transformation": &schema.Schema{ 58 Type: schema.TypeString, 59 Required: true, 60 }, 61 }, 62 }, 63 }, 64 }, 65 } 66 } 67 68 func resourceAwsWafByteMatchSetCreate(d *schema.ResourceData, meta interface{}) error { 69 conn := meta.(*AWSClient).wafconn 70 71 log.Printf("[INFO] Creating ByteMatchSet: %s", d.Get("name").(string)) 72 73 wr := newWafRetryer(conn, "global") 74 out, err := wr.RetryWithToken(func(token *string) (interface{}, error) { 75 params := &waf.CreateByteMatchSetInput{ 76 ChangeToken: token, 77 Name: aws.String(d.Get("name").(string)), 78 } 79 return conn.CreateByteMatchSet(params) 80 }) 81 if err != nil { 82 return errwrap.Wrapf("[ERROR] Error creating ByteMatchSet: {{err}}", err) 83 } 84 resp := out.(*waf.CreateByteMatchSetOutput) 85 86 d.SetId(*resp.ByteMatchSet.ByteMatchSetId) 87 88 return resourceAwsWafByteMatchSetUpdate(d, meta) 89 } 90 91 func resourceAwsWafByteMatchSetRead(d *schema.ResourceData, meta interface{}) error { 92 conn := meta.(*AWSClient).wafconn 93 log.Printf("[INFO] Reading ByteMatchSet: %s", d.Get("name").(string)) 94 params := &waf.GetByteMatchSetInput{ 95 ByteMatchSetId: aws.String(d.Id()), 96 } 97 98 resp, err := conn.GetByteMatchSet(params) 99 if err != nil { 100 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "WAFNonexistentItemException" { 101 log.Printf("[WARN] WAF IPSet (%s) not found, error code (404)", d.Id()) 102 d.SetId("") 103 return nil 104 } 105 106 return err 107 } 108 109 d.Set("name", resp.ByteMatchSet.Name) 110 d.Set("byte_match_tuples", flattenWafByteMatchTuples(resp.ByteMatchSet.ByteMatchTuples)) 111 112 return nil 113 } 114 115 func resourceAwsWafByteMatchSetUpdate(d *schema.ResourceData, meta interface{}) error { 116 conn := meta.(*AWSClient).wafconn 117 118 log.Printf("[INFO] Updating ByteMatchSet: %s", d.Get("name").(string)) 119 120 if d.HasChange("byte_match_tuples") { 121 o, n := d.GetChange("byte_match_tuples") 122 oldT, newT := o.(*schema.Set).List(), n.(*schema.Set).List() 123 err := updateByteMatchSetResource(d.Id(), oldT, newT, conn) 124 if err != nil { 125 return errwrap.Wrapf("[ERROR] Error updating ByteMatchSet: {{err}}", err) 126 } 127 } 128 129 return resourceAwsWafByteMatchSetRead(d, meta) 130 } 131 132 func resourceAwsWafByteMatchSetDelete(d *schema.ResourceData, meta interface{}) error { 133 conn := meta.(*AWSClient).wafconn 134 135 oldTuples := d.Get("byte_match_tuples").(*schema.Set).List() 136 if len(oldTuples) > 0 { 137 noTuples := []interface{}{} 138 err := updateByteMatchSetResource(d.Id(), oldTuples, noTuples, conn) 139 if err != nil { 140 return fmt.Errorf("Error updating ByteMatchSet: %s", err) 141 } 142 } 143 144 wr := newWafRetryer(conn, "global") 145 _, err := wr.RetryWithToken(func(token *string) (interface{}, error) { 146 req := &waf.DeleteByteMatchSetInput{ 147 ChangeToken: token, 148 ByteMatchSetId: aws.String(d.Id()), 149 } 150 log.Printf("[INFO] Deleting WAF ByteMatchSet: %s", req) 151 return conn.DeleteByteMatchSet(req) 152 }) 153 if err != nil { 154 return errwrap.Wrapf("[ERROR] Error deleting ByteMatchSet: {{err}}", err) 155 } 156 157 return nil 158 } 159 160 func updateByteMatchSetResource(id string, oldT, newT []interface{}, conn *waf.WAF) error { 161 wr := newWafRetryer(conn, "global") 162 _, err := wr.RetryWithToken(func(token *string) (interface{}, error) { 163 req := &waf.UpdateByteMatchSetInput{ 164 ChangeToken: token, 165 ByteMatchSetId: aws.String(id), 166 Updates: diffWafByteMatchSetTuples(oldT, newT), 167 } 168 169 return conn.UpdateByteMatchSet(req) 170 }) 171 if err != nil { 172 return errwrap.Wrapf("[ERROR] Error updating ByteMatchSet: {{err}}", err) 173 } 174 175 return nil 176 } 177 178 func flattenWafByteMatchTuples(bmt []*waf.ByteMatchTuple) []interface{} { 179 out := make([]interface{}, len(bmt), len(bmt)) 180 for i, t := range bmt { 181 m := make(map[string]interface{}) 182 183 if t.FieldToMatch != nil { 184 m["field_to_match"] = flattenFieldToMatch(t.FieldToMatch) 185 } 186 m["positional_constraint"] = *t.PositionalConstraint 187 m["target_string"] = string(t.TargetString) 188 m["text_transformation"] = *t.TextTransformation 189 190 out[i] = m 191 } 192 return out 193 } 194 195 func expandFieldToMatch(d map[string]interface{}) *waf.FieldToMatch { 196 return &waf.FieldToMatch{ 197 Type: aws.String(d["type"].(string)), 198 Data: aws.String(d["data"].(string)), 199 } 200 } 201 202 func flattenFieldToMatch(fm *waf.FieldToMatch) []interface{} { 203 m := make(map[string]interface{}) 204 if fm.Data != nil { 205 m["data"] = *fm.Data 206 } 207 if fm.Type != nil { 208 m["type"] = *fm.Type 209 } 210 return []interface{}{m} 211 } 212 213 func diffWafByteMatchSetTuples(oldT, newT []interface{}) []*waf.ByteMatchSetUpdate { 214 updates := make([]*waf.ByteMatchSetUpdate, 0) 215 216 for _, ot := range oldT { 217 tuple := ot.(map[string]interface{}) 218 219 if idx, contains := sliceContainsMap(newT, tuple); contains { 220 newT = append(newT[:idx], newT[idx+1:]...) 221 continue 222 } 223 224 updates = append(updates, &waf.ByteMatchSetUpdate{ 225 Action: aws.String(waf.ChangeActionDelete), 226 ByteMatchTuple: &waf.ByteMatchTuple{ 227 FieldToMatch: expandFieldToMatch(tuple["field_to_match"].(*schema.Set).List()[0].(map[string]interface{})), 228 PositionalConstraint: aws.String(tuple["positional_constraint"].(string)), 229 TargetString: []byte(tuple["target_string"].(string)), 230 TextTransformation: aws.String(tuple["text_transformation"].(string)), 231 }, 232 }) 233 } 234 235 for _, nt := range newT { 236 tuple := nt.(map[string]interface{}) 237 238 updates = append(updates, &waf.ByteMatchSetUpdate{ 239 Action: aws.String(waf.ChangeActionInsert), 240 ByteMatchTuple: &waf.ByteMatchTuple{ 241 FieldToMatch: expandFieldToMatch(tuple["field_to_match"].(*schema.Set).List()[0].(map[string]interface{})), 242 PositionalConstraint: aws.String(tuple["positional_constraint"].(string)), 243 TargetString: []byte(tuple["target_string"].(string)), 244 TextTransformation: aws.String(tuple["text_transformation"].(string)), 245 }, 246 }) 247 } 248 return updates 249 }