Repository: getmoto/moto
Pull request #8336 (2024-11-19 16:13:49)

ResourceGroupsTaggingAPI: Add support for Secrets Manager for get_resources
Add support for the Secrets Manager for the get_resources function of the resourcegroupstaggingapi.


Patch:
diff --git a/docs/docs/services/medialive.rst b/docs/docs/services/medialive.rst
index 4de881aac..be7400cc1 100644
--- a/docs/docs/services/medialive.rst
+++ b/docs/docs/services/medialive.rst
@@ -86,10 +86,6 @@ medialive
 - [ ] get_signal_map
 - [ ] list_channel_placement_groups
 - [X] list_channels
-  
-        Pagination is not yet implemented
-        
-
 - [ ] list_cloud_watch_alarm_template_groups
 - [ ] list_cloud_watch_alarm_templates
 - [ ] list_clusters
@@ -99,10 +95,6 @@ medialive
 - [ ] list_input_devices
 - [ ] list_input_security_groups
 - [X] list_inputs
-  
-        Pagination is not yet implemented
-        
-
 - [ ] list_multiplex_programs
 - [ ] list_multiplexes
 - [ ] list_networks
diff --git a/docs/docs/services/wafv2.rst b/docs/docs/services/wafv2.rst
index 2dec4b6e2..65e6fc181 100644
--- a/docs/docs/services/wafv2.rst
+++ b/docs/docs/services/wafv2.rst
@@ -59,10 +59,6 @@ wafv2
 - [ ] list_resources_for_web_acl
 - [X] list_rule_groups
 - [X] list_tags_for_resource
-  
-        Pagination is not yet implemented
-        
-
 - [X] list_web_acls
 - [X] put_logging_configuration
 - [ ] put_managed_rule_set_versions
diff --git a/moto/ds/models.py b/moto/ds/models.py
index 25f0e78c2..b55e95932 100644
--- a/moto/ds/models.py
+++ b/moto/ds/models.py
@@ -222,7 +222,7 @@ class Directory(BaseModel):  # pylint: disable=too-many-instance-attributes
         """Enable/disable ldaps based on whether new_state is True or False.
         This method is only for MicrosoftAD.
         """
-        if self.directory_type != "MicrosoftAD":
+        if self.directory_type not in ("MicrosoftAD", "ADConnector"):
             raise UnsupportedOperationException(
                 "LDAPS operations are not supported for this Directory Type."
             )
@@ -673,7 +673,7 @@ class DirectoryServiceBackend(BaseBackend):
         """Describe LDAPS settings for a Directory"""
         self._validate_directory_id(directory_id)
         directory = self.directories[directory_id]
-        if directory.directory_type != "MicrosoftAD":
+        if directory.directory_type not in ("MicrosoftAD", "ADConnector"):
             raise UnsupportedOperationException(
                 "LDAPS operations are not supported for this Directory Type."
             )
diff --git a/moto/ec2/resources/instance_types.json b/moto/ec2/resources/instance_types.json
index e855c163f..8d2c93508 100644
--- a/moto/ec2/resources/instance_types.json
+++ b/moto/ec2/resources/instance_types.json
@@ -16059,7 +16059,7 @@
    ],
    "NetworkPerformance": "22.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -16217,7 +16217,7 @@
    ],
    "NetworkPerformance": "30 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -16388,7 +16388,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -16503,7 +16503,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -16626,7 +16626,7 @@
    ],
    "NetworkPerformance": "15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -16765,7 +16765,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -17075,7 +17075,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -54294,7 +54294,7 @@
    ],
    "NetworkPerformance": "22.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -54440,7 +54440,7 @@
    ],
    "NetworkPerformance": "30 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -54599,7 +54599,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -54702,7 +54702,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -54813,7 +54813,7 @@
    ],
    "NetworkPerformance": "15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -54940,7 +54940,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -55214,7 +55214,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -55325,7 +55325,7 @@
    ],
    "NetworkPerformance": "22.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -55483,7 +55483,7 @@
    ],
    "NetworkPerformance": "30 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -55654,7 +55654,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -55769,7 +55769,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -55892,7 +55892,7 @@
    ],
    "NetworkPerformance": "15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -56031,7 +56031,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -56341,7 +56341,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -78781,7 +78781,7 @@
    ],
    "NetworkPerformance": "22.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -78927,7 +78927,7 @@
    ],
    "NetworkPerformance": "30 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -79086,7 +79086,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -79189,7 +79189,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -79300,7 +79300,7 @@
    ],
    "NetworkPerformance": "15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -79427,7 +79427,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -79701,7 +79701,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -79812,7 +79812,7 @@
    ],
    "NetworkPerformance": "22.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -79970,7 +79970,7 @@
    ],
    "NetworkPerformance": "30 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -80141,7 +80141,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -80256,7 +80256,7 @@
    ],
    "NetworkPerformance": "Up to 15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -80379,7 +80379,7 @@
    ],
    "NetworkPerformance": "15 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -80518,7 +80518,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
@@ -80828,7 +80828,7 @@
    ],
    "NetworkPerformance": "Up to 12.5 Gigabit"
   },
-  "NitroEnclavesSupport": "unsupported",
+  "NitroEnclavesSupport": "supported",
   "NitroTpmInfo": {
    "SupportedVersions": [
     "2.0"
diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py
index 03341a69d..c50516152 100644
--- a/moto/ec2/utils.py
+++ b/moto/ec2/utils.py
@@ -90,7 +90,7 @@ def random_resource_id(size: int = 8) -> str:
     return "".join(random.choice(HEX_CHARS) for _ in range(size))
 
 
-def random_id(prefix: str = "", size: int = 8) -> str:
+def random_id(prefix: str = "", size: int = 17) -> str:
     return f"{prefix}-{random_resource_id(size)}"
 
 
@@ -99,7 +99,7 @@ def random_ami_id() -> str:
 
 
 def random_instance_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["instance"], size=17)
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["instance"])
 
 
 def random_reservation_id() -> str:
@@ -107,11 +107,11 @@ def random_reservation_id() -> str:
 
 
 def random_security_group_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["security-group"], size=17)
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["security-group"])
 
 
 def random_security_group_rule_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["security-group-rule"], size=17)
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["security-group-rule"])
 
 
 def random_fleet_id() -> str:
@@ -181,7 +181,7 @@ def random_vpc_id() -> str:
 
 
 def random_vpc_ep_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpc-endpoint"], size=8)
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpc-endpoint"])
 
 
 def random_vpc_cidr_association_id() -> str:
@@ -201,9 +201,7 @@ def random_internet_gateway_id() -> str:
 
 
 def random_egress_only_internet_gateway_id() -> str:
-    return random_id(
-        prefix=EC2_RESOURCE_TO_PREFIX["egress-only-internet-gateway"], size=17
-    )
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["egress-only-internet-gateway"])
 
 
 def random_route_table_id() -> str:
@@ -227,27 +225,27 @@ def random_eni_attach_id() -> str:
 
 
 def random_nat_gateway_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["nat-gateway"], size=17)
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["nat-gateway"])
 
 
 def random_transit_gateway_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["transit-gateway"], size=17)
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["transit-gateway"])
 
 
 def random_transit_gateway_route_table_id() -> str:
-    return random_id(
-        prefix=EC2_RESOURCE_TO_PREFIX["transit-gateway-route-table"], size=17
-    )
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["transit-gateway-route-table"])
 
 
 def random_transit_gateway_attachment_id() -> str:
-    return random_id(
-        prefix=EC2_RESOURCE_TO_PREFIX["transit-gateway-attachment"], size=17
-    )
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["transit-gateway-attachment"])
+
+
+def random_managed_prefix_list_id() -> str:
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["managed-prefix-list"])
 
 
 def random_launch_template_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["launch-template"], size=17)
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["launch-template"])
 
 
 def random_launch_template_name() -> str:
@@ -259,7 +257,7 @@ def random_iam_instance_profile_association_id() -> str:
 
 
 def random_carrier_gateway_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["carrier-gateway"], size=17)
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["carrier-gateway"])
 
 
 def random_public_ip() -> str:
@@ -267,7 +265,7 @@ def random_public_ip() -> str:
 
 
 def random_dedicated_host_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["dedicated_host"], size=17)
+    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["dedicated_host"])
 
 
 def random_private_ip(cidr: Optional[str] = None, ipv6: bool = False) -> str:
