diff --git a/aws/config.go b/aws/config.go index fda4fafb566..59b93a29a54 100644 --- a/aws/config.go +++ b/aws/config.go @@ -709,6 +709,22 @@ func (c *Config) Client() (interface{}, error) { } }) + client.wafv2conn.Handlers.Retry.PushBack(func(r *request.Request) { + if isAWSErr(r.Error, wafv2.ErrCodeWAFInternalErrorException, "Retry your request") { + r.Retryable = aws.Bool(true) + } + + if r.Operation.Name == "CreateIPSet" { + // WAFv2 supports tag on create which can result in the below error codes according to the documentation + if isAWSErr(r.Error, wafv2.ErrCodeWAFTagOperationException, "Retry your request") { + r.Retryable = aws.Bool(true) + } + if isAWSErr(err, wafv2.ErrCodeWAFTagOperationInternalErrorException, "Retry your request") { + r.Retryable = aws.Bool(true) + } + } + }) + if !c.SkipGetEC2Platforms { supportedPlatforms, err := GetSupportedEC2Platforms(client.ec2conn) if err != nil { 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..da0f648e772 --- /dev/null +++ b/aws/resource_aws_wafv2_ip_set.go @@ -0,0 +1,263 @@ +package aws + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/wafv2" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func resourceAwsWafv2IPSet() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsWafv2IPSetCreate, + 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": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 50, + Elem: &schema.Schema{Type: schema.TypeString}, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + o, n := d.GetChange("addresses") + oldAddresses := o.(*schema.Set).List() + newAddresses := n.(*schema.Set).List() + if len(oldAddresses) == len(newAddresses) { + for _, ov := range oldAddresses { + hasAddress := false + for _, nv := range newAddresses { + // isIpv6CidrsEquals works for both IPv4 and IPv6 + if isIpv6CidrsEquals(ov.(string), nv.(string)) { + hasAddress = true + break + } + } + if !hasAddress { + return false + } + } + return true + } + return false + }, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + "ip_address_version": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + wafv2.IPAddressVersionIpv4, + wafv2.IPAddressVersionIpv6, + }, false), + }, + "lock_token": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "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 + params := &wafv2.CreateIPSetInput{ + Addresses: aws.StringSlice([]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(v.(*schema.Set)) + } + + if v, ok := d.GetOk("description"); ok { + params.Description = aws.String(v.(string)) + } + + if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { + params.Tags = keyvaluetags.New(v).IgnoreAws().Wafv2Tags() + } + + resp, err := conn.CreateIPSet(params) + + if err != nil { + return fmt.Errorf("Error creating WAFv2 IPSet: %s", err) + } + + if resp == nil || resp.Summary == nil { + return fmt.Errorf("Error creating WAFv2 IPSet") + } + + d.SetId(aws.StringValue(resp.Summary.Id)) + + return resourceAwsWafv2IPSetRead(d, meta) +} + +func resourceAwsWafv2IPSetRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + 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 isAWSErr(err, wafv2.ErrCodeWAFNonexistentItemException, "") { + log.Printf("[WARN] WAFv2 IPSet (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return err + } + + if resp == nil || resp.IPSet == nil { + return fmt.Errorf("Error reading WAFv2 IPSet") + } + + d.Set("name", aws.StringValue(resp.IPSet.Name)) + d.Set("description", aws.StringValue(resp.IPSet.Description)) + d.Set("ip_address_version", aws.StringValue(resp.IPSet.IPAddressVersion)) + d.Set("arn", aws.StringValue(resp.IPSet.ARN)) + d.Set("lock_token", aws.StringValue(resp.LockToken)) + + if err := d.Set("addresses", flattenStringSet(resp.IPSet.Addresses)); err != nil { + return fmt.Errorf("Error setting addresses: %s", err) + } + + arn := aws.StringValue(resp.IPSet.ARN) + tags, err := keyvaluetags.Wafv2ListTags(conn, arn) + if err != nil { + return fmt.Errorf("Error listing tags for WAFv2 IpSet (%s): %s", arn, err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).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 + + log.Printf("[INFO] Updating WAFv2 IPSet %s", d.Id()) + + params := &wafv2.UpdateIPSetInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + Addresses: aws.StringSlice([]string{}), + LockToken: aws.String(d.Get("lock_token").(string)), + } + + if v, ok := d.GetOk("addresses"); ok && v.(*schema.Set).Len() > 0 { + params.Addresses = expandStringSet(v.(*schema.Set)) + } + + if v, ok := d.GetOk("description"); ok && len(v.(string)) > 0 { + params.Description = aws.String(v.(string)) + } + + _, err := conn.UpdateIPSet(params) + + if err != nil { + 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) +} + +func resourceAwsWafv2IPSetDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + + log.Printf("[INFO] Deleting WAFv2 IPSet %s", d.Id()) + + params := &wafv2.DeleteIPSetInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + LockToken: aws.String(d.Get("lock_token").(string)), + } + + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + var err error + _, err = conn.DeleteIPSet(params) + if err != nil { + if isAWSErr(err, wafv2.ErrCodeWAFAssociatedItemException, "") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.DeleteIPSet(params) + } + + 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..dacd0567f2a --- /dev/null +++ b/aws/resource_aws_wafv2_ip_set_test.go @@ -0,0 +1,405 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/wafv2" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +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(resourceName, &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), + resource.TestCheckResourceAttr(resourceName, "addresses.#", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag1", "Value1"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag2", "Value2"), + ), + }, + { + Config: testAccAwsWafv2IPSetConfigUpdate(ipSetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists(resourceName, &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"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAWSWafv2IPSetImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2IPSet_Disappears(t *testing.T) { + var r 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(resourceName, &r), + testAccCheckResourceDisappears(testAccProvider, resourceAwsWafv2IPSet(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAwsWafv2IPSet_IPv6(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: testAccAwsWafv2IPSetConfigIPv6(ipSetName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafv2IPSetExists(resourceName, &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.IPAddressVersionIpv6), + resource.TestCheckResourceAttr(resourceName, "addresses.#", "3"), + resource.TestCheckResourceAttr(resourceName, "addresses.1676510651", "1234:5678:9abc:6811:0000:0000:0000:0000/64"), + resource.TestCheckResourceAttr(resourceName, "addresses.3671909787", "2001:db8::/32"), + resource.TestCheckResourceAttr(resourceName, "addresses.4089736081", "0:0:0:0:0:ffff:7f00:1/64"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAWSWafv2IPSetImportStateIdFunc(resourceName), + }, + }, + }) +} + +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(resourceName, &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"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAWSWafv2IPSetImportStateIdFunc(resourceName), + }, + }, + }) +} + +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(resourceName, &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(resourceName, &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 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(resourceName, &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(resourceName, &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(resourceName, &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 { + 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 == nil || resp.IPSet == nil { + return fmt.Errorf("Error getting WAFv2 IPSet") + } + if aws.StringValue(resp.IPSet.Id) == rs.Primary.ID { + return fmt.Errorf("WAFv2 IPSet %s still exists", rs.Primary.ID) + } + return nil + } + + // Return nil if the IPSet is already destroyed + if isAWSErr(err, 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 == nil || resp.IPSet == nil { + return fmt.Errorf("Error getting WAFv2 IPSet") + } + + if aws.StringValue(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"] + + tags = { + Tag1 = "Value1" + Tag2 = "Value2" + } +} +`, 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 testAccAwsWafv2IPSetConfigIPv6(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_ip_set" "ip_set" { + name = "%s" + description = "%s" + scope = "REGIONAL" + ip_address_version = "IPV6" + addresses = [ + "0:0:0:0:0:ffff:7f00:1/64", + "1234:5678:9abc:6811:0000:0000:0000:0000/64", + "2001:db8::/32" + ] +} +`, name, 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) +} + +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 = { + "%s" = "%s" + } +} +`, 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 = { + "%s" = "%s" + "%s" = "%s" + } +} +`, 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] + 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 + } +} 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 @@ +