Repository: getmoto/moto
Pull request #8818 (2025-04-16 09:54:47)

EC2: Implement CreateDefaultSubnet operation
This PR implements the EC2 [CreateDefaultSubnet](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateDefaultSubnet.html) operation.

> `CreateDefaultSubnet` operation creates a default subnet with a size `/20` IPv4 CIDR block in the specified Availability Zone in your default VPC. You can have only one default subnet per Availability Zone.

Moto already supports the EC2 [CreateDefaultVpc](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateDefaultVpc.html) and this is updated such that it also [creates the default subnets](https://docs.aws.amazon.com/vpc/latest/userguide/default-vpc-components.html).

The implementation assigns the `/20` CIDR blocks to AZs in sequence as follows, but that's not always the case on AWS:
- ...a: `172.31.0.0/20`
- ...b: `172.31.16.0/20`
- ...c: `172.31.32.0/20`
- etc.


Patch:
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index beb10ffb5..281d272b4 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -2483,7 +2483,7 @@
 - [ ] create_coip_cidr
 - [ ] create_coip_pool
 - [X] create_customer_gateway
-- [ ] create_default_subnet
+- [X] create_default_subnet
 - [X] create_default_vpc
 - [X] create_dhcp_options
 - [X] create_egress_only_internet_gateway
@@ -10025,4 +10025,4 @@
 - workmailmessageflow
 - workspaces-thin-client
 - xray
-</details>
\ No newline at end of file
+</details>
diff --git a/docs/docs/services/ec2.rst b/docs/docs/services/ec2.rst
index 9190db79f..6bc81e068 100644
--- a/docs/docs/services/ec2.rst
+++ b/docs/docs/services/ec2.rst
@@ -84,7 +84,7 @@ ec2
 - [ ] create_coip_cidr
 - [ ] create_coip_pool
 - [X] create_customer_gateway
-- [ ] create_default_subnet
+- [X] create_default_subnet
 - [X] create_default_vpc
 - [X] create_dhcp_options
 - [X] create_egress_only_internet_gateway
diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py
index 807942e05..828d8a94b 100644
--- a/moto/cloudformation/parsing.py
+++ b/moto/cloudformation/parsing.py
@@ -82,6 +82,9 @@ NULL_MODELS = [
     "AWS::CloudFormation::WaitConditionHandle",
 ]
 
+# How often CF should attempt to update/delete a resource, before giving up
+MAX_UPDATE_ATTEMPTS = 5
+
 DEFAULT_REGION = "us-east-1"
 
 logger = logging.getLogger("moto")
@@ -842,10 +845,21 @@ class ResourceMap(collections_abc.Mapping):  # type: ignore[type-arg]
     ) -> None:
         resource_names_by_action = self.build_resource_diff(template, parameters)
 
-        for logical_name in resource_names_by_action["Remove"]:
-            resource_json = self._resource_json_map[logical_name]
-            resource = self._parsed_resources[logical_name]
-            self._delete_resource(resource, resource_json)
+        tries = 1
+        while resource_names_by_action["Remove"] and tries < MAX_UPDATE_ATTEMPTS:
+            for logical_name in resource_names_by_action["Remove"].copy():
+                resource_json = self._resource_json_map[logical_name]
+                try:
+                    resource = self._parsed_resources[logical_name]
+                    self._delete_resource(resource, resource_json)
+                except Exception as e:
+                    # skip over dependency violations, and try again in another pass
+                    last_exception = e
+                else:
+                    resource_names_by_action["Remove"].remove(logical_name)
+            tries += 1
+        if tries == MAX_UPDATE_ATTEMPTS:
+            raise last_exception
 
         self._template = template
         if parameters:
@@ -862,7 +876,7 @@ class ResourceMap(collections_abc.Mapping):  # type: ignore[type-arg]
             _ = self[logical_name]
 
         tries = 1
-        while resource_names_by_action["Modify"] and tries < 5:
+        while resource_names_by_action["Modify"] and tries < MAX_UPDATE_ATTEMPTS:
             for logical_name in resource_names_by_action["Modify"].copy():
                 resource_json = self._resource_json_map[logical_name]
                 try:
@@ -881,7 +895,7 @@ class ResourceMap(collections_abc.Mapping):  # type: ignore[type-arg]
                     self._parsed_resources[logical_name] = changed_resource
                     resource_names_by_action["Modify"].remove(logical_name)
             tries += 1
-        if tries == 5:
+        if tries == MAX_UPDATE_ATTEMPTS:
             raise last_exception
 
     def delete(self) -> None:
diff --git a/moto/cloudfront/models.py b/moto/cloudfront/models.py
index b1b01c8ff..f1a9f5d8c 100644
--- a/moto/cloudfront/models.py
+++ b/moto/cloudfront/models.py
@@ -65,6 +65,14 @@ class ForwardedValues:
         self.cookies: List[Dict[str, Any]] = config.get("Cookies") or []
 
 
+class TrustedSigners:
+    def __init__(self, config: Dict[str, Any]):
+        items = config.get("Items") or {}
+        self.acct_nums = items.get("AwsAccountNumber") or []
+        if isinstance(self.acct_nums, str):
+            self.acct_nums = [self.acct_nums]
+
+
 class TrustedKeyGroups:
     def __init__(self, config: Dict[str, Any]):
         items = config.get("Items") or {}