@@ -319,10 +317,6 @@ def generate_route_id(
     return f"{route_table_id}~{cidr_block}"
 
 
-def random_managed_prefix_list_id() -> str:
-    return random_id(prefix=EC2_RESOURCE_TO_PREFIX["managed-prefix-list"], size=8)
-
-
 def create_dns_entries(service_name: str, vpc_endpoint_id: str) -> Dict[str, str]:
     return {
         "dns_name": f"{vpc_endpoint_id}-{random_resource_id(8)}.{service_name}",
diff --git a/moto/ecs/models.py b/moto/ecs/models.py
index a39f3fb7f..beb898d87 100644
--- a/moto/ecs/models.py
+++ b/moto/ecs/models.py
@@ -14,6 +14,7 @@ from moto.moto_api._internal import mock_random
 from moto.moto_api._internal.managed_state_model import ManagedState
 from moto.utilities.utils import ARN_PARTITION_REGEX, get_partition
 
+from ..ec2.exceptions import InvalidSecurityGroupNotFoundError, InvalidSubnetIdError
 from ..ec2.utils import random_private_ip
 from .exceptions import (
     ClusterNotFoundException,
@@ -548,6 +549,7 @@ class Service(BaseObject, CloudFormationModel):
         launch_type: Optional[str] = None,
         service_registries: Optional[List[Dict[str, Any]]] = None,
         platform_version: Optional[str] = None,
+        network_configuration: Optional[Dict[str, Dict[str, List[str]]]] = None,
         propagate_tags: str = "NONE",
     ):
         self.cluster_name = cluster.name
@@ -598,6 +600,37 @@ class Service(BaseObject, CloudFormationModel):
             self.deployments = []
         self.propagate_tags = propagate_tags
 
+        if network_configuration is not None:
+            self.network_configuration = self._validate_network(network_configuration)
+        else:
+            self.network_configuration = {}
+
+    def _validate_network(
+        self, nc: Dict[str, Dict[str, List[str]]]
+    ) -> Dict[str, Dict[str, List[str]]]:
+        c = nc["awsvpcConfiguration"]
+        if len(c["subnets"]) == 0:
+            raise InvalidParameterException("subnets can not be empty.")
+
+        ec2_backend = ec2_backends[self._account_id][self.region_name]
+        try:
+            ec2_backend.describe_subnets(subnet_ids=c["subnets"])
+        except InvalidSubnetIdError as exc:
+            subnet_id = exc.message.split("'")[1]
+            raise InvalidParameterException(
+                f"Error retrieving subnet information for [{subnet_id}]: {exc.message} (ErrorCode: {exc.error_type})"
+            )
+
+        try:
+            ec2_backend.describe_security_groups(group_ids=c["securityGroups"])
+        except InvalidSecurityGroupNotFoundError as exc:
+            sg = exc.message.split("'{'")[1].split("'}'")[0]
+            raise InvalidParameterException(
+                f"Error retrieving security group information for [{sg}]: "
+                f"The security group '{sg}' does not exist (ErrorCode: InvalidGroup.NotFound)"
+            )
+        return nc
+
     @property
     def arn(self) -> str:
         if self._backend.enable_long_arn_for_name(name="serviceLongArnFormat"):
@@ -633,6 +666,7 @@ class Service(BaseObject, CloudFormationModel):
                 deployment["updatedAt"] = unix_time(
                     deployment["updatedAt"].replace(tzinfo=None)
                 )
+        response_object["networkConfiguration"] = self.network_configuration
 
         return response_object
 
@@ -1613,6 +1647,7 @@ class EC2ContainerServiceBackend(BaseBackend):
         service_registries: Optional[List[Dict[str, Any]]] = None,
         platform_version: Optional[str] = None,
         propagate_tags: str = "NONE",
+        network_configuration: Optional[Dict[str, Dict[str, List[str]]]] = None,
     ) -> Service:
         cluster = self._get_cluster(cluster_str)
 
@@ -1640,6 +1675,7 @@ class EC2ContainerServiceBackend(BaseBackend):
             service_registries=service_registries,
             platform_version=platform_version,
             propagate_tags=propagate_tags,
+            network_configuration=network_configuration,
         )
         cluster_service_pair = f"{cluster.name}:{service_name}"
         self.services[cluster_service_pair] = service
diff --git a/moto/ecs/responses.py b/moto/ecs/responses.py
index 090069c96..8d9ca0fc3 100644
--- a/moto/ecs/responses.py
+++ b/moto/ecs/responses.py
@@ -262,6 +262,7 @@ class EC2ContainerServiceResponse(BaseResponse):
         launch_type = self._get_param("launchType")
         platform_version = self._get_param("platformVersion")
         propagate_tags = self._get_param("propagateTags") or "NONE"
