Repository: kubernetes-sigs/aws-alb-ingress-controller
Pull request #1211 (2020-03-30 17:40:14)

WAFv2 support
This PR adds support for attaching WAFv2 WebACLs to ALBs managed by the ALB Ingress Controller. 

To do this, a new annotation is added:
- `alb.ingress.kubernetes.io/wafv2-acl-arn`

Closes https://github.com/kubernetes-sigs/aws-alb-ingress-controller/issues/1089

---

Now, this is a draft PR because I want to at least know I am on the right path. Early feedback and communication is good!
The code is not fully ready to be merged yet.

I saw that the AWS Console did allow me to attach both WAFv1 and WAFv2 WebACLs to the same ALB. That directly led to me creating a new controller for WAFv2, instead of extending the pre-existing WAF controller.
The WAFv2 implementation is **heavily** based on the existing WAF code and patterns.

Still to do, after I get a hint I am on the good path:
- [x] cleanup 
    - [x] a couple of random comments 
    - [x] a cache to implement so we don't get rate-limited - code deleted, no more need
- [x] tests. Unsure exactly what I can test, but we shall see
- [x] docs. I have to update the documentation with the new annotations and how they're used


Patch:
diff --git a/docs/examples/iam-policy.json b/docs/examples/iam-policy.json
index 0d8d3461a..4dff0d0af 100644
--- a/docs/examples/iam-policy.json
+++ b/docs/examples/iam-policy.json
@@ -114,6 +114,16 @@
       ],
       "Resource": "*"
     },