@@ -77,7 +85,7 @@ class DefaultCacheBehaviour:
     def __init__(self, config: Dict[str, Any]):
         self.target_origin_id = config["TargetOriginId"]
         self.trusted_signers_enabled = False
-        self.trusted_signers: List[Any] = []
+        self.trusted_signers = TrustedSigners(config.get("TrustedSigners") or {})
         self.trusted_key_groups = TrustedKeyGroups(config.get("TrustedKeyGroups") or {})
         self.viewer_protocol_policy = config["ViewerProtocolPolicy"]
         methods = config.get("AllowedMethods", {})
diff --git a/moto/cloudfront/responses.py b/moto/cloudfront/responses.py
index 023d10186..0da2fd695 100644
--- a/moto/cloudfront/responses.py
+++ b/moto/cloudfront/responses.py
@@ -325,10 +325,10 @@ DIST_CONFIG_TEMPLATE = """
       <DefaultCacheBehavior>
         <TargetOriginId>{{ distribution.distribution_config.default_cache_behavior.target_origin_id }}</TargetOriginId>
         <TrustedSigners>
-          <Enabled>{{ distribution.distribution_config.default_cache_behavior.trusted_signers_enabled }}</Enabled>
-          <Quantity>{{ distribution.distribution_config.default_cache_behavior.trusted_signers|length }}</Quantity>
+          <Enabled>{{ 'true' if distribution.distribution_config.default_cache_behavior.trusted_signers.acct_nums|length > 0 else 'false' }}</Enabled>          
+          <Quantity>{{ distribution.distribution_config.default_cache_behavior.trusted_signers.acct_nums|length }}</Quantity>
           <Items>
-            {% for aws_account_number  in distribution.distribution_config.default_cache_behavior.trusted_signers %}
+            {% for aws_account_number  in distribution.distribution_config.default_cache_behavior.trusted_signers.acct_nums %}
               <AwsAccountNumber>{{ aws_account_number }}</AwsAccountNumber>
             {% endfor %}
           </Items>
@@ -432,10 +432,10 @@ DIST_CONFIG_TEMPLATE = """
                 <PathPattern>{{ behaviour.path_pattern }}</PathPattern>
                 <TargetOriginId>{{ behaviour.target_origin_id }}</TargetOriginId>
                 <TrustedSigners>
-                  <Enabled>{{ behaviour.trusted_signers.enabled }}</Enabled>
-                  <Quantity>{{ behaviour.trusted_signers | length }}</Quantity>
+                  <Enabled>{{ 'true' if behaviour.trusted_signers.acct_nums|length > 0 else 'false' }}</Enabled>
+                  <Quantity>{{ behaviour.trusted_signers.acct_nums | length }}</Quantity>
                   <Items>
-                    {% for account_nr  in behaviour.trusted_signers %}
+                    {% for account_nr  in behaviour.trusted_signers.acct_nums %}
                       <AwsAccountNumber>{{ account_nr }}</AwsAccountNumber>
                     {% endfor %}
                   </Items>
diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py
index 71e8842e0..26eb36438 100644
--- a/moto/ec2/exceptions.py
+++ b/moto/ec2/exceptions.py
@@ -38,6 +38,22 @@ class DefaultVpcAlreadyExists(EC2ClientError):
         )
 
 
+class DefaultVpcDoesNotExistError(EC2ClientError):
+    def __init__(self) -> None:
+        super().__init__(
+            "DefaultVpcDoesNotExist",
+            "No default VPC exists for this account in this region.",
+        )
+
+
+class DefaultSubnetAlreadyExistsInAvailabilityZoneError(EC2ClientError):
+    def __init__(self, subnet_id: str, availability_zone: str) -> None:
+        super().__init__(
+            "DefaultSubnetAlreadyExistsInAvailabilityZone",
+            f"'{subnet_id}' is already the default subnet in {availability_zone}.",
+        )
+
+
 class DependencyViolationError(EC2ClientError):
     def __init__(self, message: str):
         super().__init__("DependencyViolation", message)
