diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index 0ad4eba98..e060dbda9 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -66,6 +66,7 @@ import ( "github.com/aws/aws-sdk-go/service/ssm" "github.com/aws/aws-sdk-go/service/sts" "github.com/aws/aws-sdk-go/service/waf" + "github.com/aws/aws-sdk-go/service/wafregional" "github.com/davecgh/go-spew/spew" "github.com/hashicorp/errwrap" "github.com/hashicorp/go-cleanhttp" @@ -172,6 +173,7 @@ type AWSClient struct { sfnconn *sfn.SFN ssmconn *ssm.SSM wafconn *waf.WAF + wafregionalconn *wafregional.WAFRegional } func (c *AWSClient) S3() *s3.S3 { @@ -379,6 +381,7 @@ func (c *Config) Client() (interface{}, error) { client.sqsconn = sqs.New(awsSqsSess) client.ssmconn = ssm.New(sess) client.wafconn = waf.New(sess) + client.wafregionalconn = wafregional.New(sess) return &client, nil } diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 96433c52e..bc5c07d0d 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -457,6 +457,8 @@ func Provider() terraform.ResourceProvider { "aws_waf_web_acl": resourceAwsWafWebAcl(), "aws_waf_xss_match_set": resourceAwsWafXssMatchSet(), "aws_waf_sql_injection_match_set": resourceAwsWafSqlInjectionMatchSet(), + "aws_wafregional_byte_match_set": resourceAwsWafRegionalByteMatchSet(), + "aws_wafregional_ipset": resourceAwsWafRegionalIPSet(), }, ConfigureFunc: providerConfigure, } diff --git a/builtin/providers/aws/resource_aws_wafregional_byte_match_set.go b/builtin/providers/aws/resource_aws_wafregional_byte_match_set.go new file mode 100644 index 000000000..d7f916ad3 --- /dev/null +++ b/builtin/providers/aws/resource_aws_wafregional_byte_match_set.go @@ -0,0 +1,266 @@ +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 + } + + d.Set("byte_match_tuple", flattenWafByteMatchTuplesWR(resp.ByteMatchSet.ByteMatchTuples)) + d.Set("name", resp.ByteMatchSet.Name) + + return nil +} + +func flattenWafByteMatchTuplesWR(in []*waf.ByteMatchTuple) []interface{} { + tuples := make([]interface{}, len(in), len(in)) + + for i, tuple := range in { + 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[i] = tuple + } + + return tuples +} + +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 +} diff --git a/builtin/providers/aws/resource_aws_wafregional_byte_match_set_test.go b/builtin/providers/aws/resource_aws_wafregional_byte_match_set_test.go new file mode 100644 index 000000000..2600349ae --- /dev/null +++ b/builtin/providers/aws/resource_aws_wafregional_byte_match_set_test.go @@ -0,0 +1,437 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "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/errwrap" + "github.com/hashicorp/terraform/helper/acctest" +) + +func TestAccAWSWafRegionalByteMatchSet_basic(t *testing.T) { + var v waf.ByteMatchSet + byteMatchSet := fmt.Sprintf("byteMatchSet-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalByteMatchSetDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSWafRegionalByteMatchSetConfig(byteMatchSet), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRegionalByteMatchSetExists("aws_wafregional_byte_match_set.byte_set", &v), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "name", byteMatchSet), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.#", "2"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.field_to_match.2991901334.data", "referer"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.field_to_match.2991901334.type", "HEADER"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.positional_constraint", "CONTAINS"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.target_string", "badrefer1"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.text_transformation", "NONE"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.field_to_match.2991901334.data", "referer"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.field_to_match.2991901334.type", "HEADER"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.positional_constraint", "CONTAINS"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.target_string", "badrefer2"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.text_transformation", "NONE"), + ), + }, + }, + }) +} + +func TestAccAWSWafRegionalByteMatchSet_changeNameForceNew(t *testing.T) { + var before, after waf.ByteMatchSet + byteMatchSet := fmt.Sprintf("byteMatchSet-%s", acctest.RandString(5)) + byteMatchSetNewName := fmt.Sprintf("byteMatchSet-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalByteMatchSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRegionalByteMatchSetConfig(byteMatchSet), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRegionalByteMatchSetExists("aws_wafregional_byte_match_set.byte_set", &before), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "name", byteMatchSet), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.#", "2"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.field_to_match.2991901334.data", "referer"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.field_to_match.2991901334.type", "HEADER"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.positional_constraint", "CONTAINS"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.target_string", "badrefer1"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.text_transformation", "NONE"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.field_to_match.2991901334.data", "referer"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.field_to_match.2991901334.type", "HEADER"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.positional_constraint", "CONTAINS"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.target_string", "badrefer2"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.text_transformation", "NONE"), + ), + }, + { + Config: testAccAWSWafRegionalByteMatchSetConfigChangeName(byteMatchSetNewName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRegionalByteMatchSetExists("aws_wafregional_byte_match_set.byte_set", &after), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "name", byteMatchSetNewName), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.#", "2"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.field_to_match.2991901334.data", "referer"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.field_to_match.2991901334.type", "HEADER"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.positional_constraint", "CONTAINS"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.target_string", "badrefer1"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.text_transformation", "NONE"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.field_to_match.2991901334.data", "referer"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.field_to_match.2991901334.type", "HEADER"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.positional_constraint", "CONTAINS"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.target_string", "badrefer2"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.text_transformation", "NONE"), + ), + }, + }, + }) +} + +func TestAccAWSWafRegionalByteMatchSet_changeByteMatchTuple(t *testing.T) { + var before, after waf.ByteMatchSet + byteMatchSetName := fmt.Sprintf("byte-batch-set-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalByteMatchSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRegionalByteMatchSetConfig(byteMatchSetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRegionalByteMatchSetExists("aws_wafregional_byte_match_set.byte_set", &before), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "name", byteMatchSetName), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.#", "2"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.field_to_match.2991901334.data", "referer"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.field_to_match.2991901334.type", "HEADER"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.positional_constraint", "CONTAINS"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.target_string", "badrefer1"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2174619346.text_transformation", "NONE"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.field_to_match.2991901334.data", "referer"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.field_to_match.2991901334.type", "HEADER"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.positional_constraint", "CONTAINS"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.target_string", "badrefer2"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.839525137.text_transformation", "NONE"), + ), + }, + { + Config: testAccAWSWafRegionalByteMatchSetConfigChangeByteMatchTuple(byteMatchSetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRegionalByteMatchSetExists("aws_wafregional_byte_match_set.byte_set", &after), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "name", byteMatchSetName), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.#", "2"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2397850647.field_to_match.4253810390.data", "GET"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2397850647.field_to_match.4253810390.type", "METHOD"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2397850647.positional_constraint", "STARTS_WITH"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2397850647.target_string", "badrefer1+"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.2397850647.text_transformation", "LOWERCASE"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.4153613423.field_to_match.3756326843.data", ""), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.4153613423.field_to_match.3756326843.type", "URI"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.4153613423.positional_constraint", "ENDS_WITH"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.4153613423.target_string", "badrefer2+"), + resource.TestCheckResourceAttr( + "aws_wafregional_byte_match_set.byte_set", "byte_match_tuple.4153613423.text_transformation", "LOWERCASE"), + ), + }, + }, + }) +} + +func TestAccAWSWafRegionalByteMatchSet_noByteMatchTuples(t *testing.T) { + var byteMatchSet waf.ByteMatchSet + byteMatchSetName := fmt.Sprintf("byte-batch-set-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalByteMatchSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRegionalByteMatchSetConfig_noDescriptors(byteMatchSetName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSWafRegionalByteMatchSetExists("aws_wafregional_byte_match_set.byte_match_set", &byteMatchSet), + resource.TestCheckResourceAttr("aws_wafregional_byte_match_set.byte_match_set", "name", byteMatchSetName), + resource.TestCheckResourceAttr("aws_wafregional_byte_match_set.byte_match_set", "byte_match_tuple.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSWafRegionalByteMatchSet_disappears(t *testing.T) { + var v waf.ByteMatchSet + byteMatchSet := fmt.Sprintf("byteMatchSet-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalByteMatchSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRegionalByteMatchSetConfig(byteMatchSet), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRegionalByteMatchSetExists("aws_wafregional_byte_match_set.byte_set", &v), + testAccCheckAWSWafRegionalByteMatchSetDisappears(&v), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAWSWafRegionalByteMatchSetDisappears(v *waf.ByteMatchSet) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).wafregionalconn + region := testAccProvider.Meta().(*AWSClient).region + + wr := newWafRegionalRetryer(conn, region) + _, err := wr.RetryWithToken(func(token *string) (interface{}, error) { + req := &waf.UpdateByteMatchSetInput{ + ChangeToken: token, + ByteMatchSetId: v.ByteMatchSetId, + } + + for _, ByteMatchTuple := range v.ByteMatchTuples { + ByteMatchUpdate := &waf.ByteMatchSetUpdate{ + Action: aws.String("DELETE"), + ByteMatchTuple: &waf.ByteMatchTuple{ + FieldToMatch: ByteMatchTuple.FieldToMatch, + PositionalConstraint: ByteMatchTuple.PositionalConstraint, + TargetString: ByteMatchTuple.TargetString, + TextTransformation: ByteMatchTuple.TextTransformation, + }, + } + req.Updates = append(req.Updates, ByteMatchUpdate) + } + + return conn.UpdateByteMatchSet(req) + }) + if err != nil { + return errwrap.Wrapf("[ERROR] Error updating ByteMatchSet: {{err}}", err) + } + + _, err = wr.RetryWithToken(func(token *string) (interface{}, error) { + opts := &waf.DeleteByteMatchSetInput{ + ChangeToken: token, + ByteMatchSetId: v.ByteMatchSetId, + } + return conn.DeleteByteMatchSet(opts) + }) + if err != nil { + return errwrap.Wrapf("[ERROR] Error deleting ByteMatchSet: {{err}}", err) + } + + return nil + } +} + +func testAccCheckAWSWafRegionalByteMatchSetExists(n string, v *waf.ByteMatchSet) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No WAF ByteMatchSet ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).wafregionalconn + resp, err := conn.GetByteMatchSet(&waf.GetByteMatchSetInput{ + ByteMatchSetId: aws.String(rs.Primary.ID), + }) + + if err != nil { + return err + } + + if *resp.ByteMatchSet.ByteMatchSetId == rs.Primary.ID { + *v = *resp.ByteMatchSet + return nil + } + + return fmt.Errorf("WAF ByteMatchSet (%s) not found", rs.Primary.ID) + } +} + +func testAccCheckAWSWafRegionalByteMatchSetDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_wafregional_byte_match_set" { + continue + } + + conn := testAccProvider.Meta().(*AWSClient).wafregionalconn + resp, err := conn.GetByteMatchSet( + &waf.GetByteMatchSetInput{ + ByteMatchSetId: aws.String(rs.Primary.ID), + }) + + if err == nil { + if *resp.ByteMatchSet.ByteMatchSetId == rs.Primary.ID { + return fmt.Errorf("WAF ByteMatchSet %s still exists", rs.Primary.ID) + } + } + + // Return nil if the ByteMatchSet is already destroyed + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() == "WAFNonexistentItemException" { + return nil + } + } + + return err + } + + return nil +} + +func testAccAWSWafRegionalByteMatchSetConfig(name string) string { + return fmt.Sprintf(` +resource "aws_wafregional_byte_match_set" "byte_set" { + name = "%s" + byte_match_tuple { + text_transformation = "NONE" + target_string = "badrefer1" + positional_constraint = "CONTAINS" + field_to_match { + type = "HEADER" + data = "referer" + } + } + + byte_match_tuple { + text_transformation = "NONE" + target_string = "badrefer2" + positional_constraint = "CONTAINS" + field_to_match { + type = "HEADER" + data = "referer" + } + } +}`, name) +} + +func testAccAWSWafRegionalByteMatchSetConfigChangeName(name string) string { + return fmt.Sprintf(` +resource "aws_wafregional_byte_match_set" "byte_set" { + name = "%s" + byte_match_tuple { + text_transformation = "NONE" + target_string = "badrefer1" + positional_constraint = "CONTAINS" + field_to_match { + type = "HEADER" + data = "referer" + } + } + + byte_match_tuple { + text_transformation = "NONE" + target_string = "badrefer2" + positional_constraint = "CONTAINS" + field_to_match { + type = "HEADER" + data = "referer" + } + } +}`, name) +} + +func testAccAWSWafRegionalByteMatchSetConfig_noDescriptors(name string) string { + return fmt.Sprintf(` +resource "aws_wafregional_byte_match_set" "byte_match_set" { + name = "%s" +}`, name) +} + +func testAccAWSWafRegionalByteMatchSetConfigChangeByteMatchTuple(name string) string { + return fmt.Sprintf(` +resource "aws_wafregional_byte_match_set" "byte_set" { + name = "%s" + byte_match_tuple { + text_transformation = "LOWERCASE" + target_string = "badrefer1+" + positional_constraint = "STARTS_WITH" + field_to_match { + type = "METHOD" + data = "GET" + } + } + + byte_match_tuple { + text_transformation = "LOWERCASE" + target_string = "badrefer2+" + positional_constraint = "ENDS_WITH" + field_to_match { + type = "URI" + } + } +}`, name) +} diff --git a/builtin/providers/aws/resource_aws_wafregional_ipset.go b/builtin/providers/aws/resource_aws_wafregional_ipset.go new file mode 100644 index 000000000..0507621ee --- /dev/null +++ b/builtin/providers/aws/resource_aws_wafregional_ipset.go @@ -0,0 +1,170 @@ +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/aws/aws-sdk-go/service/wafregional" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsWafRegionalIPSet() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsWafRegionalIPSetCreate, + Read: resourceAwsWafRegionalIPSetRead, + Update: resourceAwsWafRegionalIPSetUpdate, + Delete: resourceAwsWafRegionalIPSetDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "ip_set_descriptor": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "value": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + } +} + +func resourceAwsWafRegionalIPSetCreate(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.CreateIPSetInput{ + ChangeToken: token, + Name: aws.String(d.Get("name").(string)), + } + return conn.CreateIPSet(params) + }) + if err != nil { + return err + } + resp := out.(*waf.CreateIPSetOutput) + d.SetId(*resp.IPSet.IPSetId) + return resourceAwsWafRegionalIPSetUpdate(d, meta) +} + +func resourceAwsWafRegionalIPSetRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafregionalconn + + params := &waf.GetIPSetInput{ + IPSetId: aws.String(d.Id()), + } + + resp, err := conn.GetIPSet(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 + } + + d.Set("ip_set_descriptor", flattenWafIpSetDescriptorWR(resp.IPSet.IPSetDescriptors)) + d.Set("name", resp.IPSet.Name) + + return nil +} + +func flattenWafIpSetDescriptorWR(in []*waf.IPSetDescriptor) []interface{} { + descriptors := make([]interface{}, len(in), len(in)) + + for i, descriptor := range in { + d := map[string]interface{}{ + "type": *descriptor.Type, + "value": *descriptor.Value, + } + descriptors[i] = d + } + + return descriptors +} + +func resourceAwsWafRegionalIPSetUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafregionalconn + region := meta.(*AWSClient).region + + if d.HasChange("ip_set_descriptor") { + o, n := d.GetChange("ip_set_descriptor") + oldD, newD := o.(*schema.Set).List(), n.(*schema.Set).List() + + err := updateIPSetResourceWR(d.Id(), oldD, newD, conn, region) + if err != nil { + return fmt.Errorf("Error Updating WAF IPSet: %s", err) + } + } + return resourceAwsWafRegionalIPSetRead(d, meta) +} + +func resourceAwsWafRegionalIPSetDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafregionalconn + region := meta.(*AWSClient).region + + oldD := d.Get("ip_set_descriptor").(*schema.Set).List() + + if len(oldD) > 0 { + noD := []interface{}{} + err := updateIPSetResourceWR(d.Id(), oldD, noD, conn, region) + + if err != nil { + return fmt.Errorf("Error Removing IPSetDescriptors: %s", err) + } + } + + wr := newWafRegionalRetryer(conn, region) + _, err := wr.RetryWithToken(func(token *string) (interface{}, error) { + req := &waf.DeleteIPSetInput{ + ChangeToken: token, + IPSetId: aws.String(d.Id()), + } + log.Printf("[INFO] Deleting WAF IPSet") + return conn.DeleteIPSet(req) + }) + if err != nil { + return fmt.Errorf("Error Deleting WAF IPSet: %s", err) + } + + return nil +} + +func updateIPSetResourceWR(id string, oldD, newD []interface{}, conn *wafregional.WAFRegional, region string) error { + + wr := newWafRegionalRetryer(conn, region) + _, err := wr.RetryWithToken(func(token *string) (interface{}, error) { + req := &waf.UpdateIPSetInput{ + ChangeToken: token, + IPSetId: aws.String(id), + Updates: diffWafIpSetDescriptors(oldD, newD), + } + log.Printf("[INFO] Updating IPSet descriptor: %s", req) + + return conn.UpdateIPSet(req) + }) + if err != nil { + return fmt.Errorf("Error Updating WAF IPSet: %s", err) + } + + return nil +} diff --git a/builtin/providers/aws/resource_aws_wafregional_ipset_test.go b/builtin/providers/aws/resource_aws_wafregional_ipset_test.go new file mode 100644 index 000000000..3aa251d43 --- /dev/null +++ b/builtin/providers/aws/resource_aws_wafregional_ipset_test.go @@ -0,0 +1,402 @@ +package aws + +import ( + "fmt" + "reflect" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "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/terraform/helper/acctest" +) + +func TestAccAWSWafRegionalIPSet_basic(t *testing.T) { + var v waf.IPSet + ipsetName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalIPSetDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSWafRegionalIPSetConfig(ipsetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRegionalIPSetExists("aws_wafregional_ipset.ipset", &v), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "name", ipsetName), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.4037960608.type", "IPV4"), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.4037960608.value", "192.0.7.0/24"), + ), + }, + }, + }) +} + +func TestAccAWSWafRegionalIPSet_disappears(t *testing.T) { + var v waf.IPSet + ipsetName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalIPSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRegionalIPSetConfig(ipsetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRegionalIPSetExists("aws_wafregional_ipset.ipset", &v), + testAccCheckAWSWafRegionalIPSetDisappears(&v), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSWafRegionalIPSet_changeNameForceNew(t *testing.T) { + var before, after waf.IPSet + ipsetName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + ipsetNewName := fmt.Sprintf("ip-set-new-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalIPSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRegionalIPSetConfig(ipsetName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSWafRegionalIPSetExists("aws_wafregional_ipset.ipset", &before), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "name", ipsetName), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.4037960608.type", "IPV4"), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.4037960608.value", "192.0.7.0/24"), + ), + }, + { + Config: testAccAWSWafRegionalIPSetConfigChangeName(ipsetNewName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSWafRegionalIPSetExists("aws_wafregional_ipset.ipset", &after), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "name", ipsetNewName), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.4037960608.type", "IPV4"), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.4037960608.value", "192.0.7.0/24"), + ), + }, + }, + }) +} + +func TestAccAWSWafRegionalIPSet_changeDescriptors(t *testing.T) { + var before, after waf.IPSet + ipsetName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalIPSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRegionalIPSetConfig(ipsetName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSWafRegionalIPSetExists("aws_wafregional_ipset.ipset", &before), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "name", ipsetName), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.#", "1"), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.4037960608.type", "IPV4"), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.4037960608.value", "192.0.7.0/24"), + ), + }, + { + Config: testAccAWSWafRegionalIPSetConfigChangeIPSetDescriptors(ipsetName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSWafRegionalIPSetExists("aws_wafregional_ipset.ipset", &after), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "name", ipsetName), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.#", "1"), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.115741513.type", "IPV4"), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.115741513.value", "192.0.8.0/24"), + ), + }, + }, + }) +} + +func TestAccAWSWafRegionalIPSet_noDescriptors(t *testing.T) { + var ipset waf.IPSet + ipsetName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafRegionalIPSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRegionalIPSetConfig_noDescriptors(ipsetName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSWafRegionalIPSetExists("aws_wafregional_ipset.ipset", &ipset), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "name", ipsetName), + resource.TestCheckResourceAttr( + "aws_wafregional_ipset.ipset", "ip_set_descriptor.#", "0"), + ), + }, + }, + }) +} + +func TestDiffWafRegionalIpSetDescriptors(t *testing.T) { + testCases := []struct { + Old []interface{} + New []interface{} + ExpectedUpdates []*waf.IPSetUpdate + }{ + { + // Change + Old: []interface{}{ + map[string]interface{}{"type": "IPV4", "value": "192.0.7.0/24"}, + }, + New: []interface{}{ + map[string]interface{}{"type": "IPV4", "value": "192.0.8.0/24"}, + }, + ExpectedUpdates: []*waf.IPSetUpdate{ + &waf.IPSetUpdate{ + Action: aws.String(wafregional.ChangeActionDelete), + IPSetDescriptor: &waf.IPSetDescriptor{ + Type: aws.String("IPV4"), + Value: aws.String("192.0.7.0/24"), + }, + }, + &waf.IPSetUpdate{ + Action: aws.String(wafregional.ChangeActionInsert), + IPSetDescriptor: &waf.IPSetDescriptor{ + Type: aws.String("IPV4"), + Value: aws.String("192.0.8.0/24"), + }, + }, + }, + }, + { + // Fresh IPSet + Old: []interface{}{}, + New: []interface{}{ + map[string]interface{}{"type": "IPV4", "value": "10.0.1.0/24"}, + map[string]interface{}{"type": "IPV4", "value": "10.0.2.0/24"}, + map[string]interface{}{"type": "IPV4", "value": "10.0.3.0/24"}, + }, + ExpectedUpdates: []*waf.IPSetUpdate{ + &waf.IPSetUpdate{ + Action: aws.String(wafregional.ChangeActionInsert), + IPSetDescriptor: &waf.IPSetDescriptor{ + Type: aws.String("IPV4"), + Value: aws.String("10.0.1.0/24"), + }, + }, + &waf.IPSetUpdate{ + Action: aws.String(wafregional.ChangeActionInsert), + IPSetDescriptor: &waf.IPSetDescriptor{ + Type: aws.String("IPV4"), + Value: aws.String("10.0.2.0/24"), + }, + }, + &waf.IPSetUpdate{ + Action: aws.String(wafregional.ChangeActionInsert), + IPSetDescriptor: &waf.IPSetDescriptor{ + Type: aws.String("IPV4"), + Value: aws.String("10.0.3.0/24"), + }, + }, + }, + }, + { + // Deletion + Old: []interface{}{ + map[string]interface{}{"type": "IPV4", "value": "192.0.7.0/24"}, + map[string]interface{}{"type": "IPV4", "value": "192.0.8.0/24"}, + }, + New: []interface{}{}, + ExpectedUpdates: []*waf.IPSetUpdate{ + &waf.IPSetUpdate{ + Action: aws.String(wafregional.ChangeActionDelete), + IPSetDescriptor: &waf.IPSetDescriptor{ + Type: aws.String("IPV4"), + Value: aws.String("192.0.7.0/24"), + }, + }, + &waf.IPSetUpdate{ + Action: aws.String(wafregional.ChangeActionDelete), + IPSetDescriptor: &waf.IPSetDescriptor{ + Type: aws.String("IPV4"), + Value: aws.String("192.0.8.0/24"), + }, + }, + }, + }, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + updates := diffWafIpSetDescriptors(tc.Old, tc.New) + if !reflect.DeepEqual(updates, tc.ExpectedUpdates) { + t.Fatalf("IPSet updates don't match.\nGiven: %s\nExpected: %s", + updates, tc.ExpectedUpdates) + } + }) + } +} + +func testAccCheckAWSWafRegionalIPSetDisappears(v *waf.IPSet) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).wafregionalconn + region := testAccProvider.Meta().(*AWSClient).region + + wr := newWafRegionalRetryer(conn, region) + _, err := wr.RetryWithToken(func(token *string) (interface{}, error) { + req := &waf.UpdateIPSetInput{ + ChangeToken: token, + IPSetId: v.IPSetId, + } + + for _, IPSetDescriptor := range v.IPSetDescriptors { + IPSetUpdate := &waf.IPSetUpdate{ + Action: aws.String("DELETE"), + IPSetDescriptor: &waf.IPSetDescriptor{ + Type: IPSetDescriptor.Type, + Value: IPSetDescriptor.Value, + }, + } + req.Updates = append(req.Updates, IPSetUpdate) + } + + return conn.UpdateIPSet(req) + }) + if err != nil { + return fmt.Errorf("Error Updating WAF IPSet: %s", err) + } + + _, err = wr.RetryWithToken(func(token *string) (interface{}, error) { + opts := &waf.DeleteIPSetInput{ + ChangeToken: token, + IPSetId: v.IPSetId, + } + return conn.DeleteIPSet(opts) + }) + if err != nil { + return fmt.Errorf("Error Deleting WAF IPSet: %s", err) + } + return nil + } +} + +func testAccCheckAWSWafRegionalIPSetDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_wafregional_ipset" { + continue + } + + conn := testAccProvider.Meta().(*AWSClient).wafregionalconn + resp, err := conn.GetIPSet( + &waf.GetIPSetInput{ + IPSetId: aws.String(rs.Primary.ID), + }) + + if err == nil { + if *resp.IPSet.IPSetId == rs.Primary.ID { + return fmt.Errorf("WAF IPSet %s still exists", rs.Primary.ID) + } + } + + // Return nil if the IPSet is already destroyed + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() == "WAFNonexistentItemException" { + return nil + } + } + + return err + } + + return nil +} + +func testAccCheckAWSWafRegionalIPSetExists(n string, v *waf.IPSet) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No WAF IPSet ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).wafregionalconn + resp, err := conn.GetIPSet(&waf.GetIPSetInput{ + IPSetId: aws.String(rs.Primary.ID), + }) + + if err != nil { + return err + } + + if *resp.IPSet.IPSetId == rs.Primary.ID { + *v = *resp.IPSet + return nil + } + + return fmt.Errorf("WAF IPSet (%s) not found", rs.Primary.ID) + } +} + +func testAccAWSWafRegionalIPSetConfig(name string) string { + return fmt.Sprintf(` +resource "aws_wafregional_ipset" "ipset" { + name = "%s" + ip_set_descriptor { + type = "IPV4" + value = "192.0.7.0/24" + } +}`, name) +} + +func testAccAWSWafRegionalIPSetConfigChangeName(name string) string { + return fmt.Sprintf(`resource "aws_wafregional_ipset" "ipset" { + name = "%s" + ip_set_descriptor { + type = "IPV4" + value = "192.0.7.0/24" + } +}`, name) +} + +func testAccAWSWafRegionalIPSetConfigChangeIPSetDescriptors(name string) string { + return fmt.Sprintf(`resource "aws_wafregional_ipset" "ipset" { + name = "%s" + ip_set_descriptor { + type = "IPV4" + value = "192.0.8.0/24" + } +}`, name) +} + +func testAccAWSWafRegionalIPSetConfig_noDescriptors(name string) string { + return fmt.Sprintf(`resource "aws_wafregional_ipset" "ipset" { + name = "%s" + }`, name) +} diff --git a/builtin/providers/aws/wafregionl_token_handlers.go b/builtin/providers/aws/wafregionl_token_handlers.go new file mode 100644 index 000000000..da3d8b58f --- /dev/null +++ b/builtin/providers/aws/wafregionl_token_handlers.go @@ -0,0 +1,50 @@ +package aws + +import ( + "time" + + "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/resource" +) + +type WafRegionalRetryer struct { + Connection *wafregional.WAFRegional + Region string +} + +type withRegionalTokenFunc func(token *string) (interface{}, error) + +func (t *WafRegionalRetryer) RetryWithToken(f withRegionalTokenFunc) (interface{}, error) { + awsMutexKV.Lock(t.Region) + defer awsMutexKV.Unlock(t.Region) + + var out interface{} + err := resource.Retry(15*time.Minute, func() *resource.RetryError { + var err error + var tokenOut *waf.GetChangeTokenOutput + + tokenOut, err = t.Connection.GetChangeToken(&waf.GetChangeTokenInput{}) + if err != nil { + return resource.NonRetryableError(errwrap.Wrapf("Failed to acquire change token: {{err}}", err)) + } + + out, err = f(tokenOut.ChangeToken) + if err != nil { + awsErr, ok := err.(awserr.Error) + if ok && awsErr.Code() == "WAFStaleDataException" { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + return out, err +} + +func newWafRegionalRetryer(conn *wafregional.WAFRegional, region string) *WafRegionalRetryer { + return &WafRegionalRetryer{Connection: conn, Region: region} +} diff --git a/website/source/docs/providers/aws/r/wafregional_byte_match_set.html.markdown b/website/source/docs/providers/aws/r/wafregional_byte_match_set.html.markdown new file mode 100644 index 000000000..1ebd97a48 --- /dev/null +++ b/website/source/docs/providers/aws/r/wafregional_byte_match_set.html.markdown @@ -0,0 +1,55 @@ +--- +layout: "aws" +page_title: "AWS: wafregional_byte_match_set" +sidebar_current: "docs-aws-resource-wafregional-bytematchset" +description: |- + Provides a AWS WAF Regional ByteMatchSet resource for use with ALB. +--- + +# aws\_wafregional\_byte\_match\_set + +Provides a WAF Regional Byte Match Set Resource for use with Application Load Balancer. + +## Example Usage + +``` +resource "aws_wafregional_byte_match_set" "byte_set" { + name = "tf_waf_byte_match_set" + byte_match_tuple { + text_transformation = "NONE" + target_string = "badrefer1" + positional_constraint = "CONTAINS" + field_to_match { + type = "HEADER" + data = "referer" + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name or description of the ByteMatchSet. +* `byte_match_tuple` - (Optional)Settings for the ByteMatchSet, such as the bytes (typically a string that corresponds with ASCII characters) that you want AWS WAF to search for in web requests. ByteMatchTuple documented below. + +ByteMatchTuple(byte_match_tuple) support the following: + +* `field_to_match` - (Required) Settings for the ByteMatchTuple. FieldToMatch documented below. +* `positional_constraint` - (Required) Within the portion of a web request that you want to search. +* `target_string` - (Required) The value that you want AWS WAF to search for. The maximum length of the value is 50 bytes. +* `text_transformation` - (Required) The formatting way for web request. + +FieldToMatch(field_to_match) support following: + +* `data` - (Optional) When the value of Type is HEADER, enter the name of the header that you want AWS WAF to search, for example, User-Agent or Referer. If the value of Type is any other value, omit Data. +* `type` - (Required) The part of the web request that you want AWS WAF to search for a specified string. + +## Remarks + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the WAF ByteMatchSet. diff --git a/website/source/docs/providers/aws/r/wafregional_ipset.html.markdown b/website/source/docs/providers/aws/r/wafregional_ipset.html.markdown new file mode 100644 index 000000000..5ad381266 --- /dev/null +++ b/website/source/docs/providers/aws/r/wafregional_ipset.html.markdown @@ -0,0 +1,44 @@ +--- +layout: "aws" +page_title: "AWS: wafregional_ipset" +sidebar_current: "docs-aws-resource-wafregional-ipset" +description: |- + Provides a AWS WAF Regional IPSet resource for use with ALB. +--- + +# aws\_wafregional\_ipset + +Provides a WAF Regional IPSet Resource for use with Application Load Balancer. + +## Example Usage + +``` +resource "aws_wafregional_ipset" "ipset" { + name = "tfIPSet" + ip_set_descriptor { + type = "IPV4" + value = "192.0.7.0/24" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name or description of the IPSet. +* `ip_set_descriptor` - (Optional) The IP address type and IP address range (in CIDR notation) from which web requests originate. + +IPSetDescriptor(ip_set_descriptor) support following: + +* `type` - (Required) The string like IPV4 or IPV6. +* `value` - (Required) The CIDR notation. + + +## Remarks + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the WAF IPSet. diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index a00b43666..f1ebd244d 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -1122,6 +1122,21 @@ +