Repository: getmoto/moto
Pull request #8834 (2025-04-20 17:26:19)

feat(acm): implement `GetAccountConfiguration` and `PutAccountConfiguration` APIs
## Description

Adds support for the following in ACM:

- https://docs.aws.amazon.com/acm/latest/APIReference/API_GetAccountConfiguration.html
- https://docs.aws.amazon.com/acm/latest/APIReference/API_PutAccountConfiguration.html


Patch:
diff --git a/docs/docs/services/acm.rst b/docs/docs/services/acm.rst
index cebe0d025..9a1c6cf95 100644
--- a/docs/docs/services/acm.rst
+++ b/docs/docs/services/acm.rst
@@ -18,12 +18,12 @@ acm
 - [X] delete_certificate
 - [X] describe_certificate
 - [X] export_certificate
-- [ ] get_account_configuration
+- [X] get_account_configuration
 - [X] get_certificate
 - [X] import_certificate
 - [X] list_certificates
 - [ ] list_tags_for_certificate
-- [ ] put_account_configuration
+- [X] put_account_configuration
 - [X] remove_tags_from_certificate
 - [ ] renew_certificate
 - [X] request_certificate
diff --git a/moto/acm/models.py b/moto/acm/models.py
index 913b9da9c..f5af53ca0 100644
--- a/moto/acm/models.py
+++ b/moto/acm/models.py
@@ -435,6 +435,14 @@ class CertBundle(BaseModel):
         return "<Certificate>"
 
 
+class AccountConfiguration:
+    def __init__(self, days_before_expiry: int = 45):
+        self.days_before_expiry = days_before_expiry
+
+    def to_dict(self):  # type: ignore
+        return {"ExpiryEvents": {"DaysBeforeExpiry": self.days_before_expiry}}
+
+
 class AWSCertificateManagerBackend(BaseBackend):
     MIN_PASSPHRASE_LEN = 4
 
@@ -442,6 +450,7 @@ class AWSCertificateManagerBackend(BaseBackend):
         super().__init__(region_name, account_id)
         self._certificates: Dict[str, CertBundle] = {}
         self._idempotency_tokens: Dict[str, Any] = {}
+        self._account_config = AccountConfiguration()
 
     def set_certificate_in_use_by(self, arn: str, load_balancer_name: str) -> None:
         if arn not in self._certificates:
@@ -604,5 +613,23 @@ class AWSCertificateManagerBackend(BaseBackend):
 
         return certificate, certificate_chain, private_key
 
+    def get_account_configuration(self) -> Dict[str, Any]:
+        return self._account_config.to_dict()  # type: ignore
+
+    def put_account_configuration(
+        self, days_before_expiry: int, idempotency_token: str
+    ) -> None:
+        if idempotency_token is not None:
+            arn = self._get_arn_from_idempotency_token(idempotency_token)
+            if arn:
+                return
+
+        if days_before_expiry < 1 or days_before_expiry > 90:
+            raise AWSValidationException("DaysBeforeExpiry must be between 1 and 90")
+
+        self._account_config = AccountConfiguration(days_before_expiry)
+        if idempotency_token is not None:
+            self._set_idempotency_token_arn(idempotency_token, "account_config")
+
 
 acm_backends = BackendDict(AWSCertificateManagerBackend, "acm")
diff --git a/moto/acm/responses.py b/moto/acm/responses.py
index 97c27cae1..8c1320c31 100644
--- a/moto/acm/responses.py
+++ b/moto/acm/responses.py
@@ -251,3 +251,31 @@ class AWSCertificateManagerResponse(BaseResponse):
                 PrivateKey=private_key,
             )
         )