+        network_configuration = self._get_param("networkConfiguration")
         service = self.ecs_backend.create_service(
             cluster_str,
             service_name,
@@ -272,6 +273,7 @@ class EC2ContainerServiceResponse(BaseResponse):
             tags,
             deployment_controller,
             launch_type,
+            network_configuration=network_configuration,
             service_registries=service_registries,
             platform_version=platform_version,
             propagate_tags=propagate_tags,
diff --git a/moto/iotdata/models.py b/moto/iotdata/models.py
index b8f3e78fa..eecf3e37e 100644
--- a/moto/iotdata/models.py
+++ b/moto/iotdata/models.py
@@ -71,20 +71,58 @@ class FakeShadow(BaseModel):
         return FakeShadow(desired, reported, payload, version)
 
     @classmethod
-    def parse_payload(cls, desired: Optional[str], reported: Optional[str]) -> Any:  # type: ignore[misc]
+    def parse_payload(cls, desired: Any, reported: Any) -> Any:  # type: ignore[misc]
         if not desired and not reported:
             delta = None
         elif reported is None and desired:
             delta = desired
         elif desired and reported:
-            delta = jsondiff.diff(reported, desired)
-            delta.pop(jsondiff.add, None)  # type: ignore
-            delta.pop(jsondiff.delete, None)  # type: ignore
-            delta.pop(jsondiff.replace, None)  # type: ignore
+            delta = jsondiff.diff(reported, desired, syntax="symmetric")
+            delta.pop(jsondiff.add, None)
+            delta.pop(jsondiff.delete, None)
+            delta.pop(jsondiff.replace, None)
+            cls._resolve_nested_deltas(desired, reported, delta)
         else:
             delta = None
         return delta
 
+    @classmethod
+    def _resolve_nested_deltas(cls, desired: Any, reported: Any, delta: Any) -> None:  # type: ignore[misc]
+        for key, value in delta.items():
+            if isinstance(value, dict):
+                # delta = {insert: [(0, val1),, ], delete: [(0, val2),..] }
+                if isinstance(reported.get(key), list):
+                    # Delta is a dict, but were supposed to have a list
+                    # Explicitly insert/delete from the original list
+                    list_delta = reported[key].copy()
+                    if jsondiff.delete in value:
+                        for idx, _ in sorted(value[jsondiff.delete], reverse=True):
+                            del list_delta[idx]
+                    if jsondiff.insert in value:
+                        for new_val in sorted(value[jsondiff.insert], reverse=True):
+                            list_delta.insert(*new_val)
+                    delta[key] = list_delta
+                if isinstance(reported.get(key), dict):
+                    # Delta is a dict, exactly what we're expecting
+                    # Just delete any unknown symbols
+                    value.pop(jsondiff.add, None)
+                    value.pop(jsondiff.delete, None)
+                    value.pop(jsondiff.replace, None)
+            elif isinstance(value, list):
+                # delta = [v1, v2]
+                # If the actual value is a string/bool/int and our delta is a list,
+                # that means that the delta reports both values - reported and desired
+                # But we only want to show the desired value here
+                # (Note that bool is a type of int, so we don't have to explicitly mention it)
+                if isinstance(desired.get(key), (str, int, float)):
+                    delta[key] = desired[key]
+
+            # Resolve nested deltas
+            if isinstance(delta[key], dict):
+                cls._resolve_nested_deltas(
+                    desired.get(key), reported.get(key), delta[key]
+                )
+
     def _create_metadata_from_state(self, state: Any, ts: Any) -> Any:
         """
         state must be desired or reported stype dict object
diff --git a/moto/medialive/models.py b/moto/medialive/models.py
index a5dd85a9c..e09777634 100644
--- a/moto/medialive/models.py
+++ b/moto/medialive/models.py
@@ -4,8 +4,24 @@ from typing import Any, Dict, List, Optional
 from moto.core.base_backend import BackendDict, BaseBackend
 from moto.core.common_models import BaseModel
 from moto.moto_api._internal import mock_random
+from moto.utilities.paginator import paginate
 from moto.utilities.utils import get_partition
 
+PAGINATION_MODEL = {
+    "list_channels": {
+        "input_token": "next_token",
+        "limit_key": "max_results",
+        "limit_default": 100,
+        "unique_attribute": "arn",
+    },
+    "list_inputs": {
+        "input_token": "next_token",
+        "limit_key": "max_results",
+        "limit_default": 100,
+        "unique_attribute": "arn",
+    },
+}
+
 
 class Input(BaseModel):
     def __init__(self, **kwargs: Any):
@@ -157,13 +173,9 @@ class MediaLiveBackend(BaseBackend):
         self._channels[channel_id] = channel
         return channel
 
-    def list_channels(self, max_results: Optional[int]) -> List[Dict[str, Any]]:
-        """
-        Pagination is not yet implemented
-        """
+    @paginate(pagination_model=PAGINATION_MODEL)  # type: ignore[misc]
+    def list_channels(self) -> List[Dict[str, Any]]:
         channels = list(self._channels.values())
-        if max_results is not None:
-            channels = channels[:max_results]
         return [
             c.to_dict(exclude=["encoderSettings", "pipelineDetails"]) for c in channels
         ]
@@ -255,13 +267,9 @@ class MediaLiveBackend(BaseBackend):
         a_input._resolve_transient_states()
         return a_input
 
-    def list_inputs(self, max_results: Optional[int]) -> List[Dict[str, Any]]:
-        """
-        Pagination is not yet implemented
-        """
+    @paginate(PAGINATION_MODEL)  # type: ignore[misc]
+    def list_inputs(self) -> List[Dict[str, Any]]:
         inputs = list(self._inputs.values())
-        if max_results is not None:
-            inputs = inputs[:max_results]
         return [i.to_dict() for i in inputs]
 
     def delete_input(self, input_id: str) -> None:
diff --git a/moto/medialive/responses.py b/moto/medialive/responses.py
index f9515fe8a..2356bfa70 100644
--- a/moto/medialive/responses.py
+++ b/moto/medialive/responses.py
@@ -43,9 +43,12 @@ class MediaLiveResponse(BaseResponse):
 
     def list_channels(self) -> str:
         max_results = self._get_int_param("maxResults")
-        channels = self.medialive_backend.list_channels(max_results=max_results)
+        next_token = self._get_param("nextToken")
+        channels, next_token = self.medialive_backend.list_channels(
+            max_results=max_results, next_token=next_token
+        )
 
-        return json.dumps(dict(channels=channels, nextToken=None))
+        return json.dumps(dict(channels=channels, nextToken=next_token))
 
     def describe_channel(self) -> str:
         channel_id = self._get_param("channelId")
@@ -120,9 +123,12 @@ class MediaLiveResponse(BaseResponse):
 
     def list_inputs(self) -> str:
         max_results = self._get_int_param("maxResults")
-        inputs = self.medialive_backend.list_inputs(max_results=max_results)
+        next_token = self._get_param("nextToken")
+        inputs, next_token = self.medialive_backend.list_inputs(
+            max_results=max_results, next_token=next_token
+        )
 
-        return json.dumps(dict(inputs=inputs, nextToken=None))
+        return json.dumps(dict(inputs=inputs, nextToken=next_token))
 
     def delete_input(self) -> str:
         input_id = self._get_param("inputId")
diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py
index 19dc9339a..d48a314dd 100644
--- a/moto/resourcegroupstaggingapi/models.py
+++ b/moto/resourcegroupstaggingapi/models.py
@@ -22,6 +22,8 @@ from moto.rds.models import RDSBackend, rds_backends
 from moto.redshift.models import RedshiftBackend, redshift_backends
 from moto.s3.models import S3Backend, s3_backends
 from moto.sagemaker.models import SageMakerModelBackend, sagemaker_backends
+from moto.secretsmanager import secretsmanager_backends
+from moto.secretsmanager.models import ReplicaSecret, SecretsManagerBackend
 from moto.sns.models import SNSBackend, sns_backends
 from moto.sqs.models import SQSBackend, sqs_backends
 from moto.ssm.models import SimpleSystemManagerBackend, ssm_backends
@@ -110,6 +112,10 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
     def acm_backend(self) -> AWSCertificateManagerBackend:
         return acm_backends[self.account_id][self.region_name]
 
+    @property
+    def secretsmanager_backend(self) -> SecretsManagerBackend:
+        return secretsmanager_backends[self.account_id][self.region_name]
+
     @property
     def sns_backend(self) -> SNSBackend:
         return sns_backends[self.account_id][self.region_name]
@@ -496,6 +502,24 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
         # RedShift Snapshot
         # RedShift Subnet group
 
+        # Secrets Manager
+        if (
+            not resource_type_filters
+            or "secretsmanager" in resource_type_filters
+            or "secretsmanager:secret" in resource_type_filters
+        ):
+            for secret in self.secretsmanager_backend.secrets.values():
+                if isinstance(secret, ReplicaSecret):
+                    secret_tags = secret.source.tags
+                else:
+                    secret_tags = secret.tags
+
+                if secret_tags:
+                    formated_tags = format_tag_keys(secret_tags, ["Key", "Value"])
+                    if not formated_tags or not tag_filter(formated_tags):
+                        continue
+                    yield {"ResourceARN": f"{secret.arn}", "Tags": formated_tags}
+
         # SQS
         if not resource_type_filters or "sqs" in resource_type_filters:
             for queue in self.sqs_backend.queues.values():
diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py
index b2f185fb0..a0c7acad0 100644
--- a/moto/secretsmanager/models.py
+++ b/moto/secretsmanager/models.py
@@ -907,9 +907,13 @@ class SecretsManagerBackend(BaseBackend):
         filters: List[Dict[str, Any]],
         max_results: int = MAX_RESULTS_DEFAULT,
         next_token: Optional[str] = None,
+        include_planned_deletion: bool = False,
     ) -> Tuple[List[Dict[str, Any]], Optional[str]]:
         secret_list: List[Dict[str, Any]] = []
         for secret in self.secrets.values():
+            if hasattr(secret, "deleted_date"):
+                if secret.deleted_date and not include_planned_deletion:
+                    continue
             if _matches(secret, filters):
                 secret_list.append(secret.to_dict())
 
diff --git a/moto/secretsmanager/responses.py b/moto/secretsmanager/responses.py
index 72a7403fa..2cfc7a3f6 100644
--- a/moto/secretsmanager/responses.py
+++ b/moto/secretsmanager/responses.py
@@ -175,8 +175,14 @@ class SecretsManagerResponse(BaseResponse):
         _validate_filters(filters)
         max_results = self._get_int_param("MaxResults")
         next_token = self._get_param("NextToken")
+        include_planned_deletion = self._get_param(
+            "IncludePlannedDeletion", if_none=False
+        )
         secret_list, next_token = self.backend.list_secrets(
-            filters=filters, max_results=max_results, next_token=next_token
+            filters=filters,
+            max_results=max_results,
+            next_token=next_token,
+            include_planned_deletion=include_planned_deletion,
         )
         return json.dumps(dict(SecretList=secret_list, NextToken=next_token))
 
diff --git a/moto/ssm/resources/regions.json b/moto/ssm/resources/regions.json
index ea7066613..52aeebab9 100644
--- a/moto/ssm/resources/regions.json
+++ b/moto/ssm/resources/regions.json
@@ -418,6 +418,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "connectcampaigns": {
+                "Value": "connectcampaigns",
+                "endpoint": {
+                  "Value": "connect-campaigns.af-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "connectparticipant": {
                 "Value": "connectparticipant",
                 "endpoint": {
@@ -12530,6 +12539,9 @@
                   "Value": "api.thinclient.ap-south-1.amazonaws.com"
                 }
               },
+              "timestream": {
+                "Value": "timestream"
+              },
               "timestream-influxdb": {
                 "Value": "timestream-influxdb",
                 "endpoint": {
@@ -12539,6 +12551,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "timestream-write": {
+                "Value": "timestream-write",
+                "endpoint": {
+                  "Value": "ingest.timestream.ap-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "transcribe": {
                 "Value": "transcribe",
                 "endpoint": {
@@ -13968,6 +13989,15 @@
               "vpc": {
                 "Value": "vpc"
               },
+              "vpc-lattice": {
+                "Value": "vpc-lattice",
+                "endpoint": {
+                  "Value": "vpc-lattice.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "vpn": {
                 "Value": "vpn",
                 "endpoint": {
@@ -22755,6 +22785,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "license-manager-linux-subscriptions": {
+                "Value": "license-manager-linux-subscriptions",
+                "endpoint": {
+                  "Value": "license-manager-linux-subscriptions.ap-southeast-5.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "logs": {
                 "Value": "logs",
                 "endpoint": {
@@ -23085,6 +23124,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "wickr": {
+                "Value": "wickr",
+                "endpoint": {
+                  "Value": "api.messaging.wickr.ap-southeast-5.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "xray": {
                 "Value": "xray",
                 "endpoint": {
@@ -26164,6 +26212,15 @@
               "marketplace": {
                 "Value": "marketplace"
               },
+              "mq": {
+                "Value": "mq",
+                "endpoint": {
+                  "Value": "mq.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "mwaa": {
                 "Value": "mwaa",
                 "endpoint": {
@@ -33564,6 +33621,15 @@
               "vpc": {
                 "Value": "vpc"
               },
+              "vpc-lattice": {
+                "Value": "vpc-lattice",
+                "endpoint": {
+                  "Value": "vpc-lattice.eu-central-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "vpn": {
                 "Value": "vpn",
                 "endpoint": {
@@ -49060,6 +49126,15 @@
               "vpc": {
                 "Value": "vpc"
               },
+              "vpc-lattice": {
+                "Value": "vpc-lattice",
+                "endpoint": {
+                  "Value": "vpc-lattice.me-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "vpn": {
                 "Value": "vpn",
                 "endpoint": {
@@ -53283,6 +53358,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "billing": {
+                "Value": "billing",
+                "endpoint": {
+                  "Value": "billing.us-east-1.api.aws"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "billingconductor": {
                 "Value": "billingconductor",
                 "endpoint": {
@@ -55179,6 +55263,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "partnercentral-selling": {
+                "Value": "partnercentral-selling",
+                "endpoint": {
+                  "Value": "partnercentral-selling.us-east-1.api.aws"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "payment-cryptography": {
                 "Value": "payment-cryptography"
               },
diff --git a/moto/ssm/resources/services.json b/moto/ssm/resources/services.json
index 9fb45494b..4f3456c40 100644
--- a/moto/ssm/resources/services.json
+++ b/moto/ssm/resources/services.json
@@ -8182,6 +8182,22 @@
               }
             }
           },
+          "billing": {
+            "longName": {
+              "Value": "AWS Billing"
+            },
+            "regions": {
+              "us-east-1": {
+                "Value": "us-east-1",
+                "endpoint": {
+                  "Value": "billing.us-east-1.api.aws"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              }
+            }
+          },
           "billingconductor": {
             "Value": "billingconductor",
             "longName": {
@@ -15666,6 +15682,15 @@
               "Value": "Amazon Connect Campaign Service"
             },
             "regions": {
+              "af-south-1": {
+                "Value": "af-south-1",
+                "endpoint": {
+                  "Value": "connect-campaigns.af-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-southeast-2": {
                 "Value": "ap-southeast-2",
                 "endpoint": {
@@ -38579,6 +38604,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-southeast-5": {
+                "Value": "ap-southeast-5",
+                "endpoint": {
+                  "Value": "license-manager-linux-subscriptions.ap-southeast-5.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ca-central-1": {
                 "Value": "ca-central-1",
                 "endpoint": {
@@ -43769,6 +43803,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ca-west-1": {
+                "Value": "ca-west-1",
+                "endpoint": {
+                  "Value": "mq.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "cn-north-1": {
                 "Value": "cn-north-1",
                 "endpoint": {
@@ -47021,6 +47064,23 @@
               }
             }
           },
+          "partnercentral-selling": {
+            "Value": "partnercentral-selling",
+            "longName": {
+              "Value": "Partner Central Selling API"
+            },
+            "regions": {
+              "us-east-1": {
+                "Value": "us-east-1",
+                "endpoint": {
+                  "Value": "partnercentral-selling.us-east-1.api.aws"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              }
+            }
+          },
           "payment-cryptography": {
             "Value": "payment-cryptography",
             "longName": {
@@ -65232,6 +65292,9 @@
               "ap-northeast-1": {
                 "Value": "ap-northeast-1"
               },
+              "ap-south-1": {
+                "Value": "ap-south-1"
+              },
               "ap-southeast-2": {
                 "Value": "ap-southeast-2"
               },
@@ -65449,6 +65512,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-south-1": {
+                "Value": "ap-south-1",
+                "endpoint": {
+                  "Value": "ingest.timestream.ap-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-southeast-2": {
                 "Value": "ap-southeast-2",
                 "endpoint": {
@@ -67570,6 +67642,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-south-2": {
+                "Value": "ap-south-2",
+                "endpoint": {
+                  "Value": "vpc-lattice.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-southeast-1": {
                 "Value": "ap-southeast-1",
                 "endpoint": {
@@ -67615,6 +67696,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "eu-central-2": {
+                "Value": "eu-central-2",
+                "endpoint": {
+                  "Value": "vpc-lattice.eu-central-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "eu-north-1": {
                 "Value": "eu-north-1",
                 "endpoint": {
@@ -67660,6 +67750,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "me-central-1": {
+                "Value": "me-central-1",
+                "endpoint": {
+                  "Value": "vpc-lattice.me-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "me-south-1": {
                 "Value": "me-south-1",
                 "endpoint": {
@@ -68837,6 +68936,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-southeast-5": {
+                "Value": "ap-southeast-5",
+                "endpoint": {
+                  "Value": "api.messaging.wickr.ap-southeast-5.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ca-central-1": {
                 "Value": "ca-central-1",
                 "endpoint": {
diff --git a/moto/wafv2/models.py b/moto/wafv2/models.py
index 4c0e1b87b..b704f1635 100644
--- a/moto/wafv2/models.py
+++ b/moto/wafv2/models.py
@@ -9,6 +9,7 @@ from moto.core.utils import (
     iso_8601_datetime_with_milliseconds,
 )
 from moto.moto_api._internal import mock_random
+from moto.utilities.paginator import paginate
 from moto.utilities.tagging_service import TaggingService
 from moto.utilities.utils import ARN_PARTITION_REGEX, PARTITION_NAMES
 
@@ -31,6 +32,39 @@ APIGATEWAY_REGEX = (
     + r":apigateway:[a-zA-Z0-9-]+::/restapis/[a-zA-Z0-9]+/stages/[a-zA-Z0-9]+"
 )
 
+PAGINATION_MODEL = {
+    "list_ip_sets": {
+        "input_token": "next_marker",
+        "limit_key": "limit",
+        "limit_default": 100,
+        "unique_attribute": "arn",
+    },
+    "list_logging_configurations": {
+        "input_token": "next_marker",
+        "limit_key": "limit",
+        "limit_default": 100,
+        "unique_attribute": "arn",
+    },
+    "list_rule_groups": {
+        "input_token": "next_marker",
+        "limit_key": "limit",
+        "limit_default": 100,
+        "unique_attribute": "arn",
+    },
+    "list_tags_for_resource": {
+        "input_token": "next_marker",
+        "limit_key": "limit",
+        "limit_default": 100,
+        "unique_attribute": "Key",
+    },
+    "list_web_acls": {
+        "input_token": "next_marker",
+        "limit_key": "limit",
+        "limit_default": 100,
+        "unique_attribute": "ARN",
+    },
+}
+
 
 class FakeRule(BaseModel):
     def __init__(
@@ -479,6 +513,7 @@ class WAFV2Backend(BaseBackend):
                 return wacl
         raise WAFNonexistentItemException
 
+    @paginate(PAGINATION_MODEL)  # type: ignore
     def list_web_acls(self) -> List[Dict[str, Any]]:
         return [wacl.to_short_dict() for wacl in self.wacls.values()]
 
@@ -486,16 +521,15 @@ class WAFV2Backend(BaseBackend):
         all_wacl_names = set(wacl.name for wacl in self.wacls.values())
         return name in all_wacl_names
 
+    @paginate(PAGINATION_MODEL)  # type: ignore
     def list_rule_groups(self, scope: str) -> List[Any]:
         rule_groups = [
             group for group in self.rule_groups.values() if group.scope == scope
         ]
         return rule_groups
 
+    @paginate(PAGINATION_MODEL)  # type: ignore
     def list_tags_for_resource(self, arn: str) -> List[Dict[str, str]]:
-        """
-        Pagination is not yet implemented
-        """
         return self.tagging_service.list_tags_for_resource(arn)["Tags"]
 
     def tag_resource(self, arn: str, tags: List[Dict[str, str]]) -> None:
@@ -591,6 +625,7 @@ class WAFV2Backend(BaseBackend):
 
         self.ip_sets.pop(arn)
 
+    @paginate(PAGINATION_MODEL)  # type: ignore
     def list_ip_sets(self, scope: str) -> List[FakeIPSet]:
         ip_sets = [
             ip_set for arn, ip_set in self.ip_sets.items() if ip_set.scope == scope
@@ -665,6 +700,7 @@ class WAFV2Backend(BaseBackend):
             raise WAFNonexistentItemException()
         return logging_configuration
 
+    @paginate(PAGINATION_MODEL)  # type: ignore
     def list_logging_configurations(self, scope: str) -> List[FakeLoggingConfiguration]:
         if scope == "CLOUDFRONT":
             scope = "global"
diff --git a/moto/wafv2/responses.py b/moto/wafv2/responses.py
index b8b03c22e..c00812708 100644
--- a/moto/wafv2/responses.py
+++ b/moto/wafv2/responses.py
@@ -3,7 +3,6 @@ import json
 from moto.core.common_types import TYPE_RESPONSE
 from moto.core.responses import BaseResponse
 
-from ..moto_api._internal import mock_random
 from .models import GLOBAL_REGION, WAFV2Backend, wafv2_backends
 
 
@@ -98,8 +97,12 @@ class WAFV2Response(BaseResponse):
         scope = self._get_param("Scope")
         if scope == "CLOUDFRONT":
             self.region = GLOBAL_REGION
-        all_web_acls = self.wafv2_backend.list_web_acls()
-        response = {"NextMarker": "Not Implemented", "WebACLs": all_web_acls}
+        limit = self._get_int_param("Limit")
+        next_marker = self._get_param("NextMarker")
+        web_acls, next_marker = self.wafv2_backend.list_web_acls(
+            limit=limit, next_marker=next_marker
+        )
+        response = {"NextMarker": next_marker, "WebACLs": web_acls}
         response_headers = {"Content-Type": "application/json"}
         return 200, response_headers, json.dumps(response)
 
@@ -107,16 +110,30 @@ class WAFV2Response(BaseResponse):
         scope = self._get_param("Scope")
         if scope == "CLOUDFRONT":
             self.region = GLOBAL_REGION
-        rule_groups = self.wafv2_backend.list_rule_groups(scope)
-        response = {"RuleGroups": [rg.to_short_dict() for rg in rule_groups]}
+        limit = self._get_int_param("Limit")
+        next_marker = self._get_param("NextMarker")
+        rule_groups, next_marker = self.wafv2_backend.list_rule_groups(
+            scope, limit=limit, next_marker=next_marker
+        )
+        response = {
+            "RuleGroups": [rg.to_short_dict() for rg in rule_groups],
+            "NextMarker": next_marker,
+        }
         response_headers = {"Content-Type": "application/json"}
         return 200, response_headers, json.dumps(response)
 
     def list_tags_for_resource(self) -> TYPE_RESPONSE:
         arn = self._get_param("ResourceARN")
         self.region = arn.split(":")[3]
-        tags = self.wafv2_backend.list_tags_for_resource(arn)
-        response = {"TagInfoForResource": {"ResourceARN": arn, "TagList": tags}}
+        limit = self._get_int_param("Limit")
+        next_marker = self._get_param("NextMarker")
+        tags, next_marker = self.wafv2_backend.list_tags_for_resource(
+            arn, limit=limit, next_marker=next_marker
+        )
+        response = {
+            "TagInfoForResource": {"ResourceARN": arn, "TagList": tags},
+            "NextMarker": next_marker,
+        }
         response_headers = {"Content-Type": "application/json"}
         return 200, response_headers, json.dumps(response)
 
@@ -217,11 +234,15 @@ class WAFV2Response(BaseResponse):
 
         return 200, {}, "{}"
 
-    def list_ip_sets(self) -> TYPE_RESPONSE:
+    def list_ip_sets(self) -> str:
         scope = self._get_param("Scope")
         if scope == "CLOUDFRONT":
             self.region = GLOBAL_REGION
-        ip_sets = self.wafv2_backend.list_ip_sets(scope)
+        next_marker = self._get_param("NextMarker")
+        limit = self._get_int_param("Limit")
+        ip_sets, next_token = self.wafv2_backend.list_ip_sets(
+            scope, next_marker=next_marker, limit=limit
+        )
 
         formatted_ip_sets = [
             {
@@ -234,13 +255,7 @@ class WAFV2Response(BaseResponse):
             for ip_set in ip_sets
         ]
 
-        return (
-            200,
-            {},
-            json.dumps(
-                {"NextMarker": str(mock_random.uuid4()), "IPSets": formatted_ip_sets}
-            ),
-        )
+        return json.dumps({"NextMarker": next_token, "IPSets": formatted_ip_sets})
 
     def get_ip_set(self) -> TYPE_RESPONSE:
         scope = self._get_param("Scope")
@@ -327,16 +342,22 @@ class WAFV2Response(BaseResponse):
             ),
         )
 
-    def list_logging_configurations(self) -> TYPE_RESPONSE:
+    def list_logging_configurations(self) -> str:
         body = json.loads(self.body)
         scope = body.get("Scope")
-        log_configs = self.wafv2_backend.list_logging_configurations(scope)
+        limit = self._get_int_param("Limit")
+        next_marker = self._get_param("NextMarker")
+        log_configs, next_marker = self.wafv2_backend.list_logging_configurations(
+            scope, limit=limit, next_marker=next_marker
+        )
 
         formatted = [
             {k: v for k, v in config.to_dict().items() if v is not None}
             for config in log_configs
         ]
-        return 200, {}, json.dumps({"LoggingConfigurations": formatted})
+        return json.dumps(
+            {"LoggingConfigurations": formatted, "NextMarker": next_marker}
+        )
 
     def delete_logging_configuration(self) -> TYPE_RESPONSE:
         body = json.loads(self.body)


Test patch:
diff --git a/tests/test_ds/test_ds_ad_connect.py b/tests/test_ds/test_ds_ad_connect.py
index e95bc4c41..1418ee478 100644
--- a/tests/test_ds/test_ds_ad_connect.py
+++ b/tests/test_ds/test_ds_ad_connect.py
@@ -287,3 +287,34 @@ def test_ds_get_connect_directory_limits():
     assert limits["ConnectedDirectoriesLimitReached"]
     assert not limits["CloudOnlyDirectoriesCurrentCount"]
     assert not limits["CloudOnlyMicrosoftADCurrentCount"]
+
+
+@mock_aws
+def test_enable_describe_disable_ldaps():
+    """Test good and bad invocations of describe_directories()."""
+    client = boto3.client("ds", region_name=TEST_REGION)
+    ec2_client = boto3.client("ec2", region_name=TEST_REGION)
+
+    directory_id = create_test_ad_connector(client, ec2_client)
+
+    # Describe LDAPS settings for AD Connector without LDAPS enabled
+    ldaps = client.describe_ldaps_settings(DirectoryId=directory_id)[
+        "LDAPSSettingsInfo"
+    ]
+    assert ldaps == []
+
+    # Enable LDAPS for AD Connector and verify it is enabled
+    client.enable_ldaps(DirectoryId=directory_id, Type="Client")
+    ldaps = client.describe_ldaps_settings(DirectoryId=directory_id)[
+        "LDAPSSettingsInfo"
+    ]
+    assert len(ldaps) == 1
+    assert ldaps[0]["LDAPSStatus"] == "Enabled"
+
+    # Disable LDAPS for AD Connector and verify it is disabled
+    client.disable_ldaps(DirectoryId=directory_id, Type="Client")
+    ldaps = client.describe_ldaps_settings(DirectoryId=directory_id)[
+        "LDAPSSettingsInfo"
+    ]
+    assert len(ldaps) == 1
+    assert ldaps[0]["LDAPSStatus"] == "Disabled"
diff --git a/tests/test_ecs/test_ecs_boto3.py b/tests/test_ecs/test_ecs_boto3.py
index 5f6baba84..465178f1b 100644
--- a/tests/test_ecs/test_ecs_boto3.py
+++ b/tests/test_ecs/test_ecs_boto3.py
@@ -648,52 +648,53 @@ def test_deregister_task_definition_2():
 @mock_aws
 def test_create_service():
     client = boto3.client("ecs", region_name=ECS_REGION)
-    client.create_cluster(clusterName="test_ecs_cluster")
-    client.register_task_definition(
-        family="test_ecs_task",
-        containerDefinitions=[
-            {
-                "name": "hello_world",
-                "image": "docker/hello-world:latest",
-                "cpu": 1024,
-                "memory": 400,
-                "essential": True,
-                "environment": [
-                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
-                ],
-                "logConfiguration": {"logDriver": "json-file"},
-            }
-        ],
-    )
+    ec2 = boto3.resource("ec2", region_name=ECS_REGION)
+
+    # ECS setup
+    subnet, sg = setup_ecs(client, ec2)
     response = client.create_service(
         cluster="test_ecs_cluster",
         serviceName="test_ecs_service",
         taskDefinition="test_ecs_task",
         desiredCount=2,
         platformVersion="2",
+        networkConfiguration={
+            "awsvpcConfiguration": {
+                "subnets": [subnet.id],
+                "securityGroups": [sg.id],
+            }
+        },
     )
     assert (
         response["service"]["clusterArn"]
         == f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:cluster/test_ecs_cluster"
     )
-    assert response["service"]["desiredCount"] == 2
-    assert len(response["service"]["events"]) == 0
-    assert len(response["service"]["loadBalancers"]) == 0
-    assert response["service"]["pendingCount"] == 2
-    assert response["service"]["runningCount"] == 0
+    svc = response["service"]
+    assert svc["desiredCount"] == 2
+    assert len(svc["events"]) == 0
+    assert len(svc["loadBalancers"]) == 0
+    assert svc["pendingCount"] == 2
+    assert svc["runningCount"] == 0
     assert (
-        response["service"]["serviceArn"]
+        svc["serviceArn"]
         == f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:service/test_ecs_cluster/test_ecs_service"
     )
-    assert response["service"]["serviceName"] == "test_ecs_service"
-    assert response["service"]["status"] == "ACTIVE"
+    assert svc["serviceName"] == "test_ecs_service"
+    assert svc["status"] == "ACTIVE"
     assert (
-        response["service"]["taskDefinition"]
+        svc["taskDefinition"]
         == f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:task-definition/test_ecs_task:1"
     )
-    assert response["service"]["schedulingStrategy"] == "REPLICA"
-    assert response["service"]["launchType"] == "EC2"
-    assert response["service"]["platformVersion"] == "2"
+    assert svc["schedulingStrategy"] == "REPLICA"
+    assert svc["launchType"] == "EC2"
+    assert svc["platformVersion"] == "2"
+
+    assert "networkConfiguration" in svc
+    nc = svc["networkConfiguration"]["awsvpcConfiguration"]
+    assert len(["subnets"]) == 1
+    assert len(["securityGroups"]) == 1
+    assert nc["subnets"][0] == subnet.id
+    assert nc["securityGroups"][0] == sg.id
 
 
 @mock_aws
@@ -3844,3 +3845,54 @@ def setup_ecs_cluster_with_ec2_instance(client, test_cluster_name):
             }
         ],
     )
+
+
+c1 = {
+    "awsvpcConfiguration": {
+        "subnets": ["fake-subnet"],
+    }
+}
+e1 = "Error retrieving subnet information for [fake-subnet]: The subnet ID 'fake-subnet' does not exist (ErrorCode: InvalidSubnetID.NotFound)"
+c2 = {
+    "awsvpcConfiguration": {
+        "subnets": "[subnet.id]",
+        "securityGroups": ["sg-123456"],
+    }
+}
+e2 = "Error retrieving security group information for [sg-123456]: The security group 'sg-123456' does not exist (ErrorCode: InvalidGroup.NotFound)"
+c3 = {
+    "awsvpcConfiguration": {
+        "subnets": [],
+    }
+}
+e3 = "subnets can not be empty."
+
+
+@pytest.mark.parametrize(
+    "net_config,error_message",
+    [(c1, e1), (c2, e2), (c3, e3)],
+)
+@mock_aws
+def test_service_exceptions(net_config, error_message):
+    client = boto3.client("ecs", region_name=ECS_REGION)
+    ec2 = boto3.resource("ec2", region_name=ECS_REGION)
+
+    # ECS setup
+    subnet, sg = setup_ecs(client, ec2)
+    if net_config["awsvpcConfiguration"]["subnets"] == "[subnet.id]":
+        # sub real subnet for evaluation
+        net_config["awsvpcConfiguration"]["subnets"] = [subnet.id]
+
+    with pytest.raises(ClientError) as exc:
+        client.create_service(
+            cluster="test_ecs_cluster",
+            serviceName="test_ecs_service",
+            taskDefinition="test_ecs_task",
+            desiredCount=2,
+            platformVersion="2",
+            networkConfiguration=net_config,
+        )
+    ex = exc.value
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+    assert ex.response["Error"]["Code"] == "InvalidParameterException"
+    assert ex.response["Error"]["Message"] == error_message
diff --git a/tests/test_iotdata/test_iotdata.py b/tests/test_iotdata/test_iotdata.py
index b2185859e..234c2cc36 100644
--- a/tests/test_iotdata/test_iotdata.py
+++ b/tests/test_iotdata/test_iotdata.py
@@ -223,6 +223,61 @@ def test_delete_field_from_device_shadow(name: Optional[str] = None) -> None:
             {"reported": {"online": False}},
             {"reported": {"online": False}},
         ),
+        # Remove from list
+        (
+            {"reported": {"list": ["value_1", "value_2"]}},
+            {"reported": {"list": ["value_1", "value_2"]}},
+            {"desired": {"list": ["value_1"]}},
+            {
+                "desired": {"list": ["value_1"]},
+                "reported": {"list": ["value_1", "value_2"]},
+                "delta": {"list": ["value_1"]},
+            },
+        ),
+        # Remove And Update from list
+        (
+            {"reported": {"list": ["value_1", "value_2"]}},
+            {"reported": {"list": ["value_1", "value_2"]}},
+            {"desired": {"list": ["value_1", "value_3"]}},
+            {
+                "desired": {"list": ["value_1", "value_3"]},
+                "reported": {"list": ["value_1", "value_2"]},
+                "delta": {"list": ["value_1", "value_3"]},
+            },
+        ),
+        # Remove from nested lists
+        (
+            {"reported": {"list": [["value_1"], ["value_2"]]}},
+            {"reported": {"list": [["value_1"], ["value_2"]]}},
+            {"desired": {"list": [["value_1"]]}},
+            {
+                "desired": {"list": [["value_1"]]},
+                "reported": {"list": [["value_1"], ["value_2"]]},
+                "delta": {"list": [["value_1"]]},
+            },
+        ),
+        # Append to nested list
+        (
+            {"reported": {"a": {"b": ["d"]}}},
+            {"reported": {"a": {"b": ["d"]}}},
+            {"desired": {"a": {"b": ["c", "d"]}}},
+            {
+                "delta": {"a": {"b": ["c", "d"]}},
+                "desired": {"a": {"b": ["c", "d"]}},
+                "reported": {"a": {"b": ["d"]}},
+            },
+        ),
+        # Update nested dict
+        (
+            {"reported": {"a": {"b": {"c": "d", "e": "f"}}}},
+            {"reported": {"a": {"b": {"c": "d", "e": "f"}}}},
+            {"desired": {"a": {"b": {"c": "d2"}}}},
+            {
+                "delta": {"a": {"b": {"c": "d2"}}},
+                "desired": {"a": {"b": {"c": "d2"}}},
+                "reported": {"a": {"b": {"c": "d", "e": "f"}}},
+            },
+        ),
     ],
 )
 def test_delta_calculation(
diff --git a/tests/test_medialive/test_medialive.py b/tests/test_medialive/test_medialive.py
index e3c0568fa..a49cb9a21 100644
--- a/tests/test_medialive/test_medialive.py
+++ b/tests/test_medialive/test_medialive.py
@@ -126,24 +126,41 @@ def test_create_channel_succeeds():
 @mock_aws
 def test_list_channels_succeeds():
     client = boto3.client("medialive", region_name=region)
-    channel1_config = _create_channel_config("test channel 1", request_id="request-1")
-    channel2_config = _create_channel_config("test channel 2", request_id="request-2")
-    channel2_config["ChannelClass"] = "SINGLE_PIPELINE"
+    channel_configs = [
+        _create_channel_config(f"test {idx}", request_id=f"req-{idx}")
+        for idx in range(10)
+    ]
+    channel_configs[1]["ChannelClass"] = "SINGLE_PIPELINE"
 
-    client.create_channel(**channel1_config)
-    client.create_channel(**channel2_config)
+    for config in channel_configs:
+        client.create_channel(**config)
 
     response = client.list_channels()
-    assert len(response["Channels"]) == 2
+    assert len(response["Channels"]) == 10
 
-    assert response["Channels"][0]["Name"] == "test channel 1"
+    assert response["Channels"][0]["Name"] == "test 0"
     assert response["Channels"][0]["ChannelClass"] == "STANDARD"
     assert response["Channels"][0]["PipelinesRunningCount"] == 2
 
-    assert response["Channels"][1]["Name"] == "test channel 2"
+    assert response["Channels"][1]["Name"] == "test 1"
     assert response["Channels"][1]["ChannelClass"] == "SINGLE_PIPELINE"
     assert response["Channels"][1]["PipelinesRunningCount"] == 1
 
+    page1 = client.list_channels(MaxResults=2)
+    assert len(page1["Channels"]) == 2
+    channel_names = [c["Name"] for c in page1["Channels"]]
+    assert ["test 0", "test 1"] == sorted(channel_names)
+
+    page2 = client.list_channels(MaxResults=5, NextToken=page1["NextToken"])
+    assert len(page2["Channels"]) == 5
+    channel_names = [c["Name"] for c in page2["Channels"]]
+    assert ["test 2", "test 3", "test 4", "test 5", "test 6"] == sorted(channel_names)
+
+    page3 = client.list_channels(NextToken=page2["NextToken"])
+    assert len(page3["Channels"]) == 3
+    channel_names = [c["Name"] for c in page3["Channels"]]
+    assert ["test 7", "test 8", "test 9"] == sorted(channel_names)
+
 
 @mock_aws
 def test_delete_channel_moves_channel_in_deleted_state():
@@ -272,16 +289,27 @@ def test_describe_input_succeeds():
 @mock_aws
 def test_list_inputs_succeeds():
     client = boto3.client("medialive", region_name=region)
-    input_config1 = _create_input_config("Input One")
-    client.create_input(**input_config1)
-    input_config2 = _create_input_config("Input Two")
-    client.create_input(**input_config2)
+    configs = [_create_input_config(f"Input {idx}") for idx in range(10)]
+    for config in configs:
+        client.create_input(**config)
 
     inputs = client.list_inputs()["Inputs"]
-    assert len(inputs) == 2
-
-    assert inputs[0]["Name"] == "Input One"
-    assert inputs[1]["Name"] == "Input Two"
+    assert len(inputs) == 10
+
+    page1 = client.list_inputs(MaxResults=2)
+    assert len(page1["Inputs"]) == 2
+    names = [i["Name"] for i in page1["Inputs"]]
+    assert sorted(names) == ["Input 0", "Input 1"]
+
+    page2 = client.list_inputs(MaxResults=5, NextToken=page1["NextToken"])
+    assert len(page2["Inputs"]) == 5
+    names = [i["Name"] for i in page2["Inputs"]]
+    assert sorted(names) == ["Input 2", "Input 3", "Input 4", "Input 5", "Input 6"]
+
+    page3 = client.list_inputs(NextToken=page2["NextToken"])
+    assert len(page3["Inputs"]) == 3
+    names = [i["Name"] for i in page3["Inputs"]]
+    assert sorted(names) == ["Input 7", "Input 8", "Input 9"]
 
 
 @mock_aws
diff --git a/tests/test_moto_api/seeder/test_seeder.py b/tests/test_moto_api/seeder/test_seeder.py
index 970c30524..e85ae78b3 100644
--- a/tests/test_moto_api/seeder/test_seeder.py
+++ b/tests/test_moto_api/seeder/test_seeder.py
@@ -41,7 +41,7 @@ class TestDifferentAccountsDoesNotBreakSeeding:
         instances = self.ec2_client.run_instances(MaxCount=1, MinCount=1)["Instances"]
 
         instance_ids = [instance["InstanceId"] for instance in instances]
-        assert instance_ids == ["i-73bd4755d05ad7853"]
+        assert instance_ids == ["i-6e8b5359309cc6273"]
 
     def test_1(self) -> None:
         # Create some data in a different account (111111111111)
diff --git a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
index 759c44569..5e77cafbb 100644
--- a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
+++ b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
@@ -1,4 +1,5 @@
 import json
+import typing
 
 import boto3
 import pytest
@@ -1240,3 +1241,58 @@ def test_get_resources_workspacesweb():
         TagFilters=[{"Key": "TestKey3", "Values": ["TestValue3"]}],
     )
     assert len(resp["ResourceTagMappingList"]) == 1
+
+
+@pytest.mark.parametrize("resource_type", ["secretsmanager", "secretsmanager:secret"])
+@mock_aws
+def test_get_resources_secretsmanager(resource_type):
+    def assert_tagging_works(region_name: str, regional_response_keys: typing.Set[str]):
+        rtapi = boto3.client("resourcegroupstaggingapi", region_name=region_name)
+        resp = rtapi.get_resources(
+            ResourcesPerPage=2, ResourceTypeFilters=[resource_type]
+        )
+        for resource in resp["ResourceTagMappingList"]:
+            regional_response_keys.remove(resource["Tags"][0]["Key"])
+
+        assert len(regional_response_keys) == 2
+
+        resp = rtapi.get_resources(
+            ResourcesPerPage=2,
+            PaginationToken=resp["PaginationToken"],
+            ResourceTypeFilters=[resource_type],
+        )
+        for resource in resp["ResourceTagMappingList"]:
+            regional_response_keys.remove(resource["Tags"][0]["Key"])
+
+        assert len(regional_response_keys) == 0
+
+    # Tests pagination
+    secretsmanager_client = boto3.client("secretsmanager", region_name="eu-central-1")
+
+    # Will end up having key1,key2,key3,key4
+    response_keys = set()
+
+    # Create 4 tagged secrets
+    for i in range(1, 5):
+        i_str = str(i)
+        secretsmanager_client.create_secret(
+            Name="test_secret" + i_str,
+            SecretString="very_secret",
+            AddReplicaRegions=[{"Region": "eu-west-1"}],
+        )
+        secretsmanager_client.tag_resource(
+            SecretId="test_secret" + i_str,
+            Tags=[{"Key": "key" + i_str, "Value": "value" + i_str}],
+        )
+        response_keys.add("key" + i_str)
+
+    # add an untagged secret to cover this case as well
+    secretsmanager_client: secretsmanager_client.create_secret(
+        Name="untagged_secret",
+        SecretString="very_secret",
+        AddReplicaRegions=[{"Region": "eu-west-1"}],
+    )
+
+    # Make sure it works for normal and replicated secrets
+    assert_tagging_works("eu-central-1", set(response_keys))
+    assert_tagging_works("eu-west-1", set(response_keys))
diff --git a/tests/test_secretsmanager/test_list_secrets.py b/tests/test_secretsmanager/test_list_secrets.py
index 1acb8a9d3..37de56a78 100644
--- a/tests/test_secretsmanager/test_list_secrets.py
+++ b/tests/test_secretsmanager/test_list_secrets.py
@@ -285,3 +285,36 @@ def test_filter_with_owning_service():
 
     resp = conn.list_secrets(Filters=[{"Key": "owning-service", "Values": ["n/a"]}])
     assert resp["SecretList"] == []
+
+
+@mock_aws
+def test_with_include_planned_deleted_secrets():
+    conn = boto_client()
+
+    conn.create_secret(Name="foo", SecretString="secret")
+    conn.create_secret(Name="bar", SecretString="secret")
+
+    secrets = conn.list_secrets()
+    assert len(secrets["SecretList"]) == 2
+
+    conn.delete_secret(SecretId="foo")
+
+    # By default list secrets doesn't include deleted secrets
+    secrets = conn.list_secrets()
+    assert len(secrets["SecretList"]) == 1
+    assert secrets["SecretList"][0]["ARN"] is not None
+    assert secrets["SecretList"][0]["Name"] == "bar"
+    assert secrets["SecretList"][0]["SecretVersionsToStages"] is not None
+
+    # list secrets when IncludePlannedDeletion param included
+    secrets = conn.list_secrets(IncludePlannedDeletion=True)
+    assert len(secrets["SecretList"]) == 2
+
+    # list secret with filter and IncludePlannedDeletion params
+    secrets = conn.list_secrets(
+        IncludePlannedDeletion=True, Filters=[{"Key": "name", "Values": ["foo"]}]
+    )
+    assert len(secrets["SecretList"]) == 1
+    assert secrets["SecretList"][0]["ARN"] is not None
+    assert secrets["SecretList"][0]["Name"] == "foo"
+    assert secrets["SecretList"][0]["SecretVersionsToStages"] is not None
diff --git a/tests/test_wafv2/test_helper_functions.py b/tests/test_wafv2/test_helper_functions.py
index 2ab8fbd22..51d30f7df 100644
--- a/tests/test_wafv2/test_helper_functions.py
+++ b/tests/test_wafv2/test_helper_functions.py
@@ -9,7 +9,3 @@ def CREATE_WEB_ACL_BODY(name: str, scope: str) -> dict:
             "MetricName": "idk",
         },
     }
-
-
-def LIST_WEB_ACL_BODY(scope: str) -> dict:
-    return {"Scope": scope}
diff --git a/tests/test_wafv2/test_server.py b/tests/test_wafv2/test_server.py
index 11f1efcc6..4a805b4bb 100644
--- a/tests/test_wafv2/test_server.py
+++ b/tests/test_wafv2/test_server.py
@@ -6,7 +6,7 @@ import moto.server as server
 from moto import mock_aws, settings
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
 
-from .test_helper_functions import CREATE_WEB_ACL_BODY, LIST_WEB_ACL_BODY
+from .test_helper_functions import CREATE_WEB_ACL_BODY
 
 CREATE_WEB_ACL_HEADERS = {
     "X-Amz-Target": "AWSWAF_20190729.CreateWebACL",
@@ -89,7 +89,7 @@ def test_list_web_ac_ls():
         json=CREATE_WEB_ACL_BODY("Sarah", "CLOUDFRONT"),
     )
     res = test_client.post(
-        "/", headers=LIST_WEB_ACL_HEADERS, json=LIST_WEB_ACL_BODY("REGIONAL")
+        "/", headers=LIST_WEB_ACL_HEADERS, json={"Scope": "REGIONAL"}
     )
     assert res.status_code == 200
 
@@ -99,7 +99,7 @@ def test_list_web_ac_ls():
     assert web_acls[1]["Name"] == "JohnSon"
 
     res = test_client.post(
-        "/", headers=LIST_WEB_ACL_HEADERS, json=LIST_WEB_ACL_BODY("CLOUDFRONT")
+        "/", headers=LIST_WEB_ACL_HEADERS, json={"Scope": "CLOUDFRONT"}
     )
     assert res.status_code == 200
     web_acls = res.json["WebACLs"]
diff --git a/tests/test_wafv2/test_wafv2.py b/tests/test_wafv2/test_wafv2.py
index 5c8af466f..a4d0bfeb8 100644
--- a/tests/test_wafv2/test_wafv2.py
+++ b/tests/test_wafv2/test_wafv2.py
@@ -5,7 +5,7 @@ from botocore.exceptions import ClientError
 from moto import mock_aws
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
 
-from .test_helper_functions import CREATE_WEB_ACL_BODY, LIST_WEB_ACL_BODY
+from .test_helper_functions import CREATE_WEB_ACL_BODY
 
 
 @mock_aws
@@ -163,18 +163,30 @@ def test_list_web_acl():
     conn = boto3.client("wafv2", region_name="us-east-1")
     conn.create_web_acl(**CREATE_WEB_ACL_BODY("Daphne", "REGIONAL"))
     conn.create_web_acl(**CREATE_WEB_ACL_BODY("Penelope", "CLOUDFRONT"))
-    conn.create_web_acl(**CREATE_WEB_ACL_BODY("Sarah", "REGIONAL"))
-    res = conn.list_web_acls(**LIST_WEB_ACL_BODY("REGIONAL"))
+    for idx in range(5):
+        conn.create_web_acl(**CREATE_WEB_ACL_BODY(f"Sarah {idx}", "REGIONAL"))
+    res = conn.list_web_acls(Scope="REGIONAL")
     web_acls = res["WebACLs"]
-    assert len(web_acls) == 2
+    assert len(web_acls) == 6
     assert web_acls[0]["Name"] == "Daphne"
-    assert web_acls[1]["Name"] == "Sarah"
+    assert web_acls[1]["Name"] == "Sarah 0"
 
-    res = conn.list_web_acls(**LIST_WEB_ACL_BODY("CLOUDFRONT"))
+    res = conn.list_web_acls(Scope="CLOUDFRONT")
     web_acls = res["WebACLs"]
     assert len(web_acls) == 1
     assert web_acls[0]["Name"] == "Penelope"
 
+    page1 = conn.list_web_acls(Scope="REGIONAL", Limit=2)
+    assert len(page1["WebACLs"]) == 2
+
+    page2 = conn.list_web_acls(
+        Scope="REGIONAL", Limit=1, NextMarker=page1["NextMarker"]
+    )
+    assert len(page2["WebACLs"]) == 1
+
+    page3 = conn.list_web_acls(Scope="REGIONAL", NextMarker=page2["NextMarker"])
+    assert len(page3["WebACLs"]) == 3
+
 
 @mock_aws
 def test_delete_web_acl():
@@ -191,7 +203,7 @@ def test_delete_web_acl():
         Name="Daphne", Id=wacl["Id"], Scope="REGIONAL", LockToken=wacl["LockToken"]
     )
 
-    res = conn.list_web_acls(**LIST_WEB_ACL_BODY("REGIONAL"))
+    res = conn.list_web_acls(Scope="REGIONAL")
     assert len(res["WebACLs"]) == 0
 
     with pytest.raises(ClientError) as exc:
@@ -347,7 +359,6 @@ def test_ip_set_crud():
             for key in ["ARN", "Description", "Id", "LockToken", "Name"]
         ]
     )
-    assert "NextMarker" in list_response
 
     client.delete_ip_set(
         Name=summary["Name"],
@@ -365,55 +376,29 @@ def test_ip_set_crud():
 
 
 @mock_aws
-def test_logging_configuration_crud():
-    wafv2_client = boto3.client("wafv2", region_name="us-east-1")
-    create_web_acl_response = wafv2_client.create_web_acl(
-        Name="TestWebACL",
-        Scope="REGIONAL",
-        DefaultAction={"Allow": {}},
-        VisibilityConfig={
-            "SampledRequestsEnabled": True,
-            "CloudWatchMetricsEnabled": True,
-            "MetricName": "TestWebACLMetric",
-        },
-        Rules=[],
-    )
-    web_acl_arn = create_web_acl_response["Summary"]["ARN"]
-
-    # Create log groups
-    logs_client = boto3.client("logs", region_name="us-east-1")
-    logs_client.create_log_group(logGroupName="aws-waf-logs-test")
-    log_group = logs_client.describe_log_groups(logGroupNamePrefix="aws-waf-logs-test")[
-        "logGroups"
-    ][0]
-
-    create_response = wafv2_client.put_logging_configuration(
-        LoggingConfiguration={
-            "ResourceArn": web_acl_arn,
-            "LogDestinationConfigs": [log_group["arn"]],
-        }
-    )
+def test_list_ip_sets_pagination():
+    client = boto3.client("wafv2", region_name="us-east-1")
 
-    assert "LoggingConfiguration" in create_response
-    logging_configuration = create_response["LoggingConfiguration"]
-    assert logging_configuration["ResourceArn"]
+    for idx in range(10):
+        client.create_ip_set(
+            Name=f"test-ip-set-{idx}",
+            Scope="CLOUDFRONT",
+            Description="Test IP set",
+            IPAddressVersion="IPV4",
+            Addresses=["192.168.0.1/32", "10.0.0.0/8"],
+            Tags=[{"Key": "Environment", "Value": "Test"}],
+        )
 
-    get_response = wafv2_client.get_logging_configuration(
-        ResourceArn=logging_configuration["ResourceArn"]
-    )
-    assert "LoggingConfiguration" in get_response
+    list_all = client.list_ip_sets(Scope="CLOUDFRONT")["IPSets"]
+    assert len(list_all) == 10
 
-    list_response = wafv2_client.list_logging_configurations(
-        Scope="REGIONAL",
-    )
-    assert len(list_response["LoggingConfigurations"]) > 0
+    page1 = client.list_ip_sets(Scope="CLOUDFRONT", Limit=2)
+    assert len(page1["IPSets"]) == 2
 
-    wafv2_client.delete_logging_configuration(
-        ResourceArn=logging_configuration["ResourceArn"],
+    page2 = client.list_ip_sets(
+        Scope="CLOUDFRONT", Limit=5, NextMarker=page1["NextMarker"]
     )
+    assert len(page2["IPSets"]) == 5
 
-    with pytest.raises(ClientError) as e:
-        wafv2_client.get_logging_configuration(
-            ResourceArn=logging_configuration["ResourceArn"]
-        )
-    assert e.value.response["Error"]["Code"] == "WAFNonexistentItemException"
+    page3 = client.list_ip_sets(Scope="CLOUDFRONT", NextMarker=page2["NextMarker"])
+    assert len(page3["IPSets"]) == 3
diff --git a/tests/test_wafv2/test_wafv2_logging_configuration.py b/tests/test_wafv2/test_wafv2_logging_configuration.py
new file mode 100644
index 000000000..7f584c954
--- /dev/null
+++ b/tests/test_wafv2/test_wafv2_logging_configuration.py
@@ -0,0 +1,95 @@
+import boto3
+import pytest
+from botocore.exceptions import ClientError
+
+from moto import mock_aws
+
+
+@mock_aws
+def test_logging_configuration_crud():
+    wafv2_client = boto3.client("wafv2", region_name="us-east-1")
+    create_web_acl_response = wafv2_client.create_web_acl(
+        Name="TestWebACL",
+        Scope="REGIONAL",
+        DefaultAction={"Allow": {}},
+        VisibilityConfig={
+            "SampledRequestsEnabled": True,
+            "CloudWatchMetricsEnabled": True,
+            "MetricName": "TestWebACLMetric",
+        },
+        Rules=[],
+    )
+    web_acl_arn = create_web_acl_response["Summary"]["ARN"]
+
+    # Create log groups
+    logs_client = boto3.client("logs", region_name="us-east-1")
+    logs_client.create_log_group(logGroupName="aws-waf-logs-test")
+    log_group = logs_client.describe_log_groups(logGroupNamePrefix="aws-waf-logs-test")[
+        "logGroups"
+    ][0]
+
+    create_response = wafv2_client.put_logging_configuration(
+        LoggingConfiguration={
+            "ResourceArn": web_acl_arn,
+            "LogDestinationConfigs": [log_group["arn"]],
+        }
+    )
+
+    assert "LoggingConfiguration" in create_response
+    logging_configuration = create_response["LoggingConfiguration"]
+    assert logging_configuration["ResourceArn"]
+
+    get_response = wafv2_client.get_logging_configuration(
+        ResourceArn=logging_configuration["ResourceArn"]
+    )
+    assert "LoggingConfiguration" in get_response
+
+    wafv2_client.delete_logging_configuration(
+        ResourceArn=logging_configuration["ResourceArn"],
+    )
+
+    with pytest.raises(ClientError) as e:
+        wafv2_client.get_logging_configuration(
+            ResourceArn=logging_configuration["ResourceArn"]
+        )
+    assert e.value.response["Error"]["Code"] == "WAFNonexistentItemException"
+
+
+@mock_aws
+def test_list_logging_config_pagination():
+    wafv2_client = boto3.client("wafv2", region_name="us-east-1")
+
+    for idx in range(10):
+        web_acl_arn = wafv2_client.create_web_acl(
+            Name=f"TestWebACL{idx}",
+            Scope="REGIONAL",
+            DefaultAction={"Allow": {}},
+            VisibilityConfig={
+                "SampledRequestsEnabled": True,
+                "CloudWatchMetricsEnabled": True,
+                "MetricName": "TestWebACLMetric",
+            },
+            Rules=[],
+        )["Summary"]["ARN"]
+        wafv2_client.put_logging_configuration(
+            LoggingConfiguration={
+                "ResourceArn": web_acl_arn,
+                "LogDestinationConfigs": ["arn:aws:us-east-1:logs:arn"],
+            }
+        )
+
+    list_all = wafv2_client.list_logging_configurations(Scope="REGIONAL")
+    assert len(list_all["LoggingConfigurations"]) == 10
+
+    page1 = wafv2_client.list_logging_configurations(Scope="REGIONAL", Limit=2)
+    assert len(page1["LoggingConfigurations"]) == 2
+
+    page2 = wafv2_client.list_logging_configurations(
+        Scope="REGIONAL", Limit=3, NextMarker=page1["NextMarker"]
+    )
+    assert len(page2["LoggingConfigurations"]) == 3
+
+    page3 = wafv2_client.list_logging_configurations(
+        Scope="REGIONAL", NextMarker=page2["NextMarker"]
+    )
+    assert len(page3["LoggingConfigurations"]) == 5
diff --git a/tests/test_wafv2/test_wafv2_rules.py b/tests/test_wafv2/test_wafv2_rules.py
index 1d4f7fba2..910c4aa1c 100644
--- a/tests/test_wafv2/test_wafv2_rules.py
+++ b/tests/test_wafv2/test_wafv2_rules.py
@@ -334,25 +334,41 @@ def test_list_rule_groups():
             "MetricName": "test-group-1",
         },
     )["Summary"]
-    cf_group = client.create_rule_group(
-        Capacity=100,
-        Name="test-group-2",
-        Scope="CLOUDFRONT",
-        VisibilityConfig={
-            "SampledRequestsEnabled": True,
-            "CloudWatchMetricsEnabled": True,
-            "MetricName": "test-group-2",
-        },
-    )["Summary"]
+    for idx in range(2, 10):
+        client.create_rule_group(
+            Capacity=100,
+            Name=f"test-group-{idx}",
+            Scope="CLOUDFRONT",
+            VisibilityConfig={
+                "SampledRequestsEnabled": True,
+                "CloudWatchMetricsEnabled": True,
+                "MetricName": "test-group-2",
+            },
+        )["Summary"]
 
     reg_groups = client.list_rule_groups(Scope="REGIONAL")["RuleGroups"]
-    cf_groups = client.list_rule_groups(Scope="CLOUDFRONT")["RuleGroups"]
-    assert len(reg_groups) == len(cf_groups) == 1
+    assert len(reg_groups) == 1
     regional = reg_groups[0]
     assert regional["ARN"] == regional_group["ARN"]
-    assert cf_groups[0]["ARN"] == cf_group["ARN"]
     assert regional["Name"] == "test-group-1"
     assert "Scope" not in regional
     assert is_valid_uuid(regional["LockToken"])
     assert is_valid_uuid(regional["Id"]) and regional["Id"] == regional_group["Id"]
     assert "VisibilityConfig" not in regional
+
+    cf_groups = client.list_rule_groups(Scope="CLOUDFRONT")["RuleGroups"]
+    assert len(cf_groups) == 8
+    assert cf_groups[0]["ARN"].startswith(
+        f"arn:aws:wafv2:global:{ACCOUNT_ID}:global/rulegroup/test-group-2/"
+    )
+
+    page1 = client.list_rule_groups(Scope="CLOUDFRONT", Limit=2)
+    assert len(page1["RuleGroups"]) == 2
+
+    page2 = client.list_rule_groups(
+        Scope="CLOUDFRONT", Limit=1, NextMarker=page1["NextMarker"]
+    )
+    assert len(page2["RuleGroups"]) == 1
+
+    page3 = client.list_rule_groups(Scope="CLOUDFRONT", NextMarker=page2["NextMarker"])
+    assert len(page3["RuleGroups"]) == 5
diff --git a/tests/test_wafv2/test_wafv2_tags.py b/tests/test_wafv2/test_wafv2_tags.py
index 50ac16e69..57c6b4f19 100644
--- a/tests/test_wafv2/test_wafv2_tags.py
+++ b/tests/test_wafv2/test_wafv2_tags.py
@@ -23,7 +23,7 @@ def test_list_tags_for_resource():
         Name="test",
         Scope="CLOUDFRONT",
         DefaultAction={"Allow": {}},
-        Tags=[{"Key": "k1", "Value": "v1"}],
+        Tags=[{"Key": f"k{idx}", "Value": "v1"} for idx in range(10)],
         VisibilityConfig={
             "SampledRequestsEnabled": False,
             "CloudWatchMetricsEnabled": False,
@@ -32,7 +32,18 @@ def test_list_tags_for_resource():
     )["Summary"]["ARN"]
 
     tag_info = conn.list_tags_for_resource(ResourceARN=arn)["TagInfoForResource"]
-    assert tag_info["TagList"] == [{"Key": "k1", "Value": "v1"}]
+    assert len(tag_info["TagList"]) == 10
+
+    page1 = conn.list_tags_for_resource(ResourceARN=arn, Limit=2)
+    assert len(page1["TagInfoForResource"]["TagList"]) == 2
+
+    page2 = conn.list_tags_for_resource(
+        ResourceARN=arn, Limit=3, NextMarker=page1["NextMarker"]
+    )
+    assert len(page2["TagInfoForResource"]["TagList"]) == 3
+
+    page3 = conn.list_tags_for_resource(ResourceARN=arn, NextMarker=page2["NextMarker"])
+    assert len(page3["TagInfoForResource"]["TagList"]) == 5
 
 
 @mock_aws