diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index ac3a386f6f20..e3051b732125 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -360,6 +360,7 @@ func Provider() terraform.ResourceProvider { "aws_waf_size_constraint_set": resourceAwsWafSizeConstraintSet(), "aws_waf_web_acl": resourceAwsWafWebAcl(), "aws_waf_xss_match_set": resourceAwsWafXssMatchSet(), + "aws_waf_sql_injection_match_set": resourceAwsWafSqlInjectionMatchSet(), }, ConfigureFunc: providerConfigure, } diff --git a/builtin/providers/aws/resource_aws_waf_sql_injection_match_set.go b/builtin/providers/aws/resource_aws_waf_sql_injection_match_set.go new file mode 100644 index 000000000000..01efd6a32c51 --- /dev/null +++ b/builtin/providers/aws/resource_aws_waf_sql_injection_match_set.go @@ -0,0 +1,181 @@ +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/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsWafSqlInjectionMatchSet() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsWafSqlInjectionMatchSetCreate, + Read: resourceAwsWafSqlInjectionMatchSetRead, + Update: resourceAwsWafSqlInjectionMatchSetUpdate, + Delete: resourceAwsWafSqlInjectionMatchSetDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "sql_injection_match_tuples": &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, + }, + }, + }, + }, + "text_transformation": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + } +} + +func resourceAwsWafSqlInjectionMatchSetCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafconn + + log.Printf("[INFO] Creating SqlInjectionMatchSet: %s", d.Get("name").(string)) + + // ChangeToken + var ct *waf.GetChangeTokenInput + + res, err := conn.GetChangeToken(ct) + if err != nil { + return errwrap.Wrapf("[ERROR] Error getting change token: {{err}}", err) + } + + params := &waf.CreateSqlInjectionMatchSetInput{ + ChangeToken: res.ChangeToken, + Name: aws.String(d.Get("name").(string)), + } + + resp, err := conn.CreateSqlInjectionMatchSet(params) + + if err != nil { + return errwrap.Wrapf("[ERROR] Error creating SqlInjectionMatchSet: {{err}}", err) + } + + d.SetId(*resp.SqlInjectionMatchSet.SqlInjectionMatchSetId) + + return resourceAwsWafSqlInjectionMatchSetUpdate(d, meta) +} + +func resourceAwsWafSqlInjectionMatchSetRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafconn + log.Printf("[INFO] Reading SqlInjectionMatchSet: %s", d.Get("name").(string)) + params := &waf.GetSqlInjectionMatchSetInput{ + SqlInjectionMatchSetId: aws.String(d.Id()), + } + + resp, err := conn.GetSqlInjectionMatchSet(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("name", resp.SqlInjectionMatchSet.Name) + + return nil +} + +func resourceAwsWafSqlInjectionMatchSetUpdate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[INFO] Updating SqlInjectionMatchSet: %s", d.Get("name").(string)) + err := updateSqlInjectionMatchSetResource(d, meta, waf.ChangeActionInsert) + if err != nil { + return errwrap.Wrapf("[ERROR] Error updating SqlInjectionMatchSet: {{err}}", err) + } + return resourceAwsWafSqlInjectionMatchSetRead(d, meta) +} + +func resourceAwsWafSqlInjectionMatchSetDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafconn + + log.Printf("[INFO] Deleting SqlInjectionMatchSet: %s", d.Get("name").(string)) + err := updateSqlInjectionMatchSetResource(d, meta, waf.ChangeActionDelete) + if err != nil { + return errwrap.Wrapf("[ERROR] Error deleting SqlInjectionMatchSet: {{err}}", err) + } + + var ct *waf.GetChangeTokenInput + + resp, err := conn.GetChangeToken(ct) + + req := &waf.DeleteSqlInjectionMatchSetInput{ + ChangeToken: resp.ChangeToken, + SqlInjectionMatchSetId: aws.String(d.Id()), + } + + _, err = conn.DeleteSqlInjectionMatchSet(req) + + if err != nil { + return errwrap.Wrapf("[ERROR] Error deleting SqlInjectionMatchSet: {{err}}", err) + } + + return nil +} + +func updateSqlInjectionMatchSetResource(d *schema.ResourceData, meta interface{}, ChangeAction string) error { + conn := meta.(*AWSClient).wafconn + + var ct *waf.GetChangeTokenInput + + resp, err := conn.GetChangeToken(ct) + if err != nil { + return errwrap.Wrapf("[ERROR] Error getting change token: {{err}}", err) + } + + req := &waf.UpdateSqlInjectionMatchSetInput{ + ChangeToken: resp.ChangeToken, + SqlInjectionMatchSetId: aws.String(d.Id()), + } + + sqlInjectionMatchTuples := d.Get("sql_injection_match_tuples").(*schema.Set) + for _, sqlInjectionMatchTuple := range sqlInjectionMatchTuples.List() { + simt := sqlInjectionMatchTuple.(map[string]interface{}) + sizeConstraintUpdate := &waf.SqlInjectionMatchSetUpdate{ + Action: aws.String(ChangeAction), + SqlInjectionMatchTuple: &waf.SqlInjectionMatchTuple{ + FieldToMatch: expandFieldToMatch(simt["field_to_match"].(*schema.Set).List()[0].(map[string]interface{})), + TextTransformation: aws.String(simt["text_transformation"].(string)), + }, + } + req.Updates = append(req.Updates, sizeConstraintUpdate) + } + + _, err = conn.UpdateSqlInjectionMatchSet(req) + if err != nil { + return errwrap.Wrapf("[ERROR] Error updating SqlInjectionMatchSet: {{err}}", err) + } + + return nil +} diff --git a/builtin/providers/aws/resource_aws_waf_sql_injection_match_set_test.go b/builtin/providers/aws/resource_aws_waf_sql_injection_match_set_test.go new file mode 100644 index 000000000000..f13f6711efd2 --- /dev/null +++ b/builtin/providers/aws/resource_aws_waf_sql_injection_match_set_test.go @@ -0,0 +1,226 @@ +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 TestAccAWSWafSqlInjectionMatchSet_basic(t *testing.T) { + var v waf.SqlInjectionMatchSet + sqlInjectionMatchSet := fmt.Sprintf("sqlInjectionMatchSet-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafSqlInjectionMatchSetDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSWafSqlInjectionMatchSetConfig(sqlInjectionMatchSet), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafSqlInjectionMatchSetExists("aws_waf_sql_injection_match_set.sql_injection_match_set", &v), + resource.TestCheckResourceAttr( + "aws_waf_sql_injection_match_set.sql_injection_match_set", "name", sqlInjectionMatchSet), + resource.TestCheckResourceAttr( + "aws_waf_sql_injection_match_set.sql_injection_match_set", "sql_injection_match_tuples.#", "1"), + ), + }, + }, + }) +} + +func TestAccAWSWafSqlInjectionMatchSet_changeNameForceNew(t *testing.T) { + var before, after waf.SqlInjectionMatchSet + sqlInjectionMatchSet := fmt.Sprintf("sqlInjectionMatchSet-%s", acctest.RandString(5)) + sqlInjectionMatchSetNewName := fmt.Sprintf("sqlInjectionMatchSetNewName-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafSqlInjectionMatchSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafSqlInjectionMatchSetConfig(sqlInjectionMatchSet), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafSqlInjectionMatchSetExists("aws_waf_sql_injection_match_set.sql_injection_match_set", &before), + resource.TestCheckResourceAttr( + "aws_waf_sql_injection_match_set.sql_injection_match_set", "name", sqlInjectionMatchSet), + resource.TestCheckResourceAttr( + "aws_waf_sql_injection_match_set.sql_injection_match_set", "sql_injection_match_tuples.#", "1"), + ), + }, + { + Config: testAccAWSWafSqlInjectionMatchSetConfigChangeName(sqlInjectionMatchSetNewName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafSqlInjectionMatchSetExists("aws_waf_sql_injection_match_set.sql_injection_match_set", &after), + resource.TestCheckResourceAttr( + "aws_waf_sql_injection_match_set.sql_injection_match_set", "name", sqlInjectionMatchSetNewName), + resource.TestCheckResourceAttr( + "aws_waf_sql_injection_match_set.sql_injection_match_set", "sql_injection_match_tuples.#", "1"), + ), + }, + }, + }) +} + +func TestAccAWSWafSqlInjectionMatchSet_disappears(t *testing.T) { + var v waf.SqlInjectionMatchSet + sqlInjectionMatchSet := fmt.Sprintf("sqlInjectionMatchSet-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafSqlInjectionMatchSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafSqlInjectionMatchSetConfig(sqlInjectionMatchSet), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafSqlInjectionMatchSetExists("aws_waf_sql_injection_match_set.sql_injection_match_set", &v), + testAccCheckAWSWafSqlInjectionMatchSetDisappears(&v), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAWSWafSqlInjectionMatchSetDisappears(v *waf.SqlInjectionMatchSet) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).wafconn + + var ct *waf.GetChangeTokenInput + + resp, err := conn.GetChangeToken(ct) + if err != nil { + return fmt.Errorf("Error getting change token: %s", err) + } + + req := &waf.UpdateSqlInjectionMatchSetInput{ + ChangeToken: resp.ChangeToken, + SqlInjectionMatchSetId: v.SqlInjectionMatchSetId, + } + + for _, sqlInjectionMatchTuple := range v.SqlInjectionMatchTuples { + sqlInjectionMatchTupleUpdate := &waf.SqlInjectionMatchSetUpdate{ + Action: aws.String("DELETE"), + SqlInjectionMatchTuple: &waf.SqlInjectionMatchTuple{ + FieldToMatch: sqlInjectionMatchTuple.FieldToMatch, + TextTransformation: sqlInjectionMatchTuple.TextTransformation, + }, + } + req.Updates = append(req.Updates, sqlInjectionMatchTupleUpdate) + } + _, err = conn.UpdateSqlInjectionMatchSet(req) + if err != nil { + return errwrap.Wrapf("[ERROR] Error updating SqlInjectionMatchSet: {{err}}", err) + } + + resp, err = conn.GetChangeToken(ct) + if err != nil { + return errwrap.Wrapf("[ERROR] Error getting change token: {{err}}", err) + } + + opts := &waf.DeleteSqlInjectionMatchSetInput{ + ChangeToken: resp.ChangeToken, + SqlInjectionMatchSetId: v.SqlInjectionMatchSetId, + } + if _, err := conn.DeleteSqlInjectionMatchSet(opts); err != nil { + return err + } + return nil + } +} + +func testAccCheckAWSWafSqlInjectionMatchSetExists(n string, v *waf.SqlInjectionMatchSet) 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 SqlInjectionMatchSet ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).wafconn + resp, err := conn.GetSqlInjectionMatchSet(&waf.GetSqlInjectionMatchSetInput{ + SqlInjectionMatchSetId: aws.String(rs.Primary.ID), + }) + + if err != nil { + return err + } + + if *resp.SqlInjectionMatchSet.SqlInjectionMatchSetId == rs.Primary.ID { + *v = *resp.SqlInjectionMatchSet + return nil + } + + return fmt.Errorf("WAF SqlInjectionMatchSet (%s) not found", rs.Primary.ID) + } +} + +func testAccCheckAWSWafSqlInjectionMatchSetDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_waf_byte_match_set" { + continue + } + + conn := testAccProvider.Meta().(*AWSClient).wafconn + resp, err := conn.GetSqlInjectionMatchSet( + &waf.GetSqlInjectionMatchSetInput{ + SqlInjectionMatchSetId: aws.String(rs.Primary.ID), + }) + + if err == nil { + if *resp.SqlInjectionMatchSet.SqlInjectionMatchSetId == rs.Primary.ID { + return fmt.Errorf("WAF SqlInjectionMatchSet %s still exists", rs.Primary.ID) + } + } + + // Return nil if the SqlInjectionMatchSet is already destroyed + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() == "WAFNonexistentItemException" { + return nil + } + } + + return err + } + + return nil +} + +func testAccAWSWafSqlInjectionMatchSetConfig(name string) string { + return fmt.Sprintf(` +resource "aws_waf_sql_injection_match_set" "sql_injection_match_set" { + name = "%s" + sql_injection_match_tuples { + text_transformation = "URL_DECODE" + field_to_match { + type = "QUERY_STRING" + } + } +}`, name) +} + +func testAccAWSWafSqlInjectionMatchSetConfigChangeName(name string) string { + return fmt.Sprintf(` +resource "aws_waf_sql_injection_match_set" "sql_injection_match_set" { + name = "%s" + sql_injection_match_tuples { + text_transformation = "URL_DECODE" + field_to_match { + type = "QUERY_STRING" + } + } +}`, name) +} diff --git a/website/source/docs/providers/aws/r/waf_size_constraint_set.html.markdown b/website/source/docs/providers/aws/r/waf_size_constraint_set.html.markdown index 5d7aa3349283..8c4f914daf29 100644 --- a/website/source/docs/providers/aws/r/waf_size_constraint_set.html.markdown +++ b/website/source/docs/providers/aws/r/waf_size_constraint_set.html.markdown @@ -35,4 +35,4 @@ The following arguments are supported: The following attributes are exported: -* `id` - The ID of the WAF ByteMatchSet. +* `id` - The ID of the WAF SizeConstraintSet. diff --git a/website/source/docs/providers/aws/r/waf_sql_injection_match_set.html.markdown b/website/source/docs/providers/aws/r/waf_sql_injection_match_set.html.markdown new file mode 100644 index 000000000000..ef6ca3ce59e4 --- /dev/null +++ b/website/source/docs/providers/aws/r/waf_sql_injection_match_set.html.markdown @@ -0,0 +1,36 @@ +--- +layout: "aws" +page_title: "AWS: waf_sql_injection_match_set" +sidebar_current: "docs-aws-resource-waf-sql-injection-match-set" +description: |- + Provides a AWS WAF SqlInjectionMatchSet resource. +--- + +## Example Usage + +``` +resource "aws_waf_sql_injection_match_set" "sql_injection_match_set" { + name = "tf-sql_injection_match_set" + sql_injection_match_tuples { + text_transformation = "URL_DECODE" + field_to_match { + type = "QUERY_STRING" + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name or description of the SizeConstraintSet. +* `sql_injection_match_tuples` - The parts of web requests that you want AWS WAF to inspect for malicious SQL code and, if you want AWS WAF to inspect a header, the name of the header. + +## Remarks + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the WAF SqlInjectionMatchSet. diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index b74d450e96db..d3f57e09f6a6 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -824,6 +824,10 @@ aws_waf_xss_match_set +