+
+    def get_account_configuration(self) -> str:
+        config = self.acm_backend.get_account_configuration()
+        return json.dumps(config)
+
+    def put_account_configuration(self) -> str:
+        expiry_events = self._get_param("ExpiryEvents")
+        idempotency_token = self._get_param("IdempotencyToken")
+
+        if expiry_events is None:
+            msg = "A required parameter for the specified action is not supplied."
+            return (  # type: ignore
+                json.dumps({"__type": "MissingParameter", "message": msg}),
+                dict(status=400),
+            )
+
+        days_before_expiry = expiry_events.get("DaysBeforeExpiry")
+        if days_before_expiry is None:
+            msg = "DaysBeforeExpiry is required in ExpiryEvents."
+            return (  # type: ignore
+                json.dumps({"__type": "ValidationException", "message": msg}),
+                dict(status=400),
+            )
+
+        self.acm_backend.put_account_configuration(
+            days_before_expiry, idempotency_token
+        )
+        return "{}"
diff --git a/moto/iam/models.py b/moto/iam/models.py
index 868e30797..a2a3da00b 100644
--- a/moto/iam/models.py
+++ b/moto/iam/models.py
@@ -802,7 +802,7 @@ class Role(CloudFormationModel):
         account_id: str,
         region_name: str,
     ) -> None:
-        backend = iam_backends[account_id][get_partition(region_name)]
+        backend: IAMBackend = iam_backends[account_id][get_partition(region_name)]
         for profile in backend.instance_profiles.values():
             profile.delete_role(role_name=resource_name)
 
@@ -810,6 +810,13 @@ class Role(CloudFormationModel):
             if role.name == resource_name:
                 for arn in list(role.policies.keys()):
                     role.delete_policy(arn)
+
+        # We may have inline policies attached to this role - make sure that they don't reference a deleted role
+        # If the reference still exists, it's impossible to delete the policy, because on deletion
+        # it will try to detach itself from a role that no longer exists
+        for policy in backend.inline_policies.values():
+            policy.role_names = [n for n in policy.role_names if n != resource_name]
+
         backend.delete_role(resource_name)
 
     @property
diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py
index 21a98e8d5..bfd600f7f 100644
--- a/moto/s3/exceptions.py
+++ b/moto/s3/exceptions.py
@@ -620,26 +620,6 @@ class HeadOnDeleteMarker(Exception):
         self.marker = marker
 
 
-class DaysMustNotProvidedForSelectRequest(S3ClientError):
-    code = 400
-
-    def __init__(self) -> None:
-        super().__init__(
-            "DaysMustNotProvidedForSelectRequest",
-            "`Days` must not be provided for select requests",
-        )
-
-
-class DaysMustProvidedExceptForSelectRequest(S3ClientError):
-    code = 400
-
-    def __init__(self) -> None:
-        super().__init__(
-            "DaysMustProvidedExceptForSelectRequest",
-            "`Days` must be provided except for select requests",
-        )
-
-
 class MethodNotAllowed(S3ClientError):
     code = 405
 