+    {
+      "Effect": "Allow",
+      "Action": [
+        "wafv2:GetWebACL",
+        "wafv2:GetWebACLForResource",
+        "wafv2:AssociateWebACL",
+        "wafv2:DisassociateWebACL"
+      ],
+      "Resource": "*"
+    },
     {
       "Effect": "Allow",
       "Action": [
diff --git a/docs/guide/ingress/annotation.md b/docs/guide/ingress/annotation.md
index 0fe6de557..a415b9557 100644
--- a/docs/guide/ingress/annotation.md
+++ b/docs/guide/ingress/annotation.md
@@ -47,6 +47,7 @@ You can add kubernetes annotations to ingress and service objects to customize t
 |[alb.ingress.kubernetes.io/target-type](#target-type)|instance \| ip|instance|ingress,service|
 |[alb.ingress.kubernetes.io/unhealthy-threshold-count](#unhealthy-threshold-count)|integer|'2'|ingress,service|
 |[alb.ingress.kubernetes.io/waf-acl-id](#waf-acl-id)|string|N/A|ingress|
+|[alb.ingress.kubernetes.io/wafv2-acl-arn](#wafv2-acl-arn)|string|N/A|ingress|
 
 ## Traffic Listening
 Traffic Listening can be controlled with following annotations:
@@ -497,6 +498,19 @@ Health check on target groups can be controlled with following annotations:
         ```alb.ingress.kubernetes.io/waf-acl-id: 499e8b99-6671-4614-a86d-adb1810b7fbe
         ```
 
+## WAFv2
+- <a name="wafv2-acl-arn">`alb.ingress.kubernetes.io/wafv2-acl-arn`</a> specifies ARN for the Amazon WAFv2 web ACL.
+
+    !!!warning ""
+        Only Regional WAFv2 is supported.
+
+    !!!example
+        ```alb.ingress.kubernetes.io/wafv2-acl-arn: arn:aws:wafv2:us-west-2:xxxxx:regional/webacl/xxxxxxx/3ab78708-85b0-49d3-b4e1-7a9615a6613b
+        ```
+
+    !!!tip ""
+        To get the WAFv2 Web ACL ARN from the Console, click the gear icon in the upper right and enable the ARN column.
+
 ## Shield Advanced
 - <a name="shield-advanced-protection">`alb.ingress.kubernetes.io/shield-advanced-protection`</a> turns on / off the AWS Shield Advanced protection for the load balancer.
 
diff --git a/internal/alb/lb/loadbalancer.go b/internal/alb/lb/loadbalancer.go
index b1430e952..66135bcae 100644
--- a/internal/alb/lb/loadbalancer.go
+++ b/internal/alb/lb/loadbalancer.go
@@ -46,6 +46,7 @@ func NewController(
 	tagsController tags.Controller) Controller {
 	attrsController := NewAttributesController(cloud)
 	wafController := NewWAFController(cloud)
+	wafV2Controller := NewWAFV2Controller(cloud)
 	shieldController := NewShieldController(cloud)
 
 	return &defaultController{
@@ -58,6 +59,7 @@ func NewController(
 		tagsController:          tagsController,
 		attrsController:         attrsController,
 		wafController:           wafController,
+		wafV2Controller:         wafV2Controller,
 		shieldController:        shieldController,
 	}
 }
@@ -83,6 +85,7 @@ type defaultController struct {
 	tagsController          tags.Controller
 	attrsController         AttributesController
 	wafController           WAFController
+	wafV2Controller         WAFV2Controller
 	shieldController        ShieldController
 }
 
@@ -122,6 +125,12 @@ func (controller *defaultController) Reconcile(ctx context.Context, ingress *ext
 		}
 	}
 
+	if controller.store.GetConfig().FeatureGate.Enabled(config.WAFV2) {
+		if err := controller.wafV2Controller.Reconcile(ctx, lbArn, ingress); err != nil {
+			return nil, err
+		}
+	}
+
 	if controller.store.GetConfig().FeatureGate.Enabled(config.ShieldAdvanced) {
 		if err := controller.shieldController.Reconcile(ctx, lbArn, ingress); err != nil {
 			return nil, err
diff --git a/internal/alb/lb/wafv2.go b/internal/alb/lb/wafv2.go
new file mode 100644
index 000000000..43eded1dc
--- /dev/null
+++ b/internal/alb/lb/wafv2.go
@@ -0,0 +1,92 @@
+package lb
+
+import (
+	"context"
+	"time"
+
+	"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/albctx"
+	"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/aws"
+	"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/ingress/annotations"
+	"github.com/pkg/errors"
+	extensions "k8s.io/api/extensions/v1beta1"
+	"k8s.io/apimachinery/pkg/util/cache"
+)
+
+const (
+	webACLARNForLBCacheMaxSize = 1024
+	webACLARNForLBCacheTTL     = 10 * time.Minute
+)
+
+// WAFCV2ontroller provides functionality to manage ALB's WAF V2 associations.
+type WAFV2Controller interface {
+	Reconcile(ctx context.Context, lbArn string, ingress *extensions.Ingress) error
+}
+
+func NewWAFV2Controller(cloud aws.CloudAPI) WAFV2Controller {
+	return &defaultWAFV2Controller{
+		cloud:               cloud,
+		webACLARNForLBCache: cache.NewLRUExpireCache(webACLARNForLBCacheMaxSize),
+	}
+}
+
+type defaultWAFV2Controller struct {
+	cloud aws.CloudAPI
+
+	// cache that stores webACLARNForLBCache for LoadBalancerARN.
+	// The cache value is string, while "" represents no webACL.
+	webACLARNForLBCache *cache.LRUExpireCache
+}
+
+func (c *defaultWAFV2Controller) Reconcile(ctx context.Context, lbArn string, ing *extensions.Ingress) error {
+	var desiredWebACLARN string
+
+	_ = annotations.LoadStringAnnotation("wafv2-acl-arn", &desiredWebACLARN, ing.Annotations)
+
+	currentWebACLId, err := c.getCurrentWebACLARN(ctx, lbArn)
+	if err != nil {
+		return err
+	}
+
+	switch {
+	case desiredWebACLARN == "" && currentWebACLId != "":
+		albctx.GetLogger(ctx).Infof("disassociate WAFv2 webACL on %v", lbArn)
+		if _, err := c.cloud.DisassociateWAFV2(ctx, aws.String(lbArn)); err != nil {
+			return errors.Wrapf(err, "failed to disassociate WAFv2 webACL on LoadBalancer %v", lbArn)
+		}
+		c.webACLARNForLBCache.Add(lbArn, desiredWebACLARN, webACLARNForLBCacheTTL)
+	case desiredWebACLARN != "" && currentWebACLId != "" && desiredWebACLARN != currentWebACLId:
+		albctx.GetLogger(ctx).Infof("change WAFv2 webACL on %v from %v to %v", lbArn, currentWebACLId, desiredWebACLARN)
+		if _, err := c.cloud.AssociateWAFV2(ctx, aws.String(lbArn), aws.String(desiredWebACLARN)); err != nil {
+			return errors.Wrapf(err, "failed to associate WAFv2 webACL on LoadBalancer %v", lbArn)
+		}
+		c.webACLARNForLBCache.Add(lbArn, desiredWebACLARN, webACLARNForLBCacheTTL)
+	case desiredWebACLARN != "" && currentWebACLId == "":
+		albctx.GetLogger(ctx).Infof("associate WAFv2 webACL %v on %v", desiredWebACLARN, lbArn)
+		if _, err := c.cloud.AssociateWAFV2(ctx, aws.String(lbArn), aws.String(desiredWebACLARN)); err != nil {
+			return errors.Wrapf(err, "failed to associate WAFv2 webACL on LoadBalancer %v", lbArn)
+		}
+		c.webACLARNForLBCache.Add(lbArn, desiredWebACLARN, webACLARNForLBCacheTTL)
+	}
+
+	return nil
+}
+
+func (c *defaultWAFV2Controller) getCurrentWebACLARN(ctx context.Context, lbArn string) (string, error) {
+	cachedWebACLARN, exists := c.webACLARNForLBCache.Get(lbArn)
+	if exists {
+		return cachedWebACLARN.(string), nil
+	}
+
+	webACL, err := c.cloud.GetWAFV2WebACLSummary(ctx, aws.String(lbArn))
+	if err != nil {
+		return "", errors.Wrapf(err, "failed get WAFv2 webACL for load balancer %v", lbArn)
+	}
+
+	var webACLARN string
+	if webACL != nil {
+		webACLARN = aws.StringValue(webACL.ARN)
+	}
+
+	c.webACLARNForLBCache.Add(lbArn, webACLARN, webACLARNForLBCacheTTL)
+	return webACLARN, nil
+}
diff --git a/internal/aws/cloud.go b/internal/aws/cloud.go
index 220b79053..4db59fac9 100644
--- a/internal/aws/cloud.go
+++ b/internal/aws/cloud.go
@@ -22,6 +22,8 @@ import (
 	"github.com/aws/aws-sdk-go/service/shield/shieldiface"
 	"github.com/aws/aws-sdk-go/service/wafregional"
 	"github.com/aws/aws-sdk-go/service/wafregional/wafregionaliface"
+	"github.com/aws/aws-sdk-go/service/wafv2"
+	"github.com/aws/aws-sdk-go/service/wafv2/wafv2iface"
 	"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/ingress/metric"
 	"github.com/ticketmaster/aws-sdk-go-cache/cache"
 )
@@ -34,6 +36,7 @@ type CloudAPI interface {
 	ResourceGroupsTaggingAPIAPI
 	ShieldAPI
 	WAFRegionalAPI
+	WAFV2API
 
 	GetClusterName() string
 	GetVpcID() string
@@ -51,6 +54,7 @@ type Cloud struct {
 	shield      shieldiface.ShieldAPI
 	rgt         resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI
 	wafregional wafregionaliface.WAFRegionalAPI
+	wafv2       wafv2iface.WAFV2API
 }
 
 // Initialize the global AWS clients.
@@ -88,6 +92,7 @@ func New(cfg CloudConfig, clusterName string, mc metric.Collector, ce bool, cc *
 		shield.New(awsSession, &aws.Config{Region: aws.String("us-east-1")}),
 		resourcegroupstaggingapi.New(awsSession),
 		wafregional.New(awsSession),
+		wafv2.New(awsSession),
 	}, nil
 }
 
diff --git a/internal/aws/wafv2.go b/internal/aws/wafv2.go
new file mode 100644
index 000000000..954134f70
--- /dev/null
+++ b/internal/aws/wafv2.go
@@ -0,0 +1,53 @@
+package aws
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go/service/wafv2"
+)
+
+type WAFV2API interface {
+	GetWAFV2WebACLSummary(ctx context.Context, webACLId *string) (*wafv2.WebACL, error)
+	AssociateWAFV2(ctx context.Context, resourceArn *string, webACLId *string) (*wafv2.AssociateWebACLOutput, error)
+	DisassociateWAFV2(ctx context.Context, resourceArn *string) (*wafv2.DisassociateWebACLOutput, error)
+}
+
+// GetWAFV2WebACLSummary return associated summary for resource.
+func (c *Cloud) GetWAFV2WebACLSummary(ctx context.Context, resourceArn *string) (*wafv2.WebACL, error) {
+	result, err := c.wafv2.GetWebACLForResourceWithContext(ctx, &wafv2.GetWebACLForResourceInput{
+		ResourceArn: resourceArn,
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	return result.WebACL, nil
+}
+
+// AssociateWAFV2 WAF ACL to resource.
+func (c *Cloud) AssociateWAFV2(ctx context.Context, resourceArn *string, webACLARN *string) (*wafv2.AssociateWebACLOutput, error) {
+	result, err := c.wafv2.AssociateWebACLWithContext(ctx, &wafv2.AssociateWebACLInput{
+		ResourceArn: resourceArn,
+		WebACLArn:   webACLARN,
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+// DisassociateWAFV2 WAFv2 ACL from resource.
+func (c *Cloud) DisassociateWAFV2(ctx context.Context, resourceArn *string) (*wafv2.DisassociateWebACLOutput, error) {
+	result, err := c.wafv2.DisassociateWebACLWithContext(ctx, &wafv2.DisassociateWebACLInput{
+		ResourceArn: resourceArn,
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
diff --git a/internal/ingress/controller/config/features.go b/internal/ingress/controller/config/features.go
index 27d44a829..911a54489 100644
--- a/internal/ingress/controller/config/features.go
+++ b/internal/ingress/controller/config/features.go
@@ -28,6 +28,7 @@ type Feature string
 
 const (
 	WAF            Feature = "waf"
+	WAFV2          Feature = "wafv2"
 	ShieldAdvanced Feature = "shield"
 )
 
@@ -57,6 +58,7 @@ func NewFeatureGate() FeatureGate {
 	return &defaultFeatureGate{
 		featureState: map[Feature]bool{
 			WAF:            true,
+			WAFV2:          true,
 			ShieldAdvanced: true,
 		},
 	}
diff --git a/internal/ingress/controller/store/mock_Storer.go b/internal/ingress/controller/store/mock_Storer.go
index bde6acb27..e2a726425 100644
--- a/internal/ingress/controller/store/mock_Storer.go
+++ b/internal/ingress/controller/store/mock_Storer.go
@@ -97,6 +97,29 @@ func (_m *MockStorer) GetNodeInstanceID(node *v1.Node) (string, error) {
 	return r0, r1
 }
 
+// GetPod provides a mock function with given fields: key
+func (_m *MockStorer) GetPod(key string) (*v1.Pod, error) {
+	ret := _m.Called(key)
+
+	var r0 *v1.Pod
+	if rf, ok := ret.Get(0).(func(string) *v1.Pod); ok {
+		r0 = rf(key)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*v1.Pod)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(string) error); ok {
+		r1 = rf(key)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // GetService provides a mock function with given fields: key
 func (_m *MockStorer) GetService(key string) (*v1.Service, error) {
 	ret := _m.Called(key)
@@ -166,29 +189,6 @@ func (_m *MockStorer) GetServiceEndpoints(key string) (*v1.Endpoints, error) {
 	return r0, r1
 }
 
-// GetPod provides a mock function with given fields: key
-func (_m *MockStorer) GetPod(key string) (*v1.Pod, error) {
-	ret := _m.Called(key)
-
-	var r0 *v1.Pod
-	if rf, ok := ret.Get(0).(func(string) *v1.Pod); ok {
-		r0 = rf(key)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*v1.Pod)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(string) error); ok {
-		r1 = rf(key)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
 // ListNodes provides a mock function with given fields:
 func (_m *MockStorer) ListNodes() []*v1.Node {
 	ret := _m.Called()
diff --git a/mocks/CloudAPI.go b/mocks/CloudAPI.go
index 9ddbdac11..6efb4fcb6 100644
--- a/mocks/CloudAPI.go
+++ b/mocks/CloudAPI.go
@@ -20,6 +20,8 @@ import (
 	waf "github.com/aws/aws-sdk-go/service/waf"
 
 	wafregional "github.com/aws/aws-sdk-go/service/wafregional"
+
+	wafv2 "github.com/aws/aws-sdk-go/service/wafv2"
 )
 
 // CloudAPI is an autogenerated mock type for the CloudAPI type
@@ -110,6 +112,29 @@ func (_m *CloudAPI) AssociateWAF(ctx context.Context, resourceArn *string, webAC
 	return r0, r1
 }
 
+// AssociateWAFV2 provides a mock function with given fields: ctx, resourceArn, webACLId
+func (_m *CloudAPI) AssociateWAFV2(ctx context.Context, resourceArn *string, webACLId *string) (*wafv2.AssociateWebACLOutput, error) {
+	ret := _m.Called(ctx, resourceArn, webACLId)
+
+	var r0 *wafv2.AssociateWebACLOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *string, *string) *wafv2.AssociateWebACLOutput); ok {
+		r0 = rf(ctx, resourceArn, webACLId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.AssociateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *string, *string) error); ok {
+		r1 = rf(ctx, resourceArn, webACLId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // AuthorizeSecurityGroupIngressWithContext provides a mock function with given fields: _a0, _a1
 func (_m *CloudAPI) AuthorizeSecurityGroupIngressWithContext(_a0 context.Context, _a1 *ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error) {
 	ret := _m.Called(_a0, _a1)
@@ -649,6 +674,29 @@ func (_m *CloudAPI) DisassociateWAF(ctx context.Context, resourceArn *string) (*
 	return r0, r1
 }
 
+// DisassociateWAFV2 provides a mock function with given fields: ctx, resourceArn
+func (_m *CloudAPI) DisassociateWAFV2(ctx context.Context, resourceArn *string) (*wafv2.DisassociateWebACLOutput, error) {
+	ret := _m.Called(ctx, resourceArn)
+
+	var r0 *wafv2.DisassociateWebACLOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *string) *wafv2.DisassociateWebACLOutput); ok {
+		r0 = rf(ctx, resourceArn)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DisassociateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *string) error); ok {
+		r1 = rf(ctx, resourceArn)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // GetClusterName provides a mock function with given fields:
 func (_m *CloudAPI) GetClusterName() string {
 	ret := _m.Called()
@@ -1029,22 +1077,22 @@ func (_m *CloudAPI) GetVpcWithContext(_a0 context.Context) (*ec2.Vpc, error) {
 	return r0, r1
 }
 
-// GetWebACLSummary provides a mock function with given fields: ctx, resourceArn
-func (_m *CloudAPI) GetWebACLSummary(ctx context.Context, resourceArn *string) (*waf.WebACLSummary, error) {
-	ret := _m.Called(ctx, resourceArn)
+// GetWAFV2WebACLSummary provides a mock function with given fields: ctx, webACLId
+func (_m *CloudAPI) GetWAFV2WebACLSummary(ctx context.Context, webACLId *string) (*wafv2.WebACL, error) {
+	ret := _m.Called(ctx, webACLId)
 
-	var r0 *waf.WebACLSummary
-	if rf, ok := ret.Get(0).(func(context.Context, *string) *waf.WebACLSummary); ok {
-		r0 = rf(ctx, resourceArn)
+	var r0 *wafv2.WebACL
+	if rf, ok := ret.Get(0).(func(context.Context, *string) *wafv2.WebACL); ok {
+		r0 = rf(ctx, webACLId)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*waf.WebACLSummary)
+			r0 = ret.Get(0).(*wafv2.WebACL)
 		}
 	}
 
 	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *string) error); ok {
-		r1 = rf(ctx, resourceArn)
+		r1 = rf(ctx, webACLId)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -1052,20 +1100,22 @@ func (_m *CloudAPI) GetWebACLSummary(ctx context.Context, resourceArn *string) (
 	return r0, r1
 }
 
-// IsNodeHealthy provides a mock function with given fields: _a0
-func (_m *CloudAPI) IsNodeHealthy(_a0 string) (bool, error) {
-	ret := _m.Called(_a0)
+// GetWebACLSummary provides a mock function with given fields: ctx, resourceArn
+func (_m *CloudAPI) GetWebACLSummary(ctx context.Context, resourceArn *string) (*waf.WebACLSummary, error) {
+	ret := _m.Called(ctx, resourceArn)
 
-	var r0 bool
-	if rf, ok := ret.Get(0).(func(string) bool); ok {
-		r0 = rf(_a0)
+	var r0 *waf.WebACLSummary
+	if rf, ok := ret.Get(0).(func(context.Context, *string) *waf.WebACLSummary); ok {
+		r0 = rf(ctx, resourceArn)
 	} else {
-		r0 = ret.Get(0).(bool)
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*waf.WebACLSummary)
+		}
 	}
 
 	var r1 error
-	if rf, ok := ret.Get(1).(func(string) error); ok {
-		r1 = rf(_a0)
+	if rf, ok := ret.Get(1).(func(context.Context, *string) error); ok {
+		r1 = rf(ctx, resourceArn)
 	} else {
 		r1 = ret.Error(1)
 	}
diff --git a/mocks/WAFV2API.go b/mocks/WAFV2API.go
new file mode 100644
index 000000000..1eab55ef9
--- /dev/null
+++ b/mocks/WAFV2API.go
@@ -0,0 +1,2825 @@
+// Code generated by mockery v1.0.0. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	request "github.com/aws/aws-sdk-go/aws/request"
+	mock "github.com/stretchr/testify/mock"
+
+	wafv2 "github.com/aws/aws-sdk-go/service/wafv2"
+)
+
+// WAFV2API is an autogenerated mock type for the WAFV2API type
+type WAFV2API struct {
+	mock.Mock
+}
+
+// AssociateWebACL provides a mock function with given fields: _a0
+func (_m *WAFV2API) AssociateWebACL(_a0 *wafv2.AssociateWebACLInput) (*wafv2.AssociateWebACLOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.AssociateWebACLOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.AssociateWebACLInput) *wafv2.AssociateWebACLOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.AssociateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.AssociateWebACLInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// AssociateWebACLRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) AssociateWebACLRequest(_a0 *wafv2.AssociateWebACLInput) (*request.Request, *wafv2.AssociateWebACLOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.AssociateWebACLInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.AssociateWebACLOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.AssociateWebACLInput) *wafv2.AssociateWebACLOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.AssociateWebACLOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// AssociateWebACLWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) AssociateWebACLWithContext(_a0 context.Context, _a1 *wafv2.AssociateWebACLInput, _a2 ...request.Option) (*wafv2.AssociateWebACLOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.AssociateWebACLOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.AssociateWebACLInput, ...request.Option) *wafv2.AssociateWebACLOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.AssociateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.AssociateWebACLInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CheckCapacity provides a mock function with given fields: _a0
+func (_m *WAFV2API) CheckCapacity(_a0 *wafv2.CheckCapacityInput) (*wafv2.CheckCapacityOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.CheckCapacityOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.CheckCapacityInput) *wafv2.CheckCapacityOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CheckCapacityOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.CheckCapacityInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CheckCapacityRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) CheckCapacityRequest(_a0 *wafv2.CheckCapacityInput) (*request.Request, *wafv2.CheckCapacityOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.CheckCapacityInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.CheckCapacityOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.CheckCapacityInput) *wafv2.CheckCapacityOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.CheckCapacityOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// CheckCapacityWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) CheckCapacityWithContext(_a0 context.Context, _a1 *wafv2.CheckCapacityInput, _a2 ...request.Option) (*wafv2.CheckCapacityOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.CheckCapacityOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.CheckCapacityInput, ...request.Option) *wafv2.CheckCapacityOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CheckCapacityOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.CheckCapacityInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateIPSet provides a mock function with given fields: _a0
+func (_m *WAFV2API) CreateIPSet(_a0 *wafv2.CreateIPSetInput) (*wafv2.CreateIPSetOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.CreateIPSetOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.CreateIPSetInput) *wafv2.CreateIPSetOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CreateIPSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.CreateIPSetInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateIPSetRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) CreateIPSetRequest(_a0 *wafv2.CreateIPSetInput) (*request.Request, *wafv2.CreateIPSetOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.CreateIPSetInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.CreateIPSetOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.CreateIPSetInput) *wafv2.CreateIPSetOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.CreateIPSetOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// CreateIPSetWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) CreateIPSetWithContext(_a0 context.Context, _a1 *wafv2.CreateIPSetInput, _a2 ...request.Option) (*wafv2.CreateIPSetOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.CreateIPSetOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.CreateIPSetInput, ...request.Option) *wafv2.CreateIPSetOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CreateIPSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.CreateIPSetInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateRegexPatternSet provides a mock function with given fields: _a0
+func (_m *WAFV2API) CreateRegexPatternSet(_a0 *wafv2.CreateRegexPatternSetInput) (*wafv2.CreateRegexPatternSetOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.CreateRegexPatternSetOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.CreateRegexPatternSetInput) *wafv2.CreateRegexPatternSetOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CreateRegexPatternSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.CreateRegexPatternSetInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateRegexPatternSetRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) CreateRegexPatternSetRequest(_a0 *wafv2.CreateRegexPatternSetInput) (*request.Request, *wafv2.CreateRegexPatternSetOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.CreateRegexPatternSetInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.CreateRegexPatternSetOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.CreateRegexPatternSetInput) *wafv2.CreateRegexPatternSetOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.CreateRegexPatternSetOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// CreateRegexPatternSetWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) CreateRegexPatternSetWithContext(_a0 context.Context, _a1 *wafv2.CreateRegexPatternSetInput, _a2 ...request.Option) (*wafv2.CreateRegexPatternSetOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.CreateRegexPatternSetOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.CreateRegexPatternSetInput, ...request.Option) *wafv2.CreateRegexPatternSetOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CreateRegexPatternSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.CreateRegexPatternSetInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateRuleGroup provides a mock function with given fields: _a0
+func (_m *WAFV2API) CreateRuleGroup(_a0 *wafv2.CreateRuleGroupInput) (*wafv2.CreateRuleGroupOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.CreateRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.CreateRuleGroupInput) *wafv2.CreateRuleGroupOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CreateRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.CreateRuleGroupInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateRuleGroupRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) CreateRuleGroupRequest(_a0 *wafv2.CreateRuleGroupInput) (*request.Request, *wafv2.CreateRuleGroupOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.CreateRuleGroupInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.CreateRuleGroupOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.CreateRuleGroupInput) *wafv2.CreateRuleGroupOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.CreateRuleGroupOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// CreateRuleGroupWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) CreateRuleGroupWithContext(_a0 context.Context, _a1 *wafv2.CreateRuleGroupInput, _a2 ...request.Option) (*wafv2.CreateRuleGroupOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.CreateRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.CreateRuleGroupInput, ...request.Option) *wafv2.CreateRuleGroupOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CreateRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.CreateRuleGroupInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateWebACL provides a mock function with given fields: _a0
+func (_m *WAFV2API) CreateWebACL(_a0 *wafv2.CreateWebACLInput) (*wafv2.CreateWebACLOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.CreateWebACLOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.CreateWebACLInput) *wafv2.CreateWebACLOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CreateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.CreateWebACLInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateWebACLRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) CreateWebACLRequest(_a0 *wafv2.CreateWebACLInput) (*request.Request, *wafv2.CreateWebACLOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.CreateWebACLInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.CreateWebACLOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.CreateWebACLInput) *wafv2.CreateWebACLOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.CreateWebACLOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// CreateWebACLWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) CreateWebACLWithContext(_a0 context.Context, _a1 *wafv2.CreateWebACLInput, _a2 ...request.Option) (*wafv2.CreateWebACLOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.CreateWebACLOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.CreateWebACLInput, ...request.Option) *wafv2.CreateWebACLOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.CreateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.CreateWebACLInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteIPSet provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteIPSet(_a0 *wafv2.DeleteIPSetInput) (*wafv2.DeleteIPSetOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.DeleteIPSetOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteIPSetInput) *wafv2.DeleteIPSetOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteIPSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteIPSetInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteIPSetRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteIPSetRequest(_a0 *wafv2.DeleteIPSetInput) (*request.Request, *wafv2.DeleteIPSetOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteIPSetInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.DeleteIPSetOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteIPSetInput) *wafv2.DeleteIPSetOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.DeleteIPSetOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// DeleteIPSetWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) DeleteIPSetWithContext(_a0 context.Context, _a1 *wafv2.DeleteIPSetInput, _a2 ...request.Option) (*wafv2.DeleteIPSetOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.DeleteIPSetOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.DeleteIPSetInput, ...request.Option) *wafv2.DeleteIPSetOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteIPSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.DeleteIPSetInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteLoggingConfiguration provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteLoggingConfiguration(_a0 *wafv2.DeleteLoggingConfigurationInput) (*wafv2.DeleteLoggingConfigurationOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.DeleteLoggingConfigurationOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteLoggingConfigurationInput) *wafv2.DeleteLoggingConfigurationOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteLoggingConfigurationOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteLoggingConfigurationInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteLoggingConfigurationRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteLoggingConfigurationRequest(_a0 *wafv2.DeleteLoggingConfigurationInput) (*request.Request, *wafv2.DeleteLoggingConfigurationOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteLoggingConfigurationInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.DeleteLoggingConfigurationOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteLoggingConfigurationInput) *wafv2.DeleteLoggingConfigurationOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.DeleteLoggingConfigurationOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// DeleteLoggingConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) DeleteLoggingConfigurationWithContext(_a0 context.Context, _a1 *wafv2.DeleteLoggingConfigurationInput, _a2 ...request.Option) (*wafv2.DeleteLoggingConfigurationOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.DeleteLoggingConfigurationOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.DeleteLoggingConfigurationInput, ...request.Option) *wafv2.DeleteLoggingConfigurationOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteLoggingConfigurationOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.DeleteLoggingConfigurationInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteRegexPatternSet provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteRegexPatternSet(_a0 *wafv2.DeleteRegexPatternSetInput) (*wafv2.DeleteRegexPatternSetOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.DeleteRegexPatternSetOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteRegexPatternSetInput) *wafv2.DeleteRegexPatternSetOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteRegexPatternSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteRegexPatternSetInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteRegexPatternSetRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteRegexPatternSetRequest(_a0 *wafv2.DeleteRegexPatternSetInput) (*request.Request, *wafv2.DeleteRegexPatternSetOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteRegexPatternSetInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.DeleteRegexPatternSetOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteRegexPatternSetInput) *wafv2.DeleteRegexPatternSetOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.DeleteRegexPatternSetOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// DeleteRegexPatternSetWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) DeleteRegexPatternSetWithContext(_a0 context.Context, _a1 *wafv2.DeleteRegexPatternSetInput, _a2 ...request.Option) (*wafv2.DeleteRegexPatternSetOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.DeleteRegexPatternSetOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.DeleteRegexPatternSetInput, ...request.Option) *wafv2.DeleteRegexPatternSetOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteRegexPatternSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.DeleteRegexPatternSetInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteRuleGroup provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteRuleGroup(_a0 *wafv2.DeleteRuleGroupInput) (*wafv2.DeleteRuleGroupOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.DeleteRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteRuleGroupInput) *wafv2.DeleteRuleGroupOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteRuleGroupInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteRuleGroupRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteRuleGroupRequest(_a0 *wafv2.DeleteRuleGroupInput) (*request.Request, *wafv2.DeleteRuleGroupOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteRuleGroupInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.DeleteRuleGroupOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteRuleGroupInput) *wafv2.DeleteRuleGroupOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.DeleteRuleGroupOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// DeleteRuleGroupWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) DeleteRuleGroupWithContext(_a0 context.Context, _a1 *wafv2.DeleteRuleGroupInput, _a2 ...request.Option) (*wafv2.DeleteRuleGroupOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.DeleteRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.DeleteRuleGroupInput, ...request.Option) *wafv2.DeleteRuleGroupOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.DeleteRuleGroupInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteWebACL provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteWebACL(_a0 *wafv2.DeleteWebACLInput) (*wafv2.DeleteWebACLOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.DeleteWebACLOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteWebACLInput) *wafv2.DeleteWebACLOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteWebACLInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteWebACLRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) DeleteWebACLRequest(_a0 *wafv2.DeleteWebACLInput) (*request.Request, *wafv2.DeleteWebACLOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.DeleteWebACLInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.DeleteWebACLOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.DeleteWebACLInput) *wafv2.DeleteWebACLOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.DeleteWebACLOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// DeleteWebACLWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) DeleteWebACLWithContext(_a0 context.Context, _a1 *wafv2.DeleteWebACLInput, _a2 ...request.Option) (*wafv2.DeleteWebACLOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.DeleteWebACLOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.DeleteWebACLInput, ...request.Option) *wafv2.DeleteWebACLOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DeleteWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.DeleteWebACLInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DescribeManagedRuleGroup provides a mock function with given fields: _a0
+func (_m *WAFV2API) DescribeManagedRuleGroup(_a0 *wafv2.DescribeManagedRuleGroupInput) (*wafv2.DescribeManagedRuleGroupOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.DescribeManagedRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.DescribeManagedRuleGroupInput) *wafv2.DescribeManagedRuleGroupOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DescribeManagedRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.DescribeManagedRuleGroupInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DescribeManagedRuleGroupRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) DescribeManagedRuleGroupRequest(_a0 *wafv2.DescribeManagedRuleGroupInput) (*request.Request, *wafv2.DescribeManagedRuleGroupOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.DescribeManagedRuleGroupInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.DescribeManagedRuleGroupOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.DescribeManagedRuleGroupInput) *wafv2.DescribeManagedRuleGroupOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.DescribeManagedRuleGroupOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// DescribeManagedRuleGroupWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) DescribeManagedRuleGroupWithContext(_a0 context.Context, _a1 *wafv2.DescribeManagedRuleGroupInput, _a2 ...request.Option) (*wafv2.DescribeManagedRuleGroupOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.DescribeManagedRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.DescribeManagedRuleGroupInput, ...request.Option) *wafv2.DescribeManagedRuleGroupOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DescribeManagedRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.DescribeManagedRuleGroupInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DisassociateWebACL provides a mock function with given fields: _a0
+func (_m *WAFV2API) DisassociateWebACL(_a0 *wafv2.DisassociateWebACLInput) (*wafv2.DisassociateWebACLOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.DisassociateWebACLOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.DisassociateWebACLInput) *wafv2.DisassociateWebACLOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DisassociateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.DisassociateWebACLInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DisassociateWebACLRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) DisassociateWebACLRequest(_a0 *wafv2.DisassociateWebACLInput) (*request.Request, *wafv2.DisassociateWebACLOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.DisassociateWebACLInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.DisassociateWebACLOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.DisassociateWebACLInput) *wafv2.DisassociateWebACLOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.DisassociateWebACLOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// DisassociateWebACLWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) DisassociateWebACLWithContext(_a0 context.Context, _a1 *wafv2.DisassociateWebACLInput, _a2 ...request.Option) (*wafv2.DisassociateWebACLOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.DisassociateWebACLOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.DisassociateWebACLInput, ...request.Option) *wafv2.DisassociateWebACLOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.DisassociateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.DisassociateWebACLInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetIPSet provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetIPSet(_a0 *wafv2.GetIPSetInput) (*wafv2.GetIPSetOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.GetIPSetOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.GetIPSetInput) *wafv2.GetIPSetOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetIPSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.GetIPSetInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetIPSetRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetIPSetRequest(_a0 *wafv2.GetIPSetInput) (*request.Request, *wafv2.GetIPSetOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.GetIPSetInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.GetIPSetOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.GetIPSetInput) *wafv2.GetIPSetOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.GetIPSetOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// GetIPSetWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) GetIPSetWithContext(_a0 context.Context, _a1 *wafv2.GetIPSetInput, _a2 ...request.Option) (*wafv2.GetIPSetOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.GetIPSetOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.GetIPSetInput, ...request.Option) *wafv2.GetIPSetOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetIPSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.GetIPSetInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetLoggingConfiguration provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetLoggingConfiguration(_a0 *wafv2.GetLoggingConfigurationInput) (*wafv2.GetLoggingConfigurationOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.GetLoggingConfigurationOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.GetLoggingConfigurationInput) *wafv2.GetLoggingConfigurationOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetLoggingConfigurationOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.GetLoggingConfigurationInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetLoggingConfigurationRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetLoggingConfigurationRequest(_a0 *wafv2.GetLoggingConfigurationInput) (*request.Request, *wafv2.GetLoggingConfigurationOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.GetLoggingConfigurationInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.GetLoggingConfigurationOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.GetLoggingConfigurationInput) *wafv2.GetLoggingConfigurationOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.GetLoggingConfigurationOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// GetLoggingConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) GetLoggingConfigurationWithContext(_a0 context.Context, _a1 *wafv2.GetLoggingConfigurationInput, _a2 ...request.Option) (*wafv2.GetLoggingConfigurationOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.GetLoggingConfigurationOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.GetLoggingConfigurationInput, ...request.Option) *wafv2.GetLoggingConfigurationOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetLoggingConfigurationOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.GetLoggingConfigurationInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetRateBasedStatementManagedKeys provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetRateBasedStatementManagedKeys(_a0 *wafv2.GetRateBasedStatementManagedKeysInput) (*wafv2.GetRateBasedStatementManagedKeysOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.GetRateBasedStatementManagedKeysOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.GetRateBasedStatementManagedKeysInput) *wafv2.GetRateBasedStatementManagedKeysOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetRateBasedStatementManagedKeysOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.GetRateBasedStatementManagedKeysInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetRateBasedStatementManagedKeysRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetRateBasedStatementManagedKeysRequest(_a0 *wafv2.GetRateBasedStatementManagedKeysInput) (*request.Request, *wafv2.GetRateBasedStatementManagedKeysOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.GetRateBasedStatementManagedKeysInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.GetRateBasedStatementManagedKeysOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.GetRateBasedStatementManagedKeysInput) *wafv2.GetRateBasedStatementManagedKeysOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.GetRateBasedStatementManagedKeysOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// GetRateBasedStatementManagedKeysWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) GetRateBasedStatementManagedKeysWithContext(_a0 context.Context, _a1 *wafv2.GetRateBasedStatementManagedKeysInput, _a2 ...request.Option) (*wafv2.GetRateBasedStatementManagedKeysOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.GetRateBasedStatementManagedKeysOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.GetRateBasedStatementManagedKeysInput, ...request.Option) *wafv2.GetRateBasedStatementManagedKeysOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetRateBasedStatementManagedKeysOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.GetRateBasedStatementManagedKeysInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetRegexPatternSet provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetRegexPatternSet(_a0 *wafv2.GetRegexPatternSetInput) (*wafv2.GetRegexPatternSetOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.GetRegexPatternSetOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.GetRegexPatternSetInput) *wafv2.GetRegexPatternSetOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetRegexPatternSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.GetRegexPatternSetInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetRegexPatternSetRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetRegexPatternSetRequest(_a0 *wafv2.GetRegexPatternSetInput) (*request.Request, *wafv2.GetRegexPatternSetOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.GetRegexPatternSetInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.GetRegexPatternSetOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.GetRegexPatternSetInput) *wafv2.GetRegexPatternSetOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.GetRegexPatternSetOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// GetRegexPatternSetWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) GetRegexPatternSetWithContext(_a0 context.Context, _a1 *wafv2.GetRegexPatternSetInput, _a2 ...request.Option) (*wafv2.GetRegexPatternSetOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.GetRegexPatternSetOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.GetRegexPatternSetInput, ...request.Option) *wafv2.GetRegexPatternSetOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetRegexPatternSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.GetRegexPatternSetInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetRuleGroup provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetRuleGroup(_a0 *wafv2.GetRuleGroupInput) (*wafv2.GetRuleGroupOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.GetRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.GetRuleGroupInput) *wafv2.GetRuleGroupOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.GetRuleGroupInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetRuleGroupRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetRuleGroupRequest(_a0 *wafv2.GetRuleGroupInput) (*request.Request, *wafv2.GetRuleGroupOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.GetRuleGroupInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.GetRuleGroupOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.GetRuleGroupInput) *wafv2.GetRuleGroupOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.GetRuleGroupOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// GetRuleGroupWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) GetRuleGroupWithContext(_a0 context.Context, _a1 *wafv2.GetRuleGroupInput, _a2 ...request.Option) (*wafv2.GetRuleGroupOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.GetRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.GetRuleGroupInput, ...request.Option) *wafv2.GetRuleGroupOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.GetRuleGroupInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetSampledRequests provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetSampledRequests(_a0 *wafv2.GetSampledRequestsInput) (*wafv2.GetSampledRequestsOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.GetSampledRequestsOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.GetSampledRequestsInput) *wafv2.GetSampledRequestsOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetSampledRequestsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.GetSampledRequestsInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetSampledRequestsRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetSampledRequestsRequest(_a0 *wafv2.GetSampledRequestsInput) (*request.Request, *wafv2.GetSampledRequestsOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.GetSampledRequestsInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.GetSampledRequestsOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.GetSampledRequestsInput) *wafv2.GetSampledRequestsOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.GetSampledRequestsOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// GetSampledRequestsWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) GetSampledRequestsWithContext(_a0 context.Context, _a1 *wafv2.GetSampledRequestsInput, _a2 ...request.Option) (*wafv2.GetSampledRequestsOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.GetSampledRequestsOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.GetSampledRequestsInput, ...request.Option) *wafv2.GetSampledRequestsOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetSampledRequestsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.GetSampledRequestsInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetWebACL provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetWebACL(_a0 *wafv2.GetWebACLInput) (*wafv2.GetWebACLOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.GetWebACLOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.GetWebACLInput) *wafv2.GetWebACLOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.GetWebACLInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetWebACLForResource provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetWebACLForResource(_a0 *wafv2.GetWebACLForResourceInput) (*wafv2.GetWebACLForResourceOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.GetWebACLForResourceOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.GetWebACLForResourceInput) *wafv2.GetWebACLForResourceOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetWebACLForResourceOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.GetWebACLForResourceInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetWebACLForResourceRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetWebACLForResourceRequest(_a0 *wafv2.GetWebACLForResourceInput) (*request.Request, *wafv2.GetWebACLForResourceOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.GetWebACLForResourceInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.GetWebACLForResourceOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.GetWebACLForResourceInput) *wafv2.GetWebACLForResourceOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.GetWebACLForResourceOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// GetWebACLForResourceWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) GetWebACLForResourceWithContext(_a0 context.Context, _a1 *wafv2.GetWebACLForResourceInput, _a2 ...request.Option) (*wafv2.GetWebACLForResourceOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.GetWebACLForResourceOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.GetWebACLForResourceInput, ...request.Option) *wafv2.GetWebACLForResourceOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetWebACLForResourceOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.GetWebACLForResourceInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetWebACLRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) GetWebACLRequest(_a0 *wafv2.GetWebACLInput) (*request.Request, *wafv2.GetWebACLOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.GetWebACLInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.GetWebACLOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.GetWebACLInput) *wafv2.GetWebACLOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.GetWebACLOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// GetWebACLWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) GetWebACLWithContext(_a0 context.Context, _a1 *wafv2.GetWebACLInput, _a2 ...request.Option) (*wafv2.GetWebACLOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.GetWebACLOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.GetWebACLInput, ...request.Option) *wafv2.GetWebACLOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.GetWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.GetWebACLInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListAvailableManagedRuleGroups provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListAvailableManagedRuleGroups(_a0 *wafv2.ListAvailableManagedRuleGroupsInput) (*wafv2.ListAvailableManagedRuleGroupsOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.ListAvailableManagedRuleGroupsOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.ListAvailableManagedRuleGroupsInput) *wafv2.ListAvailableManagedRuleGroupsOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListAvailableManagedRuleGroupsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.ListAvailableManagedRuleGroupsInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListAvailableManagedRuleGroupsRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListAvailableManagedRuleGroupsRequest(_a0 *wafv2.ListAvailableManagedRuleGroupsInput) (*request.Request, *wafv2.ListAvailableManagedRuleGroupsOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.ListAvailableManagedRuleGroupsInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.ListAvailableManagedRuleGroupsOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.ListAvailableManagedRuleGroupsInput) *wafv2.ListAvailableManagedRuleGroupsOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.ListAvailableManagedRuleGroupsOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// ListAvailableManagedRuleGroupsWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) ListAvailableManagedRuleGroupsWithContext(_a0 context.Context, _a1 *wafv2.ListAvailableManagedRuleGroupsInput, _a2 ...request.Option) (*wafv2.ListAvailableManagedRuleGroupsOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.ListAvailableManagedRuleGroupsOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.ListAvailableManagedRuleGroupsInput, ...request.Option) *wafv2.ListAvailableManagedRuleGroupsOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListAvailableManagedRuleGroupsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.ListAvailableManagedRuleGroupsInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListIPSets provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListIPSets(_a0 *wafv2.ListIPSetsInput) (*wafv2.ListIPSetsOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.ListIPSetsOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.ListIPSetsInput) *wafv2.ListIPSetsOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListIPSetsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.ListIPSetsInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListIPSetsRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListIPSetsRequest(_a0 *wafv2.ListIPSetsInput) (*request.Request, *wafv2.ListIPSetsOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.ListIPSetsInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.ListIPSetsOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.ListIPSetsInput) *wafv2.ListIPSetsOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.ListIPSetsOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// ListIPSetsWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) ListIPSetsWithContext(_a0 context.Context, _a1 *wafv2.ListIPSetsInput, _a2 ...request.Option) (*wafv2.ListIPSetsOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.ListIPSetsOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.ListIPSetsInput, ...request.Option) *wafv2.ListIPSetsOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListIPSetsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.ListIPSetsInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListLoggingConfigurations provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListLoggingConfigurations(_a0 *wafv2.ListLoggingConfigurationsInput) (*wafv2.ListLoggingConfigurationsOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.ListLoggingConfigurationsOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.ListLoggingConfigurationsInput) *wafv2.ListLoggingConfigurationsOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListLoggingConfigurationsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.ListLoggingConfigurationsInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListLoggingConfigurationsRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListLoggingConfigurationsRequest(_a0 *wafv2.ListLoggingConfigurationsInput) (*request.Request, *wafv2.ListLoggingConfigurationsOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.ListLoggingConfigurationsInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.ListLoggingConfigurationsOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.ListLoggingConfigurationsInput) *wafv2.ListLoggingConfigurationsOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.ListLoggingConfigurationsOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// ListLoggingConfigurationsWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) ListLoggingConfigurationsWithContext(_a0 context.Context, _a1 *wafv2.ListLoggingConfigurationsInput, _a2 ...request.Option) (*wafv2.ListLoggingConfigurationsOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.ListLoggingConfigurationsOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.ListLoggingConfigurationsInput, ...request.Option) *wafv2.ListLoggingConfigurationsOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListLoggingConfigurationsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.ListLoggingConfigurationsInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListRegexPatternSets provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListRegexPatternSets(_a0 *wafv2.ListRegexPatternSetsInput) (*wafv2.ListRegexPatternSetsOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.ListRegexPatternSetsOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.ListRegexPatternSetsInput) *wafv2.ListRegexPatternSetsOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListRegexPatternSetsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.ListRegexPatternSetsInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListRegexPatternSetsRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListRegexPatternSetsRequest(_a0 *wafv2.ListRegexPatternSetsInput) (*request.Request, *wafv2.ListRegexPatternSetsOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.ListRegexPatternSetsInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.ListRegexPatternSetsOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.ListRegexPatternSetsInput) *wafv2.ListRegexPatternSetsOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.ListRegexPatternSetsOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// ListRegexPatternSetsWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) ListRegexPatternSetsWithContext(_a0 context.Context, _a1 *wafv2.ListRegexPatternSetsInput, _a2 ...request.Option) (*wafv2.ListRegexPatternSetsOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.ListRegexPatternSetsOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.ListRegexPatternSetsInput, ...request.Option) *wafv2.ListRegexPatternSetsOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListRegexPatternSetsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.ListRegexPatternSetsInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListResourcesForWebACL provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListResourcesForWebACL(_a0 *wafv2.ListResourcesForWebACLInput) (*wafv2.ListResourcesForWebACLOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.ListResourcesForWebACLOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.ListResourcesForWebACLInput) *wafv2.ListResourcesForWebACLOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListResourcesForWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.ListResourcesForWebACLInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListResourcesForWebACLRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListResourcesForWebACLRequest(_a0 *wafv2.ListResourcesForWebACLInput) (*request.Request, *wafv2.ListResourcesForWebACLOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.ListResourcesForWebACLInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.ListResourcesForWebACLOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.ListResourcesForWebACLInput) *wafv2.ListResourcesForWebACLOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.ListResourcesForWebACLOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// ListResourcesForWebACLWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) ListResourcesForWebACLWithContext(_a0 context.Context, _a1 *wafv2.ListResourcesForWebACLInput, _a2 ...request.Option) (*wafv2.ListResourcesForWebACLOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.ListResourcesForWebACLOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.ListResourcesForWebACLInput, ...request.Option) *wafv2.ListResourcesForWebACLOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListResourcesForWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.ListResourcesForWebACLInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListRuleGroups provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListRuleGroups(_a0 *wafv2.ListRuleGroupsInput) (*wafv2.ListRuleGroupsOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.ListRuleGroupsOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.ListRuleGroupsInput) *wafv2.ListRuleGroupsOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListRuleGroupsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.ListRuleGroupsInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListRuleGroupsRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListRuleGroupsRequest(_a0 *wafv2.ListRuleGroupsInput) (*request.Request, *wafv2.ListRuleGroupsOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.ListRuleGroupsInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.ListRuleGroupsOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.ListRuleGroupsInput) *wafv2.ListRuleGroupsOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.ListRuleGroupsOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// ListRuleGroupsWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) ListRuleGroupsWithContext(_a0 context.Context, _a1 *wafv2.ListRuleGroupsInput, _a2 ...request.Option) (*wafv2.ListRuleGroupsOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.ListRuleGroupsOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.ListRuleGroupsInput, ...request.Option) *wafv2.ListRuleGroupsOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListRuleGroupsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.ListRuleGroupsInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListTagsForResource provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListTagsForResource(_a0 *wafv2.ListTagsForResourceInput) (*wafv2.ListTagsForResourceOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.ListTagsForResourceOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.ListTagsForResourceInput) *wafv2.ListTagsForResourceOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListTagsForResourceOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.ListTagsForResourceInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListTagsForResourceRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListTagsForResourceRequest(_a0 *wafv2.ListTagsForResourceInput) (*request.Request, *wafv2.ListTagsForResourceOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.ListTagsForResourceInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.ListTagsForResourceOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.ListTagsForResourceInput) *wafv2.ListTagsForResourceOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.ListTagsForResourceOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// ListTagsForResourceWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) ListTagsForResourceWithContext(_a0 context.Context, _a1 *wafv2.ListTagsForResourceInput, _a2 ...request.Option) (*wafv2.ListTagsForResourceOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.ListTagsForResourceOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.ListTagsForResourceInput, ...request.Option) *wafv2.ListTagsForResourceOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListTagsForResourceOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.ListTagsForResourceInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListWebACLs provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListWebACLs(_a0 *wafv2.ListWebACLsInput) (*wafv2.ListWebACLsOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.ListWebACLsOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.ListWebACLsInput) *wafv2.ListWebACLsOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListWebACLsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.ListWebACLsInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListWebACLsRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) ListWebACLsRequest(_a0 *wafv2.ListWebACLsInput) (*request.Request, *wafv2.ListWebACLsOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.ListWebACLsInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.ListWebACLsOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.ListWebACLsInput) *wafv2.ListWebACLsOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.ListWebACLsOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// ListWebACLsWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) ListWebACLsWithContext(_a0 context.Context, _a1 *wafv2.ListWebACLsInput, _a2 ...request.Option) (*wafv2.ListWebACLsOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.ListWebACLsOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.ListWebACLsInput, ...request.Option) *wafv2.ListWebACLsOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.ListWebACLsOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.ListWebACLsInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// PutLoggingConfiguration provides a mock function with given fields: _a0
+func (_m *WAFV2API) PutLoggingConfiguration(_a0 *wafv2.PutLoggingConfigurationInput) (*wafv2.PutLoggingConfigurationOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.PutLoggingConfigurationOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.PutLoggingConfigurationInput) *wafv2.PutLoggingConfigurationOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.PutLoggingConfigurationOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.PutLoggingConfigurationInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// PutLoggingConfigurationRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) PutLoggingConfigurationRequest(_a0 *wafv2.PutLoggingConfigurationInput) (*request.Request, *wafv2.PutLoggingConfigurationOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.PutLoggingConfigurationInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.PutLoggingConfigurationOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.PutLoggingConfigurationInput) *wafv2.PutLoggingConfigurationOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.PutLoggingConfigurationOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// PutLoggingConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) PutLoggingConfigurationWithContext(_a0 context.Context, _a1 *wafv2.PutLoggingConfigurationInput, _a2 ...request.Option) (*wafv2.PutLoggingConfigurationOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.PutLoggingConfigurationOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.PutLoggingConfigurationInput, ...request.Option) *wafv2.PutLoggingConfigurationOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.PutLoggingConfigurationOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.PutLoggingConfigurationInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// TagResource provides a mock function with given fields: _a0
+func (_m *WAFV2API) TagResource(_a0 *wafv2.TagResourceInput) (*wafv2.TagResourceOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.TagResourceOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.TagResourceInput) *wafv2.TagResourceOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.TagResourceOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.TagResourceInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// TagResourceRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) TagResourceRequest(_a0 *wafv2.TagResourceInput) (*request.Request, *wafv2.TagResourceOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.TagResourceInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.TagResourceOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.TagResourceInput) *wafv2.TagResourceOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.TagResourceOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// TagResourceWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) TagResourceWithContext(_a0 context.Context, _a1 *wafv2.TagResourceInput, _a2 ...request.Option) (*wafv2.TagResourceOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.TagResourceOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.TagResourceInput, ...request.Option) *wafv2.TagResourceOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.TagResourceOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.TagResourceInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UntagResource provides a mock function with given fields: _a0
+func (_m *WAFV2API) UntagResource(_a0 *wafv2.UntagResourceInput) (*wafv2.UntagResourceOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.UntagResourceOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.UntagResourceInput) *wafv2.UntagResourceOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UntagResourceOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.UntagResourceInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UntagResourceRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) UntagResourceRequest(_a0 *wafv2.UntagResourceInput) (*request.Request, *wafv2.UntagResourceOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.UntagResourceInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.UntagResourceOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.UntagResourceInput) *wafv2.UntagResourceOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.UntagResourceOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// UntagResourceWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) UntagResourceWithContext(_a0 context.Context, _a1 *wafv2.UntagResourceInput, _a2 ...request.Option) (*wafv2.UntagResourceOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.UntagResourceOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.UntagResourceInput, ...request.Option) *wafv2.UntagResourceOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UntagResourceOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.UntagResourceInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateIPSet provides a mock function with given fields: _a0
+func (_m *WAFV2API) UpdateIPSet(_a0 *wafv2.UpdateIPSetInput) (*wafv2.UpdateIPSetOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.UpdateIPSetOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.UpdateIPSetInput) *wafv2.UpdateIPSetOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UpdateIPSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.UpdateIPSetInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateIPSetRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) UpdateIPSetRequest(_a0 *wafv2.UpdateIPSetInput) (*request.Request, *wafv2.UpdateIPSetOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.UpdateIPSetInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.UpdateIPSetOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.UpdateIPSetInput) *wafv2.UpdateIPSetOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.UpdateIPSetOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// UpdateIPSetWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) UpdateIPSetWithContext(_a0 context.Context, _a1 *wafv2.UpdateIPSetInput, _a2 ...request.Option) (*wafv2.UpdateIPSetOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.UpdateIPSetOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.UpdateIPSetInput, ...request.Option) *wafv2.UpdateIPSetOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UpdateIPSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.UpdateIPSetInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateRegexPatternSet provides a mock function with given fields: _a0
+func (_m *WAFV2API) UpdateRegexPatternSet(_a0 *wafv2.UpdateRegexPatternSetInput) (*wafv2.UpdateRegexPatternSetOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.UpdateRegexPatternSetOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.UpdateRegexPatternSetInput) *wafv2.UpdateRegexPatternSetOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UpdateRegexPatternSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.UpdateRegexPatternSetInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateRegexPatternSetRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) UpdateRegexPatternSetRequest(_a0 *wafv2.UpdateRegexPatternSetInput) (*request.Request, *wafv2.UpdateRegexPatternSetOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.UpdateRegexPatternSetInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.UpdateRegexPatternSetOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.UpdateRegexPatternSetInput) *wafv2.UpdateRegexPatternSetOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.UpdateRegexPatternSetOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// UpdateRegexPatternSetWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) UpdateRegexPatternSetWithContext(_a0 context.Context, _a1 *wafv2.UpdateRegexPatternSetInput, _a2 ...request.Option) (*wafv2.UpdateRegexPatternSetOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.UpdateRegexPatternSetOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.UpdateRegexPatternSetInput, ...request.Option) *wafv2.UpdateRegexPatternSetOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UpdateRegexPatternSetOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.UpdateRegexPatternSetInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateRuleGroup provides a mock function with given fields: _a0
+func (_m *WAFV2API) UpdateRuleGroup(_a0 *wafv2.UpdateRuleGroupInput) (*wafv2.UpdateRuleGroupOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.UpdateRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.UpdateRuleGroupInput) *wafv2.UpdateRuleGroupOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UpdateRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.UpdateRuleGroupInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateRuleGroupRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) UpdateRuleGroupRequest(_a0 *wafv2.UpdateRuleGroupInput) (*request.Request, *wafv2.UpdateRuleGroupOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.UpdateRuleGroupInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.UpdateRuleGroupOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.UpdateRuleGroupInput) *wafv2.UpdateRuleGroupOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.UpdateRuleGroupOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// UpdateRuleGroupWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) UpdateRuleGroupWithContext(_a0 context.Context, _a1 *wafv2.UpdateRuleGroupInput, _a2 ...request.Option) (*wafv2.UpdateRuleGroupOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.UpdateRuleGroupOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.UpdateRuleGroupInput, ...request.Option) *wafv2.UpdateRuleGroupOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UpdateRuleGroupOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.UpdateRuleGroupInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateWebACL provides a mock function with given fields: _a0
+func (_m *WAFV2API) UpdateWebACL(_a0 *wafv2.UpdateWebACLInput) (*wafv2.UpdateWebACLOutput, error) {
+	ret := _m.Called(_a0)
+
+	var r0 *wafv2.UpdateWebACLOutput
+	if rf, ok := ret.Get(0).(func(*wafv2.UpdateWebACLInput) *wafv2.UpdateWebACLOutput); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UpdateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(*wafv2.UpdateWebACLInput) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateWebACLRequest provides a mock function with given fields: _a0
+func (_m *WAFV2API) UpdateWebACLRequest(_a0 *wafv2.UpdateWebACLInput) (*request.Request, *wafv2.UpdateWebACLOutput) {
+	ret := _m.Called(_a0)
+
+	var r0 *request.Request
+	if rf, ok := ret.Get(0).(func(*wafv2.UpdateWebACLInput) *request.Request); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*request.Request)
+		}
+	}
+
+	var r1 *wafv2.UpdateWebACLOutput
+	if rf, ok := ret.Get(1).(func(*wafv2.UpdateWebACLInput) *wafv2.UpdateWebACLOutput); ok {
+		r1 = rf(_a0)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*wafv2.UpdateWebACLOutput)
+		}
+	}
+
+	return r0, r1
+}
+
+// UpdateWebACLWithContext provides a mock function with given fields: _a0, _a1, _a2
+func (_m *WAFV2API) UpdateWebACLWithContext(_a0 context.Context, _a1 *wafv2.UpdateWebACLInput, _a2 ...request.Option) (*wafv2.UpdateWebACLOutput, error) {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *wafv2.UpdateWebACLOutput
+	if rf, ok := ret.Get(0).(func(context.Context, *wafv2.UpdateWebACLInput, ...request.Option) *wafv2.UpdateWebACLOutput); ok {
+		r0 = rf(_a0, _a1, _a2...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*wafv2.UpdateWebACLOutput)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *wafv2.UpdateWebACLInput, ...request.Option) error); ok {
+		r1 = rf(_a0, _a1, _a2...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
diff --git a/scripts/gen_mocks.sh b/scripts/gen_mocks.sh
index 41cdc419a..87cc13512 100755
--- a/scripts/gen_mocks.sh
+++ b/scripts/gen_mocks.sh
@@ -13,6 +13,7 @@ mockery -name ELBV2API -dir ./vendor/github.com/aws/aws-sdk-go/service/elbv2/elb
 mockery -name IAMAPI -dir ./vendor/github.com/aws/aws-sdk-go/service/iam/iamiface
 mockery -name ResourceGroupsTaggingAPIAPI -dir ./vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface
 mockery -name WAFRegionalAPI -dir ./vendor/github.com/aws/aws-sdk-go/service/wafregional/wafregionaliface
+mockery -name WAFV2API -dir ./vendor/github.com/aws/aws-sdk-go/service/wafv2/wafv2iface
 mockery -name ShieldAPI -dir ./vendor/github.com/aws/aws-sdk-go/service/shield/shieldiface
 
 


Test patch:
diff --git a/internal/alb/lb/wafv2_test.go b/internal/alb/lb/wafv2_test.go
new file mode 100644
index 000000000..869d1bbe0
--- /dev/null
+++ b/internal/alb/lb/wafv2_test.go
@@ -0,0 +1,205 @@
+package lb
+
+import (
+	"context"
+	"testing"
+
+	"k8s.io/apimachinery/pkg/util/intstr"
+
+	"github.com/aws/aws-sdk-go/service/wafv2"
+	"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/aws"
+	"github.com/kubernetes-sigs/aws-alb-ingress-controller/mocks"
+	"github.com/stretchr/testify/assert"
+	apiv1 "k8s.io/api/core/v1"
+	extensions "k8s.io/api/extensions/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func buildWAFV2TestIngress(wafIngressAnnotations map[string]string) *extensions.Ingress {
+	defaultBackend := extensions.IngressBackend{
+		ServiceName: "default-backend",
+		ServicePort: intstr.FromInt(80),
+	}
+
+	return &extensions.Ingress{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:        "foo",
+			Namespace:   apiv1.NamespaceDefault,
+			Annotations: wafIngressAnnotations,
+		},
+		Spec: extensions.IngressSpec{
+			Backend: &extensions.IngressBackend{
+				ServiceName: "default-backend",
+				ServicePort: intstr.FromInt(80),
+			},
+			Rules: []extensions.IngressRule{
+				{
+					Host: "foo.bar.com",
+					IngressRuleValue: extensions.IngressRuleValue{
+						HTTP: &extensions.HTTPIngressRuleValue{
+							Paths: []extensions.HTTPIngressPath{
+								{
+									Path:    "/foo",
+									Backend: defaultBackend,
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+}
+
+func Test_defaultWAFV2Controller_Reconcile(t *testing.T) {
+	for _, tc := range []struct {
+		Name                             string
+		GetWAFV2WebACLSummaryResponse    *wafv2.WebACL
+		GetWAFV2WebACLSummaryError       error
+		AssociateWAFV2Response           *wafv2.AssociateWebACLOutput
+		AssociateWAFV2Error              error
+		DisassociateWAFV2Response        *wafv2.DisassociateWebACLOutput
+		DisassociateWAFV2Error           error
+		Expected                         error
+		ExpectedError                    error
+		LoadBalancerARN                  string
+		IngressAnnotations               *extensions.Ingress
+		DesiredWebACLARN                 string
+		GetWAFV2WebACLSummaryTimesCalled int
+		AssociateWAFV2TimesCalled        int
+		DisassociateWAFV2TimesCalled     int
+	}{
+		{
+			Name: "No annotation, confirm nothing is attached",
+			GetWAFV2WebACLSummaryResponse: &wafv2.WebACL{
+				ARN: nil,
+			},
+			GetWAFV2WebACLSummaryError: nil,
+			AssociateWAFV2Response:     &wafv2.AssociateWebACLOutput{},
+			AssociateWAFV2Error:        nil,
+			Expected:                   nil,
+			LoadBalancerARN:            "arn:lb",
+			IngressAnnotations: buildWAFV2TestIngress(
+				map[string]string{},
+			),
+			DesiredWebACLARN:                 "",
+			GetWAFV2WebACLSummaryTimesCalled: 1,
+			AssociateWAFV2TimesCalled:        0,
+			DisassociateWAFV2TimesCalled:     0,
+		},
+		{
+			Name: "Empty WAFv2 annotation",
+			GetWAFV2WebACLSummaryResponse: &wafv2.WebACL{
+				ARN: nil,
+			},
+			GetWAFV2WebACLSummaryError: nil,
+			AssociateWAFV2Response:     &wafv2.AssociateWebACLOutput{},
+			AssociateWAFV2Error:        nil,
+			Expected:                   nil,
+			LoadBalancerARN:            "arn:lb",
+			IngressAnnotations: buildWAFV2TestIngress(
+				map[string]string{
+					"alb.ingress.kubernetes.io/wafv2-acl-arn": "",
+				},
+			),
+			DesiredWebACLARN:                 "",
+			GetWAFV2WebACLSummaryTimesCalled: 1,
+			AssociateWAFV2TimesCalled:        0,
+			DisassociateWAFV2TimesCalled:     0,
+		},
+		{
+			Name: "No annotation, detach WAFv2",
+			GetWAFV2WebACLSummaryResponse: &wafv2.WebACL{
+				ARN: aws.String("arn:aws:wafv2:us-east-1:000000000000:regional/webacl/name/0aa00000-00a0-00a0-a0a0-0a0000a0000a"),
+			},
+			GetWAFV2WebACLSummaryError: nil,
+			AssociateWAFV2Response:     &wafv2.AssociateWebACLOutput{},
+			AssociateWAFV2Error:        nil,
+			Expected:                   nil,
+			LoadBalancerARN:            "arn:lb",
+			IngressAnnotations: buildWAFV2TestIngress(
+				map[string]string{},
+			),
+			DesiredWebACLARN:                 "",
+			GetWAFV2WebACLSummaryTimesCalled: 1,
+			AssociateWAFV2TimesCalled:        0,
+			DisassociateWAFV2TimesCalled:     1,
+		},
+		{
+			Name: "Empty annotation, dissassociate WAFv2",
+			GetWAFV2WebACLSummaryResponse: &wafv2.WebACL{
+				ARN: aws.String("arn:aws:wafv2:us-east-1:000000000000:regional/webacl/name/0aa00000-00a0-00a0-a0a0-0a0000a0000a"),
+			},
+			GetWAFV2WebACLSummaryError: nil,
+			AssociateWAFV2Response:     &wafv2.AssociateWebACLOutput{},
+			AssociateWAFV2Error:        nil,
+			Expected:                   nil,
+			LoadBalancerARN:            "arn:lb",
+			IngressAnnotations: buildWAFV2TestIngress(
+				map[string]string{
+					"alb.ingress.kubernetes.io/wafv2-acl-arn": "",
+				},
+			),
+			DesiredWebACLARN:                 "",
+			GetWAFV2WebACLSummaryTimesCalled: 1,
+			AssociateWAFV2TimesCalled:        0,
+			DisassociateWAFV2TimesCalled:     1,
+		},
+		{
+			Name: "Annotation, associate WAFv2",
+			GetWAFV2WebACLSummaryResponse: &wafv2.WebACL{
+				ARN: nil,
+			},
+			GetWAFV2WebACLSummaryError: nil,
+			AssociateWAFV2Response:     &wafv2.AssociateWebACLOutput{},
+			AssociateWAFV2Error:        nil,
+			Expected:                   nil,
+			LoadBalancerARN:            "arn:lb",
+			IngressAnnotations: buildWAFV2TestIngress(
+				map[string]string{
+					"alb.ingress.kubernetes.io/wafv2-acl-arn": "arn:aws:wafv2:us-east-1:000000000000:regional/webacl/name/0aa00000-00a0-00a0-a0a0-0a0000a0000a",
+				},
+			),
+			DesiredWebACLARN:                 "arn:aws:wafv2:us-east-1:000000000000:regional/webacl/name/0aa00000-00a0-00a0-a0a0-0a0000a0000a",
+			GetWAFV2WebACLSummaryTimesCalled: 1,
+			AssociateWAFV2TimesCalled:        1,
+			DisassociateWAFV2TimesCalled:     0,
+		},
+		{
+			Name: "Annotation, change WAFv2",
+			GetWAFV2WebACLSummaryResponse: &wafv2.WebACL{
+				ARN: aws.String("arn:aws:wafv2:us-east-1:000000000000:regional/webacl/name/0bb00000-00b0-00b0-b0b0-0b0000a0000b"),
+			},
+			GetWAFV2WebACLSummaryError: nil,
+			Expected:                   nil,
+			AssociateWAFV2Response:     &wafv2.AssociateWebACLOutput{},
+			AssociateWAFV2Error:        nil,
+			LoadBalancerARN:            "arn:lb",
+			IngressAnnotations: buildWAFV2TestIngress(
+				map[string]string{
+					"alb.ingress.kubernetes.io/wafv2-acl-arn": "arn:aws:wafv2:us-east-1:000000000000:regional/webacl/name/0aa00000-00a0-00a0-a0a0-0a0000a0000a",
+				},
+			),
+			DesiredWebACLARN:                 "arn:aws:wafv2:us-east-1:000000000000:regional/webacl/name/0aa00000-00a0-00a0-a0a0-0a0000a0000a",
+			GetWAFV2WebACLSummaryTimesCalled: 1,
+			AssociateWAFV2TimesCalled:        1,
+			DisassociateWAFV2TimesCalled:     0,
+		},
+	} {
+		t.Run(tc.Name, func(t *testing.T) {
+			ctx := context.Background()
+			cloud := &mocks.CloudAPI{}
+
+			cloud.On("GetWAFV2WebACLSummary", ctx, aws.String(tc.LoadBalancerARN)).Return(tc.GetWAFV2WebACLSummaryResponse, tc.GetWAFV2WebACLSummaryError)
+			cloud.On("AssociateWAFV2", ctx, aws.String(tc.LoadBalancerARN), aws.String(tc.DesiredWebACLARN)).Return(tc.AssociateWAFV2Response, tc.AssociateWAFV2Error)
+			cloud.On("DisassociateWAFV2", ctx, aws.String(tc.LoadBalancerARN)).Return(tc.DisassociateWAFV2Response, tc.DisassociateWAFV2Error)
+
+			controller := NewWAFV2Controller(cloud)
+			err := controller.Reconcile(ctx, tc.LoadBalancerARN, tc.IngressAnnotations)
+			assert.Equal(t, tc.Expected, err)
+			cloud.AssertNumberOfCalls(t, "GetWAFV2WebACLSummary", tc.GetWAFV2WebACLSummaryTimesCalled)
+			cloud.AssertNumberOfCalls(t, "AssociateWAFV2", tc.AssociateWAFV2TimesCalled)
+			cloud.AssertNumberOfCalls(t, "DisassociateWAFV2", tc.DisassociateWAFV2TimesCalled)
+		})
+	}
+}
diff --git a/internal/aws/wafv2_test.go b/internal/aws/wafv2_test.go
new file mode 100644
index 000000000..c5ceb4e94
--- /dev/null
+++ b/internal/aws/wafv2_test.go
@@ -0,0 +1,137 @@
+package aws
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/wafv2"
+	"github.com/kubernetes-sigs/aws-alb-ingress-controller/mocks"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestCloud_GetWAFV2WebACLSummary(t *testing.T) {
+	resourceArn := aws.String("arn")
+
+	for _, tc := range []struct {
+		Name                                    string
+		GetWebACLForResourceWithContextResponse *wafv2.GetWebACLForResourceOutput
+		GetWebACLForResourceWithContextError    error
+		Expected                                *wafv2.WebACL
+		ExpectedError                           error
+	}{
+		{
+			Name:                                    "No error from GetWAFV2WebACLSummary",
+			GetWebACLForResourceWithContextResponse: &wafv2.GetWebACLForResourceOutput{WebACL: &wafv2.WebACL{}},
+			GetWebACLForResourceWithContextError:    nil,
+			Expected:                                &wafv2.WebACL{},
+		},
+		{
+			Name:                                 "Error from GetWAFV2WebACLSummary, ACL doesn't exist",
+			GetWebACLForResourceWithContextError: errors.New("not found error"),
+			ExpectedError:                        errors.New("not found error"),
+		},
+	} {
+		t.Run(tc.Name, func(t *testing.T) {
+			ctx := context.Background()
+			wafsvc := &mocks.WAFV2API{}
+			wafsvc.On("GetWebACLForResourceWithContext", ctx, &wafv2.GetWebACLForResourceInput{
+				ResourceArn: resourceArn,
+			}).Return(tc.GetWebACLForResourceWithContextResponse, tc.GetWebACLForResourceWithContextError)
+
+			cloud := &Cloud{
+				wafv2: wafsvc,
+			}
+
+			output, err := cloud.GetWAFV2WebACLSummary(ctx, resourceArn)
+			assert.Equal(t, tc.Expected, output)
+			assert.Equal(t, tc.ExpectedError, err)
+			wafsvc.AssertExpectations(t)
+		})
+	}
+}
+
+func TestCloud_AssociateWAFV2(t *testing.T) {
+	resourceArn := aws.String("arn")
+	webACLARN := aws.String("web_acl_arn")
+
+	for _, tc := range []struct {
+		Name                               string
+		AssociateWebACLWithContextResponse *wafv2.AssociateWebACLOutput
+		AssociateWebACLWithContextError    error
+		Expected                           *wafv2.AssociateWebACLOutput
+		ExpectedError                      error
+	}{
+		{
+			Name:                               "No error from API",
+			AssociateWebACLWithContextResponse: &wafv2.AssociateWebACLOutput{},
+			AssociateWebACLWithContextError:    nil,
+			Expected:                           &wafv2.AssociateWebACLOutput{},
+		},
+		{
+			Name:                            "Error from API",
+			AssociateWebACLWithContextError: errors.New("api query failed"),
+			ExpectedError:                   errors.New("api query failed"),
+		},
+	} {
+		t.Run(tc.Name, func(t *testing.T) {
+			ctx := context.Background()
+			wafsvc := &mocks.WAFV2API{}
+			wafsvc.On("AssociateWebACLWithContext", ctx, &wafv2.AssociateWebACLInput{
+				ResourceArn: resourceArn,
+				WebACLArn:   webACLARN,
+			}).Return(tc.AssociateWebACLWithContextResponse, tc.AssociateWebACLWithContextError)
+
+			cloud := &Cloud{
+				wafv2: wafsvc,
+			}
+
+			output, err := cloud.AssociateWAFV2(ctx, resourceArn, webACLARN)
+			assert.Equal(t, tc.Expected, output)
+			assert.Equal(t, tc.ExpectedError, err)
+			wafsvc.AssertExpectations(t)
+		})
+	}
+}
+
+func TestCloud_DisassociateWAFV2(t *testing.T) {
+	resourceArn := aws.String("arn")
+
+	for _, tc := range []struct {
+		Name                                  string
+		DisassociateWebACLWithContextResponse *wafv2.DisassociateWebACLOutput
+		DisassociateWebACLWithContextError    error
+		Expected                              *wafv2.DisassociateWebACLOutput
+		ExpectedError                         error
+	}{
+		{
+			Name:                                  "No error from API",
+			DisassociateWebACLWithContextResponse: &wafv2.DisassociateWebACLOutput{},
+			DisassociateWebACLWithContextError:    nil,
+			Expected:                              &wafv2.DisassociateWebACLOutput{},
+		},
+		{
+			Name:                               "Error from API",
+			DisassociateWebACLWithContextError: errors.New("api query failed"),
+			ExpectedError:                      errors.New("api query failed"),
+		},
+	} {
+		t.Run(tc.Name, func(t *testing.T) {
+			ctx := context.Background()
+			wafsvc := &mocks.WAFV2API{}
+			wafsvc.On("DisassociateWebACLWithContext", ctx, &wafv2.DisassociateWebACLInput{
+				ResourceArn: resourceArn,
+			}).Return(tc.DisassociateWebACLWithContextResponse, tc.DisassociateWebACLWithContextError)
+
+			cloud := &Cloud{
+				wafv2: wafsvc,
+			}
+
+			output, err := cloud.DisassociateWAFV2(ctx, resourceArn)
+			assert.Equal(t, tc.Expected, output)
+			assert.Equal(t, tc.ExpectedError, err)
+			wafsvc.AssertExpectations(t)
+		})
+	}
+}
diff --git a/tester/test-config.yaml b/tester/test-config.yaml
index 6f26ef7ff..27a13f90a 100644
--- a/tester/test-config.yaml
+++ b/tester/test-config.yaml
@@ -122,6 +122,14 @@ cluster:
                 ],
                 "Resource": "*"
               },
+              {
+                "Effect": "Allow",
+                "Action": [
+                  "wafv2:GetWebACL",
+                  "wafv2:GetWebACLForResource"
+                ],
+                "Resource": "*"
+              },
               {
                 "Effect": "Allow",
                 "Action": [