diff --git a/moto/ec2/models/__init__.py b/moto/ec2/models/__init__.py
index 03e2b3b9d..991d5e1d3 100644
--- a/moto/ec2/models/__init__.py
+++ b/moto/ec2/models/__init__.py
@@ -152,17 +152,6 @@ class EC2Backend(
 
         self.default_vpc = vpc
 
-        # Create default subnet for each availability zone
-        ip, _ = vpc.cidr_block.split("/")
-        ip = ip.split(".")  # type: ignore
-        ip[2] = 0  # type: ignore
-
-        for zone in self.describe_availability_zones():
-            az_name = zone.name
-            cidr_block = ".".join(str(i) for i in ip) + "/20"
-            self.create_subnet(vpc.id, cidr_block, availability_zone=az_name)
-            ip[2] += 16  # type: ignore
-
     # Use this to generate a proper error template response when in a response
     # handler.
     def raise_error(self, code: str, message: str) -> None:
diff --git a/moto/ec2/models/instances.py b/moto/ec2/models/instances.py
index 1d055b036..953e959c6 100644
--- a/moto/ec2/models/instances.py
+++ b/moto/ec2/models/instances.py
@@ -528,10 +528,8 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
                     nic_subnet: Subnet = self.ec2_backend.get_subnet(nic["SubnetId"])
                 else:
                     # Get default Subnet
-                    zone = self._placement.zone
-                    nic_subnet = self.ec2_backend.get_default_subnet(
-                        availability_zone=zone
-                    )
+                    default_subnets = self.ec2_backend.get_default_subnets()
+                    nic_subnet = default_subnets[self._placement.zone]
 
                 group_ids = nic.get("SecurityGroupId") or []
                 if security_groups:
diff --git a/moto/ec2/models/subnets.py b/moto/ec2/models/subnets.py
index c3096fc5b..2689a97e8 100644
--- a/moto/ec2/models/subnets.py
+++ b/moto/ec2/models/subnets.py
@@ -11,6 +11,8 @@ if TYPE_CHECKING:
 from moto.ec2.models.availability_zones_and_regions import Zone
 
 from ..exceptions import (
+    DefaultSubnetAlreadyExistsInAvailabilityZoneError,
+    DefaultVpcDoesNotExistError,
     GenericInvalidParameterValueError,
     InvalidAvailabilityZoneError,
     InvalidCIDRBlockParameterError,
@@ -303,14 +305,32 @@ class SubnetBackend:
                 return subnets_per_zone[subnet_id]
         raise InvalidSubnetIdError(subnet_id)
 
-    def get_default_subnet(self, availability_zone: str) -> Subnet:
-        return [
-            subnet
-            for subnet in self.describe_subnets(
-                filters={"availabilityZone": availability_zone}
-            )
+    def get_default_subnets(self) -> Dict[str, Subnet]:
+        return {
+            subnet.availability_zone: subnet
+            for subnet in self.describe_subnets()
             if subnet.default_for_az
-        ][0]
+        }
+
+    def create_default_subnet(self, availability_zone: str) -> Subnet:
+        default_subnets = self.get_default_subnets()
+        if availability_zone in default_subnets:
+            raise DefaultSubnetAlreadyExistsInAvailabilityZoneError(
+                default_subnets[availability_zone].id, availability_zone
+            )
+
+        default_vpc = self.get_default_vpc()  # type: ignore[attr-defined]
+        if default_vpc is None:
+            raise DefaultVpcDoesNotExistError()
+
+        cidr_block = default_vpc.cidr_block
+        blocks = list(ipaddress.ip_network(cidr_block).subnets(new_prefix=20))
+        subnet = self.create_subnet(
+            vpc_id=default_vpc.id,
+            cidr_block=str(blocks[len(default_subnets)]),
+            availability_zone=availability_zone,
+        )
+        return subnet
 
     def create_subnet(
         self,
@@ -358,7 +378,7 @@ class SubnetBackend:
 
         # if this is the first subnet for an availability zone,
         # consider it the default
-        default_for_az = str(availability_zone not in self.subnets).lower()
+        default_for_az = str(len(self.subnets.get(availability_zone, [])) == 0).lower()  # type: ignore[arg-type]
         map_public_ip_on_launch = default_for_az
 
         if availability_zone is None and not availability_zone_id:
diff --git a/moto/ec2/models/vpcs.py b/moto/ec2/models/vpcs.py
index 1ea59d839..a2774985e 100644
--- a/moto/ec2/models/vpcs.py
+++ b/moto/ec2/models/vpcs.py
@@ -709,7 +709,17 @@ class VPCBackend:
         if default_vpc:
             raise DefaultVpcAlreadyExists
         cidr_block = "172.31.0.0/16"
-        return self.create_vpc(cidr_block=cidr_block, is_default=True)
+        vpc = self.create_vpc(cidr_block=cidr_block, is_default=True)
+
+        for zone in self.describe_availability_zones():  # type: ignore[attr-defined]
+            self.create_default_subnet(zone.name)  # type: ignore[attr-defined]
+        return vpc
+
+    def get_default_vpc(self) -> Optional[VPC]:
+        for vpc in self.vpcs.values():
+            if vpc.is_default == "true":
+                return vpc
+        return None
 
     def create_vpc(
         self,
@@ -775,6 +785,15 @@ class VPCBackend:
         return matches
 
     def delete_vpc(self, vpc_id: str) -> VPC:
+        # Refuse to delete if there are dependent subnets
+        subnets = self.describe_subnets(filters={"vpc-id": vpc_id})  # type: ignore[attr-defined]
+        if subnets:
+            raise DependencyViolationError(
+                f"The vpc '{vpc_id}' has dependencies and cannot be deleted."
+            )
+
+        # TODO: Refuse to delete if there are dependent IGW
+
         # Do not delete if any VPN Gateway is attached
         vpn_gateways = self.describe_vpn_gateways(filters={"attachment.vpc-id": vpc_id})  # type: ignore[attr-defined]
         vpn_gateways = [
diff --git a/moto/ec2/responses/subnets.py b/moto/ec2/responses/subnets.py
index 365d8c27b..672e6f2ad 100644
--- a/moto/ec2/responses/subnets.py
+++ b/moto/ec2/responses/subnets.py
@@ -28,6 +28,13 @@ class Subnets(EC2BaseResponse):
         template = self.response_template(CREATE_SUBNET_RESPONSE)
         return template.render(subnet=subnet)
 
+    def create_default_subnet(self) -> str:
+        availability_zone = self._get_param("AvailabilityZone")
+
+        subnet = self.ec2_backend.create_default_subnet(availability_zone)
+        template = self.response_template(CREATE_DEFAULT_SUBNET_RESPONSE)
+        return template.render(subnet=subnet)
+
     def delete_subnet(self) -> str:
         subnet_id = self._get_param("SubnetId")
         subnet = self.ec2_backend.delete_subnet(subnet_id)
@@ -119,6 +126,36 @@ CREATE_SUBNET_RESPONSE = """
   </subnet>
 </CreateSubnetResponse>"""
 
+CREATE_DEFAULT_SUBNET_RESPONSE = """
+<?xml version="1.0" encoding="UTF-8"?>
+<CreateDefaultSubnetResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
+    <requestId>68b1ebc6-3ff3-4f5b-9f2c-a468cb0a5030</requestId>
+    <subnet>
+        <assignIpv6AddressOnCreation>{{ 'false' if not subnet.assign_ipv6_address_on_creation or subnet.assign_ipv6_address_on_creation == 'false' else 'true'}}</assignIpv6AddressOnCreation>
+        <availabilityZone>{{ subnet._availability_zone.name }}</availabilityZone>
+        <availabilityZoneId>{{ subnet._availability_zone.zone_id }}</availabilityZoneId>
+        <availableIpAddressCount>{{ subnet.available_ip_addresses or '0' }}</availableIpAddressCount>
+        <cidrBlock>{{ subnet.cidr_block }}</cidrBlock>
+        <defaultForAz>{{ subnet.default_for_az }}</defaultForAz>
+        <enableDns64>false</enableDns64>
+        <ipv6CidrBlockAssociationSet/>
+        <ipv6Native>false</ipv6Native>
+        <mapCustomerOwnedIpOnLaunch>false</mapCustomerOwnedIpOnLaunch>
+        <mapPublicIpOnLaunch>{{ subnet.map_public_ip_on_launch }}</mapPublicIpOnLaunch>
+        <ownerId>{{ subnet.owner_id }}</ownerId>
+        <privateDnsNameOptionsOnLaunch>
+            <enableResourceNameDnsAAAARecord>false</enableResourceNameDnsAAAARecord>
+            <enableResourceNameDnsARecord>false</enableResourceNameDnsARecord>
+            <hostnameType>ip-name</hostnameType>
+        </privateDnsNameOptionsOnLaunch>
+        <state>available</state>
+        <subnetId>{{ subnet.id }}</subnetId>
+        <tagSet/>
+        <vpcId>{{ subnet.vpc_id }}</vpcId>
+    </subnet>
+</CreateDefaultSubnetResponse>
+"""
+
 DELETE_SUBNET_RESPONSE = """
 <DeleteSubnetResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
    <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
diff --git a/moto/elb/models.py b/moto/elb/models.py
index c186dde05..4cd0903c9 100644
--- a/moto/elb/models.py
+++ b/moto/elb/models.py
@@ -309,10 +309,8 @@ class ELBBackend(BaseBackend):
             subnet = ec2_backend.get_subnet(subnets[0])
             vpc_id = subnet.vpc_id
         elif zones:
-            subnets = [
-                ec2_backend.get_default_subnet(availability_zone=zone).id
-                for zone in zones
-            ]
+            default_subnets = ec2_backend.get_default_subnets()
+            subnets = [default_subnets[zone].id for zone in zones]
             subnet = ec2_backend.get_subnet(subnets[0])
             vpc_id = subnet.vpc_id
         if name in self.load_balancers:
diff --git a/moto/rds/models.py b/moto/rds/models.py
index 3a291b6d6..de0855cab 100644
--- a/moto/rds/models.py
+++ b/moto/rds/models.py
@@ -1454,7 +1454,9 @@ class DBInstance(EventMixin, CloudFormationModel, RDSBaseModel):
             self.master_user_secret = MasterUserSecret(
                 self, master_user_secret_kms_key_id
             )
-        elif manage_master_user_password is False:
+        elif manage_master_user_password is False and hasattr(
+            self, "master_user_secret"
+        ):
             self.master_user_secret.delete_secret()
             del self.master_user_secret
 
@@ -3141,7 +3143,9 @@ class RDSBackend(BaseBackend):
             if manage_master_user_password:
                 kms_key_id = kwargs.pop("master_user_secret_kms_key_id", None)
                 cluster.master_user_secret = MasterUserSecret(cluster, kms_key_id)
-            else:
+            elif not manage_master_user_password and hasattr(
+                cluster, "master_user_secret"
+            ):
                 cluster.master_user_secret.delete_secret()
                 del cluster.master_user_secret
 
diff --git a/moto/ses/models.py b/moto/ses/models.py
index 69fd3a3b8..899bf8694 100644
--- a/moto/ses/models.py
+++ b/moto/ses/models.py
@@ -206,6 +206,7 @@ class SESBackend(BaseBackend):
         self.identity_mail_from_domains: Dict[str, Dict[str, Any]] = {}
         self.templates: Dict[str, Dict[str, str]] = {}
         self.receipt_rule_set: Dict[str, List[Dict[str, Any]]] = {}
+        self.dkim_tokens: Dict[str, List[str]] = {}
 
     def _is_verified_address(self, source: str) -> bool:
         _, address = parseaddr(source)
@@ -725,5 +726,57 @@ class SESBackend(BaseBackend):
 
         return attributes_by_identity
 
+    def update_configuration_set_reputation_metrics_enabled(
+        self, configuration_set_name: str, enabled: bool
+    ) -> None:
+        """
+        Enable or disable reputation metrics for a configuration set.
+        """
+        if configuration_set_name not in self.config_sets:
+            raise ConfigurationSetDoesNotExist(
+                f"Configuration set <{configuration_set_name}> does not exist"
+            )
+        config_set = self.config_sets[configuration_set_name]
+        if config_set.reputation_options is None:
+            config_set.reputation_options = {}
+        config_set.reputation_options["ReputationMetricsEnabled"] = enabled
+
+    def get_identity_dkim_attributes(
+        self, identities: List[str]
+    ) -> Dict[str, Dict[str, Any]]:
+        """
+        Returns the current status of Easy DKIM signing for an entity.
+        For domain name identities, this operation also returns the DKIM tokens.
+        """
+        result = {}
+        for identity in identities:
+            is_domain = "@" not in identity
+            dkim_enabled = True
+            verification_status = (
+                "Success"
+                if identity in (self.domains + self.addresses)
+                else "NotStarted"
+            )
+
+            dkim_data = {
+                "DkimEnabled": dkim_enabled,
+                "DkimVerificationStatus": verification_status,
+            }
+
+            # Only include tokens for domain identities
+            if is_domain and verification_status == "Success":
+                if identity not in self.dkim_tokens:
+                    # Generate new DKIM tokens for the domain
+                    self.dkim_tokens[identity] = [
+                        "vvjuipp74whm76gqoni7qmwwn4w4qusjiainivf6sf",
+                        "3frqe7jn4obpuxjpwpolz6ipb3k5nvt2nhjpik2oy",
+                        "wrqplteh7oodxnad7hsl4mixg2uavzneazxv5sxi2",
+                    ]
+                dkim_data["DkimTokens"] = self.dkim_tokens[identity]
+
+            result[identity] = dkim_data
+
+        return result
+
 
 ses_backends = BackendDict(SESBackend, "ses")
diff --git a/moto/ses/responses.py b/moto/ses/responses.py
index 1b6cf00d7..b369ba611 100644
--- a/moto/ses/responses.py
+++ b/moto/ses/responses.py
@@ -402,6 +402,24 @@ class EmailResponse(BaseResponse):
             configuration_sets=config_set_names, next_token=next_token
         )
 
+    def update_configuration_set_reputation_metrics_enabled(self) -> str:
+        configuration_set_name = self._get_param("ConfigurationSetName")
+        enabled = self._get_param("Enabled")
+        self.backend.update_configuration_set_reputation_metrics_enabled(
+            configuration_set_name=configuration_set_name,
+            enabled=enabled,
+        )
+        template = self.response_template(
+            UPDATE_CONFIGURATION_SET_REPUTATION_METRICS_ENABLED_RESPONSE
+        )
+        return template.render()
+
+    def get_identity_dkim_attributes(self) -> str:
+        identities = self._get_multi_param("Identities.member.")
+        dkim_attributes = self.backend.get_identity_dkim_attributes(identities)
+        template = self.response_template(GET_IDENTITY_DKIM_ATTRIBUTES_RESPONSE)
+        return template.render(dkim_attributes=dkim_attributes)
+
 
 VERIFY_EMAIL_IDENTITY = """<VerifyEmailIdentityResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
   <VerifyEmailIdentityResult/>
@@ -861,3 +879,36 @@ LIST_CONFIGURATION_SETS_TEMPLATE = """<ListConfigurationSetsResponse xmlns="http
     <NextToken>{{ next_token }}</NextToken>
   </ListConfigurationSetsResult>
 </ListConfigurationSetsResponse>"""
+
+UPDATE_CONFIGURATION_SET_REPUTATION_METRICS_ENABLED_RESPONSE = """<UpdateConfigurationSetReputationMetricsEnabledResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
+  <UpdateConfigurationSetReputationMetricsEnabledResult/>
+  <ResponseMetadata>
+    <RequestId>47e0ef1a-9bf2-11e1-9279-0100e8cf109a</RequestId>
+  </ResponseMetadata>
+</UpdateConfigurationSetReputationMetricsEnabledResponse>"""
+
+GET_IDENTITY_DKIM_ATTRIBUTES_RESPONSE = """<GetIdentityDkimAttributesResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
+  <GetIdentityDkimAttributesResult>
+    <DkimAttributes>
+      {% for identity, attributes in dkim_attributes.items() %}
+      <entry>
+        <key>{{ identity }}</key>
+        <value>
+          <DkimEnabled>{{ attributes.DkimEnabled|lower }}</DkimEnabled>
+          <DkimVerificationStatus>{{ attributes.DkimVerificationStatus }}</DkimVerificationStatus>
+          {% if attributes.get('DkimTokens') %}
+          <DkimTokens>
+            {% for token in attributes.DkimTokens %}
+            <member>{{ token }}</member>
+            {% endfor %}
+          </DkimTokens>
+          {% endif %}
+        </value>
+      </entry>
+      {% endfor %}
+    </DkimAttributes>
+  </GetIdentityDkimAttributesResult>
+  <ResponseMetadata>
+    <RequestId>9662c15b-c469-11e1-99d1-797d6ecd6414</RequestId>
+  </ResponseMetadata>
+</GetIdentityDkimAttributesResponse>"""


Test patch:
diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
index 26df7c3bc..85f5acd29 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
@@ -1361,6 +1361,9 @@ def test_update_stack_deleted_resources_can_reference_deleted_resources():
             "AWSTemplateFormatVersion": "2010-09-09",
             "Parameters": {"TimeoutParameter": {"Default": 61, "Type": "String"}},
             "Resources": {
+                # Note that we're listing the VPC first on purpose, even though the dependency between them
+                # means that the Subnet should be deleted first, before the VPC can be deleted
+                # Our implementation needs to handle deletion of resources regardless of the order
                 "VPC": {
                     "Type": "AWS::EC2::VPC",
                     "Properties": {"CidrBlock": "10.0.0.0/16"},
diff --git a/tests/test_cloudfront/test_cloudfront.py b/tests/test_cloudfront/test_cloudfront.py
index 0d352903c..acbd0f00c 100644
--- a/tests/test_cloudfront/test_cloudfront.py
+++ b/tests/test_cloudfront/test_cloudfront.py
@@ -234,3 +234,47 @@ def test_update_distribution_applies_changes():
         "DistributionConfig"
     ]
     assert actual_dist_config["Enabled"]
+
+
+@mock_aws
+def test_update_distribution_trusted_signers():
+    client = boto3.client("cloudfront", region_name="us-east-1")
+
+    # Create standard distribution
+    config = scaffold.example_distribution_config(ref="ref")
+
+    dist = client.create_distribution(DistributionConfig=config)
+    dist_id = dist["Distribution"]["Id"]
+
+    # Assert that default TrustedSigners 'Enabled' value is false
+    actual_dist_config = client.get_distribution(Id=dist_id)["Distribution"][
+        "DistributionConfig"
+    ]
+    assert (
+        actual_dist_config["DefaultCacheBehavior"]["TrustedSigners"]["Enabled"] is False
+    )
+
+    # Add an item to TrustedSigners
+    get_config_response = client.get_distribution_config(Id=dist_id)
+    actual_dist_config = get_config_response["DistributionConfig"]
+    etag = get_config_response["ETag"]
+    actual_dist_config["DefaultCacheBehavior"]["TrustedSigners"]["Items"] = [
+        "AwsAccountNumber123"
+    ]
+
+    client.update_distribution(
+        DistributionConfig=actual_dist_config, Id=dist_id, IfMatch=etag
+    )
+
+    # Assert that all TrustedSigners fields updated
+    updated_dist_config = client.get_distribution(Id=dist_id)["Distribution"][
+        "DistributionConfig"
+    ]
+    assert (
+        updated_dist_config["DefaultCacheBehavior"]["TrustedSigners"]["Quantity"] == 1
+    )
+    assert updated_dist_config["DefaultCacheBehavior"]["TrustedSigners"]["Enabled"]
+    assert (
+        "AwsAccountNumber123"
+        in updated_dist_config["DefaultCacheBehavior"]["TrustedSigners"]["Items"]
+    )
diff --git a/tests/test_ec2/test_subnets.py b/tests/test_ec2/test_subnets.py
index e83c26668..922ce856b 100644
--- a/tests/test_ec2/test_subnets.py
+++ b/tests/test_ec2/test_subnets.py
@@ -7,7 +7,7 @@ import pytest
 from botocore.exceptions import ClientError
 
 from moto import mock_aws, settings
-from tests import EXAMPLE_AMI_ID
+from tests import DEFAULT_ACCOUNT_ID, EXAMPLE_AMI_ID
 
 
 @mock_aws
@@ -34,6 +34,98 @@ def test_subnets():
     assert ex.value.response["Error"]["Code"] == "InvalidSubnetID.NotFound"
 
 
+@mock_aws
+def test_create_default_subnet():
+    client = boto3.client("ec2", region_name="ap-northeast-1")
+
+    default_vpc = client.describe_vpcs(
+        Filters=[{"Name": "is-default", "Values": ["true"]}]
+    )["Vpcs"][0]
+
+    zones = [
+        z["ZoneName"] for z in client.describe_availability_zones()["AvailabilityZones"]
+    ]
+
+    # Ensure moto has default subnets by default
+    subnets = client.describe_subnets(
+        Filters=[{"Name": "default-for-az", "Values": ["true"]}]
+    )["Subnets"]
+    assert len(subnets) == len(zones)
+
+    default_subnets: dict[str, str] = {
+        subnet["AvailabilityZone"]: subnet["SubnetId"] for subnet in subnets
+    }
+
+    # Ensure that attempting to create a default subnet when it already exists raises
+    for zone in zones:
+        with pytest.raises(ClientError) as exc:
+            client.create_default_subnet(AvailabilityZone=zone)
+        assert (
+            exc.value.response["Error"]["Code"]
+            == "DefaultSubnetAlreadyExistsInAvailabilityZone"
+        )
+        assert (
+            exc.value.response["Error"]["Message"]
+            == f"'{default_subnets[zone]}' is already the default subnet in {zone}."
+        )
+
+    # Delete default subnets
+    for subnet in subnets:
+        client.delete_subnet(SubnetId=subnet["SubnetId"])
+    default_subnets.clear()
+
+    expected_cidr_blocks = [
+        "172.31.0.0/20",
+        "172.31.16.0/20",
+        "172.31.32.0/20",
+        "172.31.48.0/20",
+        "172.31.64.0/20",
+        "172.31.80.0/20",
+    ]
+
+    # Ensure default subnets can be created
+    for idx, zone in enumerate(zones):
+        response = client.create_default_subnet(AvailabilityZone=zone)["Subnet"]
+        assert response["OwnerId"] == DEFAULT_ACCOUNT_ID
+        assert response["VpcId"] == default_vpc["VpcId"]
+        assert response["State"] == "available"
+        assert response["CidrBlock"] == expected_cidr_blocks[idx]
+        assert response["AvailabilityZone"] == zone
+        assert response["DefaultForAz"] is True
+        subnet_id = default_subnets[zone] = response["SubnetId"]
+
+        response = client.describe_subnets(SubnetIds=[subnet_id])
+        assert len(response["Subnets"]) == 1
+        assert response["Subnets"][0]["SubnetId"] == subnet_id
+        assert response["Subnets"][0]["VpcId"] == default_vpc["VpcId"]
+        assert response["Subnets"][0]["DefaultForAz"] is True
+        assert response["Subnets"][0]["CidrBlock"] == expected_cidr_blocks[idx]
+        assert response["Subnets"][0]["AvailabilityZone"] == zone
+
+    # Delete default subnets and VPCs
+    for subnet_id in default_subnets.values():
+        client.delete_subnet(SubnetId=subnet_id)
+    client.delete_vpc(VpcId=default_vpc["VpcId"])
+
+    # Ensure attempting to create default subnet when there's no default VPC raises
+    for zone in zones:
+        with pytest.raises(ClientError) as exc:
+            client.create_default_subnet(AvailabilityZone=zone)
+
+        assert exc.value.response["Error"]["Code"] == "DefaultVpcDoesNotExist"
+        assert (
+            exc.value.response["Error"]["Message"]
+            == "No default VPC exists for this account in this region."
+        )
+
+    # Ensure creating a default VPC also creates default subnets
+    client.create_default_vpc()
+    subnets = client.describe_subnets(
+        Filters=[{"Name": "default-for-az", "Values": ["true"]}]
+    )["Subnets"]
+    assert len(subnets) == len(zones)
+
+
 @mock_aws
 def test_subnet_create_vpc_validation():
     ec2 = boto3.resource("ec2", region_name="us-east-1")
diff --git a/tests/test_ec2/test_vpcs.py b/tests/test_ec2/test_vpcs.py
index 221b66a31..68e12fe55 100644
--- a/tests/test_ec2/test_vpcs.py
+++ b/tests/test_ec2/test_vpcs.py
@@ -23,6 +23,11 @@ def test_creating_a_vpc_in_empty_region_does_not_make_this_vpc_the_default():
     client = boto3.client("ec2", region_name="eu-north-1")
     all_vpcs = retrieve_all_vpcs(client)
     for vpc in all_vpcs:
+        subnets = client.describe_subnets(
+            Filters=[{"Name": "vpc-id", "Values": [vpc["VpcId"]]}]
+        )["Subnets"]
+        for subnet in subnets:
+            client.delete_subnet(SubnetId=subnet["SubnetId"])
         client.delete_vpc(VpcId=vpc["VpcId"])
     # create vpc
     client.create_vpc(CidrBlock="10.0.0.0/16")
@@ -40,6 +45,11 @@ def test_create_default_vpc():
     client = boto3.client("ec2", region_name="eu-north-1")
     all_vpcs = retrieve_all_vpcs(client)
     for vpc in all_vpcs:
+        subnets = client.describe_subnets(
+            Filters=[{"Name": "vpc-id", "Values": [vpc["VpcId"]]}]
+        )["Subnets"]
+        for subnet in subnets:
+            client.delete_subnet(SubnetId=subnet["SubnetId"])
         client.delete_vpc(VpcId=vpc["VpcId"])
     # create default vpc
     client.create_default_vpc()
@@ -1238,3 +1248,21 @@ def test_describe_prefix_lists():
         ]
     )
     assert len(result_filtered["PrefixLists"]) == 1
+
+
+@mock_aws
+def test_delete_vpc_with_subnet_dependency():
+    client = boto3.client("ec2", region_name="us-east-1")
+    vpc = client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
+    subnet = client.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.1.0/24")["Subnet"]
+
+    with pytest.raises(ClientError) as exc:
+        client.delete_vpc(VpcId=vpc["VpcId"])
+    assert exc.value.response["Error"]["Code"] == "DependencyViolation"
+    assert (
+        exc.value.response["Error"]["Message"]
+        == f"The vpc '{vpc['VpcId']}' has dependencies and cannot be deleted."
+    )
+
+    client.delete_subnet(SubnetId=subnet["SubnetId"])
+    client.delete_vpc(VpcId=vpc["VpcId"])
diff --git a/tests/test_rds/test_rds.py b/tests/test_rds/test_rds.py
index 7fdf0446c..a9650033e 100644
--- a/tests/test_rds/test_rds.py
+++ b/tests/test_rds/test_rds.py
@@ -486,6 +486,10 @@ def test_modify_db_instance_manage_master_user_password(
         DBInstanceIdentifier=db_id, ManageMasterUserPassword=False
     )
 
+    retry_revert_modification_response = client.modify_db_instance(
+        DBInstanceIdentifier=db_id, ManageMasterUserPassword=False
+    )
+
     assert db_instance.get("MasterUserSecret") is None
     master_user_secret = modify_response["DBInstance"]["MasterUserSecret"]
     assert len(master_user_secret.keys()) == 3
@@ -510,6 +514,9 @@ def test_modify_db_instance_manage_master_user_password(
         == describe_response["DBInstances"][0]["MasterUserSecret"]["SecretArn"]
     )
     assert revert_modification_response["DBInstance"].get("MasterUserSecret") is None
+    assert (
+        retry_revert_modification_response["DBInstance"].get("MasterUserSecret") is None
+    )
 
 
 @pytest.mark.parametrize("with_apply_immediately", [True, False])
diff --git a/tests/test_rds/test_rds_clusters.py b/tests/test_rds/test_rds_clusters.py
index dd30ab133..1570656c2 100644
--- a/tests/test_rds/test_rds_clusters.py
+++ b/tests/test_rds/test_rds_clusters.py
@@ -189,6 +189,10 @@ def test_modify_db_cluster_manage_master_user_password(client, with_custom_kms_k
         DBClusterIdentifier=cluster_id, ManageMasterUserPassword=False
     )
 
+    retry_revert_modification_response = client.modify_db_cluster(
+        DBClusterIdentifier=cluster_id, ManageMasterUserPassword=False
+    )
+
     assert create_response["DBCluster"].get("MasterUserSecret") is None
     master_user_secret = modify_response["DBCluster"]["MasterUserSecret"]
     assert len(master_user_secret.keys()) == 3
@@ -213,6 +217,9 @@ def test_modify_db_cluster_manage_master_user_password(client, with_custom_kms_k
         == describe_response["DBClusters"][0]["MasterUserSecret"]["SecretArn"]
     )
     assert revert_modification_response["DBCluster"].get("MasterUserSecret") is None
+    assert (
+        retry_revert_modification_response["DBCluster"].get("MasterUserSecret") is None
+    )
 
 
 @pytest.mark.parametrize("with_apply_immediately", [True, False])
diff --git a/tests/test_ses/test_ses_boto3.py b/tests/test_ses/test_ses_boto3.py
index c5426958d..c4a1a4b40 100644
--- a/tests/test_ses/test_ses_boto3.py
+++ b/tests/test_ses/test_ses_boto3.py
@@ -1553,3 +1553,67 @@ def test_get_identity_verification_attributes():
         attributes["VerificationAttributes"]["foo@bar.com"]["VerificationStatus"]
         == "Success"
     )
+
+
+@mock_aws
+def test_update_configuration_set_reputation_metrics():
+    conn = boto3.client("ses", region_name="us-east-1")
+
+    # Create a configuration set first
+    conn.create_configuration_set(ConfigurationSet={"Name": "test-config-set"})
+
+    # Enable reputation metrics
+    response = conn.update_configuration_set_reputation_metrics_enabled(
+        ConfigurationSetName="test-config-set", Enabled=True
+    )
+    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+    # Test with non-existent configuration set
+    with pytest.raises(ClientError) as e:
+        conn.update_configuration_set_reputation_metrics_enabled(
+            ConfigurationSetName="non-existent", Enabled=True
+        )
+    assert e.value.response["Error"]["Code"] == "ConfigurationSetDoesNotExist"
+
+
+@mock_aws
+def test_get_identity_dkim_attributes():
+    conn = boto3.client("ses", region_name="us-east-1")
+
+    # Create domain identity
+    domain = "example.com"
+    conn.verify_domain_identity(Domain=domain)
+
+    # Create email identity
+    email = "test@example.com"
+    conn.verify_email_identity(EmailAddress=email)
+
+    # Test getting DKIM attributes for both domain and email
+    response = conn.get_identity_dkim_attributes(Identities=[domain, email])
+
+    # Verify response structure
+    attributes = response["DkimAttributes"]
+    assert len(attributes) == 2
+
+    # Verify domain attributes
+    domain_attrs = attributes[domain]
+    assert domain_attrs["DkimEnabled"] is True
+    assert domain_attrs["DkimVerificationStatus"] == "Success"
+    assert len(domain_attrs["DkimTokens"]) == 3
+    for token in domain_attrs["DkimTokens"]:
+        assert isinstance(token, str)
+        assert len(token) > 0
+
+    # Verify email attributes
+    email_attrs = attributes[email]
+    assert email_attrs["DkimEnabled"] is True
+    assert email_attrs["DkimVerificationStatus"] == "Success"
+    assert "DkimTokens" not in email_attrs  # Email identities don't have DKIM tokens
+
+    # Test unverified identity
+    unverified = "unverified.com"
+    response = conn.get_identity_dkim_attributes(Identities=[unverified])
+    unverified_attrs = response["DkimAttributes"][unverified]
+    assert unverified_attrs["DkimEnabled"] is True
+    assert unverified_attrs["DkimVerificationStatus"] == "NotStarted"
+    assert "DkimTokens" not in unverified_attrs