diff --git a/moto/s3/models.py b/moto/s3/models.py
index c34e30c57..6350801e7 100644
--- a/moto/s3/models.py
+++ b/moto/s3/models.py
@@ -40,8 +40,6 @@ from moto.s3.exceptions import (
     BucketNeedsToBeNew,
     CopyObjectMustChangeSomething,
     CrossLocationLoggingProhibitted,
-    DaysMustNotProvidedForSelectRequest,
-    DaysMustProvidedExceptForSelectRequest,
     EntityTooSmall,
     HeadOnDeleteMarker,
     InvalidBucketName,
@@ -3104,12 +3102,6 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
         if not key:
             raise MissingKey
 
-        if days is None and type_ is None:
-            raise DaysMustProvidedExceptForSelectRequest()
-
-        if days and type_:
-            raise DaysMustNotProvidedForSelectRequest()
-
         if key.storage_class not in ARCHIVE_STORAGE_CLASSES:
             raise InvalidObjectState(storage_class=key.storage_class)
         had_expiry_date = key.expiry_date is not None
diff --git a/moto/ssm/resources/regions.json b/moto/ssm/resources/regions.json
index e4b931efb..cd86c9077 100644
--- a/moto/ssm/resources/regions.json
+++ b/moto/ssm/resources/regions.json
@@ -1582,6 +1582,15 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "socialmessaging": {
+                "Value": "socialmessaging",
+                "endpoint": {
+                  "Value": "social-messaging.af-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sqs": {
                 "Value": "sqs",
                 "endpoint": {
@@ -3137,6 +3146,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "rum": {
+                "Value": "rum",
+                "endpoint": {
+                  "Value": "rum.ap-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "s3": {
                 "Value": "s3",
                 "endpoint": {
@@ -6032,6 +6050,15 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "socialmessaging": {
+                "Value": "socialmessaging",
+                "endpoint": {
+                  "Value": "social-messaging.ap-northeast-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sqs": {
                 "Value": "sqs",
                 "endpoint": {
@@ -8576,6 +8603,15 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "socialmessaging": {
+                "Value": "socialmessaging",
+                "endpoint": {
+                  "Value": "social-messaging.ap-northeast-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sqs": {
                 "Value": "sqs",
                 "endpoint": {
@@ -14193,6 +14229,15 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "socialmessaging": {
+                "Value": "socialmessaging",
+                "endpoint": {
+                  "Value": "social-messaging.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sqs": {
                 "Value": "sqs",
                 "endpoint": {
@@ -19038,15 +19083,6 @@
                   "Value": "HTTPS"
                 }
               },
-              "opsworkscm": {
-                "Value": "opsworkscm",
-                "endpoint": {
-                  "Value": "opsworks-cm.ap-southeast-2.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
               "opsworkspuppetenterprise": {
                 "Value": "opsworkspuppetenterprise",
                 "endpoint": {
@@ -19659,6 +19695,15 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "socialmessaging": {
+                "Value": "socialmessaging",
+                "endpoint": {
+                  "Value": "social-messaging.ap-southeast-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sqs": {
                 "Value": "sqs",
                 "endpoint": {
@@ -23553,6 +23598,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "pca-connector-scep": {
+                "Value": "pca-connector-scep",
+                "endpoint": {
+                  "Value": "pca-connector-scep.ap-southeast-5.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "pi": {
                 "Value": "pi",
                 "endpoint": {
@@ -24013,6 +24067,15 @@
               "artifact": {
                 "Value": "artifact"
               },
+              "athena": {
+                "Value": "athena",
+                "endpoint": {
+                  "Value": "athena.ap-southeast-7.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "aurora": {
                 "Value": "aurora",
                 "endpoint": {
@@ -26954,6 +27017,15 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "socialmessaging": {
+                "Value": "socialmessaging",
+                "endpoint": {
+                  "Value": "social-messaging.ca-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sqs": {
                 "Value": "sqs",
                 "endpoint": {
@@ -40744,6 +40816,15 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "socialmessaging": {
+                "Value": "socialmessaging",
+                "endpoint": {
+                  "Value": "social-messaging.eu-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sqs": {
                 "Value": "sqs",
                 "endpoint": {
@@ -42902,15 +42983,6 @@
                   "Value": "HTTPS"
                 }
               },
-              "opsworkscm": {
-                "Value": "opsworkscm",
-                "endpoint": {
-                  "Value": "opsworks-cm.eu-west-1.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
               "opsworkspuppetenterprise": {
                 "Value": "opsworkspuppetenterprise",
                 "endpoint": {
@@ -48589,6 +48661,9 @@
             "wavelength-zones": {
               "euw3-cmn1-wlz1": {
                 "Value": "euw3-cmn1-wlz1"
+              },
+              "euw3-dss1-wlz1": {
+                "Value": "euw3-dss1-wlz1"
               }
             }
           },
@@ -49677,6 +49752,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "rum": {
+                "Value": "rum",
+                "endpoint": {
+                  "Value": "rum.il-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "s3": {
                 "Value": "s3",
                 "endpoint": {
@@ -53322,6 +53406,15 @@
               "artifact": {
                 "Value": "artifact"
               },
+              "athena": {
+                "Value": "athena",
+                "endpoint": {
+                  "Value": "athena.mx-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "aurora": {
                 "Value": "aurora",
                 "endpoint": {
@@ -54036,6 +54129,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "transfer": {
+                "Value": "transfer",
+                "endpoint": {
+                  "Value": "transfer.mx-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "transitgateway": {
                 "Value": "transitgateway",
                 "endpoint": {
@@ -55951,6 +56053,15 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "socialmessaging": {
+                "Value": "socialmessaging",
+                "endpoint": {
+                  "Value": "social-messaging.sa-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sqs": {
                 "Value": "sqs",
                 "endpoint": {
@@ -58510,15 +58621,6 @@
                   "Value": "HTTPS"
                 }
               },
-              "opsworkscm": {
-                "Value": "opsworkscm",
-                "endpoint": {
-                  "Value": "opsworks-cm.us-east-1.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
               "opsworkspuppetenterprise": {
                 "Value": "opsworkspuppetenterprise",
                 "endpoint": {
diff --git a/moto/ssm/resources/services.json b/moto/ssm/resources/services.json
index 6222d56b2..048b59de4 100644
--- a/moto/ssm/resources/services.json
+++ b/moto/ssm/resources/services.json
@@ -5806,6 +5806,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-southeast-7": {
+                "Value": "ap-southeast-7",
+                "endpoint": {
+                  "Value": "athena.ap-southeast-7.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ca-central-1": {
                 "Value": "ca-central-1",
                 "endpoint": {
@@ -5941,6 +5950,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "mx-central-1": {
+                "Value": "mx-central-1",
+                "endpoint": {
+                  "Value": "athena.mx-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sa-east-1": {
                 "Value": "sa-east-1",
                 "endpoint": {
@@ -48322,41 +48340,6 @@
               }
             }
           },
-          "opsworkscm": {
-            "Value": "opsworkscm",
-            "longName": {
-              "Value": "AWS OpsWorks for Chef Automate"
-            },
-            "regions": {
-              "ap-southeast-2": {
-                "Value": "ap-southeast-2",
-                "endpoint": {
-                  "Value": "opsworks-cm.ap-southeast-2.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
-              "eu-west-1": {
-                "Value": "eu-west-1",
-                "endpoint": {
-                  "Value": "opsworks-cm.eu-west-1.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
-              "us-east-1": {
-                "Value": "us-east-1",
-                "endpoint": {
-                  "Value": "opsworks-cm.us-east-1.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              }
-            }
-          },
           "opsworkspuppetenterprise": {
             "Value": "opsworkspuppetenterprise",
             "longName": {
@@ -49580,6 +49563,10 @@
             }
           },
           "pca-connector-scep": {
+            "Value": "pca-connector-scep",
+            "longName": {
+              "Value": "Private CA Connector for SCEP"
+            },
             "regions": {
               "af-south-1": {
                 "Value": "af-south-1",
@@ -49680,6 +49667,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-southeast-5": {
+                "Value": "ap-southeast-5",
+                "endpoint": {
+                  "Value": "pca-connector-scep.ap-southeast-5.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ca-central-1": {
                 "Value": "ca-central-1",
                 "endpoint": {
@@ -57502,6 +57498,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-east-1": {
+                "Value": "ap-east-1",
+                "endpoint": {
+                  "Value": "rum.ap-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-northeast-1": {
                 "Value": "ap-northeast-1",
                 "endpoint": {
@@ -57664,6 +57669,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "il-central-1": {
+                "Value": "il-central-1",
+                "endpoint": {
+                  "Value": "rum.il-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "me-central-1": {
                 "Value": "me-central-1",
                 "endpoint": {
@@ -64667,6 +64681,33 @@
               "Value": "social-messaging"
             },
             "regions": {
+              "af-south-1": {
+                "Value": "af-south-1",
+                "endpoint": {
+                  "Value": "social-messaging.af-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "ap-northeast-1": {
+                "Value": "ap-northeast-1",
+                "endpoint": {
+                  "Value": "social-messaging.ap-northeast-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "ap-northeast-2": {
+                "Value": "ap-northeast-2",
+                "endpoint": {
+                  "Value": "social-messaging.ap-northeast-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-south-1": {
                 "Value": "ap-south-1",
                 "endpoint": {
@@ -64676,6 +64717,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-south-2": {
+                "Value": "ap-south-2",
+                "endpoint": {
+                  "Value": "social-messaging.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-southeast-1": {
                 "Value": "ap-southeast-1",
                 "endpoint": {
@@ -64685,6 +64735,24 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-southeast-2": {
+                "Value": "ap-southeast-2",
+                "endpoint": {
+                  "Value": "social-messaging.ap-southeast-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "ca-central-1": {
+                "Value": "ca-central-1",
+                "endpoint": {
+                  "Value": "social-messaging.ca-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "eu-central-1": {
                 "Value": "eu-central-1",
                 "endpoint": {
@@ -64694,6 +64762,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "eu-south-2": {
+                "Value": "eu-south-2",
+                "endpoint": {
+                  "Value": "social-messaging.eu-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "eu-west-1": {
                 "Value": "eu-west-1",
                 "endpoint": {
@@ -64712,6 +64789,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "sa-east-1": {
+                "Value": "sa-east-1",
+                "endpoint": {
+                  "Value": "social-messaging.sa-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "us-east-1": {
                 "Value": "us-east-1",
                 "endpoint": {
@@ -69576,6 +69662,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "mx-central-1": {
+                "Value": "mx-central-1",
+                "endpoint": {
+                  "Value": "transfer.mx-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "sa-east-1": {
                 "Value": "sa-east-1",
                 "endpoint": {


Test patch:
diff --git a/tests/test_acm/test_acm.py b/tests/test_acm/test_acm.py
index 8052d56ca..004cdea2c 100644
--- a/tests/test_acm/test_acm.py
+++ b/tests/test_acm/test_acm.py
@@ -884,3 +884,30 @@ def test_certificate_expiration_status():
     with freeze_time(expiry_time + datetime.timedelta(days=1)):  # 1 day after
         resp = client.describe_certificate(CertificateArn=arn)
         assert resp["Certificate"]["Status"] == "EXPIRED"
+
+
+@mock_aws
+def test_account_configuration():
+    client = boto3.client("acm", region_name="eu-central-1")
+
+    # Test default configuration
+    response = client.get_account_configuration()
+    assert response["ExpiryEvents"]["DaysBeforeExpiry"] == 45
+
+    # Test successful update
+    response = client.put_account_configuration(
+        ExpiryEvents={"DaysBeforeExpiry": 30}, IdempotencyToken="test-token"
+    )
+
+    # Verify the update was successful
+    response = client.get_account_configuration()
+    assert response["ExpiryEvents"]["DaysBeforeExpiry"] == 30
+
+    # Test idempotency token - trying to update with same token but different value
+    response = client.put_account_configuration(
+        ExpiryEvents={"DaysBeforeExpiry": 60}, IdempotencyToken="test-token"
+    )
+
+    # Should still be 30 due to idempotency token
+    response = client.get_account_configuration()
+    assert response["ExpiryEvents"]["DaysBeforeExpiry"] == 30
diff --git a/tests/test_iam/test_iam_cloudformation.py b/tests/test_iam/test_iam_cloudformation.py
index 5650d81ca..d69c3388f 100644
--- a/tests/test_iam/test_iam_cloudformation.py
+++ b/tests/test_iam/test_iam_cloudformation.py
@@ -71,6 +71,50 @@ Resources:
         - !Ref RootRole
 """
 
+TEMPLATE_ROLE_AND_PROFILE = """
+---
+AWSTemplateFormatVersion: '2010-09-09'
+
+Resources:
+  Role:
+    Type: AWS::IAM::Role
+    Properties:
+      AssumeRolePolicyDocument:
+        Version: "2012-10-17"
+        Statement:
+          - Effect: Allow
+            Principal:
+              Service: ecs-tasks.amazonaws.com
+            Action: sts:AssumeRole
+      Policies:
+        - PolicyName: StandardTaskPolicy
+          PolicyDocument:
+            Version: "2012-10-17"
+            Statement:
+              - Sid: AllowCloudwatchLogging
+                Effect: Allow
+                Action:
+                  - logs:CreateLogStream
+                  - logs:DescribeLogGroups
+                  - logs:DescribeLogStreams
+                Resource: '*'
+
+  Pol10:
+    Type: AWS::IAM::Policy
+    Properties:
+      PolicyName: AdditionalInlinePolicy
+      PolicyDocument:
+        Version: "2012-10-17"
+        Statement:
+          - Sid: AllowCloudwatchLogging
+            Effect: Allow
+            Action:
+              - logs:PutLogEvents
+            Resource: '*'
+      Roles:
+        - !Ref Role
+"""
+
 
 # AWS::IAM::User Tests
 @mock_aws
@@ -1091,6 +1135,28 @@ Resources:
     assert exc.value.response["Error"]["Code"] == "NoSuchEntity"
 
 
+@mock_aws
+def test_delete_cfn_stack_with_policy_attached_to_role():
+    # The Stack has two resources, a Role and an InlinePolicy attached to this role.
+    # On deletion, there are two scenarios:
+    #
+    #    1. Policy is deleted first
+    #       a) removes policy from the roles it's attached to
+    #       b) removes the policy itself
+    #       c) removes the role last
+    #    2. Role is deleted first
+    #       a) removes the role
+    #       b) removes inline policy from the roles it's attached to
+    #       c) removes policy itself
+    #
+    # This test verifies that, at step 2b, we don't try to remove a policy from a role that no longer exist
+    cfn = boto3.client("cloudformation", region_name="us-east-1")
+
+    cfn.create_stack(StackName="stack1", TemplateBody=TEMPLATE_ROLE_AND_PROFILE)
+
+    cfn.delete_stack(StackName="stack1")
+
+
 # AWS::IAM::User AccessKeys
 @mock_aws
 def test_iam_cloudformation_create_user_with_access_key():
diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py
index 952496327..4815aa317 100644
--- a/tests/test_s3/test_s3.py
+++ b/tests/test_s3/test_s3.py
@@ -711,32 +711,6 @@ def test_cannot_restore_standard_class_object():
     )
 
 
-@mock_aws
-def test_restore_object_invalid_request_params():
-    if not settings.TEST_DECORATOR_MODE:
-        raise SkipTest("Can't set transition directly in ServerMode")
-
-    s3_resource = boto3.resource("s3", region_name=DEFAULT_REGION_NAME)
-    bucket = s3_resource.Bucket("foobar")
-    bucket.create()
-
-    key = bucket.put_object(Key="the-key", Body=b"somedata", StorageClass="GLACIER")
-
-    # `Days` must be provided except for select requests
-    with pytest.raises(ClientError) as exc:
-        key.restore_object(RestoreRequest={})
-    err = exc.value.response["Error"]
-    assert err["Code"] == "DaysMustProvidedExceptForSelectRequest"
-    assert err["Message"] == "`Days` must be provided except for select requests"
-
-    # `Days` must not be provided for select requests
-    with pytest.raises(ClientError) as exc:
-        key.restore_object(RestoreRequest={"Days": 1, "Type": "SELECT"})
-    err = exc.value.response["Error"]
-    assert err["Code"] == "DaysMustNotProvidedForSelectRequest"
-    assert err["Message"] == "`Days` must not be provided for select requests"
-
-
 @mock_aws
 def test_get_versioning_status():
     s3_resource = boto3.resource("s3", region_name=DEFAULT_REGION_NAME)