From 69312e357f8b1d81e62deeafa56ee0f0b0711c99 Mon Sep 17 00:00:00 2001 From: Pascal van Buijtene Date: Thu, 16 Jan 2020 11:38:23 +0100 Subject: [PATCH 01/19] Basic functionality --- aws/provider.go | 1 + aws/resource_aws_wafv2_ip_set.go | 191 ++++++++++++++++++++++++++ aws/resource_aws_wafv2_ip_set_test.go | 116 ++++++++++++++++ 3 files changed, 308 insertions(+) create mode 100644 aws/resource_aws_wafv2_ip_set.go create mode 100644 aws/resource_aws_wafv2_ip_set_test.go diff --git a/aws/provider.go b/aws/provider.go index 6a1e1e768bf..6801fabe536 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -881,6 +881,7 @@ func Provider() terraform.ResourceProvider { "aws_wafregional_xss_match_set": resourceAwsWafRegionalXssMatchSet(), "aws_wafregional_web_acl": resourceAwsWafRegionalWebAcl(), "aws_wafregional_web_acl_association": resourceAwsWafRegionalWebAclAssociation(), + "aws_wafv2_ip_set": resourceAwsWafv2IPSet(), "aws_worklink_fleet": resourceAwsWorkLinkFleet(), "aws_worklink_website_certificate_authority_association": resourceAwsWorkLinkWebsiteCertificateAuthorityAssociation(), "aws_workspaces_directory": resourceAwsWorkspacesDirectory(), diff --git a/aws/resource_aws_wafv2_ip_set.go b/aws/resource_aws_wafv2_ip_set.go new file mode 100644 index 00000000000..a526ce6246e --- /dev/null +++ b/aws/resource_aws_wafv2_ip_set.go @@ -0,0 +1,191 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/service/wafv2" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceAwsWafv2IPSet() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsWafv2IPSetCreate, + Read: resourceAwsWafv2IPSetRead, + Update: resourceAwsWafv2IPSetUpdate, + Delete: resourceAwsWafv2IPSetDelete, + + Schema: map[string]*schema.Schema{ + "addresses": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "ip_address_version": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + wafv2.IPAddressVersionIpv4, + wafv2.IPAddressVersionIpv6, + }, false), + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "scope": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + wafv2.ScopeCloudfront, + wafv2.ScopeRegional, + }, false), + }, + "tags": tagsSchema(), + }, + } +} + +func resourceAwsWafv2IPSetCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + var resp *wafv2.CreateIPSetOutput + + params := &wafv2.CreateIPSetInput{ + Addresses: expandStringSet(d.Get("addresses").(*schema.Set)), + Description: aws.String(d.Get("description").(string)), + IPAddressVersion: aws.String(d.Get("ip_address_version").(string)), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + } + err := resource.Retry(15*time.Minute, func() *resource.RetryError { + var err error + resp, err = conn.CreateIPSet(params) + if err != nil { + if isAWSErr(err, wafv2.ErrCodeWAFInternalErrorException, "AWS WAF couldn’t perform the operation because of a system problem") { + return resource.RetryableError(err) + } + if isAWSErr(err, wafv2.ErrCodeWAFTagOperationException, "An error occurred during the tagging operation") { + return resource.RetryableError(err) + } + if isAWSErr(err, wafv2.ErrCodeWAFTagOperationInternalErrorException, "AWS WAF couldn’t perform your tagging operation because of an internal error") { + return resource.RetryableError(err) + } + if isAWSErr(err, wafv2.ErrCodeWAFOptimisticLockException, "AWS WAF couldn’t save your changes because you tried to update or delete a resource that has changed since you last retrieved it") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + if isResourceTimeoutError(err) { + _, err = conn.CreateIPSet(params) + } + if err != nil { + return err + } + d.SetId(*resp.Summary.Id) + + return resourceAwsWafv2IPSetRead(d, meta) +} + +func resourceAwsWafv2IPSetRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + + params := &wafv2.GetIPSetInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + } + + resp, err := conn.GetIPSet(params) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == wafv2.ErrCodeWAFNonexistentItemException { + log.Printf("[WARN] WAFV2 IPSet (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + return err + } + + d.Set("name", resp.IPSet.Name) + d.Set("description", resp.IPSet.Description) + d.Set("ip_address_version", resp.IPSet.IPAddressVersion) + d.Set("addresses", resp.IPSet.Addresses) + d.Set("arn", resp.IPSet.ARN) + + return nil +} + +func resourceAwsWafv2IPSetUpdate(d *schema.ResourceData, meta interface{}) error { + return resourceAwsWafv2IPSetRead(d, meta) +} + +func resourceAwsWafv2IPSetDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + var resp *wafv2.GetIPSetOutput + params := &wafv2.GetIPSetInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + } + log.Printf("[INFO] Deleting WAFV2 IPSet %s", d.Id()) + + err := resource.Retry(15*time.Minute, func() *resource.RetryError { + var err error + resp, err = conn.GetIPSet(params) + if err != nil { + return resource.NonRetryableError(fmt.Errorf("Error getting lock token: %s", err)) + } + + _, err = conn.DeleteIPSet(&wafv2.DeleteIPSetInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + LockToken: resp.LockToken, + }) + + if err != nil { + if isAWSErr(err, wafv2.ErrCodeWAFInternalErrorException, "AWS WAF couldn’t perform the operation because of a system problem") { + return resource.RetryableError(err) + } + if isAWSErr(err, wafv2.ErrCodeWAFOptimisticLockException, "AWS WAF couldn’t save your changes because you tried to update or delete a resource that has changed since you last retrieved it") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.DeleteIPSet(&wafv2.DeleteIPSetInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + LockToken: resp.LockToken, + }) + } + + if err != nil { + return fmt.Errorf("Error deleting WAFV2 IPSet: %s", err) + } + + return nil +} diff --git a/aws/resource_aws_wafv2_ip_set_test.go b/aws/resource_aws_wafv2_ip_set_test.go new file mode 100644 index 00000000000..01dc52d1ae2 --- /dev/null +++ b/aws/resource_aws_wafv2_ip_set_test.go @@ -0,0 +1,116 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/wafv2" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + + "github.com/aws/aws-sdk-go/aws" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" +) + +func TestAccAwsWafv2IPSet_basic(t *testing.T) { + var v wafv2.IPSet + ipSetName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + resourceName := "aws_wafv2_ip_set.ip_set" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafv2IPSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2IPSetConfig(ipSetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists("aws_wafv2_ip_set.ip_set", &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/ipset/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", ipSetName), + resource.TestCheckResourceAttr(resourceName, "description", ipSetName), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "ip_address_version", wafv2.IPAddressVersionIpv4), + ), + }, + }, + }) +} + +func testAccCheckAWSWafv2IPSetDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_wafv2_ip_set" { + continue + } + + conn := testAccProvider.Meta().(*AWSClient).wafv2conn + resp, err := conn.GetIPSet( + &wafv2.GetIPSetInput{ + Id: aws.String(rs.Primary.ID), + Name: aws.String(rs.Primary.Attributes["name"]), + Scope: aws.String(rs.Primary.Attributes["scope"]), + }) + + if err == nil { + if *resp.IPSet.Id == rs.Primary.ID { + return fmt.Errorf("WAFV2 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() == wafv2.ErrCodeWAFNonexistentItemException { + return nil + } + } + + return err + } + + return nil +} + +func testAccCheckAWSWafv2IPSetExists(n string, v *wafv2.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 WAFV2 IPSet ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).wafv2conn + resp, err := conn.GetIPSet(&wafv2.GetIPSetInput{ + Id: aws.String(rs.Primary.ID), + Name: aws.String(rs.Primary.Attributes["name"]), + Scope: aws.String(rs.Primary.Attributes["scope"]), + }) + + if err != nil { + return err + } + + if *resp.IPSet.Id == rs.Primary.ID { + *v = *resp.IPSet + return nil + } + + return fmt.Errorf("WAFV2 IPSet (%s) not found", rs.Primary.ID) + } +} + +func testAccAwsWafv2IPSetConfig(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_ip_set" "ip_set" { + name = "%s" + description = "%s" + scope = "REGIONAL" + ip_address_version = "IPV4" + addresses = ["1.2.3.4/32", "5.6.7.8/32"] +} +`, name, name) +} From 5bbb31f61291618a9890e90241ec16bcd4e87bb6 Mon Sep 17 00:00:00 2001 From: Pascal van Buijtene Date: Fri, 17 Jan 2020 17:08:39 +0100 Subject: [PATCH 02/19] Fix addresses --- aws/resource_aws_wafv2_ip_set.go | 9 +++++---- aws/resource_aws_wafv2_ip_set_test.go | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_wafv2_ip_set.go b/aws/resource_aws_wafv2_ip_set.go index a526ce6246e..86440355f6d 100644 --- a/aws/resource_aws_wafv2_ip_set.go +++ b/aws/resource_aws_wafv2_ip_set.go @@ -22,8 +22,8 @@ func resourceAwsWafv2IPSet() *schema.Resource { Schema: map[string]*schema.Schema{ "addresses": { - Type: schema.TypeSet, - Required: true, + Type: schema.TypeList, + Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, "arn": { @@ -68,12 +68,13 @@ func resourceAwsWafv2IPSetCreate(d *schema.ResourceData, meta interface{}) error var resp *wafv2.CreateIPSetOutput params := &wafv2.CreateIPSetInput{ - Addresses: expandStringSet(d.Get("addresses").(*schema.Set)), + Addresses: expandStringList(d.Get("addresses").([]interface{})), Description: aws.String(d.Get("description").(string)), IPAddressVersion: aws.String(d.Get("ip_address_version").(string)), Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), } + err := resource.Retry(15*time.Minute, func() *resource.RetryError { var err error resp, err = conn.CreateIPSet(params) @@ -128,8 +129,8 @@ func resourceAwsWafv2IPSetRead(d *schema.ResourceData, meta interface{}) error { d.Set("name", resp.IPSet.Name) d.Set("description", resp.IPSet.Description) d.Set("ip_address_version", resp.IPSet.IPAddressVersion) - d.Set("addresses", resp.IPSet.Addresses) d.Set("arn", resp.IPSet.ARN) + d.Set("addresses", flattenStringList(resp.IPSet.Addresses)) return nil } diff --git a/aws/resource_aws_wafv2_ip_set_test.go b/aws/resource_aws_wafv2_ip_set_test.go index 01dc52d1ae2..82406b35d35 100644 --- a/aws/resource_aws_wafv2_ip_set_test.go +++ b/aws/resource_aws_wafv2_ip_set_test.go @@ -33,6 +33,9 @@ func TestAccAwsWafv2IPSet_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "description", ipSetName), resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), resource.TestCheckResourceAttr(resourceName, "ip_address_version", wafv2.IPAddressVersionIpv4), + resource.TestCheckResourceAttr(resourceName, "addresses.#", "2"), + resource.TestCheckResourceAttr(resourceName, "addresses.0", "1.2.3.4/32"), + resource.TestCheckResourceAttr(resourceName, "addresses.1", "5.6.7.8/32"), ), }, }, From cfa3efc25faa02cb55efe704d3fdc8e516cdf247 Mon Sep 17 00:00:00 2001 From: Pascal van Buijtene Date: Thu, 23 Jan 2020 17:12:52 +0100 Subject: [PATCH 03/19] Add update --- aws/resource_aws_wafv2_ip_set.go | 93 ++++++++++++++++++++++++--- aws/resource_aws_wafv2_ip_set_test.go | 62 +++++++++++++++++- 2 files changed, 143 insertions(+), 12 deletions(-) diff --git a/aws/resource_aws_wafv2_ip_set.go b/aws/resource_aws_wafv2_ip_set.go index 86440355f6d..6eca7cf99ae 100644 --- a/aws/resource_aws_wafv2_ip_set.go +++ b/aws/resource_aws_wafv2_ip_set.go @@ -22,8 +22,10 @@ func resourceAwsWafv2IPSet() *schema.Resource { Schema: map[string]*schema.Schema{ "addresses": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, + MinItems: 1, + MaxItems: 50, Elem: &schema.Schema{Type: schema.TypeString}, }, "arn": { @@ -31,9 +33,9 @@ func resourceAwsWafv2IPSet() *schema.Resource { Computed: true, }, "description": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 256), }, "ip_address_version": { Type: schema.TypeString, @@ -45,9 +47,10 @@ func resourceAwsWafv2IPSet() *schema.Resource { }, false), }, "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), }, "scope": { Type: schema.TypeString, @@ -68,13 +71,20 @@ func resourceAwsWafv2IPSetCreate(d *schema.ResourceData, meta interface{}) error var resp *wafv2.CreateIPSetOutput params := &wafv2.CreateIPSetInput{ - Addresses: expandStringList(d.Get("addresses").([]interface{})), - Description: aws.String(d.Get("description").(string)), + Addresses: []*string{}, IPAddressVersion: aws.String(d.Get("ip_address_version").(string)), Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), } + if v, ok := d.GetOk("addresses"); ok && v.(*schema.Set).Len() > 0 { + params.Addresses = expandStringSet(d.Get("addresses").(*schema.Set)) + } + + if d.HasChange("description") { + params.Description = aws.String(d.Get("description").(string)) + } + err := resource.Retry(15*time.Minute, func() *resource.RetryError { var err error resp, err = conn.CreateIPSet(params) @@ -130,12 +140,75 @@ func resourceAwsWafv2IPSetRead(d *schema.ResourceData, meta interface{}) error { d.Set("description", resp.IPSet.Description) d.Set("ip_address_version", resp.IPSet.IPAddressVersion) d.Set("arn", resp.IPSet.ARN) - d.Set("addresses", flattenStringList(resp.IPSet.Addresses)) + + if err := d.Set("addresses", schema.NewSet(schema.HashString, flattenStringList(resp.IPSet.Addresses))); err != nil { + return fmt.Errorf("Error setting addresses: %s", err) + } return nil } func resourceAwsWafv2IPSetUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + var resp *wafv2.GetIPSetOutput + params := &wafv2.GetIPSetInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + } + log.Printf("[INFO] Updating WAFV2 IPSet %s", d.Id()) + + err := resource.Retry(15*time.Minute, func() *resource.RetryError { + var err error + resp, err = conn.GetIPSet(params) + if err != nil { + return resource.NonRetryableError(fmt.Errorf("Error getting lock token: %s", err)) + } + + u := &wafv2.UpdateIPSetInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + Addresses: []*string{}, + Description: aws.String(d.Get("description").(string)), + LockToken: resp.LockToken, + } + + if v, ok := d.GetOk("addresses"); ok && v.(*schema.Set).Len() > 0 { + u.Addresses = expandStringSet(d.Get("addresses").(*schema.Set)) + } + + if d.HasChange("description") { + u.Description = aws.String(d.Get("description").(string)) + } + + _, err = conn.UpdateIPSet(u) + + if err != nil { + if isAWSErr(err, wafv2.ErrCodeWAFInternalErrorException, "AWS WAF couldn’t perform the operation because of a system problem") { + return resource.RetryableError(err) + } + if isAWSErr(err, wafv2.ErrCodeWAFOptimisticLockException, "AWS WAF couldn’t save your changes because you tried to update or delete a resource that has changed since you last retrieved it") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.DeleteIPSet(&wafv2.DeleteIPSetInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + LockToken: resp.LockToken, + }) + } + + if err != nil { + return fmt.Errorf("Error updating WAFV2 IPSet: %s", err) + } + return resourceAwsWafv2IPSetRead(d, meta) } diff --git a/aws/resource_aws_wafv2_ip_set_test.go b/aws/resource_aws_wafv2_ip_set_test.go index 82406b35d35..b395eb79a96 100644 --- a/aws/resource_aws_wafv2_ip_set_test.go +++ b/aws/resource_aws_wafv2_ip_set_test.go @@ -34,8 +34,44 @@ func TestAccAwsWafv2IPSet_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), resource.TestCheckResourceAttr(resourceName, "ip_address_version", wafv2.IPAddressVersionIpv4), resource.TestCheckResourceAttr(resourceName, "addresses.#", "2"), - resource.TestCheckResourceAttr(resourceName, "addresses.0", "1.2.3.4/32"), - resource.TestCheckResourceAttr(resourceName, "addresses.1", "5.6.7.8/32"), + ), + }, + { + Config: testAccAwsWafv2IPSetConfigUpdate(ipSetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists("aws_wafv2_ip_set.ip_set", &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/ipset/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", ipSetName), + resource.TestCheckResourceAttr(resourceName, "description", "Updated"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "ip_address_version", wafv2.IPAddressVersionIpv4), + resource.TestCheckResourceAttr(resourceName, "addresses.#", "3"), + ), + }, + }, + }) +} + +func TestAccAwsWafv2IPSet_minimal(t *testing.T) { + var v wafv2.IPSet + ipSetName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + resourceName := "aws_wafv2_ip_set.ip_set" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafv2IPSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2IPSetConfigMinimal(ipSetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists("aws_wafv2_ip_set.ip_set", &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/ipset/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", ipSetName), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "ip_address_version", wafv2.IPAddressVersionIpv4), + resource.TestCheckResourceAttr(resourceName, "addresses.#", "0"), ), }, }, @@ -117,3 +153,25 @@ resource "aws_wafv2_ip_set" "ip_set" { } `, name, name) } + +func testAccAwsWafv2IPSetConfigUpdate(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_ip_set" "ip_set" { + name = "%s" + description = "Updated" + scope = "REGIONAL" + ip_address_version = "IPV4" + addresses = ["1.1.1.1/32", "2.2.2.2/32", "3.3.3.3/32"] +} +`, name) +} + +func testAccAwsWafv2IPSetConfigMinimal(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_ip_set" "ip_set" { + name = "%s" + scope = "REGIONAL" + ip_address_version = "IPV4" +} +`, name) +} From 92d36954170b38615487f95370d8829997a15db9 Mon Sep 17 00:00:00 2001 From: Pascal van Buijtene Date: Wed, 12 Feb 2020 16:20:21 +0100 Subject: [PATCH 04/19] Add force new test --- aws/resource_aws_wafv2_ip_set_test.go | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/aws/resource_aws_wafv2_ip_set_test.go b/aws/resource_aws_wafv2_ip_set_test.go index b395eb79a96..986ad0c7aa7 100644 --- a/aws/resource_aws_wafv2_ip_set_test.go +++ b/aws/resource_aws_wafv2_ip_set_test.go @@ -78,6 +78,45 @@ func TestAccAwsWafv2IPSet_minimal(t *testing.T) { }) } +func TestAccAwsWafv2IPSet_changeNameForceNew(t *testing.T) { + var before, after wafv2.IPSet + ipSetName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + ipSetNewName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + resourceName := "aws_wafv2_ip_set.ip_set" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafv2IPSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2IPSetConfig(ipSetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists("aws_wafv2_ip_set.ip_set", &before), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/ipset/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", ipSetName), + resource.TestCheckResourceAttr(resourceName, "description", ipSetName), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "ip_address_version", wafv2.IPAddressVersionIpv4), + resource.TestCheckResourceAttr(resourceName, "addresses.#", "2"), + ), + }, + { + Config: testAccAwsWafv2IPSetConfig(ipSetNewName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists("aws_wafv2_ip_set.ip_set", &after), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/ipset/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", ipSetNewName), + resource.TestCheckResourceAttr(resourceName, "description", ipSetNewName), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "ip_address_version", wafv2.IPAddressVersionIpv4), + resource.TestCheckResourceAttr(resourceName, "addresses.#", "2"), + ), + }, + }, + }) +} + func testAccCheckAWSWafv2IPSetDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { if rs.Type != "aws_wafv2_ip_set" { From d6c40b64661862e7cf1da81a2f1deb8fb60701aa Mon Sep 17 00:00:00 2001 From: Pascal van Buijtene Date: Wed, 12 Feb 2020 17:28:20 +0100 Subject: [PATCH 05/19] Add import --- aws/resource_aws_wafv2_ip_set.go | 16 ++++++++++++++++ aws/resource_aws_wafv2_ip_set_test.go | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/aws/resource_aws_wafv2_ip_set.go b/aws/resource_aws_wafv2_ip_set.go index 6eca7cf99ae..f2a27f4f24c 100644 --- a/aws/resource_aws_wafv2_ip_set.go +++ b/aws/resource_aws_wafv2_ip_set.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "log" + "strings" "time" "github.com/aws/aws-sdk-go/aws" @@ -19,6 +20,21 @@ func resourceAwsWafv2IPSet() *schema.Resource { Read: resourceAwsWafv2IPSetRead, Update: resourceAwsWafv2IPSetUpdate, Delete: resourceAwsWafv2IPSetDelete, + Importer: &schema.ResourceImporter{ + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + idParts := strings.Split(d.Id(), "/") + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + return nil, fmt.Errorf("Unexpected format of ID (%q), expected ID/NAME/SCOPE", d.Id()) + } + id := idParts[0] + name := idParts[1] + scope := idParts[2] + d.SetId(id) + d.Set("name", name) + d.Set("scope", scope) + return []*schema.ResourceData{d}, nil + }, + }, Schema: map[string]*schema.Schema{ "addresses": { diff --git a/aws/resource_aws_wafv2_ip_set_test.go b/aws/resource_aws_wafv2_ip_set_test.go index 986ad0c7aa7..8ff911d300f 100644 --- a/aws/resource_aws_wafv2_ip_set_test.go +++ b/aws/resource_aws_wafv2_ip_set_test.go @@ -48,6 +48,12 @@ func TestAccAwsWafv2IPSet_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "addresses.#", "3"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAWSWafv2IPSetImportStateIdFunc(resourceName), + }, }, }) } @@ -117,6 +123,8 @@ func TestAccAwsWafv2IPSet_changeNameForceNew(t *testing.T) { }) } +// TAGS + func testAccCheckAWSWafv2IPSetDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { if rs.Type != "aws_wafv2_ip_set" { @@ -214,3 +222,14 @@ resource "aws_wafv2_ip_set" "ip_set" { } `, name) } + +func testAccAWSWafv2IPSetImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not found: %s", resourceName) + } + + return fmt.Sprintf("%s/%s/%s", rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]), nil + } +} From e0b7a44d96fef0aedd933c66a1f09518818dcbad Mon Sep 17 00:00:00 2001 From: Pascal van Buijtene Date: Mon, 17 Feb 2020 17:31:03 +0100 Subject: [PATCH 06/19] Add tags support --- aws/resource_aws_wafv2_ip_set.go | 22 +++++++ aws/resource_aws_wafv2_ip_set_test.go | 89 ++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_wafv2_ip_set.go b/aws/resource_aws_wafv2_ip_set.go index f2a27f4f24c..37e5712e832 100644 --- a/aws/resource_aws_wafv2_ip_set.go +++ b/aws/resource_aws_wafv2_ip_set.go @@ -5,6 +5,7 @@ import ( "github.com/aws/aws-sdk-go/service/wafv2" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "log" "strings" "time" @@ -101,6 +102,10 @@ func resourceAwsWafv2IPSetCreate(d *schema.ResourceData, meta interface{}) error params.Description = aws.String(d.Get("description").(string)) } + if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { + params.Tags = keyvaluetags.New(v).IgnoreAws().Wafv2Tags() + } + err := resource.Retry(15*time.Minute, func() *resource.RetryError { var err error resp, err = conn.CreateIPSet(params) @@ -161,11 +166,21 @@ func resourceAwsWafv2IPSetRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting addresses: %s", err) } + tags, err := keyvaluetags.Wafv2ListTags(conn, *resp.IPSet.ARN) + if err != nil { + return fmt.Errorf("error listing tags for WAFV2 IpSet (%s): %s", *resp.IPSet.ARN, err) + } + + if err := d.Set("tags", tags.IgnoreAws().Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + return nil } func resourceAwsWafv2IPSetUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).wafv2conn + //tags := keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().Wafv2Tags() var resp *wafv2.GetIPSetOutput params := &wafv2.GetIPSetInput{ Id: aws.String(d.Id()), @@ -225,6 +240,13 @@ func resourceAwsWafv2IPSetUpdate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error updating WAFV2 IPSet: %s", err) } + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.Wafv2UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } + } + return resourceAwsWafv2IPSetRead(d, meta) } diff --git a/aws/resource_aws_wafv2_ip_set_test.go b/aws/resource_aws_wafv2_ip_set_test.go index 8ff911d300f..1697922c6cd 100644 --- a/aws/resource_aws_wafv2_ip_set_test.go +++ b/aws/resource_aws_wafv2_ip_set_test.go @@ -34,6 +34,9 @@ func TestAccAwsWafv2IPSet_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), resource.TestCheckResourceAttr(resourceName, "ip_address_version", wafv2.IPAddressVersionIpv4), resource.TestCheckResourceAttr(resourceName, "addresses.#", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag1", "Value1"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag2", "Value2"), ), }, { @@ -123,7 +126,53 @@ func TestAccAwsWafv2IPSet_changeNameForceNew(t *testing.T) { }) } -// TAGS +func TestAccAwsWafv2IPSet_tags(t *testing.T) { + var v wafv2.IPSet + ipSetName := fmt.Sprintf("ip-set-%s", acctest.RandString(5)) + resourceName := "aws_wafv2_ip_set.ip_set" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafv2IPSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2IPSetConfigOneTag(ipSetName, "Tag1", "Value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists("aws_wafv2_ip_set.ip_set", &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/ipset/.+$`)), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag1", "Value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAWSWafv2IPSetImportStateIdFunc(resourceName), + }, + { + Config: testAccAwsWafv2IPSetConfigTwoTags(ipSetName, "Tag1", "Value1Updated", "Tag2", "Value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists("aws_wafv2_ip_set.ip_set", &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/ipset/.+$`)), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag1", "Value1Updated"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag2", "Value2"), + ), + }, + { + Config: testAccAwsWafv2IPSetConfigOneTag(ipSetName, "Tag2", "Value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists("aws_wafv2_ip_set.ip_set", &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/ipset/.+$`)), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag2", "Value2"), + ), + }, + }, + }) +} func testAccCheckAWSWafv2IPSetDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { @@ -197,6 +246,11 @@ resource "aws_wafv2_ip_set" "ip_set" { scope = "REGIONAL" ip_address_version = "IPV4" addresses = ["1.2.3.4/32", "5.6.7.8/32"] + + tags = { + Tag1 = "Value1" + Tag2 = "Value2" + } } `, name, name) } @@ -223,6 +277,39 @@ resource "aws_wafv2_ip_set" "ip_set" { `, name) } +func testAccAwsWafv2IPSetConfigOneTag(name, tagKey, tagValue string) string { + return fmt.Sprintf(` +resource "aws_wafv2_ip_set" "ip_set" { + name = "%s" + description = "%s" + scope = "REGIONAL" + ip_address_version = "IPV4" + addresses = ["1.2.3.4/32", "5.6.7.8/32"] + + tags = { + %q = %q + } +} +`, name, name, tagKey, tagValue) +} + +func testAccAwsWafv2IPSetConfigTwoTags(name, tag1Key, tag1Value, tag2Key, tag2Value string) string { + return fmt.Sprintf(` +resource "aws_wafv2_ip_set" "ip_set" { + name = "%s" + description = "%s" + scope = "REGIONAL" + ip_address_version = "IPV4" + addresses = ["1.2.3.4/32", "5.6.7.8/32"] + + tags = { + %q = %q + %q = %q + } +} +`, name, name, tag1Key, tag1Value, tag2Key, tag2Value) +} + func testAccAWSWafv2IPSetImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { rs, ok := s.RootModule().Resources[resourceName] From 8b7b3ce273c686566455f09f9e066e6cc3d6f423 Mon Sep 17 00:00:00 2001 From: Pascal van Buijtene Date: Fri, 21 Feb 2020 15:10:23 +0100 Subject: [PATCH 07/19] Add docs --- website/allowed-subcategories.txt | 1 + website/aws.erb | 13 ++++++ website/docs/r/wafv2_ip_set.html.markdown | 54 +++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 website/docs/r/wafv2_ip_set.html.markdown diff --git a/website/allowed-subcategories.txt b/website/allowed-subcategories.txt index 2f7427c5abb..191b7117809 100644 --- a/website/allowed-subcategories.txt +++ b/website/allowed-subcategories.txt @@ -105,6 +105,7 @@ Transfer VPC WAF Regional WAF +WAFv2 WorkLink WorkMail WorkSpaces diff --git a/website/aws.erb b/website/aws.erb index 9c2b258301d..58c7fdf91e1 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -3553,6 +3553,19 @@ +
  • + WAFv2 + +
  • WorkLink