Repository: getmoto/moto
Pull request #9067 (2025-07-17 01:49:21)

[RDS] Adds support for modify db cluster  params.
To reviewers and maintainers @bblommers and @bpandola. This PR adds support for 
- [x] [modify_db_cluster_parameter_group](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds/client/modify_db_cluster_parameter_group.html). It also fixes the  [describe_db_cluster_parameters](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds/client/describe_db_cluster_parameters.html) making tests easier. 

### Cloud Engineers Humor  (Dev's bad break-up with RDS )

```mermaid
flowchart TD
    subgraph Scene1 ["Scene 1"]
        A1["**👨‍💻 Dev**: Hey RDS, we need to talk..."]
    end

    subgraph Scene2 ["Scene 2"]
        A2["**🗄️ RDS**: Is it about the read replicas again?"]
        A3["**👨‍💻 Dev**: No, it's... something deeper."]
    end

    subgraph Scene3 ["Scene 3"]
        A4["**👨‍💻 Dev**: You have too many connections... and you never commit."]
    end

    subgraph Scene4 ["Scene 4"]
        A5["**🗄️ RDS**: That's not true! I committed last night at 03:00 UTC."]
        A6["**👨‍💻 Dev**: To a snapshot, not this relationship."]
    end

    %% Connect scenes
    A1 --> A2 --> A3 --> A4 --> A5 --> A6
```

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/getmoto/moto/pull/9067?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) Report
Attention: Patch coverage is `96.00000%` with `1 line` in your changes missing coverage. Please review.
> Project coverage is 92.88%. Comparing base [(`a9ecdd1`)](https://app.codecov.io/gh/getmoto/moto/commit/a9ecdd1f7646f4efc21226f9b48e36a601210247?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) to head [(`4cbd08a`)](https://app.codecov.io/gh/getmoto/moto/commit/4cbd08abf8e5dd45c499bdb7635a7de289e137ab?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).

| [Files with missing lines](https://app.codecov.io/gh/getmoto/moto/pull/9067?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | Patch % | Lines |
|---|---|---|
| [moto/rds/models.py](https://app.codecov.io/gh/getmoto/moto/pull/9067?src=pr&el=tree&filepath=moto%2Frds%2Fmodels.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto#diff-bW90by9yZHMvbW9kZWxzLnB5) | 93.75% | [1 Missing :warning: ](https://app.codecov.io/gh/getmoto/moto/pull/9067?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) |

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #9067   +/-   ##
=======================================
  Coverage   92.88%   92.88%           
=======================================
  Files        1287     1287           
  Lines      113742   113763   +21     
=======================================
+ Hits       105645   105665   +20     
- Misses       8097     8098    +1     
```

| [Flag](https://app.codecov.io/gh/getmoto/moto/pull/9067/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | Coverage Δ | |
|---|---|---|
| [servertests](https://app.codecov.io/gh/getmoto/moto/pull/9067/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `28.59% <16.00%> (-0.01%)` | :arrow_down: |
| [unittests](https://app.codecov.io/gh/getmoto/moto/pull/9067/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `92.85% <96.00%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/getmoto/moto/pull/9067?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>

Patch:
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..92b58ab2d
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,7 @@
+# Ignore everything by default
+*
+
+!README.md
+!moto/
+!setup.cfg
+!setup.py
diff --git a/Dockerfile b/Dockerfile
index 6b545ef72..d0d3feb50 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,16 +1,14 @@
-FROM python:3.11-slim
+FROM python:3.13-alpine
 
 ADD . /moto/
 ENV PYTHONUNBUFFERED 1
 
 WORKDIR /moto/
 RUN  pip3 --no-cache-dir install --upgrade pip setuptools && \
-     pip3 --no-cache-dir install ".[server]"
+     pip3 --no-cache-dir install --editable ".[server]"
 
 # Install cURL
-RUN  apt-get update && \
-     apt-get install -y curl && \
-     rm -rf /var/lib/apt/lists/*
+RUN  apk add --no-cache curl
 
 ENTRYPOINT ["/usr/local/bin/moto_server", "-H", "0.0.0.0"]
 
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 04ec8a962..b1d21802a 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -6269,7 +6269,7 @@
 - [X] list_tags_for_resource
 - [X] modify_db_cluster
 - [ ] modify_db_cluster_endpoint
-- [ ] modify_db_cluster_parameter_group
+- [X] modify_db_cluster_parameter_group
 - [X] modify_db_cluster_snapshot_attribute
 - [X] modify_db_instance
 - [X] modify_db_parameter_group
diff --git a/moto/core/serialize.py b/moto/core/serialize.py
index 5a5c58706..e10abb07a 100644
--- a/moto/core/serialize.py
+++ b/moto/core/serialize.py
@@ -56,6 +56,7 @@ import abc
 import base64
 import calendar
 import json
+import warnings
 from dataclasses import dataclass
 from datetime import datetime
 from typing import (
@@ -67,6 +68,7 @@ from typing import (
     Optional,
     TypedDict,
     Union,
+    cast,
 )
 
 import xmltodict
@@ -75,7 +77,6 @@ from botocore.compat import formatdate
 from botocore.model import (
     ListShape,
     MapShape,
-    NoShapeFoundError,
     OperationModel,
     ServiceModel,
     Shape,
@@ -87,6 +88,18 @@ from moto.core.utils import MISSING, get_value
 
 Serialized = MutableMapping[str, Any]
 
+# These are common error codes that are *not* included in the service definitions.
+# For example:
+# https://docs.aws.amazon.com/emr/latest/APIReference/CommonErrors.html
+# https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/CommonErrors.html
+# TODO: Augment the service definitions with shape models for these errors.
+COMMON_ERROR_CODES = [
+    "InvalidParameterCombination",
+    "InvalidParameterValue",
+    "ValidationError",
+    "ValidationException",
+]
+
 
 class ResponseDict(TypedDict):
     body: str
@@ -100,6 +113,8 @@ class SerializationContext:
 
 
 class ErrorShape(StructureShape):
+    _shape_model: dict[str, Any]
+
     # Overriding super class property to keep mypy happy...
     @property
     def error_code(self) -> str:
@@ -259,6 +274,7 @@ class ResponseSerializer(ShapeHelpersMixin):
         value_picker: Any = None,
     ) -> None:
         self.operation_model = operation_model
+        self.service_model = operation_model.service_model
         self.context = context or SerializationContext()
         self.pretty_print = pretty_print
         if value_picker is None:
@@ -355,35 +371,32 @@ class ResponseSerializer(ShapeHelpersMixin):
         )
         return self._value_picker(context)
 
-    @staticmethod
-    def _get_error_shape_name(error: Exception) -> str:
-        shape_name = getattr(error, "code", error.__class__.__name__)
-        return shape_name
-
     def _get_error_shape(self, error: Exception) -> ErrorShape:
-        shape_name = self._get_error_shape_name(error)
-        try:
-            # TODO: there is also an errors array in the operation model,
-            # but I think it only includes the possible errors for that
-            # operation.  Maybe we try that first, then try all shapes?
-            shape = self.operation_model.service_model.shape_for(shape_name)
-            # We convert to ErrorShape to keep mypy happy...
-            shape = ErrorShape(
-                shape_name,
-                shape._shape_model,  # type: ignore[attr-defined]
-                shape._shape_resolver,  # type: ignore[attr-defined]
-            )
-        except NoShapeFoundError:
+        error_code = getattr(error, "code", MISSING)
+        error_name = error.__class__.__name__
+        error_shapes = cast(list[ErrorShape], self.service_model.error_shapes)
+        for error_shape in error_shapes:
+            if error_shape.error_code == error_code:
+                break
+            if error_shape.name in [error_code, error_name]:
+                break
+        else:
+            error_shape = None
+        if error_shape is None:
+            service_id = self.service_model.metadata.get("serviceId")
+            if service_id and error_code not in COMMON_ERROR_CODES:
+                warning = f"{service_id} service model does not contain an error shape that matches code {error_code} from Exception({error_name})"
+                warnings.warn(warning)
             generic_error_model = {
                 "exception": True,
                 "type": "structure",
                 "members": {},
                 "error": {
-                    "code": shape_name,
+                    "code": error_code,
                 },
             }
-            shape = ErrorShape(shape_name, generic_error_model)
-        return shape
+            error_shape = ErrorShape(error_name, generic_error_model)
+        return error_shape
 
     #
     # Default serializers for the various model Shape types.
@@ -649,8 +662,9 @@ class BaseXMLSerializer(ResponseSerializer):
         error: Exception,
         shape: ErrorShape,
     ) -> None:
-        sender_fault = shape.metadata.get("error", {}).get("senderFault", True)
-        serialized["Type"] = "Sender" if sender_fault else "Receiver"
+        at_fault = shape._shape_model.get("fault", False)
+        sender_fault = shape.metadata.get("error", {}).get("senderFault", False)
+        serialized["Type"] = "Receiver" if (at_fault and not sender_fault) else "Sender"
         serialized["Code"] = shape.error_code
         message = getattr(error, "message", None)
         if message is not None:
diff --git a/moto/elb/exceptions.py b/moto/elb/exceptions.py
index 154fc348d..67a0e2526 100644
--- a/moto/elb/exceptions.py
+++ b/moto/elb/exceptions.py
@@ -1,13 +1,10 @@
 from typing import Any
 
-from moto.core.exceptions import RESTError
+from moto.core.exceptions import ServiceException
 
 
-class ELBClientError(RESTError):
-    code = 400
-
-    def __init__(self, error_type: str, message: str):
-        super().__init__(error_type, message, template="wrapped_single_error")
+class ELBClientError(ServiceException):
+    pass
 
 
 class DuplicateTagKeysError(ELBClientError):
diff --git a/moto/elb/models.py b/moto/elb/models.py
index 921ab4cb4..4266e1be4 100644
--- a/moto/elb/models.py
+++ b/moto/elb/models.py
@@ -62,6 +62,10 @@ class FakeListener(BaseModel):
         self.ssl_certificate_id = ssl_certificate_id
         self.policy_names: List[str] = []
 
+    @property
+    def instance_protocol(self) -> str:
+        return self.protocol
+
     def __repr__(self) -> str:
         return f"FakeListener(lbp: {self.load_balancer_port}, inp: {self.instance_port}, pro: {self.protocol}, cid: {self.ssl_certificate_id}, policies: {self.policy_names})"
 
@@ -75,9 +79,10 @@ class FakeBackend(BaseModel):
         return f"FakeBackend(inp: {self.instance_port}, policies: {self.policy_names})"
 
 
-class FakeLoadBalancer(CloudFormationModel):
+class LoadBalancer(CloudFormationModel):
     def __init__(
         self,
+        account_id: str,
         name: str,
         zones: List[str],
         ports: List[Dict[str, Any]],
@@ -86,16 +91,17 @@ class FakeLoadBalancer(CloudFormationModel):
         subnets: Optional[List[str]],
         security_groups: Optional[List[str]],
     ):
+        self.account_id = account_id
         self.name = name
         self.health_check: Optional[FakeHealthCheck] = None
         self.instance_sparse_ids: List[str] = []
         self.instance_autoscaling_ids: List[str] = []
-        self.zones = zones
+        self.availability_zones = zones
         self.listeners = []
         self.backends = []
         self.created_time = datetime.datetime.now(datetime.timezone.utc)
         self.scheme = scheme or "internet-facing"
-        self.attributes = FakeLoadBalancer.get_default_attributes()
+        self.attributes = LoadBalancer.get_default_attributes()
         self.policies: List[Policy] = []
         self.security_groups = security_groups or []
         self.subnets = subnets or []
@@ -123,6 +129,33 @@ class FakeLoadBalancer(CloudFormationModel):
             )
             self.backends.append(backend)
 
+    @property
+    def source_security_group(self) -> dict[str, str]:
+        return {"OwnerAlias": self.account_id, "GroupName": "default"}
+
+    @property
+    def instances(self) -> list[dict[str, str]]:
+        return [{"InstanceId": id_} for id_ in self.instance_ids]
+
+    @property
+    def backend_server_descriptions(self) -> list[FakeBackend]:
+        return [backend for backend in self.backends if backend.policy_names]
+
+    @property
+    def canonical_hosted_zone_name(self) -> str:
+        return self.dns_name
+
+    @property
+    def listener_descriptions(self) -> list[dict[str, Any]]:
+        descriptions = [
+            {
+                "PolicyNames": listener.policy_names,
+                "Listener": listener,
+            }
+            for listener in self.listeners
+        ]
+        return descriptions
+
     @property
     def instance_ids(self) -> List[str]:
         """Return all the instances attached to the ELB"""
@@ -145,7 +178,7 @@ class FakeLoadBalancer(CloudFormationModel):
         account_id: str,
         region_name: str,
         **kwargs: Any,
-    ) -> "FakeLoadBalancer":
+    ) -> "LoadBalancer":
         properties = cloudformation_json["Properties"]
 
         elb_backend = elb_backends[account_id][region_name]
@@ -203,7 +236,7 @@ class FakeLoadBalancer(CloudFormationModel):
         cloudformation_json: Any,
         account_id: str,
         region_name: str,
-    ) -> "FakeLoadBalancer":
+    ) -> "LoadBalancer":
         cls.delete_from_cloudformation_json(
             original_resource.name, cloudformation_json, account_id, region_name
         )
@@ -293,7 +326,7 @@ class FakeLoadBalancer(CloudFormationModel):
 class ELBBackend(BaseBackend):
     def __init__(self, region_name: str, account_id: str):
         super().__init__(region_name, account_id)
-        self.load_balancers: Dict[str, FakeLoadBalancer] = OrderedDict()
+        self.load_balancers: Dict[str, LoadBalancer] = OrderedDict()
 
     def create_load_balancer(
         self,
@@ -303,7 +336,7 @@ class ELBBackend(BaseBackend):
         scheme: str = "internet-facing",
         subnets: Optional[List[str]] = None,
         security_groups: Optional[List[str]] = None,
-    ) -> FakeLoadBalancer:
+    ) -> LoadBalancer:
         vpc_id = None
         ec2_backend = ec2_backends[self.account_id][self.region_name]
         if subnets:
@@ -328,7 +361,8 @@ class ELBBackend(BaseBackend):
         for security_group in security_groups:
             if ec2_backend.get_security_group_from_id(security_group) is None:
                 raise InvalidSecurityGroupError()
-        new_load_balancer = FakeLoadBalancer(
+        new_load_balancer = LoadBalancer(
+            account_id=self.account_id,
             name=name,
             zones=zones,
             ports=ports,
@@ -352,7 +386,7 @@ class ELBBackend(BaseBackend):
 
     def create_load_balancer_listeners(
         self, name: str, ports: List[Dict[str, Any]]
-    ) -> Optional[FakeLoadBalancer]:
+    ) -> Optional[LoadBalancer]:
         balancer = self.load_balancers.get(name, None)
         if balancer:
             for port in ports:
@@ -387,7 +421,7 @@ class ELBBackend(BaseBackend):
 
         return balancer
 
-    def describe_load_balancers(self, names: List[str]) -> List[FakeLoadBalancer]:
+    def describe_load_balancers(self, names: List[str]) -> List[LoadBalancer]:
         balancers = list(self.load_balancers.values())
         if names:
             matched_balancers = [
@@ -438,7 +472,7 @@ class ELBBackend(BaseBackend):
 
     def delete_load_balancer_listeners(
         self, name: str, ports: List[str]
-    ) -> Optional[FakeLoadBalancer]:
+    ) -> Optional[LoadBalancer]:
         balancer = self.get_load_balancer(name)
         listeners = []
         if balancer:
@@ -458,7 +492,7 @@ class ELBBackend(BaseBackend):
         lb = self.get_load_balancer(lb_name)
         lb.policies = [p for p in lb.policies if p.policy_name != policy_name]
 
-    def get_load_balancer(self, load_balancer_name: str) -> FakeLoadBalancer:
+    def get_load_balancer(self, load_balancer_name: str) -> LoadBalancer:
         return self.load_balancers.get(load_balancer_name)  # type: ignore[return-value]
 
     def apply_security_groups_to_load_balancer(
@@ -489,7 +523,7 @@ class ELBBackend(BaseBackend):
 
     def set_load_balancer_listener_ssl_certificate(
         self, name: str, lb_port: str, ssl_certificate_id: str
-    ) -> Optional[FakeLoadBalancer]:
+    ) -> Optional[LoadBalancer]:
         balancer = self.load_balancers.get(name, None)
         if balancer:
             for idx, listener in enumerate(balancer.listeners):
@@ -509,7 +543,7 @@ class ELBBackend(BaseBackend):
         load_balancer_name: str,
         instance_ids: Iterable[str],
         from_autoscaling: bool = False,
-    ) -> FakeLoadBalancer:
+    ) -> LoadBalancer:
         load_balancer = self.get_load_balancer(load_balancer_name)
         attr_name = (
             "instance_sparse_ids"
@@ -526,7 +560,7 @@ class ELBBackend(BaseBackend):
         load_balancer_name: str,
         instance_ids: Iterable[str],
         from_autoscaling: bool = False,
-    ) -> FakeLoadBalancer:
+    ) -> LoadBalancer:
         load_balancer = self.get_load_balancer(load_balancer_name)
         attr_name = (
             "instance_sparse_ids"
@@ -584,7 +618,7 @@ class ELBBackend(BaseBackend):
         policy_name: str,
         policy_type_name: str,
         policy_attrs: List[Dict[str, str]],
-    ) -> FakeLoadBalancer:
+    ) -> LoadBalancer:
         load_balancer = self.get_load_balancer(load_balancer_name)
         if policy_name not in [p.policy_name for p in load_balancer.policies]:
             load_balancer.policies.append(
@@ -595,7 +629,7 @@ class ELBBackend(BaseBackend):
 
     def create_app_cookie_stickiness_policy(
         self, load_balancer_name: str, policy_name: str, cookie_name: str
-    ) -> FakeLoadBalancer:
+    ) -> LoadBalancer:
         load_balancer = self.get_load_balancer(load_balancer_name)
         policy = AppCookieStickinessPolicy(policy_name, cookie_name)
         load_balancer.policies.append(policy)
@@ -606,7 +640,7 @@ class ELBBackend(BaseBackend):
         load_balancer_name: str,
         policy_name: str,
         cookie_expiration_period: Optional[int],
-    ) -> FakeLoadBalancer:
+    ) -> LoadBalancer:
         load_balancer = self.get_load_balancer(load_balancer_name)
         policy = LbCookieStickinessPolicy(policy_name, cookie_expiration_period)
         load_balancer.policies.append(policy)
@@ -614,7 +648,7 @@ class ELBBackend(BaseBackend):
 
     def set_load_balancer_policies_for_backend_server(
         self, load_balancer_name: str, instance_port: int, policies: List[str]
-    ) -> FakeLoadBalancer:
+    ) -> LoadBalancer:
         load_balancer = self.get_load_balancer(load_balancer_name)
         backend = [
             b for b in load_balancer.backends if int(b.instance_port) == instance_port
@@ -626,7 +660,7 @@ class ELBBackend(BaseBackend):
 
     def set_load_balancer_policies_of_listener(
         self, load_balancer_name: str, load_balancer_port: int, policies: List[str]
-    ) -> FakeLoadBalancer:
+    ) -> LoadBalancer:
         load_balancer = self.get_load_balancer(load_balancer_name)
         listener = [
             l_listener
@@ -642,21 +676,27 @@ class ELBBackend(BaseBackend):
         self, load_balancer_name: str, availability_zones: List[str]
     ) -> List[str]:
         load_balancer = self.get_load_balancer(load_balancer_name)
-        load_balancer.zones = sorted(
-            list(set(load_balancer.zones + availability_zones))
+        load_balancer.availability_zones = sorted(
+            list(set(load_balancer.availability_zones + availability_zones))
         )
-        return load_balancer.zones
+        return load_balancer.availability_zones
 
     def disable_availability_zones_for_load_balancer(
         self, load_balancer_name: str, availability_zones: List[str]
     ) -> List[str]:
         load_balancer = self.get_load_balancer(load_balancer_name)
-        load_balancer.zones = sorted(
+        load_balancer.availability_zones = sorted(
             list(
-                set([az for az in load_balancer.zones if az not in availability_zones])
+                set(
+                    [
+                        az
+                        for az in load_balancer.availability_zones
+                        if az not in availability_zones
+                    ]
+                )
             )
         )
-        return load_balancer.zones
+        return load_balancer.availability_zones
 
     def attach_load_balancer_to_subnets(
         self, load_balancer_name: str, subnets: List[str]
diff --git a/moto/elb/policies.py b/moto/elb/policies.py
index 0ab1c137d..91b624593 100644
--- a/moto/elb/policies.py
+++ b/moto/elb/policies.py
@@ -27,4 +27,4 @@ class OtherPolicy(Policy):
         policy_attrs: List[Dict[str, Any]],
     ):
         super().__init__(policy_name, policy_type_name=policy_type_name)
-        self.attributes = policy_attrs or []
+        self.policy_attribute_descriptions = policy_attrs or []
diff --git a/moto/elb/responses.py b/moto/elb/responses.py
index 01d2230f3..548a7747e 100644
--- a/moto/elb/responses.py
+++ b/moto/elb/responses.py
@@ -1,10 +1,49 @@
-from moto.core.responses import BaseResponse
+from collections import defaultdict
+from typing import Any
+
+from moto.core.responses import ActionResult, BaseResponse, EmptyResult
 
 from .exceptions import DuplicateTagKeysError, LoadBalancerNotFoundError
-from .models import ELBBackend, FakeLoadBalancer, elb_backends
+from .models import ELBBackend, LoadBalancer, elb_backends
+from .policies import Policy
+
+
+def transform_dict(data: dict[str, str]) -> list[dict[str, str]]:
+    transformed = [{"Key": key, "Value": value} for key, value in data.items()]
+    return transformed
+
+
+def transform_tuple_list(data: list[tuple[str, str]]) -> list[dict[str, str]]:
+    transformed = [{"Key": t[0], "Value": t[1]} for t in data]
+    return transformed
+
+
+def transform_policies(policies: list[Policy]) -> dict[str, Any]:
+    segmented_policies = defaultdict(list)
+    for policy in policies:
+        if policy.policy_type_name in [
+            "AppCookieStickinessPolicy",
+            "LbCookieStickinessPolicy",
+        ]:
+            segmented_policies[policy.policy_type_name].append(policy)
+        else:
+            segmented_policies["OtherPolicy"].append(policy)
+    transformed = {
+        "AppCookieStickinessPolicies": segmented_policies["AppCookieStickinessPolicy"],
+        "LBCookieStickinessPolicies": segmented_policies["LbCookieStickinessPolicy"],
+        "OtherPolicies": [p.policy_name for p in segmented_policies["OtherPolicy"]],
+    }
+    return transformed
 
 
 class ELBResponse(BaseResponse):
+    RESPONSE_KEY_PATH_TO_TRANSFORMER = {
+        "DescribeAccessPointsOutput.LoadBalancerDescriptions.LoadBalancerDescription.Policies": transform_policies,
+        "DescribeLoadBalancerAttributesOutput.LoadBalancerAttributes.AdditionalAttributes": transform_tuple_list,
+        "DescribeTagsOutput.TagDescriptions.TagDescription.Tags": transform_dict,
+        "ModifyLoadBalancerAttributesOutput.LoadBalancerAttributes.AdditionalAttributes": transform_tuple_list,
+    }
+
     def __init__(self) -> None:
         super().__init__(service_name="elb")
 
@@ -12,7 +51,7 @@ class ELBResponse(BaseResponse):
     def elb_backend(self) -> ELBBackend:
         return elb_backends[self.current_account][self.region]
 
-    def create_load_balancer(self) -> str:
+    def create_load_balancer(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         availability_zones = self._get_multi_param("AvailabilityZones.member")
         ports = self._get_list_prefix("Listeners.member")
@@ -29,21 +68,19 @@ class ELBResponse(BaseResponse):
             security_groups=security_groups,
         )
         self._add_tags(load_balancer)
-        template = self.response_template(CREATE_LOAD_BALANCER_TEMPLATE)
-        return template.render(load_balancer=load_balancer)
+        result = {"DNSName": load_balancer.dns_name}
+        return ActionResult(result)
 
-    def create_load_balancer_listeners(self) -> str:
+    def create_load_balancer_listeners(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         ports = self._get_list_prefix("Listeners.member")
 
         self.elb_backend.create_load_balancer_listeners(
             name=load_balancer_name, ports=ports
         )
+        return EmptyResult()
 
-        template = self.response_template(CREATE_LOAD_BALANCER_LISTENERS_TEMPLATE)
-        return template.render()
-
-    def describe_load_balancers(self) -> str:
+    def describe_load_balancers(self) -> ActionResult:
         names = self._get_multi_param("LoadBalancerNames.member")
         all_load_balancers = list(self.elb_backend.describe_load_balancers(names))
         marker = self._get_param("Marker")
@@ -60,48 +97,43 @@ class ELBResponse(BaseResponse):
         if len(all_load_balancers) > start + page_size:
             next_marker = load_balancers_resp[-1].name
 
-        template = self.response_template(DESCRIBE_LOAD_BALANCERS_TEMPLATE)
-        return template.render(
-            ACCOUNT_ID=self.current_account,
-            load_balancers=load_balancers_resp,
-            marker=next_marker,
-        )
+        result = {
+            "LoadBalancerDescriptions": load_balancers_resp,
+            "NextMarker": next_marker,
+        }
+        return ActionResult(result)
 
-    def delete_load_balancer_listeners(self) -> str:
+    def delete_load_balancer_listeners(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         ports = self._get_multi_param("LoadBalancerPorts.member")
         ports = [int(port) for port in ports]
 
         self.elb_backend.delete_load_balancer_listeners(load_balancer_name, ports)
-        template = self.response_template(DELETE_LOAD_BALANCER_LISTENERS)
-        return template.render()
+        return EmptyResult()
 
-    def delete_load_balancer(self) -> str:
+    def delete_load_balancer(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         self.elb_backend.delete_load_balancer(load_balancer_name)
-        template = self.response_template(DELETE_LOAD_BALANCER_TEMPLATE)
-        return template.render()
+        return EmptyResult()
 
-    def delete_load_balancer_policy(self) -> str:
+    def delete_load_balancer_policy(self) -> ActionResult:
         load_balancer_name = self.querystring.get("LoadBalancerName")[0]  # type: ignore
         names = self._get_param("PolicyName")
         self.elb_backend.delete_load_balancer_policy(
             lb_name=load_balancer_name, policy_name=names
         )
+        return EmptyResult()
 
-        template = self.response_template(DELETE_LOAD_BALANCER_POLICY)
-        return template.render()
-
-    def apply_security_groups_to_load_balancer(self) -> str:
+    def apply_security_groups_to_load_balancer(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         security_group_ids = self._get_multi_param("SecurityGroups.member")
         self.elb_backend.apply_security_groups_to_load_balancer(
             load_balancer_name, security_group_ids
         )
-        template = self.response_template(APPLY_SECURITY_GROUPS_TEMPLATE)
-        return template.render(security_group_ids=security_group_ids)
+        result = {"SecurityGroups": security_group_ids}
+        return ActionResult(result)
 
-    def configure_health_check(self) -> str:
+    def configure_health_check(self) -> ActionResult:
         check = self.elb_backend.configure_health_check(
             load_balancer_name=self._get_param("LoadBalancerName"),
             timeout=self._get_param("HealthCheck.Timeout"),
@@ -110,22 +142,22 @@ class ELBResponse(BaseResponse):
             interval=self._get_param("HealthCheck.Interval"),
             target=self._get_param("HealthCheck.Target"),
         )
-        template = self.response_template(CONFIGURE_HEALTH_CHECK_TEMPLATE)
-        return template.render(check=check)
+        result = {"HealthCheck": check}
+        return ActionResult(result)
 
-    def register_instances_with_load_balancer(self) -> str:
+    def register_instances_with_load_balancer(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         instance_ids = [
             list(param.values())[0]
             for param in self._get_list_prefix("Instances.member")
         ]
-        template = self.response_template(REGISTER_INSTANCES_TEMPLATE)
         load_balancer = self.elb_backend.register_instances(
             load_balancer_name, instance_ids
         )
-        return template.render(load_balancer=load_balancer)
+        result = {"Instances": [load_balancer.instance_ids]}
+        return ActionResult(result)
 
-    def set_load_balancer_listener_ssl_certificate(self) -> str:
+    def set_load_balancer_listener_ssl_certificate(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         ssl_certificate_id = self.querystring["SSLCertificateId"][0]
         lb_port = self.querystring["LoadBalancerPort"][0]
@@ -133,29 +165,27 @@ class ELBResponse(BaseResponse):
         self.elb_backend.set_load_balancer_listener_ssl_certificate(
             load_balancer_name, lb_port, ssl_certificate_id
         )
+        return EmptyResult()
 
-        template = self.response_template(SET_LOAD_BALANCER_SSL_CERTIFICATE)
-        return template.render()
-
-    def deregister_instances_from_load_balancer(self) -> str:
+    def deregister_instances_from_load_balancer(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         instance_ids = [
             list(param.values())[0]
             for param in self._get_list_prefix("Instances.member")
         ]
-        template = self.response_template(DEREGISTER_INSTANCES_TEMPLATE)
         load_balancer = self.elb_backend.deregister_instances(
             load_balancer_name, instance_ids
         )
-        return template.render(load_balancer=load_balancer)
+        result = {"Instances": [load_balancer.instance_ids]}
+        return ActionResult(result)
 
-    def describe_load_balancer_attributes(self) -> str:
+    def describe_load_balancer_attributes(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         load_balancer = self.elb_backend.get_load_balancer(load_balancer_name)
-        template = self.response_template(DESCRIBE_ATTRIBUTES_TEMPLATE)
-        return template.render(attributes=load_balancer.attributes)
+        result = {"LoadBalancerAttributes": load_balancer.attributes}
+        return ActionResult(result)
 
-    def modify_load_balancer_attributes(self) -> str:
+    def modify_load_balancer_attributes(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         load_balancer = self.elb_backend.get_load_balancer(load_balancer_name)
 
@@ -202,12 +232,13 @@ class ELBResponse(BaseResponse):
             self.elb_backend.modify_load_balancer_attributes(
                 load_balancer_name, additional_attributes=additional_attributes
             )
-        template = self.response_template(MODIFY_ATTRIBUTES_TEMPLATE)
-        return template.render(
-            load_balancer=load_balancer, attributes=load_balancer.attributes
-        )
+        result = {
+            "LoadBalancerName": load_balancer.name,
+            "LoadBalancerAttributes": load_balancer.attributes,
+        }
+        return ActionResult(result)
 
-    def create_load_balancer_policy(self) -> str:
+    def create_load_balancer_policy(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
 
         policy_name = self._get_param("PolicyName")
@@ -217,11 +248,9 @@ class ELBResponse(BaseResponse):
         self.elb_backend.create_load_balancer_policy(
             load_balancer_name, policy_name, policy_type_name, policy_attrs
         )
+        return EmptyResult()
 
-        template = self.response_template(CREATE_LOAD_BALANCER_POLICY_TEMPLATE)
-        return template.render()
-
-    def create_app_cookie_stickiness_policy(self) -> str:
+    def create_app_cookie_stickiness_policy(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
 
         policy_name = self._get_param("PolicyName")
@@ -230,11 +259,9 @@ class ELBResponse(BaseResponse):
         self.elb_backend.create_app_cookie_stickiness_policy(
             load_balancer_name, policy_name, cookie_name
         )
+        return EmptyResult()
 
-        template = self.response_template(CREATE_APP_COOKIE_STICKINESS_POLICY_TEMPLATE)
-        return template.render()
-
-    def create_lb_cookie_stickiness_policy(self) -> str:
+    def create_lb_cookie_stickiness_policy(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
 
         policy_name = self._get_param("PolicyName")
@@ -247,11 +274,9 @@ class ELBResponse(BaseResponse):
         self.elb_backend.create_lb_cookie_stickiness_policy(
             load_balancer_name, policy_name, cookie_expiration_period
         )
+        return EmptyResult()
 
-        template = self.response_template(CREATE_LB_COOKIE_STICKINESS_POLICY_TEMPLATE)
-        return template.render()
-
-    def set_load_balancer_policies_of_listener(self) -> str:
+    def set_load_balancer_policies_of_listener(self) -> ActionResult:
         load_balancer_name = self._get_param("LoadBalancerName")
         load_balancer = self.elb_backend.get_load_balancer(load_balancer_name)
         load_balancer_port = int(self._get_param("LoadBalancerPort"))
@@ -267,13 +292,9 @@ class ELBResponse(BaseResponse):
                 load_balancer_name, load_balancer_port, policies
             )
         # else: explode?
+        return EmptyResult()
 
-        template = self.response_template(
-            SET_LOAD_BALANCER_POLICIES_OF_LISTENER_TEMPLATE
-        )
-        return template.render()
-
-    def set_load_balancer_policies_for_backend_server(self) -> str:
+    def set_load_balancer_policies_for_backend_server(self) -> ActionResult:
         load_balancer_name = self.querystring.get("LoadBalancerName")[0]  # type: ignore
         load_balancer = self.elb_backend.get_load_balancer(load_balancer_name)
         instance_port = int(self.querystring.get("InstancePort")[0])  # type: ignore
@@ -288,29 +309,35 @@ class ELBResponse(BaseResponse):
             )
         # else: explode?
 
-        template = self.response_template(
-            SET_LOAD_BALANCER_POLICIES_FOR_BACKEND_SERVER_TEMPLATE
-        )
-        return template.render()
+        return EmptyResult()
 
-    def describe_load_balancer_policies(self) -> str:
+    def describe_load_balancer_policies(self) -> ActionResult:
         load_balancer_name = self.querystring.get("LoadBalancerName")[0]  # type: ignore
         names = self._get_multi_param("PolicyNames.member.")
         policies = self.elb_backend.describe_load_balancer_policies(
             lb_name=load_balancer_name, policy_names=names
         )
+        result = {"PolicyDescriptions": policies}
+        return ActionResult(result)
 
-        template = self.response_template(DESCRIBE_LOAD_BALANCER_POLICIES_TEMPLATE)
-        return template.render(policies=policies)
-
-    def describe_instance_health(self) -> str:
+    def describe_instance_health(self) -> ActionResult:
         lb_name = self._get_param("LoadBalancerName")
         instances = self._get_params().get("Instances", [])
         instances = self.elb_backend.describe_instance_health(lb_name, instances)
-        template = self.response_template(DESCRIBE_INSTANCE_HEALTH_TEMPLATE)
-        return template.render(instances=instances)
-
-    def add_tags(self) -> str:
+        result = {
+            "InstanceStates": [
+                {
+                    "Description": "N/A",
+                    "InstanceId": instance["InstanceId"],
+                    "State": instance["State"],
+                    "ReasonCode": "N/A",
+                }
+                for instance in instances
+            ]
+        }
+        return ActionResult(result)
+
+    def add_tags(self) -> ActionResult:
         for key, value in self.querystring.items():
             if "LoadBalancerNames.member" in key:
                 load_balancer_name = value[0]
@@ -320,10 +347,9 @@ class ELBResponse(BaseResponse):
 
                 self._add_tags(elb)
 
-        template = self.response_template(ADD_TAGS_TEMPLATE)
-        return template.render()
+        return EmptyResult()
 
-    def remove_tags(self) -> str:
+    def remove_tags(self) -> ActionResult:
         for key in self.querystring:
             if "LoadBalancerNames.member" in key:
                 number = key.split(".")[2]
@@ -339,10 +365,9 @@ class ELBResponse(BaseResponse):
                         if t_key.split(".")[3] == "Key":
                             elb.remove_tag(t_val[0])
 
-        template = self.response_template(REMOVE_TAGS_TEMPLATE)
-        return template.render()
+        return EmptyResult()
 
-    def describe_tags(self) -> str:
+    def describe_tags(self) -> ActionResult:
         elbs = []
         for key in self.querystring:
             if "LoadBalancerNames.member" in key:
@@ -354,11 +379,14 @@ class ELBResponse(BaseResponse):
                 if not elb:
                     raise LoadBalancerNotFoundError(load_balancer_name)
                 elbs.append(elb)
-
-        template = self.response_template(DESCRIBE_TAGS_TEMPLATE)
-        return template.render(load_balancers=elbs)
-
-    def _add_tags(self, elb: FakeLoadBalancer) -> None:
+        result = {
+            "TagDescriptions": [
+                {"LoadBalancerName": elb.name, "Tags": elb.tags} for elb in elbs
+            ]
+        }
+        return ActionResult(result)
+
+    def _add_tags(self, elb: LoadBalancer) -> None:
         tag_values = []
         tag_keys = []
 
@@ -382,7 +410,7 @@ class ELBResponse(BaseResponse):
         for tag_key, tag_value in zip(tag_keys, tag_values):
             elb.add_tag(tag_key, tag_value)
 
-    def enable_availability_zones_for_load_balancer(self) -> str:
+    def enable_availability_zones_for_load_balancer(self) -> ActionResult:
         params = self._get_params()
         load_balancer_name = params.get("LoadBalancerName")
         availability_zones = params.get("AvailabilityZones")
@@ -392,12 +420,10 @@ class ELBResponse(BaseResponse):
                 availability_zones=availability_zones,  # type: ignore[arg-type]
             )
         )
-        template = self.response_template(
-            ENABLE_AVAILABILITY_ZONES_FOR_LOAD_BALANCER_TEMPLATE
-        )
-        return template.render(availability_zones=availability_zones)
+        result = {"AvailabilityZones": availability_zones}
+        return ActionResult(result)
 
-    def disable_availability_zones_for_load_balancer(self) -> str:
+    def disable_availability_zones_for_load_balancer(self) -> ActionResult:
         params = self._get_params()
         load_balancer_name = params.get("LoadBalancerName")
         availability_zones = params.get("AvailabilityZones")
@@ -407,12 +433,10 @@ class ELBResponse(BaseResponse):
                 availability_zones=availability_zones,  # type: ignore[arg-type]
             )
         )
-        template = self.response_template(
-            DISABLE_AVAILABILITY_ZONES_FOR_LOAD_BALANCER_TEMPLATE
-        )
-        return template.render(availability_zones=availability_zones)
+        result = {"AvailabilityZones": availability_zones}
+        return ActionResult(result)
 
-    def attach_load_balancer_to_subnets(self) -> str:
+    def attach_load_balancer_to_subnets(self) -> ActionResult:
         params = self._get_params()
         load_balancer_name = params.get("LoadBalancerName")
         subnets = params.get("Subnets")
@@ -421,10 +445,10 @@ class ELBResponse(BaseResponse):
             load_balancer_name,  # type: ignore[arg-type]
             subnets,  # type: ignore[arg-type]
         )
-        template = self.response_template(ATTACH_LB_TO_SUBNETS_TEMPLATE)
-        return template.render(subnets=all_subnets)
+        result = {"Subnets": all_subnets}
+        return ActionResult(result)
 
-    def detach_load_balancer_from_subnets(self) -> str:
+    def detach_load_balancer_from_subnets(self) -> ActionResult:
         params = self._get_params()
         load_balancer_name = params.get("LoadBalancerName")
         subnets = params.get("Subnets")
@@ -433,494 +457,5 @@ class ELBResponse(BaseResponse):
             load_balancer_name,  # type: ignore[arg-type]
             subnets,  # type: ignore[arg-type]
         )
-        template = self.response_template(DETACH_LB_FROM_SUBNETS_TEMPLATE)
-        return template.render(subnets=all_subnets)
-
-
-ADD_TAGS_TEMPLATE = """<AddTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <AddTagsResult/>
-  <ResponseMetadata>
-    <RequestId>360e81f7-1100-11e4-b6ed-0f30EXAMPLE</RequestId>
-  </ResponseMetadata>
-</AddTagsResponse>"""
-
-REMOVE_TAGS_TEMPLATE = """<RemoveTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <RemoveTagsResult/>
-  <ResponseMetadata>
-    <RequestId>360e81f7-1100-11e4-b6ed-0f30EXAMPLE</RequestId>
-  </ResponseMetadata>
-</RemoveTagsResponse>"""
-
-DESCRIBE_TAGS_TEMPLATE = """<DescribeTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <DescribeTagsResult>
-    <TagDescriptions>
-      {% for elb in load_balancers %}
-      <member>
-        <LoadBalancerName>{{ elb.name }}</LoadBalancerName>
-        <Tags>
-          {% for key, value in elb.tags.items() %}
-          <member>
-            <Value>{{ value }}</Value>
-            <Key>{{ key }}</Key>
-          </member>
-          {% endfor %}
-        </Tags>
-      </member>
-      {% endfor %}
-    </TagDescriptions>
-  </DescribeTagsResult>
-  <ResponseMetadata>
-    <RequestId>360e81f7-1100-11e4-b6ed-0f30EXAMPLE</RequestId>
-  </ResponseMetadata>
-</DescribeTagsResponse>"""
-
-
-DESCRIBE_LOAD_BALANCER_POLICIES_TEMPLATE = """<DescribeLoadBalancerPoliciesResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <DescribeLoadBalancerPoliciesResult>
-    <PolicyDescriptions>
-      {% for policy in policies %}
-      <member>
-        <PolicyName>{{ policy.policy_name }}</PolicyName>
-        <PolicyTypeName>{{ policy.policy_type_name }}</PolicyTypeName>
-        <PolicyAttributeDescriptions>
-          {% for attr in policy.attributes %}
-              <member>
-                <AttributeName>{{ attr["AttributeName"] }}</AttributeName>
-                <AttributeValue>{{ attr["AttributeValue"] }}</AttributeValue>
-              </member>
-          {% endfor %}
-        </PolicyAttributeDescriptions>
-      </member>
-      {% endfor %}
-    </PolicyDescriptions>
-  </DescribeLoadBalancerPoliciesResult>
-  <ResponseMetadata>
-    <RequestId>360e81f7-1100-11e4-b6ed-0f30EXAMPLE</RequestId>
-  </ResponseMetadata>
-</DescribeLoadBalancerPoliciesResponse>"""
-
-
-CREATE_LOAD_BALANCER_TEMPLATE = """<CreateLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <CreateLoadBalancerResult>
-    <DNSName>{{ load_balancer.dns_name }}</DNSName>
-  </CreateLoadBalancerResult>
-  <ResponseMetadata>
-    <RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
-  </ResponseMetadata>
-</CreateLoadBalancerResponse>"""
-
-CREATE_LOAD_BALANCER_LISTENERS_TEMPLATE = """<CreateLoadBalancerListenersResponse xmlns="http://elasticloadbalancing.amazon aws.com/doc/2012-06-01/">
-  <CreateLoadBalancerListenersResult/>
-  <ResponseMetadata>
-    <RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
-  </ResponseMetadata>
-</CreateLoadBalancerListenersResponse>"""
-
-DELETE_LOAD_BALANCER_TEMPLATE = """<DeleteLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <DeleteLoadBalancerResult/>
-  <ResponseMetadata>
-    <RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
-  </ResponseMetadata>
-</DeleteLoadBalancerResponse>"""
-
-DESCRIBE_LOAD_BALANCERS_TEMPLATE = """<DescribeLoadBalancersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <DescribeLoadBalancersResult>
-    <LoadBalancerDescriptions>
-      {% for load_balancer in load_balancers %}
-        <member>
-          <SecurityGroups>
-            {% for security_group_id in load_balancer.security_groups %}
-            <member>{{ security_group_id }}</member>
-            {% endfor %}
-          </SecurityGroups>
-          {% if load_balancer.vpc_id %}
-          <SourceSecurityGroup>
-              <OwnerAlias>{{ ACCOUNT_ID }}</OwnerAlias>
-              <GroupName>default</GroupName>
-          </SourceSecurityGroup>
-          {% endif %}
-          <LoadBalancerName>{{ load_balancer.name }}</LoadBalancerName>
-          <CreatedTime>{{ load_balancer.created_time.isoformat() }}</CreatedTime>
-          <HealthCheck>
-            {% if load_balancer.health_check %}
-              <Interval>{{ load_balancer.health_check.interval }}</Interval>
-              <Target>{{ load_balancer.health_check.target }}</Target>
-              <HealthyThreshold>{{ load_balancer.health_check.healthy_threshold }}</HealthyThreshold>
-              <Timeout>{{ load_balancer.health_check.timeout }}</Timeout>
-              <UnhealthyThreshold>{{ load_balancer.health_check.unhealthy_threshold }}</UnhealthyThreshold>
-            {% else %}
-              <Target></Target>
-            {% endif %}
-          </HealthCheck>
-          {% if load_balancer.vpc_id %}
-          <VPCId>{{ load_balancer.vpc_id }}</VPCId>
-          {% else %}
-          <VPCId />
-          {% endif %}
-          <ListenerDescriptions>
-            {% for listener in load_balancer.listeners %}
-              <member>
-                <PolicyNames>
-                  {% for policy_name in listener.policy_names %}
-                    <member>{{ policy_name }}</member>
-                  {% endfor %}
-                </PolicyNames>
-                <Listener>
-                  <Protocol>{{ listener.protocol }}</Protocol>
-                  <LoadBalancerPort>{{ listener.load_balancer_port }}</LoadBalancerPort>
-                  <InstanceProtocol>{{ listener.protocol }}</InstanceProtocol>
-                  <InstancePort>{{ listener.instance_port }}</InstancePort>
-                  <SSLCertificateId>{{ listener.ssl_certificate_id }}</SSLCertificateId>
-                </Listener>
-              </member>
-            {% endfor %}
-          </ListenerDescriptions>
-          <Instances>
-            {% for instance_id in load_balancer.instance_ids %}
-              <member>
-                <InstanceId>{{ instance_id }}</InstanceId>
-              </member>
-            {% endfor %}
-          </Instances>
-          <Policies>
-            <AppCookieStickinessPolicies>
-            {% for policy in load_balancer.policies %}
-                {% if policy.policy_type_name == "AppCookieStickinessPolicy" %}
-                    <member>
-                        <CookieName>{{ policy.cookie_name }}</CookieName>
-                        <PolicyName>{{ policy.policy_name }}</PolicyName>
-                    </member>
-                {% endif %}
-            {% endfor %}
-            </AppCookieStickinessPolicies>
-            <LBCookieStickinessPolicies>
-            {% for policy in load_balancer.policies %}
-                {% if policy.policy_type_name == "LbCookieStickinessPolicy" %}
-                    <member>
-                        {% if policy.cookie_expiration_period %}
-                        <CookieExpirationPeriod>{{ policy.cookie_expiration_period }}</CookieExpirationPeriod>
-                        {% endif %}
-                        <PolicyName>{{ policy.policy_name }}</PolicyName>
-                    </member>
-                {% endif %}
-            {% endfor %}
-            </LBCookieStickinessPolicies>
-            <OtherPolicies>
-            {% for policy in load_balancer.policies %}
-                {% if policy.policy_type_name not in ["AppCookieStickinessPolicy", "LbCookieStickinessPolicy"] %}
-                    <member>{{ policy.policy_name }}</member>
-                {% endif %}
-            {% endfor %}
-            </OtherPolicies>
-          </Policies>
-          <AvailabilityZones>
-            {% for zone in load_balancer.zones %}
-              <member>{{ zone }}</member>
-            {% endfor %}
-          </AvailabilityZones>
-          <CanonicalHostedZoneName>{{ load_balancer.dns_name }}</CanonicalHostedZoneName>
-          <CanonicalHostedZoneNameID>Z3ZONEID</CanonicalHostedZoneNameID>
-          <Scheme>{{ load_balancer.scheme }}</Scheme>
-          <DNSName>{{ load_balancer.dns_name }}</DNSName>
-          <BackendServerDescriptions>
-          {% for backend in load_balancer.backends %}
-            {% if backend.policy_names %}
-            <member>
-                <InstancePort>{{ backend.instance_port }}</InstancePort>
-                <PolicyNames>
-                    {% for policy in backend.policy_names %}
-                    <member>{{ policy }}</member>
-                    {% endfor %}
-                </PolicyNames>
-            </member>
-            {% endif %}
-          {% endfor %}
-          </BackendServerDescriptions>
-          <Subnets>
-          {% for subnet in load_balancer.subnets %}
-              <member>{{ subnet }}</member>
-          {% endfor %}
-          </Subnets>
-        </member>
-      {% endfor %}
-    </LoadBalancerDescriptions>
-    {% if marker %}
-    <NextMarker>{{ marker }}</NextMarker>
-    {% endif %}
-  </DescribeLoadBalancersResult>
-  <ResponseMetadata>
-    <RequestId>f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c</RequestId>
-  </ResponseMetadata>
-</DescribeLoadBalancersResponse>"""
-
-APPLY_SECURITY_GROUPS_TEMPLATE = """<ApplySecurityGroupsToLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <ApplySecurityGroupsToLoadBalancerResult>
-    <SecurityGroups>
-      {% for security_group_id in security_group_ids %}
-      <member>{{ security_group_id }}</member>
-      {% endfor %}
-    </SecurityGroups>
-  </ApplySecurityGroupsToLoadBalancerResult>
-  <ResponseMetadata>
-    <RequestId>f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c</RequestId>
-  </ResponseMetadata>
-</ApplySecurityGroupsToLoadBalancerResponse>"""
-
-CONFIGURE_HEALTH_CHECK_TEMPLATE = """<ConfigureHealthCheckResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <ConfigureHealthCheckResult>
-    <HealthCheck>
-      <Interval>{{ check.interval }}</Interval>
-      <Target>{{ check.target }}</Target>
-      <HealthyThreshold>{{ check.healthy_threshold }}</HealthyThreshold>
-      <Timeout>{{ check.timeout }}</Timeout>
-      <UnhealthyThreshold>{{ check.unhealthy_threshold }}</UnhealthyThreshold>
-    </HealthCheck>
-  </ConfigureHealthCheckResult>
-  <ResponseMetadata>
-    <RequestId>f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c</RequestId>
-  </ResponseMetadata>
-</ConfigureHealthCheckResponse>"""
-
-REGISTER_INSTANCES_TEMPLATE = """<RegisterInstancesWithLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <RegisterInstancesWithLoadBalancerResult>
-    <Instances>
-      {% for instance_id in load_balancer.instance_ids %}
-        <member>
-          <InstanceId>{{ instance_id }}</InstanceId>
-        </member>
-      {% endfor %}
-    </Instances>
-  </RegisterInstancesWithLoadBalancerResult>
-  <ResponseMetadata>
-    <RequestId>f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c</RequestId>
-  </ResponseMetadata>
-</RegisterInstancesWithLoadBalancerResponse>"""
-
-DEREGISTER_INSTANCES_TEMPLATE = """<DeregisterInstancesFromLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <DeregisterInstancesFromLoadBalancerResult>
-    <Instances>
-      {% for instance_id in load_balancer.instance_ids %}
-        <member>
-          <InstanceId>{{ instance_id }}</InstanceId>
-        </member>
-      {% endfor %}
-    </Instances>
-  </DeregisterInstancesFromLoadBalancerResult>
-  <ResponseMetadata>
-    <RequestId>f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c</RequestId>
-  </ResponseMetadata>
-</DeregisterInstancesFromLoadBalancerResponse>"""
-
-SET_LOAD_BALANCER_SSL_CERTIFICATE = """<SetLoadBalancerListenerSSLCertificateResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
- <SetLoadBalancerListenerSSLCertificateResult/>
-<ResponseMetadata>
-    <RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
-</ResponseMetadata>
-</SetLoadBalancerListenerSSLCertificateResponse>"""
-
-
-DELETE_LOAD_BALANCER_LISTENERS = """<DeleteLoadBalancerListenersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
- <DeleteLoadBalancerListenersResult/>
-<ResponseMetadata>
-    <RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
-</ResponseMetadata>
-</DeleteLoadBalancerListenersResponse>"""
-
-
-DELETE_LOAD_BALANCER_POLICY = """<DeleteLoadBalancerPolicyResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
- <DeleteLoadBalancerPolicyResult/>
-<ResponseMetadata>
-    <RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
-</ResponseMetadata>
-</DeleteLoadBalancerPolicyResponse>"""
-
-DESCRIBE_ATTRIBUTES_TEMPLATE = """<DescribeLoadBalancerAttributesResponse  xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <DescribeLoadBalancerAttributesResult>
-    <LoadBalancerAttributes>
-      <AccessLog>
-        <Enabled>{{ attributes["access_log"]["enabled"] }}</Enabled>
-        {% if attributes["access_log"]["enabled"] == 'true' %}
-        <S3BucketName>{{ attributes["access_log"]["s3_bucket_name"] }}</S3BucketName>
-        <S3BucketPrefix>{{ attributes["access_log"]["s3_bucket_prefix"] }}</S3BucketPrefix>
-        <EmitInterval>{{ attributes["access_log"]["emit_interval"] }}</EmitInterval>
-        {% endif %}
-      </AccessLog>
-      <ConnectionSettings>
-        <IdleTimeout>{{ attributes["connection_settings"]["idle_timeout"] }}</IdleTimeout>
-      </ConnectionSettings>
-      <CrossZoneLoadBalancing>
-        <Enabled>{{ attributes.cross_zone_load_balancing.enabled }}</Enabled>
-      </CrossZoneLoadBalancing>
-      <ConnectionDraining>
-        <Enabled>{{ attributes["connection_draining"]["enabled"] }}</Enabled>
-        {% if attributes["connection_draining"]["timeout"] %}
-        <Timeout>{{ attributes["connection_draining"]["timeout"] }}</Timeout>
-        {% endif %}
-      </ConnectionDraining>
-      <AdditionalAttributes>
-        {% for attribute in attributes.additional_attributes %}
-        <member>
-          <Key>{{ attribute[0] }}</Key>
-          <Value>{{ attribute[1] }}</Value>
-        </member>
-        {% endfor %}
-      </AdditionalAttributes>
-    </LoadBalancerAttributes>
-  </DescribeLoadBalancerAttributesResult>
-  <ResponseMetadata>
-    <RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
-  </ResponseMetadata>
-</DescribeLoadBalancerAttributesResponse>
-"""
-
-MODIFY_ATTRIBUTES_TEMPLATE = """<ModifyLoadBalancerAttributesResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <ModifyLoadBalancerAttributesResult>
-  <LoadBalancerName>{{ load_balancer.name }}</LoadBalancerName>
-    <LoadBalancerAttributes>
-      <AccessLog>
-        <Enabled>{{ attributes["access_log"]["enabled"] == 'true' }}</Enabled>
-        {% if attributes["access_log"]["enabled"] == 'true' %}
-        <S3BucketName>{{ attributes["access_log"]["s3_bucket_name"] }}</S3BucketName>
-        <S3BucketPrefix>{{ attributes["access_log"]["s3_bucket_prefix"] }}</S3BucketPrefix>
-        <EmitInterval>{{ attributes["access_log"]["emit_interval"] }}</EmitInterval>
-        {% endif %}
-      </AccessLog>
-      <ConnectionSettings>
-        <IdleTimeout>{{ attributes["connection_settings"]["idle_timeout"] }}</IdleTimeout>
-      </ConnectionSettings>
-      <CrossZoneLoadBalancing>
-        <Enabled>{{ attributes.cross_zone_load_balancing.enabled }}</Enabled>
-      </CrossZoneLoadBalancing>
-      <ConnectionDraining>
-        {% if attributes["connection_draining"]["enabled"] == 'true' %}
-        <Enabled>true</Enabled>
-        <Timeout>{{ attributes["connection_draining"]["timeout"] }}</Timeout>
-        {% else %}
-        <Enabled>false</Enabled>
-        {% endif %}
-      </ConnectionDraining>
-      <AdditionalAttributes>
-        {% for attribute in attributes.additional_attributes %}
-        <member>
-          <Key>{{ attribute[0] }}</Key>
-          <Value>{{ attribute[1] }}</Value>
-        </member>
-        {% endfor %}
-      </AdditionalAttributes>
-    </LoadBalancerAttributes>
-  </ModifyLoadBalancerAttributesResult>
-  <ResponseMetadata>
-    <RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
-  </ResponseMetadata>
-</ModifyLoadBalancerAttributesResponse>
-"""
-
-CREATE_LOAD_BALANCER_POLICY_TEMPLATE = """<CreateLoadBalancerPolicyResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <CreateLoadBalancerPolicyResult/>
-  <ResponseMetadata>
-      <RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
-  </ResponseMetadata>
-</CreateLoadBalancerPolicyResponse>
-"""
-
-CREATE_LB_COOKIE_STICKINESS_POLICY_TEMPLATE = """<CreateLBCookieStickinessPolicyResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <CreateLBCookieStickinessPolicyResult/>
-  <ResponseMetadata>
-      <RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
-  </ResponseMetadata>
-</CreateLBCookieStickinessPolicyResponse>
-"""
-
-CREATE_APP_COOKIE_STICKINESS_POLICY_TEMPLATE = """<CreateAppCookieStickinessPolicyResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <CreateAppCookieStickinessPolicyResult/>
-  <ResponseMetadata>
-      <RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
-  </ResponseMetadata>
-</CreateAppCookieStickinessPolicyResponse>
-"""
-
-SET_LOAD_BALANCER_POLICIES_OF_LISTENER_TEMPLATE = """<SetLoadBalancerPoliciesOfListenerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-    <SetLoadBalancerPoliciesOfListenerResult/>
-    <ResponseMetadata>
-        <RequestId>07b1ecbc-1100-11e3-acaf-dd7edEXAMPLE</RequestId>
-    </ResponseMetadata>
-</SetLoadBalancerPoliciesOfListenerResponse>
-"""
-
-SET_LOAD_BALANCER_POLICIES_FOR_BACKEND_SERVER_TEMPLATE = """<SetLoadBalancerPoliciesForBackendServerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-    <SetLoadBalancerPoliciesForBackendServerResult/>
-    <ResponseMetadata>
-        <RequestId>0eb9b381-dde0-11e2-8d78-6ddbaEXAMPLE</RequestId>
-    </ResponseMetadata>
-</SetLoadBalancerPoliciesForBackendServerResponse>
-"""
-
-DESCRIBE_INSTANCE_HEALTH_TEMPLATE = """<DescribeInstanceHealthResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <DescribeInstanceHealthResult>
-    <InstanceStates>
-      {% for instance in instances %}
-      <member>
-        <Description>N/A</Description>
-        <InstanceId>{{ instance['InstanceId'] }}</InstanceId>
-        <State>{{ instance['State'] }}</State>
-        <ReasonCode>N/A</ReasonCode>
-      </member>
-      {% endfor %}
-    </InstanceStates>
-  </DescribeInstanceHealthResult>
-  <ResponseMetadata>
-    <RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
-  </ResponseMetadata>
-</DescribeInstanceHealthResponse>"""
-
-ENABLE_AVAILABILITY_ZONES_FOR_LOAD_BALANCER_TEMPLATE = """<EnableAvailabilityZonesForLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <ResponseMetadata>
-    <RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
-  </ResponseMetadata>
-  <EnableAvailabilityZonesForLoadBalancerResult>
-    <AvailabilityZones>
-{% for az in availability_zones %}
-      <AvailabilityZone>{{ az }}</AvailabilityZone>
-{% endfor %}
-    </AvailabilityZones>
-  </EnableAvailabilityZonesForLoadBalancerResult>
-</EnableAvailabilityZonesForLoadBalancerResponse>"""
-
-DISABLE_AVAILABILITY_ZONES_FOR_LOAD_BALANCER_TEMPLATE = """<DisableAvailabilityZonesForLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <ResponseMetadata>
-    <RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
-  </ResponseMetadata>
-  <DisableAvailabilityZonesForLoadBalancerResult>
-    <AvailabilityZones>
-{% for az in availability_zones %}
-      <AvailabilityZone>{{ az }}</AvailabilityZone>
-{% endfor %}
-    </AvailabilityZones>
-  </DisableAvailabilityZonesForLoadBalancerResult>
-</DisableAvailabilityZonesForLoadBalancerResponse>"""
-
-ATTACH_LB_TO_SUBNETS_TEMPLATE = """<AttachLoadBalancerToSubnetsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <AttachLoadBalancerToSubnetsResult>
-    <Subnets>
-      {% for subnet in subnets %}
-      <member>{{ subnet }}</member>
-      {% endfor %}
-    </Subnets>
-  </AttachLoadBalancerToSubnetsResult>
-  <ResponseMetadata>
-    <RequestId>f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c</RequestId>
-  </ResponseMetadata>
-</AttachLoadBalancerToSubnetsResponse>"""
-
-DETACH_LB_FROM_SUBNETS_TEMPLATE = """<DetachLoadBalancerFromSubnetsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
-  <DetachLoadBalancerFromSubnetsResult>
-    <Subnets>
-      {% for subnet in subnets %}
-      <member>{{ subnet }}</member>
-      {% endfor %}
-    </Subnets>
-  </DetachLoadBalancerFromSubnetsResult>
-  <ResponseMetadata>
-    <RequestId>f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c</RequestId>
-  </ResponseMetadata>
-</DetachLoadBalancerFromSubnetsResponse>"""
+        result = {"Subnets": all_subnets}
+        return ActionResult(result)
diff --git a/moto/emr/exceptions.py b/moto/emr/exceptions.py
index bbb438296..d13fe3f6b 100644
--- a/moto/emr/exceptions.py
+++ b/moto/emr/exceptions.py
@@ -2,15 +2,20 @@ from moto.core.exceptions import ServiceException
 
 
 class InvalidRequestException(ServiceException):
-    def __init__(self, message: str):
-        super().__init__("InvalidRequestException", message)
+    code = "InvalidRequestException"
 
 
-class ValidationException(ServiceException):
-    def __init__(self, message: str):
-        super().__init__("ValidationException", message)
+class ResourceNotFoundException(InvalidRequestException):
+    error_code = "ResourceNotFound"
+
+
+class InvalidCluster(InvalidRequestException):
+    error_code = "NoSuchCluster"
 
+    def __init__(self, cluster_id: str):
+        message = f"Cluster id '{cluster_id}' is not valid."
+        super().__init__(message)
 
-class ResourceNotFoundException(ServiceException):
-    def __init__(self, message: str):
-        super().__init__("ResourceNotFoundException", message)
+
+class ValidationException(ServiceException):
+    code = "ValidationException"
diff --git a/moto/emr/models.py b/moto/emr/models.py
index 948e4d88c..53c651087 100644
--- a/moto/emr/models.py
+++ b/moto/emr/models.py
@@ -10,6 +10,7 @@ from moto.core.common_models import BaseModel, CloudFormationModel
 from moto.core.utils import utcnow
 from moto.ec2.models.instances import Instance as EC2Instance
 from moto.emr.exceptions import (
+    InvalidCluster,
     InvalidRequestException,
     ResourceNotFoundException,
     ValidationException,
@@ -925,7 +926,7 @@ class ElasticMapReduceBackend(BaseBackend):
     def describe_cluster(self, cluster_id: str) -> Cluster:
         if cluster_id in self.clusters:
             return self.clusters[cluster_id]
-        raise ResourceNotFoundException("")
+        raise InvalidCluster(cluster_id)
 
     def get_instance_groups(self, instance_group_ids: List[str]) -> List[InstanceGroup]:
         return [
@@ -1146,15 +1147,15 @@ class ElasticMapReduceBackend(BaseBackend):
 
     def get_security_configuration(self, name: str) -> SecurityConfiguration:
         if name not in self.security_configurations:
-            raise InvalidRequestException(
-                message=f"Security configuration with name '{name}' does not exist."
+            raise ResourceNotFoundException(
+                f"Security configuration with name '{name}' does not exist."
             )
         return self.security_configurations[name]
 
     def delete_security_configuration(self, name: str) -> None:
         if name not in self.security_configurations:
-            raise InvalidRequestException(
-                message=f"Security configuration with name '{name}' does not exist."
+            raise ResourceNotFoundException(
+                f"Security configuration with name '{name}' does not exist."
             )
         del self.security_configurations[name]
 
diff --git a/moto/emr/responses.py b/moto/emr/responses.py
index 38978327a..3cd59e5d4 100644
--- a/moto/emr/responses.py
+++ b/moto/emr/responses.py
@@ -259,7 +259,7 @@ class ElasticMapReduceResponse(BaseResponse):
                     "Only one AMI version and release label may be specified. "
                     "Provided AMI: {0}, release label: {1}."
                 ).format(ami_version, release_label)
-                raise ValidationException(message=message)
+                raise ValidationException(message)
         else:
             if ami_version:
                 kwargs["requested_ami_version"] = ami_version
@@ -274,10 +274,10 @@ class ElasticMapReduceResponse(BaseResponse):
                 ReleaseLabel(release_label) < ReleaseLabel("emr-5.7.0")
             ):
                 message = "Custom AMI is not allowed"
-                raise ValidationException(message=message)
+                raise ValidationException(message)
             elif ami_version:
                 message = "Custom AMI is not supported in this version of EMR"
-                raise ValidationException(message=message)
+                raise ValidationException(message)
 
         step_concurrency_level = self._get_param("StepConcurrencyLevel")
         if step_concurrency_level:
diff --git a/moto/logs/models.py b/moto/logs/models.py
index 8be2ba4d1..d10f84e7e 100644
--- a/moto/logs/models.py
+++ b/moto/logs/models.py
@@ -1021,11 +1021,14 @@ class LogsBackend(BaseBackend):
             result.append(self.destinations[destination].to_dict())
         if next_token:
             result = result[: int(next_token)]
-        result = [
-            destination
-            for destination in result
-            if destination["destinationName"].startswith(destination_name_prefix)
-        ]
+
+        if destination_name_prefix:
+            result = [
+                destination
+                for destination in result
+                if destination["destinationName"].startswith(destination_name_prefix)
+            ]
+
         return result, next_token
 
     def put_destination_policy(self, destination_name: str, access_policy: str) -> None:
diff --git a/moto/rds/exceptions.py b/moto/rds/exceptions.py
index c946abfdd..37dda08cd 100644
--- a/moto/rds/exceptions.py
+++ b/moto/rds/exceptions.py
@@ -4,10 +4,7 @@ from moto.core.exceptions import ServiceException
 
 
 class RDSClientError(ServiceException):
-    def __init__(self, code: str, message: str):
-        super().__init__(message)
-        self.code = code
-        self.message = message
+    pass
 
 
 class DBInstanceNotFoundError(RDSClientError):
@@ -40,7 +37,7 @@ class DBSecurityGroupNotFoundError(RDSClientError):
 class DBShardGroupAlreadyExistsError(RDSClientError):
     def __init__(self, shard_group_identifier: str):
         super().__init__(
-            "DBShardGroupAlreadyExistsFault",
+            "DBShardGroupAlreadyExists",
             f"DB Shard Group {shard_group_identifier} already exists.",
         )
 
@@ -55,7 +52,7 @@ class DBSubnetGroupNotFoundError(RDSClientError):
 class DBParameterGroupNotFoundError(RDSClientError):
     def __init__(self, db_parameter_group_name: str):
         super().__init__(
-            "DBParameterGroupNotFoundFault",
+            "DBParameterGroupNotFound",
             f"DB Parameter Group {db_parameter_group_name} not found.",
         )
 
@@ -63,7 +60,7 @@ class DBParameterGroupNotFoundError(RDSClientError):
 class DBParameterGroupAlreadyExistsError(RDSClientError):
     def __init__(self, db_parameter_group_name: str):
         super().__init__(
-            "DBParameterGroupAlreadyExistsFault",
+            "DBParameterGroupAlreadyExists",
             f"DB Parameter Group {db_parameter_group_name} already exists.",
         )
 
diff --git a/moto/rds/models.py b/moto/rds/models.py
index f19612423..e0b117da8 100644
--- a/moto/rds/models.py
+++ b/moto/rds/models.py
@@ -76,7 +76,7 @@ from .exceptions import (
     InvalidSubnet,
     KMSKeyNotAccessibleFault,
     OptionGroupNotFoundFaultError,
-    RDSClientError,
+    RDSClientError,  # TODO: Refactor into specific exceptions
     SharedSnapshotQuotaExceeded,
     SnapshotQuotaExceededFault,
     SourceDatabaseNotSupportedFault,
@@ -3155,7 +3155,7 @@ class RDSBackend(BaseBackend):
         db_parameter_group_id = db_parameter_group_kwargs["db_parameter_group_name"]
         if db_parameter_group_id in self.db_parameter_groups:
             raise RDSClientError(
-                "DBParameterGroupAlreadyExistsFault",
+                "DBParameterGroupAlreadyExists",
                 f"A DB parameter group named {db_parameter_group_id} already exists.",
             )
         if not db_parameter_group_kwargs.get("description"):
@@ -3242,8 +3242,36 @@ class RDSBackend(BaseBackend):
 
         return db_parameter_group
 
-    def describe_db_cluster_parameters(self) -> List[Dict[str, Any]]:
-        return []
+    def modify_db_cluster_parameter_group(
+        self,
+        db_cluster_parameter_group_name: str,
+        db_cluster_parameter_group_parameters: Iterable[Dict[str, Any]],
+    ) -> DBClusterParameterGroup:
+        if db_cluster_parameter_group_name not in self.db_cluster_parameter_groups:
+            raise DBClusterParameterGroupNotFoundError(db_cluster_parameter_group_name)
+
+        db_cluster_parameter_group = self.db_cluster_parameter_groups[
+            db_cluster_parameter_group_name
+        ]
+        db_cluster_parameter_group.update_cluster_parameters(
+            db_cluster_parameter_group_parameters
+        )
+
+        return db_cluster_parameter_group
+
+    def describe_db_cluster_parameters(
+        self, db_cluster_parameter_group_name: str
+    ) -> List[Dict[str, Any]]:
+        if db_cluster_parameter_group_name not in self.db_cluster_parameter_groups:
+            raise DBClusterParameterGroupNotFoundError(db_cluster_parameter_group_name)
+        db_cluster_parameter_group = self.db_cluster_parameter_groups[
+            db_cluster_parameter_group_name
+        ]
+        parameters = [
+            {"ParameterName": name, **values}
+            for name, values in db_cluster_parameter_group.parameters.items()
+        ]
+        return parameters
 
     def create_db_cluster(self, kwargs: Dict[str, Any]) -> DBCluster:
         cluster_id = kwargs["db_cluster_identifier"]
@@ -4328,6 +4356,13 @@ class DBClusterParameterGroup(CloudFormationModel, RDSBaseModel):
     def resource_id(self) -> str:
         return self.name
 
+    def update_cluster_parameters(
+        self, new_parameters: Iterable[Dict[str, Any]]
+    ) -> None:
+        for new_parameter in new_parameters:
+            parameter = self.parameters[new_parameter["ParameterName"]]
+            parameter.update(new_parameter)
+
 
 class Event:
     EVENT_MAP = {
diff --git a/moto/rds/responses.py b/moto/rds/responses.py
index 6dff3dc1f..7325f5614 100644
--- a/moto/rds/responses.py
+++ b/moto/rds/responses.py
@@ -389,6 +389,19 @@ class RDSResponse(BaseResponse):
         result = {"DBParameterGroupName": db_parameter_group.name}
         return ActionResult(result)
 
+    def modify_db_cluster_parameter_group(self) -> ActionResult:
+        db_parameter_group_name = self.parameters.get("DBClusterParameterGroupName")
+        param_list = self.parameters.get("Parameters", [])
+        # Raw dict is stored on the backend, so we need the original PascalCase items.
+        db_parameter_group_parameters = [
+            dict(param.original_items()) for param in param_list
+        ]
+        db_parameter_group = self.backend.modify_db_cluster_parameter_group(
+            db_parameter_group_name, db_parameter_group_parameters
+        )
+        result = {"DBClusterParameterGroupName": db_parameter_group.name}
+        return ActionResult(result)
+
     def describe_db_parameters(self) -> ActionResult:
         db_parameter_group_name = self.parameters.get("DBParameterGroupName")
         db_parameter_groups = self.backend.describe_db_parameter_groups(
@@ -406,9 +419,10 @@ class RDSResponse(BaseResponse):
         return ActionResult(db_parameter_group)
 
     def describe_db_cluster_parameters(self) -> ActionResult:
-        # TODO: This never worked at all...
-        db_parameter_group_name = self.parameters.get("DBParameterGroupName")
-        db_parameter_groups = self.backend.describe_db_cluster_parameters()
+        db_parameter_group_name = self.parameters.get("DBClusterParameterGroupName")
+        db_parameter_groups = self.backend.describe_db_cluster_parameters(
+            db_parameter_group_name
+        )
         if db_parameter_groups is None:
             raise DBParameterGroupNotFoundError(db_parameter_group_name)
         result = {"Parameters": db_parameter_groups}
diff --git a/moto/redshift/exceptions.py b/moto/redshift/exceptions.py
index 2181e26da..4f1a5e20a 100644
--- a/moto/redshift/exceptions.py
+++ b/moto/redshift/exceptions.py
@@ -4,10 +4,7 @@ from moto.core.exceptions import ServiceException
 
 
 class RedshiftClientError(ServiceException):
-    def __init__(self, code: str, message: str):
-        super().__init__(message)
-        self.code = code
-        self.message = message
+    pass
 
 
 class ClusterNotFoundError(RedshiftClientError):
@@ -18,16 +15,18 @@ class ClusterNotFoundError(RedshiftClientError):
 class ClusterSubnetGroupNotFoundError(RedshiftClientError):
     def __init__(self, subnet_identifier: str):
         super().__init__(
-            "ClusterSubnetGroupNotFound", f"Subnet group {subnet_identifier} not found."
+            "ClusterSubnetGroupNotFoundFault",
+            f"Subnet group {subnet_identifier} not found.",
         )
 
 
 class ClusterSecurityGroupNotFoundError(RedshiftClientError):
-    def __init__(self, group_identifier: str):
-        super().__init__(
-            "ClusterSecurityGroupNotFound",
-            f"Security group {group_identifier} not found.",
-        )
+    code = "ClusterSecurityGroupNotFound"
+    message = "The cluster security group name does not refer to an existing cluster security group."
+
+    def __init__(self, group_identifier: Optional[str] = None):
+        if group_identifier:
+            super().__init__(f"Security group {group_identifier} not found.")
 
 
 class ClusterParameterGroupNotFoundError(RedshiftClientError):
@@ -136,17 +135,9 @@ class UnknownSnapshotCopyRegionFaultError(RedshiftClientError):
         super().__init__("UnknownSnapshotCopyRegionFault", message)
 
 
-class ClusterSecurityGroupNotFoundFaultError(RedshiftClientError):
-    def __init__(self) -> None:
-        super().__init__(
-            "ClusterSecurityGroupNotFoundFault",
-            "The cluster security group name does not refer to an existing cluster security group.",
-        )
-
-
 class InvalidClusterSnapshotStateFaultError(RedshiftClientError):
     def __init__(self, snapshot_identifier: str):
         super().__init__(
-            "InvalidClusterSnapshotStateFault",
+            "InvalidClusterSnapshotState",
             f"Cannot delete the snapshot {snapshot_identifier} because only manual snapshots may be deleted",
         )
diff --git a/moto/redshift/models.py b/moto/redshift/models.py
index 1618f74f2..694d54a35 100644
--- a/moto/redshift/models.py
+++ b/moto/redshift/models.py
@@ -19,7 +19,6 @@ from .exceptions import (
     ClusterNotFoundError,
     ClusterParameterGroupNotFoundError,
     ClusterSecurityGroupNotFoundError,
-    ClusterSecurityGroupNotFoundFaultError,
     ClusterSnapshotAlreadyExistsError,
     ClusterSnapshotNotFoundError,
     ClusterSubnetGroupNotFoundError,
@@ -792,7 +791,7 @@ class RedshiftBackend(BaseBackend):
     ) -> SecurityGroup:
         security_group = self.security_groups.get(security_group_name)
         if not security_group:
-            raise ClusterSecurityGroupNotFoundFaultError()
+            raise ClusterSecurityGroupNotFoundError()
 
         # just adding the cidr_ip as ingress rule for now as there is no security rule
         security_group.ingress_rules.append(cidr_ip)
diff --git a/moto/sns/exceptions.py b/moto/sns/exceptions.py
index 770139005..98199179a 100644
--- a/moto/sns/exceptions.py
+++ b/moto/sns/exceptions.py
@@ -1,94 +1,53 @@
-from typing import Any, Optional
+from moto.core.exceptions import ServiceException
 
-from moto.core.exceptions import RESTError
 
-
-class SNSException(RESTError):
-    def __init__(self, *args: Any, **kwargs: Any):
-        kwargs["template"] = "wrapped_single_error"
-        super().__init__(*args, **kwargs)
+class SNSException(ServiceException):
+    pass
 
 
 class SNSNotFoundError(SNSException):
-    code = 404
-
-    def __init__(self, message: str, template: Optional[str] = None):
-        super().__init__("NotFound", message, template=template)
+    code = "NotFound"
 
 
 class TopicNotFound(SNSNotFoundError):
-    def __init__(self) -> None:
-        super().__init__(message="Topic does not exist")
+    message = "Topic does not exist"
 
 
 class ResourceNotFoundError(SNSException):
-    code = 404
-
-    def __init__(self) -> None:
-        super().__init__("ResourceNotFound", "Resource does not exist")
+    code = "ResourceNotFound"
+    message = "Resource does not exist"
 
 
 class DuplicateSnsEndpointError(SNSException):
-    code = 400
-
-    def __init__(self, message: str):
-        super().__init__("InvalidParameter", message)
+    code = "InvalidParameter"
 
 
 class SnsEndpointDisabled(SNSException):
-    code = 400
-
-    def __init__(self, message: str):
-        super().__init__("EndpointDisabled", message)
+    code = "EndpointDisabled"
 
 
 class SNSInvalidParameter(SNSException):
-    code = 400
-
-    def __init__(self, message: str):
-        super().__init__("InvalidParameter", message)
+    code = "InvalidParameter"
 
 
 class InvalidParameterValue(SNSException):
-    code = 400
-
-    def __init__(self, message: str):
-        super().__init__("InvalidParameterValue", message)
+    code = "InvalidParameter"
 
 
 class TagLimitExceededError(SNSException):
-    code = 400
-
-    def __init__(self) -> None:
-        super().__init__(
-            "TagLimitExceeded",
-            "Could not complete request: tag quota of per resource exceeded",
-        )
+    code = "TagLimitExceeded"
+    message = "Could not complete request: tag quota of per resource exceeded"
 
 
 class InternalError(SNSException):
-    code = 500
-    include_type_sender = False
-
-    def __init__(self, message: str):
-        super().__init__("InternalFailure", message)
+    code = "InternalError"
 
 
 class TooManyEntriesInBatchRequest(SNSException):
-    code = 400
-
-    def __init__(self) -> None:
-        super().__init__(
-            "TooManyEntriesInBatchRequest",
-            "The batch request contains more entries than permissible.",
-        )
+    code = "TooManyEntriesInBatchRequest"
+    message = "The batch request contains more entries than permissible."
 
 
 class BatchEntryIdsNotDistinct(SNSException):
-    code = 400
-
-    def __init__(self) -> None:
-        super().__init__(
-            "BatchEntryIdsNotDistinct",
-            "Two or more batch entries in the request have the same Id.",
-        )
+    code = "BatchEntryIdsNotDistinct"
+    message = "Two or more batch entries in the request have the same Id."
diff --git a/moto/sns/models.py b/moto/sns/models.py
index 512aa18bf..0105e9e5c 100644
--- a/moto/sns/models.py
+++ b/moto/sns/models.py
@@ -215,6 +215,7 @@ class Topic(CloudFormationModel):
 class Subscription(BaseModel):
     def __init__(self, account_id: str, topic: Topic, endpoint: str, protocol: str):
         self.account_id = account_id
+        self.owner = account_id
         self.topic = topic
         self.endpoint = endpoint
         self.protocol = protocol
@@ -224,6 +225,10 @@ class Subscription(BaseModel):
         self._filter_policy_matcher: Optional[FilterPolicyMatcher] = None
         self.confirmed = False
 
+    @property
+    def topic_arn(self) -> str:
+        return self.topic.arn
+
     def publish(
         self,
         message: str,
@@ -871,9 +876,7 @@ class SNSBackend(BaseBackend):
         subscription = self.subscriptions.get(arn)
 
         if not subscription:
-            raise SNSNotFoundError(
-                "Subscription does not exist", template="wrapped_single_error"
-            )
+            raise SNSNotFoundError("Subscription does not exist")
         # AWS does not return the FilterPolicy scope if the FilterPolicy is not set
         # if the FilterPolicy is set and not the FilterPolicyScope, it returns the default value
         attributes = {**subscription.attributes}
diff --git a/moto/sns/responses.py b/moto/sns/responses.py
index 847b76db9..051b1085c 100644
--- a/moto/sns/responses.py
+++ b/moto/sns/responses.py
@@ -1,21 +1,27 @@
-import json
 import re
 from collections import defaultdict
-from typing import Any, Dict, Tuple, Union
+from typing import Any, Dict
 
-from moto.core.responses import TYPE_RESPONSE, BaseResponse
+from moto.core.responses import TYPE_RESPONSE, ActionResult, BaseResponse, EmptyResult
 from moto.core.utils import camelcase_to_underscores
 
-from .exceptions import InvalidParameterValue, SNSNotFoundError
+from .exceptions import InvalidParameterValue, SNSInvalidParameter, SNSNotFoundError
 from .models import SNSBackend, sns_backends
 from .utils import is_e164
 
 
+def transform_tags(tags: dict[str, str]) -> list[dict[str, str]]:
+    return [{"Key": key, "Value": value} for key, value in tags.items()]
+
+
 class SNSResponse(BaseResponse):
     SMS_ATTR_REGEX = re.compile(
         r"^attributes\.entry\.(?P<index>\d+)\.(?P<type>key|value)$"
     )
     OPT_OUT_PHONE_NUMBER_REGEX = re.compile(r"^\+?\d+$")
+    RESPONSE_KEY_PATH_TO_TRANSFORMER = {
+        "ListTagsForResourceResponse.Tags": transform_tags,
+    }
 
     def __init__(self) -> None:
         super().__init__(service_name="sns")
@@ -24,10 +30,6 @@ class SNSResponse(BaseResponse):
     def backend(self) -> SNSBackend:
         return sns_backends[self.current_account][self.region]
 
-    def _error(self, code: str, message: str, sender: str = "Sender") -> str:
-        template = self.response_template(ERROR_RESPONSE)
-        return template.render(code=code, message=message, sender=sender)
-
     def _get_attributes(self) -> Dict[str, str]:
         attributes = self._get_list_prefix("Attributes.entry")
         return dict((attribute["key"], attribute["value"]) for attribute in attributes)
@@ -100,125 +102,56 @@ class SNSResponse(BaseResponse):
 
         return transformed_message_attributes
 
-    def create_topic(self) -> str:
+    def create_topic(self) -> ActionResult:
         name = self._get_param("Name")
         attributes = self._get_attributes()
         tags = self._get_tags()
         topic = self.backend.create_topic(name, attributes, tags)
+        result = {"TopicArn": topic.arn}
+        return ActionResult(result)
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "CreateTopicResponse": {
-                        "CreateTopicResult": {"TopicArn": topic.arn},
-                        "ResponseMetadata": {
-                            "RequestId": "a8dec8b3-33a4-11df-8963-01868b7c937a"
-                        },
-                    }
-                }
-            )
-
-        template = self.response_template(CREATE_TOPIC_TEMPLATE)
-        return template.render(topic=topic)
-
-    def list_topics(self) -> str:
+    def list_topics(self) -> ActionResult:
         next_token = self._get_param("NextToken")
         topics, next_token = self.backend.list_topics(next_token=next_token)
+        result = {"Topics": topics, "NextToken": next_token}
+        return ActionResult(result)
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "ListTopicsResponse": {
-                        "ListTopicsResult": {
-                            "Topics": [{"TopicArn": topic.arn} for topic in topics],
-                            "NextToken": next_token,
-                        }
-                    },
-                    "ResponseMetadata": {
-                        "RequestId": "a8dec8b3-33a4-11df-8963-01868b7c937a"
-                    },
-                }
-            )
-
-        template = self.response_template(LIST_TOPICS_TEMPLATE)
-        return template.render(topics=topics, next_token=next_token)
-
-    def delete_topic(self) -> str:
+    def delete_topic(self) -> ActionResult:
         topic_arn = self._get_param("TopicArn")
         self.backend.delete_topic(topic_arn)
+        return EmptyResult()
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "DeleteTopicResponse": {
-                        "ResponseMetadata": {
-                            "RequestId": "a8dec8b3-33a4-11df-8963-01868b7c937a"
-                        }
-                    }
-                }
-            )
-
-        template = self.response_template(DELETE_TOPIC_TEMPLATE)
-        return template.render()
-
-    def get_topic_attributes(self) -> str:
+    def get_topic_attributes(self) -> ActionResult:
         topic_arn = self._get_param("TopicArn")
         topic = self.backend.get_topic(topic_arn)
-
-        if self.request_json:
-            attributes = {
-                "Owner": topic.account_id,
-                "Policy": topic.policy,
-                "TopicArn": topic.arn,
-                "DisplayName": topic.display_name,
-                "SubscriptionsPending": topic.subscriptions_pending,
-                "SubscriptionsConfirmed": topic.subscriptions_confimed,
-                "SubscriptionsDeleted": topic.subscriptions_deleted,
-                "DeliveryPolicy": topic.delivery_policy,
-                "EffectiveDeliveryPolicy": topic.effective_delivery_policy,
-            }
-            if topic.kms_master_key_id:
-                attributes["KmsMasterKeyId"] = topic.kms_master_key_id
-            if topic.fifo_topic == "true":
-                attributes["FifoTopic"] = topic.fifo_topic
-                attributes["ContentBasedDeduplication"] = (
-                    topic.content_based_deduplication
-                )
-            response = {
-                "GetTopicAttributesResponse": {
-                    "GetTopicAttributesResult": {"Attributes": attributes},
-                    "ResponseMetadata": {
-                        "RequestId": "057f074c-33a7-11df-9540-99d0768312d3"
-                    },
-                }
-            }
-            return json.dumps(response)
-
-        template = self.response_template(GET_TOPIC_ATTRIBUTES_TEMPLATE)
-        return template.render(topic=topic)
-
-    def set_topic_attributes(self) -> str:
+        attributes = {
+            "Owner": topic.account_id,
+            "Policy": topic.policy,
+            "TopicArn": topic.arn,
+            "DisplayName": topic.display_name,
+            "SubscriptionsPending": topic.subscriptions_pending,
+            "SubscriptionsConfirmed": topic.subscriptions_confimed,
+            "SubscriptionsDeleted": topic.subscriptions_deleted,
+            "DeliveryPolicy": topic.delivery_policy,
+            "EffectiveDeliveryPolicy": topic.effective_delivery_policy,
+        }
+        if topic.kms_master_key_id:
+            attributes["KmsMasterKeyId"] = topic.kms_master_key_id
+        if topic.fifo_topic == "true":
+            attributes["FifoTopic"] = topic.fifo_topic
+            attributes["ContentBasedDeduplication"] = topic.content_based_deduplication
+        result = {"Attributes": attributes}
+        return ActionResult(result)
+
+    def set_topic_attributes(self) -> ActionResult:
         topic_arn = self._get_param("TopicArn")
         attribute_name = self._get_param("AttributeName")
         attribute_name = camelcase_to_underscores(attribute_name)
         attribute_value = self._get_param("AttributeValue")
         self.backend.set_topic_attribute(topic_arn, attribute_name, attribute_value)
+        return EmptyResult()
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "SetTopicAttributesResponse": {
-                        "ResponseMetadata": {
-                            "RequestId": "a8763b99-33a7-11df-a9b7-05d48da6f042"
-                        }
-                    }
-                }
-            )
-
-        template = self.response_template(SET_TOPIC_ATTRIBUTES_TEMPLATE)
-        return template.render()
-
-    def subscribe(self) -> str:
+    def subscribe(self) -> ActionResult:
         topic_arn = self._get_param("TopicArn")
         endpoint = self._get_param("Endpoint")
         protocol = self._get_param("Protocol")
@@ -239,107 +172,32 @@ class SNSResponse(BaseResponse):
                     subscription.arn, attr_name, attr_value
                 )
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "SubscribeResponse": {
-                        "SubscribeResult": {"SubscriptionArn": subscription.arn},
-                        "ResponseMetadata": {
-                            "RequestId": "a8763b99-33a7-11df-a9b7-05d48da6f042"
-                        },
-                    }
-                }
-            )
-
-        template = self.response_template(SUBSCRIBE_TEMPLATE)
-        return template.render(subscription=subscription)
+        result = {"SubscriptionArn": subscription.arn}
+        return ActionResult(result)
 
-    def unsubscribe(self) -> str:
+    def unsubscribe(self) -> ActionResult:
         subscription_arn = self._get_param("SubscriptionArn")
         self.backend.unsubscribe(subscription_arn)
+        return EmptyResult()
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "UnsubscribeResponse": {
-                        "ResponseMetadata": {
-                            "RequestId": "a8763b99-33a7-11df-a9b7-05d48da6f042"
-                        }
-                    }
-                }
-            )
-
-        template = self.response_template(UNSUBSCRIBE_TEMPLATE)
-        return template.render()
-
-    def list_subscriptions(self) -> str:
+    def list_subscriptions(self) -> ActionResult:
         next_token = self._get_param("NextToken")
         subscriptions, next_token = self.backend.list_subscriptions(
             next_token=next_token
         )
+        result = {"Subscriptions": subscriptions, "NextToken": next_token}
+        return ActionResult(result)
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "ListSubscriptionsResponse": {
-                        "ListSubscriptionsResult": {
-                            "Subscriptions": [
-                                {
-                                    "TopicArn": subscription.topic.arn,
-                                    "Protocol": subscription.protocol,
-                                    "SubscriptionArn": subscription.arn,
-                                    "Owner": subscription.topic.account_id,
-                                    "Endpoint": subscription.endpoint,
-                                }
-                                for subscription in subscriptions
-                            ],
-                            "NextToken": next_token,
-                        },
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937a"
-                        },
-                    }
-                }
-            )
-
-        template = self.response_template(LIST_SUBSCRIPTIONS_TEMPLATE)
-        return template.render(subscriptions=subscriptions, next_token=next_token)
-
-    def list_subscriptions_by_topic(self) -> str:
+    def list_subscriptions_by_topic(self) -> ActionResult:
         topic_arn = self._get_param("TopicArn")
         next_token = self._get_param("NextToken")
         subscriptions, next_token = self.backend.list_subscriptions_by_topic(
             topic_arn, next_token=next_token
         )
+        result = {"Subscriptions": subscriptions, "NextToken": next_token}
+        return ActionResult(result)
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "ListSubscriptionsByTopicResponse": {
-                        "ListSubscriptionsByTopicResult": {
-                            "Subscriptions": [
-                                {
-                                    "TopicArn": subscription.topic.arn,
-                                    "Protocol": subscription.protocol,
-                                    "SubscriptionArn": subscription.arn,
-                                    "Owner": subscription.topic.account_id,
-                                    "Endpoint": subscription.endpoint,
-                                }
-                                for subscription in subscriptions
-                            ],
-                            "NextToken": next_token,
-                        },
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937a"
-                        },
-                    }
-                }
-            )
-
-        template = self.response_template(LIST_SUBSCRIPTIONS_BY_TOPIC_TEMPLATE)
-        return template.render(subscriptions=subscriptions, next_token=next_token)
-
-    def publish(self) -> Union[str, Tuple[str, Dict[str, int]]]:
+    def publish(self) -> ActionResult:
         target_arn = self._get_param("TargetArn")
         topic_arn = self._get_param("TopicArn")
         phone_number = self._get_param("PhoneNumber")
@@ -354,12 +212,7 @@ class SNSResponse(BaseResponse):
         if phone_number is not None:
             # Check phone is correct syntax (e164)
             if not is_e164(phone_number):
-                return (
-                    self._error(
-                        "InvalidParameter", "Phone number does not meet the E164 format"
-                    ),
-                    dict(status=400),
-                )
+                raise SNSInvalidParameter("Phone number does not meet the E164 format")
         elif target_arn is not None:
             arn = target_arn
         else:
@@ -379,25 +232,12 @@ class SNSResponse(BaseResponse):
                 message_structure=message_structure,
             )
         except ValueError as err:
-            error_response = self._error("InvalidParameter", str(err))
-            return error_response, dict(status=400)
-
-        if self.request_json:
-            return json.dumps(
-                {
-                    "PublishResponse": {
-                        "PublishResult": {"MessageId": message_id},
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937a"
-                        },
-                    }
-                }
-            )
+            raise SNSInvalidParameter(str(err))
 
-        template = self.response_template(PUBLISH_TEMPLATE)
-        return template.render(message_id=message_id)
+        result = {"MessageId": message_id}
+        return ActionResult(result)
 
-    def publish_batch(self) -> str:
+    def publish_batch(self) -> ActionResult:
         topic_arn = self._get_param("TopicArn")
         publish_batch_request_entries = self._get_multi_param(
             "PublishBatchRequestEntries.member"
@@ -416,256 +256,92 @@ class SNSResponse(BaseResponse):
             topic_arn=topic_arn,
             publish_batch_request_entries=publish_batch_request_entries,
         )
-        template = self.response_template(PUBLISH_BATCH_TEMPLATE)
-        return template.render(successful=successful, failed=failed)
+        result = {"Successful": successful, "Failed": failed}
+        return ActionResult(result)
 
-    def create_platform_application(self) -> str:
+    def create_platform_application(self) -> ActionResult:
         name = self._get_param("Name")
         platform = self._get_param("Platform")
         attributes = self._get_attributes()
         platform_application = self.backend.create_platform_application(
             name, platform, attributes
         )
+        result = {"PlatformApplicationArn": platform_application.arn}
+        return ActionResult(result)
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "CreatePlatformApplicationResponse": {
-                        "CreatePlatformApplicationResult": {
-                            "PlatformApplicationArn": platform_application.arn
-                        },
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937b"
-                        },
-                    }
-                }
-            )
-
-        template = self.response_template(CREATE_PLATFORM_APPLICATION_TEMPLATE)
-        return template.render(platform_application=platform_application)
-
-    def get_platform_application_attributes(self) -> str:
+    def get_platform_application_attributes(self) -> ActionResult:
         arn = self._get_param("PlatformApplicationArn")
         attributes = self.backend.get_platform_application_attributes(arn)
+        result = {"Attributes": attributes}
+        return ActionResult(result)
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "GetPlatformApplicationAttributesResponse": {
-                        "GetPlatformApplicationAttributesResult": {
-                            "Attributes": attributes
-                        },
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937f"
-                        },
-                    }
-                }
-            )
-
-        template = self.response_template(GET_PLATFORM_APPLICATION_ATTRIBUTES_TEMPLATE)
-        return template.render(attributes=attributes)
-
-    def set_platform_application_attributes(self) -> str:
+    def set_platform_application_attributes(self) -> ActionResult:
         arn = self._get_param("PlatformApplicationArn")
         attributes = self._get_attributes()
 
         self.backend.set_platform_application_attributes(arn, attributes)
+        return EmptyResult()
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "SetPlatformApplicationAttributesResponse": {
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3775-12df-8963-01868b7c937f"
-                        }
-                    }
-                }
-            )
-
-        template = self.response_template(SET_PLATFORM_APPLICATION_ATTRIBUTES_TEMPLATE)
-        return template.render()
-
-    def list_platform_applications(self) -> str:
+    def list_platform_applications(self) -> ActionResult:
         applications = self.backend.list_platform_applications()
+        result = {"PlatformApplications": applications}
+        return ActionResult(result)
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "ListPlatformApplicationsResponse": {
-                        "ListPlatformApplicationsResult": {
-                            "PlatformApplications": [
-                                {
-                                    "PlatformApplicationArn": application.arn,
-                                    "attributes": application.attributes,
-                                }
-                                for application in applications
-                            ],
-                            "NextToken": None,
-                        },
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937c"
-                        },
-                    }
-                }
-            )
-
-        template = self.response_template(LIST_PLATFORM_APPLICATIONS_TEMPLATE)
-        return template.render(applications=applications)
-
-    def delete_platform_application(self) -> str:
+    def delete_platform_application(self) -> ActionResult:
         platform_arn = self._get_param("PlatformApplicationArn")
         self.backend.delete_platform_application(platform_arn)
+        return EmptyResult()
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "DeletePlatformApplicationResponse": {
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937e"
-                        }
-                    }
-                }
-            )
-
-        template = self.response_template(DELETE_PLATFORM_APPLICATION_TEMPLATE)
-        return template.render()
-
-    def create_platform_endpoint(self) -> str:
+    def create_platform_endpoint(self) -> ActionResult:
         application_arn = self._get_param("PlatformApplicationArn")
         application = self.backend.get_application(application_arn)
-
         custom_user_data = self._get_param("CustomUserData")
         token = self._get_param("Token")
         attributes = self._get_attributes()
-
         platform_endpoint = self.backend.create_platform_endpoint(
             application, custom_user_data, token, attributes
         )
+        result = {"EndpointArn": platform_endpoint.arn}
+        return ActionResult(result)
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "CreatePlatformEndpointResponse": {
-                        "CreatePlatformEndpointResult": {
-                            "EndpointArn": platform_endpoint.arn
-                        },
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3779-11df-8963-01868b7c937b"
-                        },
-                    }
-                }
-            )
-
-        template = self.response_template(CREATE_PLATFORM_ENDPOINT_TEMPLATE)
-        return template.render(platform_endpoint=platform_endpoint)
-
-    def list_endpoints_by_platform_application(self) -> str:
+    def list_endpoints_by_platform_application(self) -> ActionResult:
         application_arn = self._get_param("PlatformApplicationArn")
         self.backend.get_application(application_arn)
         endpoints = self.backend.list_endpoints_by_platform_application(application_arn)
+        result = {"Endpoints": endpoints}
+        return ActionResult(result)
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "ListEndpointsByPlatformApplicationResponse": {
-                        "ListEndpointsByPlatformApplicationResult": {
-                            "Endpoints": [
-                                {
-                                    "Attributes": endpoint.attributes,
-                                    "EndpointArn": endpoint.arn,
-                                }
-                                for endpoint in endpoints
-                            ],
-                            "NextToken": None,
-                        },
-                        "ResponseMetadata": {
-                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937a"
-                        },
-                    }
-                }
-            )
-
-        template = self.response_template(
-            LIST_ENDPOINTS_BY_PLATFORM_APPLICATION_TEMPLATE
-        )
-        return template.render(endpoints=endpoints)
-
-    def get_endpoint_attributes(self) -> Union[str, Tuple[str, Dict[str, int]]]:
+    def get_endpoint_attributes(self) -> ActionResult:
         arn = self._get_param("EndpointArn")
-        try:
-            attributes = self.backend.get_endpoint_attributes(arn)
-
-            if self.request_json:
-                return json.dumps(
-                    {
-                        "GetEndpointAttributesResponse": {
-                            "GetEndpointAttributesResult": {"Attributes": attributes},
-                            "ResponseMetadata": {
-                                "RequestId": "384ac68d-3775-11df-8963-01868b7c937f"
-                            },
-                        }
-                    }
-                )
-
-            template = self.response_template(GET_ENDPOINT_ATTRIBUTES_TEMPLATE)
-            return template.render(attributes=attributes)
-        except SNSNotFoundError:
-            error_response = self._error("NotFound", "Endpoint does not exist")
-            return error_response, dict(status=404)
+        attributes = self.backend.get_endpoint_attributes(arn)
+        result = {"Attributes": attributes}
+        return ActionResult(result)
 
-    def set_endpoint_attributes(self) -> Union[str, Tuple[str, Dict[str, int]]]:
+    def set_endpoint_attributes(self) -> ActionResult:
         arn = self._get_param("EndpointArn")
         attributes = self._get_attributes()
-
         self.backend.set_endpoint_attributes(arn, attributes)
+        return EmptyResult()
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "SetEndpointAttributesResponse": {
-                        "ResponseMetadata": {
-                            "RequestId": "384bc68d-3775-12df-8963-01868b7c937f"
-                        }
-                    }
-                }
-            )
-
-        template = self.response_template(SET_ENDPOINT_ATTRIBUTES_TEMPLATE)
-        return template.render()
-
-    def delete_endpoint(self) -> str:
+    def delete_endpoint(self) -> ActionResult:
         arn = self._get_param("EndpointArn")
         self.backend.delete_endpoint(arn)
+        return EmptyResult()
 
-        if self.request_json:
-            return json.dumps(
-                {
-                    "DeleteEndpointResponse": {
-                        "ResponseMetadata": {
-                            "RequestId": "384bc68d-3775-12df-8963-01868b7c937f"
-                        }
-                    }
-                }
-            )
-
-        template = self.response_template(DELETE_ENDPOINT_TEMPLATE)
-        return template.render()
-
-    def get_subscription_attributes(self) -> str:
+    def get_subscription_attributes(self) -> ActionResult:
         arn = self._get_param("SubscriptionArn")
         attributes = self.backend.get_subscription_attributes(arn)
-        template = self.response_template(GET_SUBSCRIPTION_ATTRIBUTES_TEMPLATE)
-        return template.render(attributes=attributes)
+        result = {"Attributes": attributes}
+        return ActionResult(result)
 
-    def set_subscription_attributes(self) -> str:
+    def set_subscription_attributes(self) -> ActionResult:
         arn = self._get_param("SubscriptionArn")
         attr_name = self._get_param("AttributeName")
         attr_value = self._get_param("AttributeValue")
         self.backend.set_subscription_attributes(arn, attr_name, attr_value)
-        template = self.response_template(SET_SUBSCRIPTION_ATTRIBUTES_TEMPLATE)
-        return template.render()
+        return EmptyResult()
 
-    def set_sms_attributes(self) -> str:
+    def set_sms_attributes(self) -> ActionResult:
         # attributes.entry.1.key
         # attributes.entry.1.value
         # to
@@ -687,54 +363,45 @@ class SNSResponse(BaseResponse):
 
         self.backend.set_sms_attributes(result)
 
-        template = self.response_template(SET_SMS_ATTRIBUTES_TEMPLATE)
-        return template.render()
+        return EmptyResult()
 
-    def get_sms_attributes(self) -> str:
+    def get_sms_attributes(self) -> ActionResult:
         filter_list = set()
         for key, value in self.querystring.items():
             if key.startswith("attributes.member.1"):
                 filter_list.add(value[0])
-
-        result = self.backend.get_sms_attributes(filter_list)
-
-        template = self.response_template(GET_SMS_ATTRIBUTES_TEMPLATE)
-        return template.render(attributes=result)
+        attributes = self.backend.get_sms_attributes(filter_list)
+        result = {"attributes": attributes}
+        return ActionResult(result)
 
     def check_if_phone_number_is_opted_out(
         self,
-    ) -> Union[str, Tuple[str, Dict[str, int]]]:
+    ) -> ActionResult:
         number = self._get_param("phoneNumber")
         if self.OPT_OUT_PHONE_NUMBER_REGEX.match(number) is None:
-            error_response = self._error(
-                code="InvalidParameter",
-                message="Invalid parameter: PhoneNumber Reason: input incorrectly formatted",
+            raise SNSInvalidParameter(
+                "Invalid parameter: PhoneNumber Reason: input incorrectly formatted"
             )
-            return error_response, dict(status=400)
 
         x = self.backend.check_if_phone_number_is_opted_out(number)
-        template = self.response_template(CHECK_IF_OPTED_OUT_TEMPLATE)
-        return template.render(opt_out=str(x).lower())
+        result = {"isOptedOut": x}
+        return ActionResult(result)
 
-    def list_phone_numbers_opted_out(self) -> str:
+    def list_phone_numbers_opted_out(self) -> ActionResult:
         numbers = self.backend.list_phone_numbers_opted_out()
-        template = self.response_template(LIST_OPTOUT_TEMPLATE)
-        return template.render(opt_outs=numbers)
+        result = {"phoneNumbers": numbers}
+        return ActionResult(result)
 
-    def opt_in_phone_number(self) -> str:
+    def opt_in_phone_number(self) -> ActionResult:
         number = self._get_param("phoneNumber")
-
         self.backend.opt_in_phone_number(number)
+        return EmptyResult()
 
-        template = self.response_template(OPT_IN_NUMBER_TEMPLATE)
-        return template.render()
-
-    def add_permission(self) -> str:
+    def add_permission(self) -> ActionResult:
         topic_arn = self._get_param("TopicArn")
         label = self._get_param("Label")
         aws_account_ids = self._get_multi_param("AWSAccountId.member.")
         action_names = self._get_multi_param("ActionName.member.")
-
         self.backend.add_permission(
             region_name=self.region,
             topic_arn=topic_arn,
@@ -742,25 +409,19 @@ class SNSResponse(BaseResponse):
             aws_account_ids=aws_account_ids,
             action_names=action_names,
         )
+        return EmptyResult()
 
-        template = self.response_template(ADD_PERMISSION_TEMPLATE)
-        return template.render()
-
-    def remove_permission(self) -> str:
+    def remove_permission(self) -> ActionResult:
         topic_arn = self._get_param("TopicArn")
         label = self._get_param("Label")
-
         self.backend.remove_permission(topic_arn, label)
+        return EmptyResult()
 
-        template = self.response_template(DEL_PERMISSION_TEMPLATE)
-        return template.render()
-
-    def confirm_subscription(self) -> Union[str, Tuple[str, Dict[str, int]]]:
+    def confirm_subscription(self) -> ActionResult:
         arn = self._get_param("TopicArn")
 
         if arn not in self.backend.topics:
-            error_response = self._error("NotFound", "Topic does not exist")
-            return error_response, dict(status=404)
+            raise SNSNotFoundError("Topic does not exist")
 
         # Once Tokens are stored by the `subscribe` endpoint and distributed
         # to the client somehow, then we can check validity of tokens
@@ -777,493 +438,31 @@ class SNSResponse(BaseResponse):
         #     )
         #     return error_response, dict(status=400)
 
-        template = self.response_template(CONFIRM_SUBSCRIPTION_TEMPLATE)
-        return template.render(sub_arn=f"{arn}:68762e72-e9b1-410a-8b3b-903da69ee1d5")
+        result = {"SubscriptionArn": f"{arn}:68762e72-e9b1-410a-8b3b-903da69ee1d5"}
+        return ActionResult(result)
 
-    def list_tags_for_resource(self) -> str:
+    def list_tags_for_resource(self) -> ActionResult:
         arn = self._get_param("ResourceArn")
+        tags = self.backend.list_tags_for_resource(arn)
+        result = {"Tags": tags}
+        return ActionResult(result)
 
-        result = self.backend.list_tags_for_resource(arn)
-
-        template = self.response_template(LIST_TAGS_FOR_RESOURCE_TEMPLATE)
-        return template.render(tags=result)
-
-    def tag_resource(self) -> str:
+    def tag_resource(self) -> ActionResult:
         arn = self._get_param("ResourceArn")
         tags = self._get_tags()
-
         self.backend.tag_resource(arn, tags)
+        return EmptyResult()
 
-        return self.response_template(TAG_RESOURCE_TEMPLATE).render()
-
-    def untag_resource(self) -> str:
+    def untag_resource(self) -> ActionResult:
         arn = self._get_param("ResourceArn")
         tag_keys = self._get_multi_param("TagKeys.member")
-
         self.backend.untag_resource(arn, tag_keys)
-
-        return self.response_template(UNTAG_RESOURCE_TEMPLATE).render()
+        return EmptyResult()
 
     @staticmethod
-    def serve_pem(request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[misc]
+    def serve_pem(request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:
         sns = SNSResponse()
         sns.setup_class(request, full_url, headers)
         key_name = full_url.split("/")[-1]
         key = sns.backend._message_public_keys[key_name]
         return 200, {}, key
-
-
-CREATE_TOPIC_TEMPLATE = """<CreateTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-     <CreateTopicResult>
-       <TopicArn>{{ topic.arn }}</TopicArn>
-     </CreateTopicResult>
-     <ResponseMetadata>
-       <RequestId>a8dec8b3-33a4-11df-8963-01868b7c937a</RequestId>
-     </ResponseMetadata>
-   </CreateTopicResponse>"""
-
-LIST_TOPICS_TEMPLATE = """<ListTopicsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ListTopicsResult>
-    <Topics>
-    {% for topic in topics %}
-      <member>
-        <TopicArn>{{ topic.arn }}</TopicArn>
-      </member>
-    {% endfor %}
-    </Topics>
-    {% if next_token  %}
-    <NextToken>{{ next_token }}</NextToken>
-    {% endif %}
-  </ListTopicsResult>
-  <ResponseMetadata>
-    <RequestId>3f1478c7-33a9-11df-9540-99d0768312d3</RequestId>
-  </ResponseMetadata>
-</ListTopicsResponse>"""
-
-DELETE_TOPIC_TEMPLATE = """<DeleteTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>f3aa9ac9-3c3d-11df-8235-9dab105e9c32</RequestId>
-  </ResponseMetadata>
-</DeleteTopicResponse>"""
-
-GET_TOPIC_ATTRIBUTES_TEMPLATE = """<GetTopicAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <GetTopicAttributesResult>
-    <Attributes>
-      <entry>
-        <key>Owner</key>
-        <value>{{ topic.account_id }}</value>
-      </entry>
-      <entry>
-        <key>Policy</key>
-        <value>{{ topic.policy }}</value>
-      </entry>
-      <entry>
-        <key>TopicArn</key>
-        <value>{{ topic.arn }}</value>
-      </entry>
-      <entry>
-        <key>DisplayName</key>
-        <value>{{ topic.display_name }}</value>
-      </entry>
-      <entry>
-        <key>SubscriptionsPending</key>
-        <value>{{ topic.subscriptions_pending }}</value>
-      </entry>
-      <entry>
-        <key>SubscriptionsConfirmed</key>
-        <value>{{ topic.subscriptions_confimed }}</value>
-      </entry>
-      <entry>
-        <key>SubscriptionsDeleted</key>
-        <value>{{ topic.subscriptions_deleted }}</value>
-      </entry>
-      <entry>
-        <key>DeliveryPolicy</key>
-        <value>{{ topic.delivery_policy }}</value>
-      </entry>
-      <entry>
-        <key>EffectiveDeliveryPolicy</key>
-        <value>{{ topic.effective_delivery_policy }}</value>
-      </entry>
-      {% if topic.kms_master_key_id %}
-      <entry>
-        <key>KmsMasterKeyId</key>
-        <value>{{ topic.kms_master_key_id }}</value>
-      </entry>
-      {% endif %}
-      {% if topic.fifo_topic == 'true' %}
-      <entry>
-        <key>FifoTopic</key>
-        <value>{{ topic.fifo_topic }}</value>
-      </entry>
-      <entry>
-        <key>ContentBasedDeduplication</key>
-        <value>{{ topic.content_based_deduplication }}</value>
-      </entry>
-      {% endif %}
-    </Attributes>
-  </GetTopicAttributesResult>
-  <ResponseMetadata>
-    <RequestId>057f074c-33a7-11df-9540-99d0768312d3</RequestId>
-  </ResponseMetadata>
-</GetTopicAttributesResponse>"""
-
-SET_TOPIC_ATTRIBUTES_TEMPLATE = """<SetTopicAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>a8763b99-33a7-11df-a9b7-05d48da6f042</RequestId>
-  </ResponseMetadata>
-</SetTopicAttributesResponse>"""
-
-CREATE_PLATFORM_APPLICATION_TEMPLATE = """<CreatePlatformApplicationResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <CreatePlatformApplicationResult>
-    <PlatformApplicationArn>{{ platform_application.arn }}</PlatformApplicationArn>
-  </CreatePlatformApplicationResult>
-  <ResponseMetadata>
-    <RequestId>b6f0e78b-e9d4-5a0e-b973-adc04e8a4ff9</RequestId>
-  </ResponseMetadata>
-</CreatePlatformApplicationResponse>"""
-
-CREATE_PLATFORM_ENDPOINT_TEMPLATE = """<CreatePlatformEndpointResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <CreatePlatformEndpointResult>
-    <EndpointArn>{{ platform_endpoint.arn }}</EndpointArn>
-  </CreatePlatformEndpointResult>
-  <ResponseMetadata>
-    <RequestId>6613341d-3e15-53f7-bf3c-7e56994ba278</RequestId>
-  </ResponseMetadata>
-</CreatePlatformEndpointResponse>"""
-
-LIST_PLATFORM_APPLICATIONS_TEMPLATE = """<ListPlatformApplicationsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ListPlatformApplicationsResult>
-    <PlatformApplications>
-    {% for application in applications %}
-      <member>
-      <PlatformApplicationArn>{{ application.arn }}</PlatformApplicationArn>
-        <Attributes>
-        {% for attribute in application.attributes %}
-          <entry>
-            <key>{{ attribute }}</key>
-            <value>{{ application.attributes[attribute] }}</value>
-          </entry>
-        {% endfor %}
-        </Attributes>
-      </member>
-    {% endfor %}
-    </PlatformApplications>
-  </ListPlatformApplicationsResult>
-  <ResponseMetadata>
-    <RequestId>315a335e-85d8-52df-9349-791283cbb529</RequestId>
-  </ResponseMetadata>
-</ListPlatformApplicationsResponse>"""
-
-DELETE_PLATFORM_APPLICATION_TEMPLATE = """<DeletePlatformApplicationResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>097dac18-7a77-5823-a8dd-e65476dcb037</RequestId>
-  </ResponseMetadata>
-</DeletePlatformApplicationResponse>"""
-
-GET_ENDPOINT_ATTRIBUTES_TEMPLATE = """<GetEndpointAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <GetEndpointAttributesResult>
-    <Attributes>
-    {% for attribute in attributes %}
-      <entry>
-        <key>{{ attribute }}</key>
-        <value>{{ attributes[attribute] }}</value>
-      </entry>
-    {% endfor %}
-    </Attributes>
-  </GetEndpointAttributesResult>
-  <ResponseMetadata>
-    <RequestId>6c725a19-a142-5b77-94f9-1055a9ea04e7</RequestId>
-  </ResponseMetadata>
-</GetEndpointAttributesResponse>"""
-
-LIST_ENDPOINTS_BY_PLATFORM_APPLICATION_TEMPLATE = """<ListEndpointsByPlatformApplicationResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ListEndpointsByPlatformApplicationResult>
-    <Endpoints>
-      {% for endpoint in endpoints %}
-      <member>
-        <EndpointArn>{{ endpoint.arn }}</EndpointArn>
-        <Attributes>
-          {% for attribute in endpoint.attributes %}
-          <entry>
-            <key>{{ attribute }}</key>
-            <value>{{ endpoint.attributes[attribute] }}</value>
-          </entry>
-          {% endfor %}
-        </Attributes>
-      </member>
-      {% endfor %}
-    </Endpoints>
-  </ListEndpointsByPlatformApplicationResult>
-  <ResponseMetadata>
-    <RequestId>9a48768c-dac8-5a60-aec0-3cc27ea08d96</RequestId>
-  </ResponseMetadata>
-</ListEndpointsByPlatformApplicationResponse>"""
-
-GET_PLATFORM_APPLICATION_ATTRIBUTES_TEMPLATE = """<GetPlatformApplicationAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <GetPlatformApplicationAttributesResult>
-    <Attributes>
-    {% for attribute in attributes %}
-      <entry>
-        <key>{{ attribute }}</key>
-        <value>{{ attributes[attribute] }}</value>
-      </entry>
-    {% endfor %}
-    </Attributes>
-  </GetPlatformApplicationAttributesResult>
-  <ResponseMetadata>
-    <RequestId>74848df2-87f6-55ed-890c-c7be80442462</RequestId>
-  </ResponseMetadata>
-</GetPlatformApplicationAttributesResponse>"""
-
-PUBLISH_TEMPLATE = """<PublishResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <PublishResult>
-    <MessageId>{{ message_id }}</MessageId>
-  </PublishResult>
-  <ResponseMetadata>
-    <RequestId>f187a3c1-376f-11df-8963-01868b7c937a</RequestId>
-  </ResponseMetadata>
-</PublishResponse>"""
-
-SET_ENDPOINT_ATTRIBUTES_TEMPLATE = """<SetEndpointAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>2fe0bfc7-3e85-5ee5-a9e2-f58b35e85f6a</RequestId>
-  </ResponseMetadata>
-</SetEndpointAttributesResponse>"""
-
-DELETE_ENDPOINT_TEMPLATE = """<DeleteEndpointResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
- <ResponseMetadata>
- <RequestId>c1d2b191-353c-5a5f-8969-fbdd3900afa8</RequestId>
- </ResponseMetadata>
-</DeleteEndpointResponse>"""
-
-
-SET_PLATFORM_APPLICATION_ATTRIBUTES_TEMPLATE = """<SetPlatformApplicationAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>cf577bcc-b3dc-5463-88f1-3180b9412395</RequestId>
-  </ResponseMetadata>
-</SetPlatformApplicationAttributesResponse>"""
-
-SUBSCRIBE_TEMPLATE = """<SubscribeResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <SubscribeResult>
-    <SubscriptionArn>{{ subscription.arn }}</SubscriptionArn>
-  </SubscribeResult>
-  <ResponseMetadata>
-    <RequestId>c4407779-24a4-56fa-982c-3d927f93a775</RequestId>
-  </ResponseMetadata>
-</SubscribeResponse>"""
-
-UNSUBSCRIBE_TEMPLATE = """<UnsubscribeResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>18e0ac39-3776-11df-84c0-b93cc1666b84</RequestId>
-  </ResponseMetadata>
-</UnsubscribeResponse>"""
-
-LIST_SUBSCRIPTIONS_TEMPLATE = """<ListSubscriptionsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ListSubscriptionsResult>
-    <Subscriptions>
-    {% for subscription in subscriptions %}
-      <member>
-        <TopicArn>{{ subscription.topic.arn }}</TopicArn>
-        <Protocol>{{ subscription.protocol }}</Protocol>
-        <SubscriptionArn>{{ subscription.arn }}</SubscriptionArn>
-        <Owner>{{ subscription.account_id }}</Owner>
-        <Endpoint>{{ subscription.endpoint }}</Endpoint>
-      </member>
-    {% endfor %}
-    </Subscriptions>
-    {% if next_token  %}
-    <NextToken>{{ next_token }}</NextToken>
-    {% endif %}
-  </ListSubscriptionsResult>
-  <ResponseMetadata>
-    <RequestId>384ac68d-3775-11df-8963-01868b7c937a</RequestId>
-  </ResponseMetadata>
-</ListSubscriptionsResponse>"""
-
-LIST_SUBSCRIPTIONS_BY_TOPIC_TEMPLATE = """<ListSubscriptionsByTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ListSubscriptionsByTopicResult>
-    <Subscriptions>
-    {% for subscription in subscriptions %}
-      <member>
-        <TopicArn>{{ subscription.topic.arn }}</TopicArn>
-        <Protocol>{{ subscription.protocol }}</Protocol>
-        <SubscriptionArn>{{ subscription.arn }}</SubscriptionArn>
-        <Owner>{{ subscription.account_id }}</Owner>
-        <Endpoint>{{ subscription.endpoint }}</Endpoint>
-      </member>
-    {% endfor %}
-    </Subscriptions>
-    {% if next_token  %}
-    <NextToken>{{ next_token }}</NextToken>
-    {% endif %}
-  </ListSubscriptionsByTopicResult>
-  <ResponseMetadata>
-    <RequestId>384ac68d-3775-11df-8963-01868b7c937a</RequestId>
-  </ResponseMetadata>
-</ListSubscriptionsByTopicResponse>"""
-
-
-# Not responding aws system attribetus like 'Owner' and 'SubscriptionArn'
-GET_SUBSCRIPTION_ATTRIBUTES_TEMPLATE = """<GetSubscriptionAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <GetSubscriptionAttributesResult>
-    <Attributes>
-      {% for name, value in attributes.items() %}
-      <entry>
-        <key>{{ name }}</key>
-        <value>{{ value }}</value>
-      </entry>
-      {% endfor %}
-    </Attributes>
-  </GetSubscriptionAttributesResult>
-  <ResponseMetadata>
-    <RequestId>057f074c-33a7-11df-9540-99d0768312d3</RequestId>
-  </ResponseMetadata>
-</GetSubscriptionAttributesResponse>"""
-
-
-SET_SUBSCRIPTION_ATTRIBUTES_TEMPLATE = """<SetSubscriptionAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>a8763b99-33a7-11df-a9b7-05d48da6f042</RequestId>
-  </ResponseMetadata>
-</SetSubscriptionAttributesResponse>"""
-
-SET_SMS_ATTRIBUTES_TEMPLATE = """<SetSMSAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <SetSMSAttributesResult/>
-  <ResponseMetadata>
-    <RequestId>26332069-c04a-5428-b829-72524b56a364</RequestId>
-  </ResponseMetadata>
-</SetSMSAttributesResponse>"""
-
-GET_SMS_ATTRIBUTES_TEMPLATE = """<GetSMSAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <GetSMSAttributesResult>
-    <attributes>
-      {% for name, value in attributes.items() %}
-      {% if value %}
-      <entry>
-        <key>{{ name }}</key>
-        <value>{{ value }}</value>
-      </entry>
-      {% endif %}
-      {% endfor %}
-    </attributes>
-  </GetSMSAttributesResult>
-  <ResponseMetadata>
-    <RequestId>287f9554-8db3-5e66-8abc-c76f0186db7e</RequestId>
-  </ResponseMetadata>
-</GetSMSAttributesResponse>"""
-
-CHECK_IF_OPTED_OUT_TEMPLATE = """<CheckIfPhoneNumberIsOptedOutResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <CheckIfPhoneNumberIsOptedOutResult>
-    <isOptedOut>{{ opt_out }}</isOptedOut>
-  </CheckIfPhoneNumberIsOptedOutResult>
-  <ResponseMetadata>
-    <RequestId>287f9554-8db3-5e66-8abc-c76f0186db7e</RequestId>
-  </ResponseMetadata>
-</CheckIfPhoneNumberIsOptedOutResponse>"""
-
-ERROR_RESPONSE = """<ErrorResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <Error>
-    <Type>{{ sender }}</Type>
-    <Code>{{ code }}</Code>
-    <Message>{{ message }}</Message>
-  </Error>
-  <RequestId>9dd01905-5012-5f99-8663-4b3ecd0dfaef</RequestId>
-</ErrorResponse>"""
-
-LIST_OPTOUT_TEMPLATE = """<ListPhoneNumbersOptedOutResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ListPhoneNumbersOptedOutResult>
-    <phoneNumbers>
-      {% for item in opt_outs %}
-      <member>{{ item }}</member>
-      {% endfor %}
-    </phoneNumbers>
-  </ListPhoneNumbersOptedOutResult>
-  <ResponseMetadata>
-    <RequestId>985e196d-a237-51b6-b33a-4b5601276b38</RequestId>
-  </ResponseMetadata>
-</ListPhoneNumbersOptedOutResponse>"""
-
-OPT_IN_NUMBER_TEMPLATE = """<OptInPhoneNumberResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <OptInPhoneNumberResult/>
-  <ResponseMetadata>
-    <RequestId>4c61842c-0796-50ef-95ac-d610c0bc8cf8</RequestId>
-  </ResponseMetadata>
-</OptInPhoneNumberResponse>"""
-
-ADD_PERMISSION_TEMPLATE = """<AddPermissionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>c046e713-c5ff-5888-a7bc-b52f0e4f1299</RequestId>
-  </ResponseMetadata>
-</AddPermissionResponse>"""
-
-DEL_PERMISSION_TEMPLATE = """<RemovePermissionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>e767cc9f-314b-5e1b-b283-9ea3fd4e38a3</RequestId>
-  </ResponseMetadata>
-</RemovePermissionResponse>"""
-
-CONFIRM_SUBSCRIPTION_TEMPLATE = """<ConfirmSubscriptionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ConfirmSubscriptionResult>
-    <SubscriptionArn>{{ sub_arn }}</SubscriptionArn>
-  </ConfirmSubscriptionResult>
-  <ResponseMetadata>
-    <RequestId>16eb4dde-7b3c-5b3e-a22a-1fe2a92d3293</RequestId>
-  </ResponseMetadata>
-</ConfirmSubscriptionResponse>"""
-
-LIST_TAGS_FOR_RESOURCE_TEMPLATE = """<ListTagsForResourceResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ListTagsForResourceResult>
-    <Tags>
-      {% for name, value in tags.items() %}
-      <member>
-        <Key>{{ name }}</Key>
-        <Value>{{ value }}</Value>
-      </member>
-      {% endfor %}
-    </Tags>
-  </ListTagsForResourceResult>
-  <ResponseMetadata>
-    <RequestId>97fa763f-861b-5223-a946-20251f2a42e2</RequestId>
-  </ResponseMetadata>
-</ListTagsForResourceResponse>"""
-
-TAG_RESOURCE_TEMPLATE = """<TagResourceResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-    <TagResourceResult/>
-    <ResponseMetadata>
-        <RequestId>fd4ab1da-692f-50a7-95ad-e7c665877d98</RequestId>
-    </ResponseMetadata>
-</TagResourceResponse>"""
-
-UNTAG_RESOURCE_TEMPLATE = """<UntagResourceResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-    <UntagResourceResult/>
-    <ResponseMetadata>
-        <RequestId>14eb7b1a-4cbd-5a56-80db-2d06412df769</RequestId>
-    </ResponseMetadata>
-</UntagResourceResponse>"""
-
-PUBLISH_BATCH_TEMPLATE = """<PublishBatchResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
-  <ResponseMetadata>
-    <RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
-  </ResponseMetadata>
-  <PublishBatchResult>
-    <Successful>
-{% for successful in successful %}
-      <member>
-        <Id>{{ successful["Id"] }}</Id>
-        <MessageId>{{ successful["MessageId"] }}</MessageId>
-      </member>
-{% endfor %}
-    </Successful>
-    <Failed>
-{% for failed in failed %}
-      <member>
-        <Id>{{ failed["Id"] }}</Id>
-        <Code>{{ failed["Code"] }}</Code>
-        <Message>{{ failed["Message"] }}</Message>
-        <SenderFault>{{'true' if failed["SenderFault"] else 'false'}}</SenderFault>
-      </member>
-{% endfor %}
-    </Failed>
-  </PublishBatchResult>
-</PublishBatchResponse>"""


Test patch:
diff --git a/tests/test_elb/test_elb.py b/tests/test_elb/test_elb.py
index 90b8ac058..5b6e7b2ec 100644
--- a/tests/test_elb/test_elb.py
+++ b/tests/test_elb/test_elb.py
@@ -64,14 +64,12 @@ def test_create_load_balancer(zones, region_name):
         "LoadBalancerPort": 80,
         "InstanceProtocol": "TCP",
         "InstancePort": 8080,
-        "SSLCertificateId": "None",
     }
     assert http == {
         "Protocol": "HTTP",
         "LoadBalancerPort": 81,
         "InstanceProtocol": "HTTP",
         "InstancePort": 9000,
-        "SSLCertificateId": "None",
     }
 
 
@@ -176,7 +174,7 @@ def test_create_load_balancer_with_invalid_certificate():
             AvailabilityZones=["us-east-2a"],
         )
     err = exc.value.response["Error"]
-    assert err["Code"] == "CertificateNotFoundException"
+    assert err["Code"] == "CertificateNotFound"
 
 
 @mock_aws
@@ -400,7 +398,7 @@ def test_create_lb_listener_with_ssl_certificate_from_acm():
     assert len(listeners) == 2
 
     assert listeners[0]["Listener"]["Protocol"] == "HTTP"
-    assert listeners[0]["Listener"]["SSLCertificateId"] == "None"
+    assert listeners[0]["Listener"].get("SSLCertificateId") is None
 
     assert listeners[1]["Listener"]["Protocol"] == "TCP"
     assert listeners[1]["Listener"]["SSLCertificateId"] == certificate_arn
@@ -440,7 +438,7 @@ def test_create_lb_listener_with_ssl_certificate_from_iam():
     assert len(listeners) == 2
 
     assert listeners[0]["Listener"]["Protocol"] == "HTTP"
-    assert listeners[0]["Listener"]["SSLCertificateId"] == "None"
+    assert listeners[0]["Listener"].get("SSLCertificateId") is None
 
     assert listeners[1]["Listener"]["Protocol"] == "TCP"
     assert listeners[1]["Listener"]["SSLCertificateId"] == certificate_arn
@@ -469,7 +467,7 @@ def test_create_lb_listener_with_invalid_ssl_certificate():
             ],
         )
     err = exc.value.response["Error"]
-    assert err["Code"] == "CertificateNotFoundException"
+    assert err["Code"] == "CertificateNotFound"
 
 
 @mock_aws
@@ -503,7 +501,7 @@ def test_set_sslcertificate():
 
     listener = elb["ListenerDescriptions"][0]["Listener"]
     assert listener["LoadBalancerPort"] == 80
-    assert listener["SSLCertificateId"] == "None"
+    assert listener.get("SSLCertificateId") is None
 
     listener = elb["ListenerDescriptions"][1]["Listener"]
     assert listener["LoadBalancerPort"] == 81
diff --git a/tests/test_emr/test_emr_boto3.py b/tests/test_emr/test_emr_boto3.py
index 65bc6e86d..90b898647 100644
--- a/tests/test_emr/test_emr_boto3.py
+++ b/tests/test_emr/test_emr_boto3.py
@@ -194,8 +194,9 @@ def test_describe_cluster_not_found():
     conn = boto3.client("emr", region_name="us-east-1")
     with pytest.raises(ClientError) as e:
         conn.describe_cluster(ClusterId="DummyId")
-
-    assert e.value.response["Error"]["Code"] == "ResourceNotFoundException"
+    resp = e.value.response
+    assert resp["Error"]["Code"] == "InvalidRequestException"
+    assert resp["ErrorCode"] == "NoSuchCluster"
 
 
 @mock_aws
@@ -1125,19 +1126,21 @@ def test_security_configurations():
 
     with pytest.raises(ClientError) as ex:
         client.describe_security_configuration(Name=security_configuration_name)
-    err = ex.value.response["Error"]
-    assert err["Code"] == "InvalidRequestException"
+    resp = ex.value.response
+    assert resp["Error"]["Code"] == "InvalidRequestException"
+    assert resp["ErrorCode"] == "ResourceNotFound"
     assert (
-        err["Message"]
+        resp["Error"]["Message"]
         == "Security configuration with name 'MySecurityConfiguration' does not exist."
     )
 
     with pytest.raises(ClientError) as ex:
         client.delete_security_configuration(Name=security_configuration_name)
-    err = ex.value.response["Error"]
-    assert err["Code"] == "InvalidRequestException"
+    resp = ex.value.response
+    assert resp["Error"]["Code"] == "InvalidRequestException"
+    assert resp["ErrorCode"] == "ResourceNotFound"
     assert (
-        err["Message"]
+        resp["Error"]["Message"]
         == "Security configuration with name 'MySecurityConfiguration' does not exist."
     )
 
diff --git a/tests/test_logs/test_logs.py b/tests/test_logs/test_logs.py
index d686fe6ea..1cacd384d 100644
--- a/tests/test_logs/test_logs.py
+++ b/tests/test_logs/test_logs.py
@@ -85,6 +85,24 @@ def _create_log_group():
     conn.delete_log_group(logGroupName=log_group_name)
 
 
+@mock_aws
+def test_destinations_no_prefix():
+    conn = boto3.client("logs", "us-west-2")
+    destination_name = "test-destination"
+    role_arn = "arn:aws:iam::123456789012:role/my-subscription-role"
+    target_arn = "arn:aws:kinesis:us-east-1:123456789012:stream/my-kinesis-stream"
+
+    response = conn.put_destination(
+        destinationName=destination_name,
+        targetArn=target_arn,
+        roleArn=role_arn,
+        tags={"Name": destination_name},
+    )
+
+    response = conn.describe_destinations()
+    assert len(response["destinations"]) == 1
+
+
 @mock_aws
 def test_destinations():
     conn = boto3.client("logs", "us-west-2")
diff --git a/tests/test_rds/test_db_cluster_params.py b/tests/test_rds/test_db_cluster_params.py
index 781ebe27e..0d1113f54 100644
--- a/tests/test_rds/test_db_cluster_params.py
+++ b/tests/test_rds/test_db_cluster_params.py
@@ -1,4 +1,5 @@
 import boto3
+import pytest
 
 from moto import mock_aws
 
@@ -6,6 +7,62 @@ from moto import mock_aws
 @mock_aws
 def test_describe_db_cluster_parameters():
     client = boto3.client("rds", "us-east-2")
+    # Create cluster
+    client.create_db_cluster(
+        DBClusterIdentifier="test-cluster",
+        Engine="aurora-postgresql",
+        MasterUsername="admin",
+        MasterUserPassword="password",
+        DBClusterParameterGroupName="group",
+    )
+    client.create_db_cluster_parameter_group(
+        DBClusterParameterGroupName="group",
+        DBParameterGroupFamily="aurora5.6",
+        Description="Test group",
+    )
 
     resp = client.describe_db_cluster_parameters(DBClusterParameterGroupName="group")
     assert resp["Parameters"] == []
+
+
+@mock_aws
+def test_modify_db_cluster_parameter_group():
+    client = boto3.client("rds", "us-east-2")
+    # Create cluster
+    client.create_db_cluster(
+        DBClusterIdentifier="test-cluster",
+        Engine="aurora-postgresql",
+        MasterUsername="admin",
+        MasterUserPassword="password",
+        DBClusterParameterGroupName="group",
+    )
+    client.create_db_cluster_parameter_group(
+        DBClusterParameterGroupName="group",
+        DBParameterGroupFamily="aurora5.6",
+        Description="Test group",
+    )
+
+    client.modify_db_cluster_parameter_group(
+        DBClusterParameterGroupName="group",
+        Parameters=[
+            {
+                "ParameterName": "rds.force_ssl",
+                "ParameterValue": "0",
+                "ApplyMethod": "immediate",
+            }
+        ],
+    )
+    resp = client.describe_db_cluster_parameters(DBClusterParameterGroupName="group")
+    assert resp["Parameters"][0]["ParameterName"] == "rds.force_ssl"
+    assert resp["Parameters"][0]["ParameterValue"] == "0"
+    assert resp["Parameters"][0]["ApplyMethod"] == "immediate"
+
+
+@mock_aws
+def test_describe_db_cluster_parameters_not_found():
+    client = boto3.client("rds", "us-east-2")
+    with pytest.raises(client.exceptions.DBParameterGroupNotFoundFault) as excinfo:
+        client.describe_db_cluster_parameters(
+            DBClusterParameterGroupName="nonexistent-group"
+        )
+    assert "DBClusterParameterGroup not found: nonexistent-group" in str(excinfo.value)
diff --git a/tests/test_redshift/test_redshift.py b/tests/test_redshift/test_redshift.py
index c231f14d8..31f82ee60 100644
--- a/tests/test_redshift/test_redshift.py
+++ b/tests/test_redshift/test_redshift.py
@@ -617,7 +617,7 @@ def test_describe_non_existent_subnet_group_boto3():
     with pytest.raises(ClientError) as ex:
         client.describe_cluster_subnet_groups(ClusterSubnetGroupName="my_subnet")
     err = ex.value.response["Error"]
-    assert err["Code"] == "ClusterSubnetGroupNotFound"
+    assert err["Code"] == "ClusterSubnetGroupNotFoundFault"
     assert err["Message"] == "Subnet group my_subnet not found."
 
 
diff --git a/tests/test_redshift/test_server.py b/tests/test_redshift/test_server.py
index 7d49802cf..f0fe846d2 100644
--- a/tests/test_redshift/test_server.py
+++ b/tests/test_redshift/test_server.py
@@ -270,7 +270,7 @@ def test_describe_unknown_cluster_security_group(is_json):
         describe_params += "&ContentType=JSON"
     res = test_client.get(describe_params)
 
-    assert res.status_code == 400
+    assert res.status_code == 404
 
     if is_json:
         response = json.loads(res.data.decode("utf-8"))
diff --git a/tests/test_sns/test_publishing_boto3.py b/tests/test_sns/test_publishing_boto3.py
index 46950758f..00f7fa770 100644
--- a/tests/test_sns/test_publishing_boto3.py
+++ b/tests/test_sns/test_publishing_boto3.py
@@ -220,7 +220,7 @@ def test_publish_to_sqs_bad():
             MessageAttributes={"store": {"DataType": "String"}},
         )
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
     try:
         # Test empty DataType (if the DataType field is missing entirely
         # botocore throws an exception during validation)
@@ -232,7 +232,7 @@ def test_publish_to_sqs_bad():
             },
         )
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
     try:
         # Test empty Value
         conn.publish(
@@ -241,7 +241,7 @@ def test_publish_to_sqs_bad():
             MessageAttributes={"store": {"DataType": "String", "StringValue": ""}},
         )
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
     try:
         # Test Number DataType, with a non numeric value
         conn.publish(
@@ -250,7 +250,7 @@ def test_publish_to_sqs_bad():
             MessageAttributes={"price": {"DataType": "Number", "StringValue": "error"}},
         )
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
         assert err.response["Error"]["Message"] == (
             "An error occurred (ParameterValueInvalid) when calling the "
             "Publish operation: Could not cast message attribute 'price' "
@@ -2603,7 +2603,7 @@ def test_publish_with_message_structure_errors():
             MessageStructure="json",
         )
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
         assert err.response["Error"]["Message"] == "Message is not valid JSON."
 
     try:
@@ -2616,7 +2616,7 @@ def test_publish_with_message_structure_errors():
             MessageStructure="json",
         )
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
         assert (
             err.response["Error"]["Message"]
             == "Message does not contain sqs or default keys."
@@ -2628,7 +2628,7 @@ def test_publish_with_message_structure_errors():
             MessageStructure="no-json",
         )
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
         assert (
             err.response["Error"]["Message"]
             == "MessageStructure must be 'json' if provided"
diff --git a/tests/test_sns/test_subscriptions_boto3.py b/tests/test_sns/test_subscriptions_boto3.py
index c851fbdbe..30695ed1f 100644
--- a/tests/test_sns/test_subscriptions_boto3.py
+++ b/tests/test_sns/test_subscriptions_boto3.py
@@ -564,7 +564,9 @@ def test_subscribe_invalid_filter_policy():
         )
 
     err = err_info.value
-    assert err.response["Error"]["Code"] == "InternalFailure"
+    assert err.response["Error"]["Code"] == "InternalError"
+    assert err.response["Error"]["Type"] == "Receiver"
+    assert err.response["ResponseMetadata"]["HTTPStatusCode"] == 500
 
     with pytest.raises(ClientError) as err_info:
         conn.subscribe(
diff --git a/tests/test_sns/test_topics_boto3.py b/tests/test_sns/test_topics_boto3.py
index c1232d635..61489a6db 100644
--- a/tests/test_sns/test_topics_boto3.py
+++ b/tests/test_sns/test_topics_boto3.py
@@ -530,7 +530,7 @@ def test_create_fifo_topic():
     try:
         conn.create_topic(Name="test_topic", Attributes={"FifoTopic": "true"})
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
         assert err.response["Error"]["Message"] == (
             "Fifo Topic names must end with .fifo and must be made up of only "
             "uppercase and lowercase ASCII letters, numbers, underscores, "
@@ -541,7 +541,7 @@ def test_create_fifo_topic():
     try:
         conn.create_topic(Name="test_topic.fifo")
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
         assert err.response["Error"]["Message"] == (
             "Topic names must be made up of only uppercase and lowercase "
             "ASCII letters, numbers, underscores, "
@@ -551,7 +551,7 @@ def test_create_fifo_topic():
     try:
         conn.create_topic(Name="topic.name.fifo", Attributes={"FifoTopic": "true"})
     except ClientError as err:
-        assert err.response["Error"]["Code"] == "InvalidParameterValue"
+        assert err.response["Error"]["Code"] == "InvalidParameter"
         assert err.response["Error"]["Message"] == (
             "Fifo Topic names must end with .fifo and must be made up of only "
             "uppercase and lowercase ASCII letters, numbers, underscores, "