Repository: getmoto/moto
Pull request #8225 (2024-10-12 23:58:37)

ELBv2: remove_tags() now throws a ResourceNotFound Exception
## Motivation
This pr aims to add parity with `remove_tags` API of `elbv2` that throws exception in certain scenarios. Currently, no exceptions were being thrown from `remove_tags`.

##  Changes
Include logic to throw exceptions similar to `add_tags` by using the method `_get_resource_by_arn` that can check for various NotFound scenarios.
Added test cases for loadbalancer, listener, listener rule and target group to reflect the new behavior. 

## Not in scope
It doesn't currently cover the singular nature of the API. Even though the parameter `ResourceArns` expects a list but only one ARN can be passed. This use case is not covered yet.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/getmoto/moto/pull/8225?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.54%. Comparing base [(`81d6f95`)](https://app.codecov.io/gh/getmoto/moto/commit/81d6f95f0459b3a3fc4a0780319e089c4eb0e1ad?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) to head [(`c02784b`)](https://app.codecov.io/gh/getmoto/moto/commit/c02784b269f286522b54d2b5d16143dec373d2bc?dropdown=coverage&el=desc&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    #8225   +/-   ##
=======================================
  Coverage   94.53%   94.54%           
=======================================
  Files        1157     1157           
  Lines       99973    99974    +1     
=======================================
+ Hits        94513    94518    +5     
+ Misses       5460     5456    -4     
```

| [Flag](https://app.codecov.io/gh/getmoto/moto/pull/8225/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/8225/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `28.80% <0.00%> (-0.01%)` | :arrow_down: |
| [unittests](https://app.codecov.io/gh/getmoto/moto/pull/8225/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `94.51% <100.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/8225?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).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0cb5d69c0..5836554ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,43 @@
 Moto Changelog
 ==============
 
+5.0.17
+-----
+Docker Digest for 5.0.17: _sha256:39372432cb24ab46211ca45648ca787e104589070b0d0a13ea0d73c6eb550079_
+
+    New Methods:
+        * CloudFront:
+            * create_key_group()
+            * create_public_key()
+            * delete_public_key()
+            * get_key_group()
+            * get_public_key()
+            * list_key_groups()
+            * list_public_keys()
+
+        * QuickSight:
+            * list_user_groups()
+            * search_groups()
+            * update_user()
+
+        * Workspaces Web:
+            * list_tags_for_resource()
+            * tag_resource()
+            * untag_resource()
+
+    Miscellaneous:
+        * APIGateway: get_api_keys()/get_usage_plan_keys() now support the nameQuery param
+        * AppSync: create_graphql_api() now supports the visibility-parameter
+        * DynamoDB: delete_item() now returns the item when a ConditionalCheckFailed is thrown and ReturnValuesOnConditionCheckFailure == ALL_OLD
+        * QuickSight: Users can now have special characters in their name
+        * QuickSight: list_group_memberships() now supports pagination
+        * QuickSight: list_groups() now supports pagination
+        * QuickSight: list_users() now supports pagination
+        * SageMaker: search() now also supports ModelPackages/Pipelines/Jobs/Executions
+        * SecretsManager: delete_secret() now allows force deletion of already marked-for-delete secret
+        * StepFunctions: create_state_machine() now supports the parameters encryptionConfiguration, tracingConfiguration, loggingConfiguration
+
+
 5.0.16
 -----
 Docker Digest for 5.0.16: _sha256:9506ad3448a87082a436533855c61afaf3f1869e73f39f6575917db975569908_
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 978088bdd..ffdc5862e 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -805,7 +805,7 @@
 
 ## bedrock-agent
 <details>
-<summary>18% implemented</summary>
+<summary>17% implemented</summary>
 
 - [ ] associate_agent_knowledge_base
 - [X] create_agent
@@ -857,6 +857,7 @@
 - [ ] prepare_agent
 - [ ] prepare_flow
 - [ ] start_ingestion_job
+- [ ] stop_ingestion_job
 - [X] tag_resource
 - [X] untag_resource
 - [ ] update_agent
@@ -2015,6 +2016,7 @@
 - [ ] apply_pending_maintenance_action
 - [ ] batch_start_recommendations
 - [ ] cancel_replication_task_assessment_run
+- [ ] create_data_migration
 - [ ] create_data_provider
 - [ ] create_endpoint
 - [ ] create_event_subscription
@@ -2027,6 +2029,7 @@
 - [X] create_replication_task
 - [ ] delete_certificate
 - [ ] delete_connection
+- [ ] delete_data_migration
 - [ ] delete_data_provider
 - [ ] delete_endpoint
 - [ ] delete_event_subscription
@@ -2044,6 +2047,7 @@
 - [ ] describe_certificates
 - [ ] describe_connections
 - [ ] describe_conversion_configuration
+- [ ] describe_data_migrations
 - [ ] describe_data_providers
 - [ ] describe_endpoint_settings
 - [ ] describe_endpoint_types
@@ -2086,6 +2090,7 @@
 - [ ] import_certificate
 - [ ] list_tags_for_resource
 - [ ] modify_conversion_configuration
+- [ ] modify_data_migration
 - [ ] modify_data_provider
 - [ ] modify_endpoint
 - [ ] modify_event_subscription
@@ -2102,6 +2107,7 @@
 - [ ] reload_tables
 - [ ] remove_tags_from_resource
 - [ ] run_fleet_advisor_lsa_analysis
+- [ ] start_data_migration
 - [ ] start_extension_pack_association
 - [ ] start_metadata_model_assessment
 - [ ] start_metadata_model_conversion
@@ -2113,6 +2119,7 @@
 - [X] start_replication_task
 - [ ] start_replication_task_assessment
 - [ ] start_replication_task_assessment_run
+- [ ] stop_data_migration
 - [ ] stop_replication
 - [X] stop_replication_task
 - [ ] test_connection
@@ -2285,6 +2292,7 @@
 <summary>33% implemented</summary>
 
 - [ ] accept_address_transfer
+- [ ] accept_capacity_reservation_billing_ownership
 - [ ] accept_reserved_instances_exchange_quote
 - [ ] accept_transit_gateway_multicast_domain_associations
 - [X] accept_transit_gateway_peering_attachment
@@ -2300,6 +2308,7 @@
 - [X] assign_private_ip_addresses
 - [ ] assign_private_nat_gateway_address
 - [X] associate_address
+- [ ] associate_capacity_reservation_billing_owner
 - [ ] associate_client_vpn_target_network
 - [X] associate_dhcp_options
 - [ ] associate_enclave_certificate_iam_role
@@ -2516,6 +2525,7 @@
 - [ ] describe_bundle_tasks
 - [ ] describe_byoip_cidrs
 - [ ] describe_capacity_block_offerings
+- [ ] describe_capacity_reservation_billing_requests
 - [ ] describe_capacity_reservation_fleets
 - [ ] describe_capacity_reservations
 - [X] describe_carrier_gateways
@@ -2681,6 +2691,7 @@
 - [X] disable_vpc_classic_link
 - [X] disable_vpc_classic_link_dns_support
 - [X] disassociate_address
+- [ ] disassociate_capacity_reservation_billing_owner
 - [ ] disassociate_client_vpn_target_network
 - [ ] disassociate_enclave_certificate_iam_role
 - [X] disassociate_iam_instance_profile
@@ -2790,6 +2801,7 @@
 - [X] modify_image_attribute
 - [X] modify_instance_attribute
 - [ ] modify_instance_capacity_reservation_attributes
+- [ ] modify_instance_cpu_options
 - [ ] modify_instance_credit_specification
 - [ ] modify_instance_event_start_time
 - [ ] modify_instance_event_window
@@ -2857,6 +2869,7 @@
 - [ ] register_instance_event_notification_attributes
 - [ ] register_transit_gateway_multicast_group_members
 - [ ] register_transit_gateway_multicast_group_sources
+- [ ] reject_capacity_reservation_billing_ownership
 - [ ] reject_transit_gateway_multicast_domain_associations
 - [X] reject_transit_gateway_peering_attachment
 - [ ] reject_transit_gateway_vpc_attachment
@@ -6221,7 +6234,7 @@
 
 ## quicksight
 <details>
-<summary>7% implemented</summary>
+<summary>9% implemented</summary>
 
 - [ ] batch_create_topic_reviewed_answer
 - [ ] batch_delete_topic_reviewed_answer
@@ -6351,7 +6364,7 @@
 - [ ] list_topic_refresh_schedules
 - [ ] list_topic_reviewed_answers
 - [ ] list_topics
-- [ ] list_user_groups
+- [X] list_user_groups
 - [X] list_users
 - [ ] list_vpc_connections
 - [ ] put_data_set_refresh_properties
@@ -6362,7 +6375,7 @@
 - [ ] search_data_sets
 - [ ] search_data_sources
 - [ ] search_folders
-- [ ] search_groups
+- [X] search_groups
 - [ ] start_asset_bundle_export_job
 - [ ] start_asset_bundle_import_job
 - [ ] start_dashboard_snapshot_job
@@ -6401,7 +6414,7 @@
 - [ ] update_topic
 - [ ] update_topic_permissions
 - [ ] update_topic_refresh_schedule
-- [ ] update_user
+- [X] update_user
 - [ ] update_vpc_connection
 </details>
 
@@ -6929,20 +6942,25 @@
 
 ## resource-groups
 <details>
-<summary>61% implemented</summary>
+<summary>47% implemented</summary>
 
+- [ ] cancel_tag_sync_task
 - [X] create_group
 - [X] delete_group
 - [ ] get_account_settings
 - [X] get_group
 - [X] get_group_configuration
 - [ ] get_group_query
+- [ ] get_tag_sync_task
 - [X] get_tags
 - [ ] group_resources
 - [ ] list_group_resources
+- [ ] list_grouping_statuses
 - [X] list_groups
+- [ ] list_tag_sync_tasks
 - [X] put_group_configuration
 - [ ] search_resources
+- [ ] start_tag_sync_task
 - [X] tag
 - [ ] ungroup_resources
 - [X] untag
@@ -8919,7 +8937,7 @@
 
 ## workspaces-web
 <details>
-<summary>39% implemented</summary>
+<summary>44% implemented</summary>
 
 - [X] associate_browser_settings
 - [ ] associate_ip_access_settings
@@ -8967,13 +8985,13 @@
 - [X] list_network_settings
 - [X] list_portals
 - [ ] list_sessions
-- [ ] list_tags_for_resource
+- [X] list_tags_for_resource
 - [ ] list_trust_store_certificates
 - [ ] list_trust_stores
 - [X] list_user_access_logging_settings
 - [X] list_user_settings
-- [ ] tag_resource
-- [ ] untag_resource
+- [X] tag_resource
+- [X] untag_resource
 - [ ] update_browser_settings
 - [ ] update_identity_provider
 - [ ] update_ip_access_settings
@@ -9137,6 +9155,7 @@
 - marketplace-catalog
 - marketplace-deployment
 - marketplace-entitlement
+- marketplace-reporting
 - marketplacecommerceanalytics
 - mediaconvert
 - mediapackage-vod
@@ -9206,6 +9225,7 @@
 - sms-voice
 - snow-device-management
 - snowball
+- socialmessaging
 - ssm-contacts
 - ssm-incidents
 - ssm-quicksetup
diff --git a/docs/docs/services/bedrock-agent.rst b/docs/docs/services/bedrock-agent.rst
index 1a37a5bc2..afb982fb2 100644
--- a/docs/docs/services/bedrock-agent.rst
+++ b/docs/docs/services/bedrock-agent.rst
@@ -66,6 +66,7 @@ bedrock-agent
 - [ ] prepare_agent
 - [ ] prepare_flow
 - [ ] start_ingestion_job
+- [ ] stop_ingestion_job
 - [X] tag_resource
 - [X] untag_resource
 - [ ] update_agent
diff --git a/docs/docs/services/cf.rst b/docs/docs/services/cf.rst
index 1beea531d..fe5398fd2 100644
--- a/docs/docs/services/cf.rst
+++ b/docs/docs/services/cf.rst
@@ -84,6 +84,8 @@ Please let us know if you'd like support for a resource not yet listed here.
   |                                       |        |        |        | - [x] AvailabilityZone                 |
   +---------------------------------------+--------+--------+--------+ - [ ] CidrBlock                        |
   |                                       |        |        |        | - [ ] SubnetId                         |
+  +---------------------------------------+--------+--------+--------+ - [ ] Ipv6CidrBlocks                   |
+  |                                       |        |        |        |                                        |
   +---------------------------------------+--------+--------+--------+----------------------------------------+
   |AWS::EC2::SubnetRouteTableAssociation  |    x   |        |        | - [ ] Id                               |
   +---------------------------------------+--------+--------+--------+----------------------------------------+
diff --git a/docs/docs/services/dms.rst b/docs/docs/services/dms.rst
index 62d76e06c..eaf63d6f8 100644
--- a/docs/docs/services/dms.rst
+++ b/docs/docs/services/dms.rst
@@ -18,6 +18,7 @@ dms
 - [ ] apply_pending_maintenance_action
 - [ ] batch_start_recommendations
 - [ ] cancel_replication_task_assessment_run
+- [ ] create_data_migration
 - [ ] create_data_provider
 - [ ] create_endpoint
 - [ ] create_event_subscription
@@ -35,6 +36,7 @@ dms
 
 - [ ] delete_certificate
 - [ ] delete_connection
+- [ ] delete_data_migration
 - [ ] delete_data_provider
 - [ ] delete_endpoint
 - [ ] delete_event_subscription
@@ -52,6 +54,7 @@ dms
 - [ ] describe_certificates
 - [ ] describe_connections
 - [ ] describe_conversion_configuration
+- [ ] describe_data_migrations
 - [ ] describe_data_providers
 - [ ] describe_endpoint_settings
 - [ ] describe_endpoint_types
@@ -98,6 +101,7 @@ dms
 - [ ] import_certificate
 - [ ] list_tags_for_resource
 - [ ] modify_conversion_configuration
+- [ ] modify_data_migration
 - [ ] modify_data_provider
 - [ ] modify_endpoint
 - [ ] modify_event_subscription
@@ -114,6 +118,7 @@ dms
 - [ ] reload_tables
 - [ ] remove_tags_from_resource
 - [ ] run_fleet_advisor_lsa_analysis
+- [ ] start_data_migration
 - [ ] start_extension_pack_association
 - [ ] start_metadata_model_assessment
 - [ ] start_metadata_model_conversion
@@ -130,6 +135,7 @@ dms
 
 - [ ] start_replication_task_assessment
 - [ ] start_replication_task_assessment_run
+- [ ] stop_data_migration
 - [ ] stop_replication
 - [X] stop_replication_task
 - [ ] test_connection
diff --git a/docs/docs/services/ec2.rst b/docs/docs/services/ec2.rst
index 91567e3b8..fd17becf9 100644
--- a/docs/docs/services/ec2.rst
+++ b/docs/docs/services/ec2.rst
@@ -17,6 +17,7 @@ ec2
 |start-h3| Implemented features for this service |end-h3|
 
 - [ ] accept_address_transfer
+- [ ] accept_capacity_reservation_billing_ownership
 - [ ] accept_reserved_instances_exchange_quote
 - [ ] accept_transit_gateway_multicast_domain_associations
 - [X] accept_transit_gateway_peering_attachment
@@ -32,6 +33,7 @@ ec2
 - [X] assign_private_ip_addresses
 - [ ] assign_private_nat_gateway_address
 - [X] associate_address
+- [ ] associate_capacity_reservation_billing_owner
 - [ ] associate_client_vpn_target_network
 - [X] associate_dhcp_options
 - [ ] associate_enclave_certificate_iam_role
@@ -257,6 +259,7 @@ ec2
 - [ ] describe_bundle_tasks
 - [ ] describe_byoip_cidrs
 - [ ] describe_capacity_block_offerings
+- [ ] describe_capacity_reservation_billing_requests
 - [ ] describe_capacity_reservation_fleets
 - [ ] describe_capacity_reservations
 - [X] describe_carrier_gateways
@@ -449,6 +452,7 @@ ec2
 - [X] disable_vpc_classic_link
 - [X] disable_vpc_classic_link_dns_support
 - [X] disassociate_address
+- [ ] disassociate_capacity_reservation_billing_owner
 - [ ] disassociate_client_vpn_target_network
 - [ ] disassociate_enclave_certificate_iam_role
 - [X] disassociate_iam_instance_profile
@@ -558,6 +562,7 @@ ec2
 - [X] modify_image_attribute
 - [X] modify_instance_attribute
 - [ ] modify_instance_capacity_reservation_attributes
+- [ ] modify_instance_cpu_options
 - [ ] modify_instance_credit_specification
 - [ ] modify_instance_event_start_time
 - [ ] modify_instance_event_window
@@ -629,6 +634,7 @@ ec2
 - [ ] register_instance_event_notification_attributes
 - [ ] register_transit_gateway_multicast_group_members
 - [ ] register_transit_gateway_multicast_group_sources
+- [ ] reject_capacity_reservation_billing_ownership
 - [ ] reject_transit_gateway_multicast_domain_associations
 - [X] reject_transit_gateway_peering_attachment
 - [ ] reject_transit_gateway_vpc_attachment
diff --git a/docs/docs/services/quicksight.rst b/docs/docs/services/quicksight.rst
index e76b8b555..90d4b7b1a 100644
--- a/docs/docs/services/quicksight.rst
+++ b/docs/docs/services/quicksight.rst
@@ -126,15 +126,7 @@ quicksight
 - [ ] list_folders
 - [ ] list_folders_for_resource
 - [X] list_group_memberships
-  
-        The NextToken and MaxResults parameters are not yet implemented
-        
-
 - [X] list_groups
-  
-        The NextToken and MaxResults parameters are not yet implemented
-        
-
 - [ ] list_iam_policy_assignments
 - [ ] list_iam_policy_assignments_for_user
 - [ ] list_identity_propagation_configs
@@ -152,12 +144,8 @@ quicksight
 - [ ] list_topic_refresh_schedules
 - [ ] list_topic_reviewed_answers
 - [ ] list_topics
-- [ ] list_user_groups
+- [X] list_user_groups
 - [X] list_users
-  
-        The NextToken and MaxResults parameters are not yet implemented
-        
-
 - [ ] list_vpc_connections
 - [ ] put_data_set_refresh_properties
 - [X] register_user
@@ -172,7 +160,7 @@ quicksight
 - [ ] search_data_sets
 - [ ] search_data_sources
 - [ ] search_folders
-- [ ] search_groups
+- [X] search_groups
 - [ ] start_asset_bundle_export_job
 - [ ] start_asset_bundle_import_job
 - [ ] start_dashboard_snapshot_job
@@ -211,6 +199,6 @@ quicksight
 - [ ] update_topic
 - [ ] update_topic_permissions
 - [ ] update_topic_refresh_schedule
-- [ ] update_user
+- [X] update_user
 - [ ] update_vpc_connection
 
diff --git a/docs/docs/services/resource-groups.rst b/docs/docs/services/resource-groups.rst
index 200b1fc78..017117765 100644
--- a/docs/docs/services/resource-groups.rst
+++ b/docs/docs/services/resource-groups.rst
@@ -14,22 +14,27 @@ resource-groups
 
 |start-h3| Implemented features for this service |end-h3|
 
+- [ ] cancel_tag_sync_task
 - [X] create_group
 - [X] delete_group
 - [ ] get_account_settings
 - [X] get_group
 - [X] get_group_configuration
 - [ ] get_group_query
+- [ ] get_tag_sync_task
 - [X] get_tags
 - [ ] group_resources
 - [ ] list_group_resources
+- [ ] list_grouping_statuses
 - [X] list_groups
   
         Pagination or the Filters-parameter is not yet implemented
         
 
+- [ ] list_tag_sync_tasks
 - [X] put_group_configuration
 - [ ] search_resources
+- [ ] start_tag_sync_task
 - [X] tag
 - [ ] ungroup_resources
 - [X] untag
diff --git a/docs/docs/services/workspaces-web.rst b/docs/docs/services/workspaces-web.rst
index 521d97961..c78e72ac3 100644
--- a/docs/docs/services/workspaces-web.rst
+++ b/docs/docs/services/workspaces-web.rst
@@ -62,13 +62,13 @@ workspaces-web
 - [X] list_network_settings
 - [X] list_portals
 - [ ] list_sessions
-- [ ] list_tags_for_resource
+- [X] list_tags_for_resource
 - [ ] list_trust_store_certificates
 - [ ] list_trust_stores
 - [X] list_user_access_logging_settings
 - [X] list_user_settings
-- [ ] tag_resource
-- [ ] untag_resource
+- [X] tag_resource
+- [X] untag_resource
 - [ ] update_browser_settings
 - [ ] update_identity_provider
 - [ ] update_ip_access_settings
diff --git a/moto/__init__.py b/moto/__init__.py
index 1ce8bd19d..fe08f4575 100644
--- a/moto/__init__.py
+++ b/moto/__init__.py
@@ -1,4 +1,4 @@
 from moto.core.decorator import mock_aws as mock_aws
 
 __title__ = "moto"
-__version__ = "5.0.17.dev"
+__version__ = "5.0.18.dev"
diff --git a/moto/apigateway/models.py b/moto/apigateway/models.py
index a84145947..fad7ca7e1 100644
--- a/moto/apigateway/models.py
+++ b/moto/apigateway/models.py
@@ -68,7 +68,19 @@ from .exceptions import (
     ValidationException,
     VpcLinkNotFound,
 )
-from .utils import create_id, to_path
+from .utils import (
+    ApigwApiKeyIdentifier,
+    ApigwAuthorizerIdentifier,
+    ApigwDeploymentIdentifier,
+    ApigwModelIdentifier,
+    ApigwRequestValidatorIdentifier,
+    ApigwResourceIdentifier,
+    ApigwRestApiIdentifier,
+    ApigwUsagePlanIdentifier,
+    ApigwVpcLinkIdentifier,
+    create_id,
+    to_path,
+)
 
 STAGE_URL = "https://{api_id}.execute-api.{region_name}.amazonaws.com/{stage_name}"
 PATCH_OPERATIONS = ["add", "remove", "replace", "move", "copy", "test"]
@@ -789,6 +801,7 @@ class Stage(BaseModel):
 class ApiKey(BaseModel):
     def __init__(
         self,
+        api_key_id: str,
         name: Optional[str] = None,
         description: Optional[str] = None,
         enabled: bool = False,
@@ -798,7 +811,7 @@ class ApiKey(BaseModel):
         tags: Optional[List[Dict[str, str]]] = None,
         customerId: Optional[str] = None,
     ):
-        self.id = create_id()
+        self.id = api_key_id
         self.value = value or "".join(
             random.sample(string.ascii_letters + string.digits, 40)
         )
@@ -846,6 +859,7 @@ class ApiKey(BaseModel):
 class UsagePlan(BaseModel):
     def __init__(
         self,
+        usage_plan_id: str,
         name: Optional[str] = None,
         description: Optional[str] = None,
         apiStages: Any = None,
@@ -854,7 +868,7 @@ class UsagePlan(BaseModel):
         productCode: Optional[str] = None,
         tags: Optional[List[Dict[str, str]]] = None,
     ):
-        self.id = create_id()
+        self.id = usage_plan_id
         self.name = name
         self.description = description
         self.api_stages = apiStages or []
@@ -985,12 +999,13 @@ class UsagePlanKey(BaseModel):
 class VpcLink(BaseModel):
     def __init__(
         self,
+        vpc_link_id: str,
         name: str,
         description: str,
         target_arns: List[str],
         tags: List[Dict[str, str]],
     ):
-        self.id = create_id()
+        self.id = vpc_link_id
         self.name = name
         self.description = description
         self.target_arns = target_arns
@@ -1162,7 +1177,9 @@ class RestAPI(CloudFormationModel):
         )
 
     def add_child(self, path: str, parent_id: Optional[str] = None) -> Resource:
-        child_id = create_id()
+        child_id = ApigwResourceIdentifier(
+            self.account_id, self.region_name, parent_id or "", path
+        ).generate()
         child = Resource(
             resource_id=child_id,
             account_id=self.account_id,
@@ -1181,7 +1198,9 @@ class RestAPI(CloudFormationModel):
         schema: str,
         content_type: str,
     ) -> "Model":
-        model_id = create_id()
+        model_id = ApigwModelIdentifier(
+            self.account_id, self.region_name, name
+        ).generate()
         new_model = Model(
             model_id=model_id,
             name=name,
@@ -1293,7 +1312,11 @@ class RestAPI(CloudFormationModel):
     ) -> Deployment:
         if stage_variables is None:
             stage_variables = {}
-        deployment_id = create_id()
+        # Since there are no unique values to a deployment, we will use the stage name for the deployment.
+        # We are also passing a list of deployment ids to generate to prevent overwriting deployments.
+        deployment_id = ApigwDeploymentIdentifier(
+            self.account_id, self.region_name, stage_name=name
+        ).generate(list(self.deployments.keys()))
         deployment = Deployment(deployment_id, name, description)
         self.deployments[deployment_id] = deployment
         if name:
@@ -1332,7 +1355,9 @@ class RestAPI(CloudFormationModel):
         validateRequestBody: Optional[bool],
         validateRequestParameters: Any,
     ) -> RequestValidator:
-        validator_id = create_id()
+        validator_id = ApigwRequestValidatorIdentifier(
+            self.account_id, self.region_name, name
+        ).generate()
         request_validator = RequestValidator(
             _id=validator_id,
             name=name,
@@ -1631,7 +1656,9 @@ class APIGatewayBackend(BaseBackend):
         minimum_compression_size: Optional[int] = None,
         disable_execute_api_endpoint: Optional[bool] = None,
     ) -> RestAPI:
-        api_id = create_id()
+        api_id = ApigwRestApiIdentifier(
+            self.account_id, self.region_name, name
+        ).generate()
         rest_api = RestAPI(
             api_id,
             self.account_id,
@@ -1882,7 +1909,9 @@ class APIGatewayBackend(BaseBackend):
         self, restapi_id: str, name: str, authorizer_type: str, **kwargs: Any
     ) -> Authorizer:
         api = self.get_rest_api(restapi_id)
-        authorizer_id = create_id()
+        authorizer_id = ApigwAuthorizerIdentifier(
+            self.account_id, self.region_name, name
+        ).generate()
         return api.create_authorizer(
             authorizer_id,
             name,
@@ -2146,7 +2175,13 @@ class APIGatewayBackend(BaseBackend):
             for api_key in self.get_api_keys():
                 if api_key.value == payload["value"]:
                     raise ApiKeyAlreadyExists()
-        key = ApiKey(**payload)
+        api_key_id = ApigwApiKeyIdentifier(
+            self.account_id,
+            self.region_name,
+            # The value of an api key must be unique on aws
+            payload.get("value", ""),
+        ).generate()
+        key = ApiKey(api_key_id=api_key_id, **payload)
         self.keys[key.id] = key
         return key
 
@@ -2170,7 +2205,10 @@ class APIGatewayBackend(BaseBackend):
         self.keys.pop(api_key_id)
 
     def create_usage_plan(self, payload: Any) -> UsagePlan:
-        plan = UsagePlan(**payload)
+        usage_plan_id = ApigwUsagePlanIdentifier(
+            self.account_id, self.region_name, payload["name"]
+        ).generate()
+        plan = UsagePlan(usage_plan_id=usage_plan_id, **payload)
         self.usage_plans[plan.id] = plan
         return plan
 
@@ -2497,8 +2535,15 @@ class APIGatewayBackend(BaseBackend):
         target_arns: List[str],
         tags: List[Dict[str, str]],
     ) -> VpcLink:
+        vpc_link_id = ApigwVpcLinkIdentifier(
+            self.account_id, self.region_name, name
+        ).generate()
         vpc_link = VpcLink(
-            name, description=description, target_arns=target_arns, tags=tags
+            vpc_link_id,
+            name,
+            description=description,
+            target_arns=target_arns,
+            tags=tags,
         )
         self.vpc_links[vpc_link.id] = vpc_link
         return vpc_link
diff --git a/moto/apigateway/utils.py b/moto/apigateway/utils.py
index 971fd218c..3fcc25f28 100644
--- a/moto/apigateway/utils.py
+++ b/moto/apigateway/utils.py
@@ -1,10 +1,81 @@
 import json
 import string
-from typing import Any, Dict
+from typing import Any, Dict, List, Union
 
 import yaml
 
 from moto.moto_api._internal import mock_random as random
+from moto.utilities.id_generator import ResourceIdentifier, Tags, generate_str_id
+
+
+class ApigwIdentifier(ResourceIdentifier):
+    service = "apigateway"
+
+    def __init__(self, account_id: str, region: str, name: str):
+        super().__init__(account_id, region, name)
+
+    def generate(
+        self, existing_ids: Union[List[str], None] = None, tags: Tags = None
+    ) -> str:
+        return generate_str_id(
+            resource_identifier=self,
+            existing_ids=existing_ids,
+            tags=tags,
+            length=10,
+            include_digits=True,
+            lower_case=True,
+        )
+
+
+class ApigwApiKeyIdentifier(ApigwIdentifier):
+    resource = "api_key"
+
+    def __init__(self, account_id: str, region: str, value: str):
+        super().__init__(account_id, region, value)
+
+
+class ApigwAuthorizerIdentifier(ApigwIdentifier):
+    resource = "authorizer"
+
+
+class ApigwDeploymentIdentifier(ApigwIdentifier):
+    resource = "deployment"
+
+    def __init__(self, account_id: str, region: str, stage_name: str):
+        super().__init__(account_id, region, stage_name)
+
+
+class ApigwModelIdentifier(ApigwIdentifier):
+    resource = "model"
+
+
+class ApigwRequestValidatorIdentifier(ApigwIdentifier):
+    resource = "request_validator"
+
+
+class ApigwResourceIdentifier(ApigwIdentifier):
+    resource = "resource"
+
+    def __init__(
+        self, account_id: str, region: str, parent_id: str = "", path_name: str = "/"
+    ):
+        super().__init__(
+            account_id,
+            region,
+            ".".join((parent_id, path_name)),
+        )
+
+
+class ApigwRestApiIdentifier(ApigwIdentifier):
+    resource = "rest_api"
+
+
+class ApigwUsagePlanIdentifier(ApigwIdentifier):
+    resource = "usage_plan"
+
+
+class ApigwVpcLinkIdentifier(ApigwIdentifier):
+    resource = "vpc_link"
 
 
 def create_id() -> str:
diff --git a/moto/ec2/models/iam_instance_profile.py b/moto/ec2/models/iam_instance_profile.py
index 4ced625bf..199e98bc9 100644
--- a/moto/ec2/models/iam_instance_profile.py
+++ b/moto/ec2/models/iam_instance_profile.py
@@ -62,7 +62,7 @@ class IamInstanceProfileAssociationBackend:
         iam_instance_profile_association = IamInstanceProfileAssociation(
             self,
             iam_association_id,
-            self.get_instance(instance_id) if instance_id else None,  # type: ignore[attr-defined]
+            self.get_instance(instance_id) if instance_id else None,  # type: ignore[attr-defined, arg-type]
             instance_profile,
         )
         # Regarding to AWS there can be only one association with ec2.
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-south-2.json b/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-south-2.json
index b7f31c8bc..aa9552b72 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-south-2.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-south-2.json
@@ -2139,6 +2139,50 @@
   "InstanceType": "m5d.xlarge",
   "Location": "aps2-az3"
  },
+ {
+  "InstanceType": "m6a.12xlarge",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.16xlarge",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.24xlarge",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.2xlarge",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.32xlarge",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.48xlarge",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.4xlarge",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.8xlarge",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.large",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.metal",
+  "Location": "aps2-az3"
+ },
+ {
+  "InstanceType": "m6a.xlarge",
+  "Location": "aps2-az3"
+ },
  {
   "InstanceType": "m6g.12xlarge",
   "Location": "aps2-az3"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-southeast-2.json b/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-southeast-2.json
index 1c2c7eb78..242390509 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-southeast-2.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-southeast-2.json
@@ -671,6 +671,30 @@
   "InstanceType": "d3.xlarge",
   "Location": "apse2-az1"
  },
+ {
+  "InstanceType": "d3en.12xlarge",
+  "Location": "apse2-az1"
+ },
+ {
+  "InstanceType": "d3en.2xlarge",
+  "Location": "apse2-az1"
+ },
+ {
+  "InstanceType": "d3en.4xlarge",
+  "Location": "apse2-az1"
+ },
+ {
+  "InstanceType": "d3en.6xlarge",
+  "Location": "apse2-az1"
+ },
+ {
+  "InstanceType": "d3en.8xlarge",
+  "Location": "apse2-az1"
+ },
+ {
+  "InstanceType": "d3en.xlarge",
+  "Location": "apse2-az1"
+ },
  {
   "InstanceType": "f1.16xlarge",
   "Location": "apse2-az1"
@@ -3227,6 +3251,30 @@
   "InstanceType": "d3.xlarge",
   "Location": "apse2-az2"
  },
+ {
+  "InstanceType": "d3en.12xlarge",
+  "Location": "apse2-az2"
+ },
+ {
+  "InstanceType": "d3en.2xlarge",
+  "Location": "apse2-az2"
+ },
+ {
+  "InstanceType": "d3en.4xlarge",
+  "Location": "apse2-az2"
+ },
+ {
+  "InstanceType": "d3en.6xlarge",
+  "Location": "apse2-az2"
+ },
+ {
+  "InstanceType": "d3en.8xlarge",
+  "Location": "apse2-az2"
+ },
+ {
+  "InstanceType": "d3en.xlarge",
+  "Location": "apse2-az2"
+ },
  {
   "InstanceType": "g4dn.12xlarge",
   "Location": "apse2-az2"
@@ -5659,6 +5707,30 @@
   "InstanceType": "d3.xlarge",
   "Location": "apse2-az3"
  },
+ {
+  "InstanceType": "d3en.12xlarge",
+  "Location": "apse2-az3"
+ },
+ {
+  "InstanceType": "d3en.2xlarge",
+  "Location": "apse2-az3"
+ },
+ {
+  "InstanceType": "d3en.4xlarge",
+  "Location": "apse2-az3"
+ },
+ {
+  "InstanceType": "d3en.6xlarge",
+  "Location": "apse2-az3"
+ },
+ {
+  "InstanceType": "d3en.8xlarge",
+  "Location": "apse2-az3"
+ },
+ {
+  "InstanceType": "d3en.xlarge",
+  "Location": "apse2-az3"
+ },
  {
   "InstanceType": "g3.16xlarge",
   "Location": "apse2-az3"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone-id/us-east-1.json b/moto/ec2/resources/instance_type_offerings/availability-zone-id/us-east-1.json
index da08ee91f..d4b5ebcbd 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone-id/us-east-1.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone-id/us-east-1.json
@@ -6115,6 +6115,10 @@
   "InstanceType": "u-18tb1.112xlarge",
   "Location": "use1-az2"
  },
+ {
+  "InstanceType": "u-24tb1.112xlarge",
+  "Location": "use1-az2"
+ },
  {
   "InstanceType": "u-3tb1.56xlarge",
   "Location": "use1-az2"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone/ap-south-2.json b/moto/ec2/resources/instance_type_offerings/availability-zone/ap-south-2.json
index 4c49da563..e3a564234 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone/ap-south-2.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone/ap-south-2.json
@@ -2139,6 +2139,50 @@
   "InstanceType": "m5d.xlarge",
   "Location": "ap-south-2c"
  },
+ {
+  "InstanceType": "m6a.12xlarge",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.16xlarge",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.24xlarge",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.2xlarge",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.32xlarge",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.48xlarge",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.4xlarge",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.8xlarge",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.large",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.metal",
+  "Location": "ap-south-2c"
+ },
+ {
+  "InstanceType": "m6a.xlarge",
+  "Location": "ap-south-2c"
+ },
  {
   "InstanceType": "m6g.12xlarge",
   "Location": "ap-south-2c"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone/ap-southeast-2.json b/moto/ec2/resources/instance_type_offerings/availability-zone/ap-southeast-2.json
index 3f220930e..4e050e8e2 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone/ap-southeast-2.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone/ap-southeast-2.json
@@ -647,6 +647,30 @@
   "InstanceType": "d3.xlarge",
   "Location": "ap-southeast-2a"
  },
+ {
+  "InstanceType": "d3en.12xlarge",
+  "Location": "ap-southeast-2a"
+ },
+ {
+  "InstanceType": "d3en.2xlarge",
+  "Location": "ap-southeast-2a"
+ },
+ {
+  "InstanceType": "d3en.4xlarge",
+  "Location": "ap-southeast-2a"
+ },
+ {
+  "InstanceType": "d3en.6xlarge",
+  "Location": "ap-southeast-2a"
+ },
+ {
+  "InstanceType": "d3en.8xlarge",
+  "Location": "ap-southeast-2a"
+ },
+ {
+  "InstanceType": "d3en.xlarge",
+  "Location": "ap-southeast-2a"
+ },
  {
   "InstanceType": "g3.16xlarge",
   "Location": "ap-southeast-2a"
@@ -3079,6 +3103,30 @@
   "InstanceType": "d3.xlarge",
   "Location": "ap-southeast-2b"
  },
+ {
+  "InstanceType": "d3en.12xlarge",
+  "Location": "ap-southeast-2b"
+ },
+ {
+  "InstanceType": "d3en.2xlarge",
+  "Location": "ap-southeast-2b"
+ },
+ {
+  "InstanceType": "d3en.4xlarge",
+  "Location": "ap-southeast-2b"
+ },
+ {
+  "InstanceType": "d3en.6xlarge",
+  "Location": "ap-southeast-2b"
+ },
+ {
+  "InstanceType": "d3en.8xlarge",
+  "Location": "ap-southeast-2b"
+ },
+ {
+  "InstanceType": "d3en.xlarge",
+  "Location": "ap-southeast-2b"
+ },
  {
   "InstanceType": "f1.16xlarge",
   "Location": "ap-southeast-2b"
@@ -5635,6 +5683,30 @@
   "InstanceType": "d3.xlarge",
   "Location": "ap-southeast-2c"
  },
+ {
+  "InstanceType": "d3en.12xlarge",
+  "Location": "ap-southeast-2c"
+ },
+ {
+  "InstanceType": "d3en.2xlarge",
+  "Location": "ap-southeast-2c"
+ },
+ {
+  "InstanceType": "d3en.4xlarge",
+  "Location": "ap-southeast-2c"
+ },
+ {
+  "InstanceType": "d3en.6xlarge",
+  "Location": "ap-southeast-2c"
+ },
+ {
+  "InstanceType": "d3en.8xlarge",
+  "Location": "ap-southeast-2c"
+ },
+ {
+  "InstanceType": "d3en.xlarge",
+  "Location": "ap-southeast-2c"
+ },
  {
   "InstanceType": "g4dn.12xlarge",
   "Location": "ap-southeast-2c"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone/us-east-1.json b/moto/ec2/resources/instance_type_offerings/availability-zone/us-east-1.json
index 1c28381c4..4f466a771 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone/us-east-1.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone/us-east-1.json
@@ -2959,6 +2959,10 @@
   "InstanceType": "u-18tb1.112xlarge",
   "Location": "us-east-1a"
  },
+ {
+  "InstanceType": "u-24tb1.112xlarge",
+  "Location": "us-east-1a"
+ },
  {
   "InstanceType": "u-3tb1.56xlarge",
   "Location": "us-east-1a"
diff --git a/moto/ec2/resources/instance_type_offerings/region/ap-southeast-2.json b/moto/ec2/resources/instance_type_offerings/region/ap-southeast-2.json
index 9e4e50aca..4f4c3ade8 100644
--- a/moto/ec2/resources/instance_type_offerings/region/ap-southeast-2.json
+++ b/moto/ec2/resources/instance_type_offerings/region/ap-southeast-2.json
@@ -671,6 +671,30 @@
   "InstanceType": "d3.xlarge",
   "Location": "ap-southeast-2"
  },
+ {
+  "InstanceType": "d3en.12xlarge",
+  "Location": "ap-southeast-2"
+ },
+ {
+  "InstanceType": "d3en.2xlarge",
+  "Location": "ap-southeast-2"
+ },
+ {
+  "InstanceType": "d3en.4xlarge",
+  "Location": "ap-southeast-2"
+ },
+ {
+  "InstanceType": "d3en.6xlarge",
+  "Location": "ap-southeast-2"
+ },
+ {
+  "InstanceType": "d3en.8xlarge",
+  "Location": "ap-southeast-2"
+ },
+ {
+  "InstanceType": "d3en.xlarge",
+  "Location": "ap-southeast-2"
+ },
  {
   "InstanceType": "f1.16xlarge",
   "Location": "ap-southeast-2"
diff --git a/moto/ec2/responses/vpcs.py b/moto/ec2/responses/vpcs.py
index 19de3cc2b..c5dc708e4 100644
--- a/moto/ec2/responses/vpcs.py
+++ b/moto/ec2/responses/vpcs.py
@@ -1,6 +1,9 @@
+from typing import List
+
 from moto.core.utils import camelcase_to_underscores
 from moto.ec2.utils import add_tag_specification
 
+from ..models.managed_prefixes import ManagedPrefixList
 from ._base_response import EC2BaseResponse
 
 
@@ -294,7 +297,7 @@ class VPCs(EC2BaseResponse):
         managed_prefix_list = self.ec2_backend.get_managed_prefix_list_entries(
             prefix_list_id=prefix_list_id
         )
-        entries = []
+        entries: List[ManagedPrefixList] = []
         if managed_prefix_list:
             entries = (
                 list(managed_prefix_list.entries.values())[-1]
diff --git a/moto/elbv2/models.py b/moto/elbv2/models.py
index 961be5ee1..f0b2e772c 100644
--- a/moto/elbv2/models.py
+++ b/moto/elbv2/models.py
@@ -1990,6 +1990,7 @@ Member must satisfy regular expression pattern: {expression}"
 
     def remove_tags(self, resource_arns: List[str], tag_keys: List[str]) -> None:
         for arn in resource_arns:
+            self._get_resource_by_arn(arn)
             self.tagging_service.untag_resource_using_names(arn, tag_keys)
 
     def describe_tags(self, resource_arns: List[str]) -> Dict[str, Dict[str, str]]:
diff --git a/moto/iam/models.py b/moto/iam/models.py
index 3e06a6cfb..aece6258f 100644
--- a/moto/iam/models.py
+++ b/moto/iam/models.py
@@ -3,7 +3,7 @@ import json
 import os
 import re
 import string
-from datetime import datetime
+from datetime import date, datetime
 from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
 from urllib import parse
 
@@ -101,6 +101,12 @@ def mark_account_as_visited(
         pass
 
 
+def _serialize_version_datetime(value: Any) -> str:
+    if isinstance(value, date):
+        return value.strftime("%Y-%m-%d")
+    raise TypeError("Unable to serialize value.")
+
+
 LIMIT_KEYS_PER_USER = 2
 
 
@@ -745,10 +751,16 @@ class Role(CloudFormationModel):
         properties = cloudformation_json["Properties"]
         role_name = properties.get("RoleName", resource_name)
 
+        assume_role_policy_document = properties["AssumeRolePolicyDocument"]
+        if not isinstance(assume_role_policy_document, str):
+            assume_role_policy_document = json.dumps(
+                assume_role_policy_document, default=_serialize_version_datetime
+            )
+
         iam_backend = iam_backends[account_id][get_partition(region_name)]
         role = iam_backend.create_role(
             role_name=role_name,
-            assume_role_policy_document=properties["AssumeRolePolicyDocument"],
+            assume_role_policy_document=assume_role_policy_document,
             path=properties.get("Path", "/"),
             permissions_boundary=properties.get("PermissionsBoundary", ""),
             description=properties.get("Description", ""),
@@ -898,7 +910,7 @@ class Role(CloudFormationModel):
       <Path>{{ role.path }}</Path>
       <Arn>{{ role.arn }}</Arn>
       <RoleName>{{ role.name }}</RoleName>
-      <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
+      <AssumeRolePolicyDocument>{{ role.assume_role_policy_document | urlencode }}</AssumeRolePolicyDocument>
       {% if role.description is not none %}
       <Description>{{ role.description_escaped }}</Description>
       {% endif %}
diff --git a/moto/iam/responses.py b/moto/iam/responses.py
index 1b2be7c1f..dd3b1836e 100644
--- a/moto/iam/responses.py
+++ b/moto/iam/responses.py
@@ -1403,7 +1403,7 @@ CREATE_INSTANCE_PROFILE_TEMPLATE = """<CreateInstanceProfileResponse xmlns="http
           <Path>{{ role.path }}</Path>
           <Arn>{{ role.arn }}</Arn>
           <RoleName>{{ role.name }}</RoleName>
-          <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
+          <AssumeRolePolicyDocument>{{ role.assume_role_policy_document | urlencode }}</AssumeRolePolicyDocument>
           <CreateDate>{{ role.created_iso_8601 }}</CreateDate>
           <RoleId>{{ role.id }}</RoleId>
         </member>
@@ -1444,7 +1444,7 @@ GET_INSTANCE_PROFILE_TEMPLATE = """<GetInstanceProfileResponse xmlns="https://ia
           <Path>{{ role.path }}</Path>
           <Arn>{{ role.arn }}</Arn>
           <RoleName>{{ role.name }}</RoleName>
-          <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
+          <AssumeRolePolicyDocument>{{ role.assume_role_policy_document | urlencode }}</AssumeRolePolicyDocument>
           <CreateDate>{{ role.created_iso_8601 }}</CreateDate>
           <RoleId>{{ role.id }}</RoleId>
         </member>
@@ -1482,7 +1482,7 @@ GET_ROLE_POLICY_TEMPLATE = """<GetRolePolicyResponse xmlns="https://iam.amazonaw
 <GetRolePolicyResult>
   <PolicyName>{{ policy_name }}</PolicyName>
   <RoleName>{{ role_name }}</RoleName>
-  <PolicyDocument>{{ policy_document }}</PolicyDocument>
+  <PolicyDocument>{{ policy_document | urlencode }}</PolicyDocument>
 </GetRolePolicyResult>
 <ResponseMetadata>
   <RequestId>7e7cd8bc-99ef-11e1-a4c3-27EXAMPLE804</RequestId>
@@ -1566,7 +1566,7 @@ LIST_ROLES_TEMPLATE = """<ListRolesResponse xmlns="https://iam.amazonaws.com/doc
         <Path>{{ role.path }}</Path>
         <Arn>{{ role.arn }}</Arn>
         <RoleName>{{ role.name }}</RoleName>
-        <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
+        <AssumeRolePolicyDocument>{{ role.assume_role_policy_document | urlencode }}</AssumeRolePolicyDocument>
         <CreateDate>{{ role.created_iso_8601 }}</CreateDate>
         <RoleId>{{ role.id }}</RoleId>
         <MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
@@ -1662,7 +1662,7 @@ LIST_INSTANCE_PROFILES_TEMPLATE = """<ListInstanceProfilesResponse xmlns="https:
             <Path>{{ role.path }}</Path>
             <Arn>{{ role.arn }}</Arn>
             <RoleName>{{ role.name }}</RoleName>
-            <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
+            <AssumeRolePolicyDocument>{{ role.assume_role_policy_document | urlencode }}</AssumeRolePolicyDocument>
             <CreateDate>{{ role.created_iso_8601 }}</CreateDate>
             <RoleId>{{ role.id }}</RoleId>
           </member>
@@ -1831,7 +1831,7 @@ GET_GROUP_POLICY_TEMPLATE = """<GetGroupPolicyResponse xmlns="https://iam.amazon
 <GetGroupPolicyResult>
   <PolicyName>{{ policy_name }}</PolicyName>
   <GroupName>{{ group_name }}</GroupName>
-  <PolicyDocument>{{ policy_document }}</PolicyDocument>
+  <PolicyDocument>{{ policy_document | urlencode }}</PolicyDocument>
 </GetGroupPolicyResult>
 <ResponseMetadata>
   <RequestId>7e7cd8bc-99ef-11e1-a4c3-27EXAMPLE804</RequestId>
@@ -1927,7 +1927,7 @@ GET_USER_POLICY_TEMPLATE = """<GetUserPolicyResponse>
       <UserName>{{ user_name }}</UserName>
       <PolicyName>{{ policy_name }}</PolicyName>
       <PolicyDocument>
-      {{ policy_document }}
+      {{ policy_document | urlencode }}
       </PolicyDocument>
    </GetUserPolicyResult>
    <ResponseMetadata>
@@ -2134,7 +2134,7 @@ LIST_INSTANCE_PROFILES_FOR_ROLE_TEMPLATE = """<ListInstanceProfilesForRoleRespon
         <Path>{{ role.path }}</Path>
         <Arn>{{ role.arn }}</Arn>
         <RoleName>{{ role.name }}</RoleName>
-        <AssumeRolePolicyDocument>{{ role.assume_policy_document }}</AssumeRolePolicyDocument>
+        <AssumeRolePolicyDocument>{{ role.assume_policy_document | urlencode }}</AssumeRolePolicyDocument>
         <CreateDate>{{ role.created_iso_8601 }}</CreateDate>
         <RoleId>{{ role.id }}</RoleId>
       </member>
@@ -2319,7 +2319,7 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR
         {% for policy in user.policies %}
             <member>
                 <PolicyName>{{ policy }}</PolicyName>
-                <PolicyDocument>{{ user.policies[policy] }}</PolicyDocument>
+                <PolicyDocument>{{ user.policies[policy] | urlencode }}</PolicyDocument>
             </member>
         {% endfor %}
         </UserPolicyList>
@@ -2355,7 +2355,7 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR
         {% for policy in group.policies %}
             <member>
                 <PolicyName>{{ policy }}</PolicyName>
-                <PolicyDocument>{{ group.policies[policy] }}</PolicyDocument>
+                <PolicyDocument>{{ group.policies[policy] | urlencode }}</PolicyDocument>
             </member>
         {% endfor %}
         </GroupPolicyList>
@@ -2369,7 +2369,7 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR
         {% for inline_policy in role.policies %}
             <member>
                 <PolicyName>{{ inline_policy }}</PolicyName>
-                <PolicyDocument>{{ role.policies[inline_policy] }}</PolicyDocument>
+                <PolicyDocument>{{ role.policies[inline_policy] | urlencode }}</PolicyDocument>
             </member>
         {% endfor %}
         </RolePolicyList>
@@ -2399,7 +2399,7 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR
                 <Path>{{ role.path }}</Path>
                 <Arn>{{ role.arn }}</Arn>
                 <RoleName>{{ role.name }}</RoleName>
-                <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
+                <AssumeRolePolicyDocument>{{ role.assume_role_policy_document | urlencode }}</AssumeRolePolicyDocument>
                 {% if role.description is not none %}
                 <Description>{{ role.description_escaped }}</Description>
                 {% endif %}
@@ -2424,7 +2424,7 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR
         <Path>{{ role.path }}</Path>
         <Arn>{{ role.arn }}</Arn>
         <RoleName>{{ role.name }}</RoleName>
-        <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
+        <AssumeRolePolicyDocument>{{ role.assume_role_policy_document | urlencode }}</AssumeRolePolicyDocument>
         <CreateDate>{{ role.created_iso_8601 }}</CreateDate>
         <RoleId>{{ role.id }}</RoleId>
       </member>
diff --git a/moto/packages/boto/ec2/instance.py b/moto/packages/boto/ec2/instance.py
index 2ab190770..9c4707d97 100644
--- a/moto/packages/boto/ec2/instance.py
+++ b/moto/packages/boto/ec2/instance.py
@@ -25,7 +25,7 @@
 Represents an EC2 Instance
 """
 
-from typing import Any
+from typing import Any, Optional
 
 from moto.packages.boto.ec2.ec2object import EC2Object, TaggedEC2Object
 from moto.packages.boto.ec2.image import ProductCodes
@@ -159,8 +159,8 @@ class Instance(TaggedEC2Object):
         self.platform = None
         self.interfaces: Any = []
         self.hypervisor = None
-        self.virtualization_type = None
-        self.architecture = None
+        self.virtualization_type: Optional[str] = None
+        self.architecture: Optional[str] = None
         self.instance_profile = None
         self._previous_state = None
         self._placement = InstancePlacement()
diff --git a/moto/quicksight/utils.py b/moto/quicksight/utils.py
index ad23c192b..9997f2916 100644
--- a/moto/quicksight/utils.py
+++ b/moto/quicksight/utils.py
@@ -1,8 +1,5 @@
 from typing import Any, Dict, List, Type, Union
 
-from jsonschema import validate
-from jsonschema.exceptions import SchemaError, ValidationError
-
 from moto.core.common_models import BaseModel
 
 from .data_models import QuicksightGroup
@@ -120,6 +117,9 @@ class QuicksightSearchFilterFactory:
                 raise ParamValidationError(
                     'Missing required parameter in input: "Filters"'
                 )
+            from jsonschema import validate
+            from jsonschema.exceptions import SchemaError, ValidationError
+
             try:
                 validate(instance=input, schema=QuicksightGroupSearchFilter.schema)
                 return QuicksightSearchFilterList(
diff --git a/moto/s3/models.py b/moto/s3/models.py
index 5ae966e9d..79a33340f 100644
--- a/moto/s3/models.py
+++ b/moto/s3/models.py
@@ -658,6 +658,9 @@ def get_canned_acl(acl: str) -> FakeAcl:
 
 
 class LifecycleFilter(BaseModel):
+    tag_key: Optional[str]
+    tag_value: Optional[str]
+
     def __init__(
         self,
         prefix: Optional[str] = None,
diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py
index bbebebb58..8d3d1d5a2 100644
--- a/moto/secretsmanager/models.py
+++ b/moto/secretsmanager/models.py
@@ -26,7 +26,11 @@ from .list_secrets.filters import (
     tag_key,
     tag_value,
 )
-from .utils import get_secret_name_from_partial_arn, random_password, secret_arn
+from .utils import (
+    SecretsManagerSecretIdentifier,
+    get_secret_name_from_partial_arn,
+    random_password,
+)
 
 MAX_RESULTS_DEFAULT = 100
 
@@ -94,7 +98,9 @@ class FakeSecret:
     ):
         self.secret_id = secret_id
         self.name = secret_id
-        self.arn = secret_arn(account_id, region_name, secret_id)
+        self.arn = SecretsManagerSecretIdentifier(
+            account_id, region_name, secret_id
+        ).generate()
         self.account_id = account_id
         self.region = region_name
         self.secret_string = secret_string
@@ -935,7 +941,9 @@ class SecretsManagerBackend(BaseBackend):
             if not force_delete_without_recovery:
                 raise SecretNotFoundException()
             else:
-                arn = secret_arn(self.account_id, self.region_name, secret_id=secret_id)
+                arn = SecretsManagerSecretIdentifier(
+                    self.account_id, self.region_name, secret_id=secret_id
+                ).generate()
                 name = secret_id
                 deletion_date = utcnow()
                 return arn, name, self._unix_time_secs(deletion_date)
diff --git a/moto/secretsmanager/utils.py b/moto/secretsmanager/utils.py
index e5435a6c9..e4b55622b 100644
--- a/moto/secretsmanager/utils.py
+++ b/moto/secretsmanager/utils.py
@@ -2,6 +2,12 @@ import re
 import string
 
 from moto.moto_api._internal import mock_random as random
+from moto.utilities.id_generator import (
+    ExistingIds,
+    ResourceIdentifier,
+    Tags,
+    generate_str_id,
+)
 from moto.utilities.utils import ARN_PARTITION_REGEX, get_partition
 
 
@@ -62,11 +68,6 @@ def random_password(
     return password
 
 
-def secret_arn(account_id: str, region: str, secret_id: str) -> str:
-    id_string = "".join(random.choice(string.ascii_letters) for _ in range(6))
-    return f"arn:{get_partition(region)}:secretsmanager:{region}:{account_id}:secret:{secret_id}-{id_string}"
-
-
 def get_secret_name_from_partial_arn(partial_arn: str) -> str:
     # We can retrieve a secret either using a full ARN, or using a partial ARN
     # name:        testsecret
@@ -99,3 +100,24 @@ def _add_password_require_each_included_type(
     password_with_required_char += required_characters
 
     return password_with_required_char
+
+
+class SecretsManagerSecretIdentifier(ResourceIdentifier):
+    service = "secretsmanager"
+    resource = "secret"
+
+    def __init__(self, account_id: str, region: str, secret_id: str):
+        super().__init__(account_id, region, name=secret_id)
+
+    def generate(self, existing_ids: ExistingIds = None, tags: Tags = None) -> str:
+        id_string = generate_str_id(
+            resource_identifier=self,
+            existing_ids=existing_ids,
+            tags=tags,
+            length=6,
+            include_digits=False,
+        )
+        return (
+            f"arn:{get_partition(self.region)}:secretsmanager:{self.region}:"
+            f"{self.account_id}:secret:{self.name}-{id_string}"
+        )
diff --git a/moto/ssm/resources/regions.json b/moto/ssm/resources/regions.json
index 691d1f839..8e7447e60 100644
--- a/moto/ssm/resources/regions.json
+++ b/moto/ssm/resources/regions.json
@@ -10166,6 +10166,15 @@
               "vpc": {
                 "Value": "vpc"
               },
+              "vpc-lattice": {
+                "Value": "vpc-lattice",
+                "endpoint": {
+                  "Value": "vpc-lattice.ap-northeast-3.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "vpn": {
                 "Value": "vpn",
                 "endpoint": {
@@ -22194,6 +22203,15 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "backup": {
+                "Value": "backup",
+                "endpoint": {
+                  "Value": "backup.ap-southeast-5.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "batch": {
                 "Value": "batch",
                 "endpoint": {
@@ -59320,6 +59338,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "schemas": {
+                "Value": "schemas",
+                "endpoint": {
+                  "Value": "schemas.us-gov-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "secretsmanager": {
                 "Value": "secretsmanager",
                 "endpoint": {
@@ -61049,6 +61076,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "schemas": {
+                "Value": "schemas",
+                "endpoint": {
+                  "Value": "schemas.us-gov-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "secretsmanager": {
                 "Value": "secretsmanager",
                 "endpoint": {
diff --git a/moto/ssm/resources/services.json b/moto/ssm/resources/services.json
index 2228c734f..7b95a5bbc 100644
--- a/moto/ssm/resources/services.json
+++ b/moto/ssm/resources/services.json
@@ -7116,6 +7116,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-southeast-5": {
+                "Value": "ap-southeast-5",
+                "endpoint": {
+                  "Value": "backup.ap-southeast-5.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ca-central-1": {
                 "Value": "ca-central-1",
                 "endpoint": {
@@ -56962,6 +56971,24 @@
                   "Value": "HTTPS"
                 }
               },
+              "us-gov-east-1": {
+                "Value": "us-gov-east-1",
+                "endpoint": {
+                  "Value": "schemas.us-gov-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "us-gov-west-1": {
+                "Value": "us-gov-west-1",
+                "endpoint": {
+                  "Value": "schemas.us-gov-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "us-west-1": {
                 "Value": "us-west-1",
                 "endpoint": {
@@ -66876,6 +66903,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-northeast-3": {
+                "Value": "ap-northeast-3",
+                "endpoint": {
+                  "Value": "vpc-lattice.ap-northeast-3.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-south-1": {
                 "Value": "ap-south-1",
                 "endpoint": {
diff --git a/moto/utilities/id_generator.py b/moto/utilities/id_generator.py
new file mode 100644
index 000000000..c5f0a2c93
--- /dev/null
+++ b/moto/utilities/id_generator.py
@@ -0,0 +1,180 @@
+import abc
+import logging
+import threading
+from typing import Any, Callable, Dict, List, TypedDict, Union
+
+from moto.moto_api._internal import mock_random
+
+log = logging.getLogger(__name__)
+
+ExistingIds = Union[List[str], None]
+Tags = Union[Dict[str, str], None]
+
+# Custom resource tag to override the generated resource ID.
+TAG_KEY_CUSTOM_ID = "_custom_id_"
+
+
+class IdSourceContext(TypedDict, total=False):
+    resource_identifier: "ResourceIdentifier"
+    tags: Tags
+    existing_ids: ExistingIds
+
+
+class ResourceIdentifier(abc.ABC):
+    """
+    Base class for resource identifiers. When implementing a new resource, it is important to set
+    the service and resource as they will be used to create the unique identifier for that resource.
+
+    It is recommended to implement the `generate` method using functions decorated with `@moto_id`.
+    This will ensure that your resource can be assigned a custom id.
+    """
+
+    service: str
+    resource: str
+
+    def __init__(self, account_id: str, region: str, name: str):
+        self.account_id = account_id
+        self.region = region
+        self.name = name or ""
+
+    @abc.abstractmethod
+    def generate(self, existing_ids: ExistingIds = None, tags: Tags = None) -> str:
+        """Method to generate a resource id"""
+
+    @property
+    def unique_identifier(self) -> str:
+        return ".".join(
+            [self.account_id, self.region, self.service, self.resource, self.name]
+        )
+
+    def __str__(self) -> str:
+        return self.unique_identifier
+
+
+class MotoIdManager:
+    """class to manage custom ids. Do not create instance and instead
+    use the `id_manager` instance created below."""
+
+    _custom_ids: Dict[str, str]
+    _id_sources: List[Callable[[IdSourceContext], Union[str, None]]]
+
+    _lock: threading.RLock
+
+    def __init__(self) -> None:
+        self._custom_ids = {}
+        self._lock = threading.RLock()
+        self._id_sources = []
+
+        self.add_id_source(self.get_id_from_tags)
+        self.add_id_source(self.get_custom_id_from_context)
+
+    def get_custom_id(
+        self, resource_identifier: ResourceIdentifier
+    ) -> Union[str, None]:
+        # retrieves a custom_id for a resource. Returns None
+        return self._custom_ids.get(resource_identifier.unique_identifier)
+
+    def set_custom_id(
+        self, resource_identifier: ResourceIdentifier, custom_id: str
+    ) -> None:
+        # Do not set a custom_id for a resource no value was found for the name
+        if not resource_identifier.name:
+            return
+        with self._lock:
+            self._custom_ids[resource_identifier.unique_identifier] = custom_id
+
+    def unset_custom_id(self, resource_identifier: ResourceIdentifier) -> None:
+        # removes a set custom_id for a resource
+        with self._lock:
+            self._custom_ids.pop(resource_identifier.unique_identifier, None)
+
+    def add_id_source(
+        self, id_source: Callable[[IdSourceContext], Union[str, None]]
+    ) -> None:
+        self._id_sources.append(id_source)
+
+    @staticmethod
+    def get_id_from_tags(id_source_context: IdSourceContext) -> Union[str, None]:
+        if tags := id_source_context.get("tags"):
+            return tags.get(TAG_KEY_CUSTOM_ID)
+
+        return None
+
+    def get_custom_id_from_context(
+        self, id_source_context: IdSourceContext
+    ) -> Union[str, None]:
+        # retrieves a custom_id for a resource. Returns None
+        if resource_identifier := id_source_context.get("resource_identifier"):
+            return self.get_custom_id(resource_identifier)
+        return None
+
+    def find_id_from_sources(
+        self, id_source_context: IdSourceContext
+    ) -> Union[str, None]:
+        existing_ids = id_source_context.get("existing_ids") or []
+        for id_source in self._id_sources:
+            if found_id := id_source(id_source_context):
+                if found_id in existing_ids:
+                    log.debug(
+                        f"Found id {found_id} for resource {id_source_context.get('resource_identifier')}, "
+                        "but a resource already exists with this id."
+                    )
+                else:
+                    return found_id
+
+        return None
+
+
+moto_id_manager = MotoIdManager()
+
+
+def moto_id(fn: Callable[..., str]) -> Callable[..., str]:
+    """
+    Decorator for helping in creation of static ids.
+
+    The decorated function should accept the following parameters
+
+    :param resource_identifier
+    :param existing_ids
+        If provided, we will omit returning a custom id if it is already on the list
+    :param tags
+        If provided will look for a tag named `_custom_id_`. This will take precedence over registered custom ids
+    """
+
+    def _wrapper(
+        resource_identifier: ResourceIdentifier,
+        existing_ids: ExistingIds = None,
+        tags: Tags = None,
+        **kwargs: Dict[str, Any],
+    ) -> str:
+        if resource_identifier and (
+            found_id := moto_id_manager.find_id_from_sources(
+                IdSourceContext(
+                    resource_identifier=resource_identifier,
+                    existing_ids=existing_ids,
+                    tags=tags,
+                )
+            )
+        ):
+            return found_id
+
+        return fn(
+            resource_identifier=resource_identifier,
+            existing_ids=existing_ids,
+            tags=tags,
+            **kwargs,
+        )
+
+    return _wrapper
+
+
+@moto_id
+def generate_str_id(  # type: ignore
+    resource_identifier: ResourceIdentifier,
+    existing_ids: ExistingIds = None,
+    tags: Tags = None,
+    length: int = 20,
+    include_digits: bool = True,
+    lower_case: bool = False,
+) -> str:
+    return mock_random.get_random_string(length, include_digits, lower_case)
diff --git a/setup.cfg b/setup.cfg
index 28eb97e9d..a7dae9c51 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = moto
-version = 5.0.17.dev
+version = 5.0.18.dev
 long_description = file:README.md
 long_description_content_type = text/markdown
 author = Steve Pulec
@@ -52,6 +52,7 @@ all =
     graphql-core
     PyYAML>=5.1
     cfn-lint>=0.40.0
+    jsonschema
     openapi-spec-validator>=0.5.0
     pyparsing>=3.0.7
     jsondiff>=1.1.2
@@ -193,7 +194,7 @@ panorama =
 personalize =
 pinpoint =
 polly =
-quicksight =
+quicksight = jsonschema
 ram =
 rds =
 redshift =


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index f723cf5f3..3a1f20cb0 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -2,6 +2,7 @@ import boto3
 import pytest
 
 from moto import mock_aws
+from moto.utilities.id_generator import ResourceIdentifier, moto_id_manager
 
 
 @pytest.fixture(scope="function")
@@ -16,3 +17,19 @@ def account_id():
         with mock_aws():
             identity = boto3.client("sts", "us-east-1").get_caller_identity()
             yield identity["Account"]
+
+
+@pytest.fixture
+def set_custom_id():
+    set_ids = []
+
+    def _set_custom_id(resource_identifier: ResourceIdentifier, custom_id):
+        moto_id_manager.set_custom_id(
+            resource_identifier=resource_identifier, custom_id=custom_id
+        )
+        set_ids.append(resource_identifier)
+
+    yield _set_custom_id
+
+    for resource_identifier in set_ids:
+        moto_id_manager.unset_custom_id(resource_identifier)
diff --git a/tests/test_apigateway/test_apigateway_custom_ids.py b/tests/test_apigateway/test_apigateway_custom_ids.py
new file mode 100644
index 000000000..1d2debbba
--- /dev/null
+++ b/tests/test_apigateway/test_apigateway_custom_ids.py
@@ -0,0 +1,140 @@
+import boto3
+import pytest
+
+from moto import mock_aws, settings
+from moto.apigateway.utils import (
+    ApigwApiKeyIdentifier,
+    ApigwDeploymentIdentifier,
+    ApigwModelIdentifier,
+    ApigwRequestValidatorIdentifier,
+    ApigwResourceIdentifier,
+    ApigwRestApiIdentifier,
+    ApigwUsagePlanIdentifier,
+)
+
+API_ID = "ApiId"
+API_KEY_ID = "ApiKeyId"
+DEPLOYMENT_ID = "DeployId"
+MODEL_ID = "ModelId"
+PET_1_RESOURCE_ID = "Pet1Id"
+PET_2_RESOURCE_ID = "Pet2Id"
+REQUEST_VALIDATOR_ID = "ReqValId"
+ROOT_RESOURCE_ID = "RootId"
+USAGE_PLAN_ID = "UPlanId"
+
+
+@mock_aws
+@pytest.mark.skipif(
+    not settings.TEST_DECORATOR_MODE, reason="Can't access the id manager in proxy mode"
+)
+def test_custom_id_rest_api(set_custom_id, account_id):
+    region_name = "us-west-2"
+    rest_api_name = "my-api"
+    model_name = "modelName"
+    request_validator_name = "request-validator-name"
+    stage_name = "stage-name"
+
+    client = boto3.client("apigateway", region_name=region_name)
+
+    set_custom_id(
+        ApigwRestApiIdentifier(account_id, region_name, rest_api_name), API_ID
+    )
+    set_custom_id(
+        ApigwResourceIdentifier(account_id, region_name, path_name="/"),
+        ROOT_RESOURCE_ID,
+    )
+    set_custom_id(
+        ApigwResourceIdentifier(
+            account_id, region_name, parent_id=ROOT_RESOURCE_ID, path_name="pet"
+        ),
+        PET_1_RESOURCE_ID,
+    )
+    set_custom_id(
+        ApigwResourceIdentifier(
+            account_id, region_name, parent_id=PET_1_RESOURCE_ID, path_name="pet"
+        ),
+        PET_2_RESOURCE_ID,
+    )
+    set_custom_id(ApigwModelIdentifier(account_id, region_name, model_name), MODEL_ID)
+    set_custom_id(
+        ApigwRequestValidatorIdentifier(
+            account_id, region_name, request_validator_name
+        ),
+        REQUEST_VALIDATOR_ID,
+    )
+    set_custom_id(
+        ApigwDeploymentIdentifier(account_id, region_name, stage_name=stage_name),
+        DEPLOYMENT_ID,
+    )
+
+    rest_api = client.create_rest_api(name=rest_api_name)
+    assert rest_api["id"] == API_ID
+    assert rest_api["rootResourceId"] == ROOT_RESOURCE_ID
+
+    pet_resource_1 = client.create_resource(
+        restApiId=API_ID, parentId=ROOT_RESOURCE_ID, pathPart="pet"
+    )
+    assert pet_resource_1["id"] == PET_1_RESOURCE_ID
+
+    # we create a second resource with the same path part to ensure we can pass different ids
+    pet_resource_2 = client.create_resource(
+        restApiId=API_ID, parentId=PET_1_RESOURCE_ID, pathPart="pet"
+    )
+    assert pet_resource_2["id"] == PET_2_RESOURCE_ID
+
+    model = client.create_model(
+        restApiId=API_ID,
+        name=model_name,
+        schema="EMPTY",
+        contentType="application/json",
+    )
+    assert model["id"] == MODEL_ID
+
+    request_validator = client.create_request_validator(
+        restApiId=API_ID, name=request_validator_name
+    )
+    assert request_validator["id"] == REQUEST_VALIDATOR_ID
+
+    # Creating the resource to make a deployment
+    client.put_method(
+        restApiId=API_ID,
+        httpMethod="ANY",
+        resourceId=PET_2_RESOURCE_ID,
+        authorizationType="NONE",
+    )
+    client.put_integration(
+        restApiId=API_ID, resourceId=PET_2_RESOURCE_ID, httpMethod="ANY", type="MOCK"
+    )
+    deployment = client.create_deployment(restApiId=API_ID, stageName=stage_name)
+    assert deployment["id"] == DEPLOYMENT_ID
+
+
+@mock_aws
+@pytest.mark.skipif(
+    not settings.TEST_DECORATOR_MODE, reason="Can't access the id manager in proxy mode"
+)
+def test_custom_id_api_key(account_id, set_custom_id):
+    region_name = "us-west-2"
+    api_key_value = "01234567890123456789"
+    usage_plan_name = "usage-plan"
+
+    client = boto3.client("apigateway", region_name=region_name)
+
+    set_custom_id(
+        ApigwApiKeyIdentifier(account_id, region_name, value=api_key_value), API_KEY_ID
+    )
+    set_custom_id(
+        ApigwUsagePlanIdentifier(account_id, region_name, usage_plan_name),
+        USAGE_PLAN_ID,
+    )
+
+    api_key = client.create_api_key(name="api-key", value=api_key_value)
+    usage_plan = client.create_usage_plan(name=usage_plan_name)
+
+    # verify that we can create a usage plan key using the custom ids
+    client.create_usage_plan_key(
+        usagePlanId=USAGE_PLAN_ID, keyId=API_KEY_ID, keyType="API_KEY"
+    )
+
+    assert api_key["id"] == API_KEY_ID
+    assert usage_plan["id"] == USAGE_PLAN_ID
diff --git a/tests/test_elbv2/test_elbv2.py b/tests/test_elbv2/test_elbv2.py
index 5abc626c8..512f31b39 100644
--- a/tests/test_elbv2/test_elbv2.py
+++ b/tests/test_elbv2/test_elbv2.py
@@ -198,6 +198,13 @@ def test_add_remove_tags():
 
     conn.remove_tags(ResourceArns=[lb["LoadBalancerArn"]], TagKeys=["a"])
 
+    with pytest.raises(ClientError) as exc:
+        # add a random string in the ARN to make the resource non-existent
+        BAD_LB_ARN = lb["LoadBalancerArn"] + "randomstring"
+        conn.remove_tags(ResourceArns=[BAD_LB_ARN], TagKeys=["a"])
+    err = exc.value.response["Error"]
+    assert err["Code"] == "LoadBalancerNotFound"
+
     tags = {
         d["Key"]: d["Value"]
         for d in conn.describe_tags(ResourceArns=[lb["LoadBalancerArn"]])[
diff --git a/tests/test_elbv2/test_elbv2_listener_rule_tags.py b/tests/test_elbv2/test_elbv2_listener_rule_tags.py
index 7a03da309..02b5d1351 100644
--- a/tests/test_elbv2/test_elbv2_listener_rule_tags.py
+++ b/tests/test_elbv2/test_elbv2_listener_rule_tags.py
@@ -1,3 +1,6 @@
+import pytest
+from botocore.exceptions import ClientError
+
 from moto import mock_aws
 
 from .test_elbv2 import create_load_balancer
@@ -83,3 +86,35 @@ def test_listener_rule_add_remove_tags():
         {"Key": "b", "Value": "b"},
         {"Key": "c", "Value": "b"},
     ]
+
+
+@mock_aws
+def test_remove_tags_to_invalid_listener_rule():
+    response, _, _, _, _, elbv2 = create_load_balancer()
+    load_balancer_arn = response.get("LoadBalancers")[0].get("LoadBalancerArn")
+
+    listener_arn = elbv2.create_listener(
+        LoadBalancerArn=load_balancer_arn,
+        Protocol="HTTP",
+        Port=80,
+        DefaultActions=[],
+    )["Listeners"][0]["ListenerArn"]
+    rule_arn = elbv2.create_rule(
+        ListenerArn=listener_arn,
+        Priority=100,
+        Conditions=[create_condition],
+        Actions=[default_action],
+        Tags=[{"Key": "k1", "Value": "v1"}],
+    )["Rules"][0]["RuleArn"]
+
+    # add a random string in the ARN to make the resource non-existent
+    BAD_ARN = rule_arn + "randostring"
+
+    # test for exceptions on remove tags
+    with pytest.raises(ClientError) as err:
+        elbv2.remove_tags(ResourceArns=[BAD_ARN], TagKeys=["a"])
+
+    assert err.value.response["Error"]["Code"] == "RuleNotFound"
+    assert (
+        err.value.response["Error"]["Message"] == "The specified rule does not exist."
+    )
diff --git a/tests/test_elbv2/test_elbv2_listener_tags.py b/tests/test_elbv2/test_elbv2_listener_tags.py
index 3b80537ce..4c1fed730 100644
--- a/tests/test_elbv2/test_elbv2_listener_tags.py
+++ b/tests/test_elbv2/test_elbv2_listener_tags.py
@@ -1,3 +1,6 @@
+import pytest
+from botocore.exceptions import ClientError
+
 from moto import mock_aws
 
 from .test_elbv2 import create_load_balancer
@@ -73,3 +76,29 @@ def test_listener_add_remove_tags():
         {"Key": "b", "Value": "b"},
         {"Key": "c", "Value": "b"},
     ]
+
+
+@mock_aws
+def test_remove_tags_to_invalid_listener():
+    response, _, _, _, _, elbv2 = create_load_balancer()
+    load_balancer_arn = response.get("LoadBalancers")[0].get("LoadBalancerArn")
+
+    listener_arn = elbv2.create_listener(
+        LoadBalancerArn=load_balancer_arn,
+        Protocol="HTTP",
+        Port=80,
+        DefaultActions=[],
+    )["Listeners"][0]["ListenerArn"]
+
+    # add a random string in the ARN to make the resource non-existent
+    BAD_ARN = listener_arn + "randomstring"
+
+    # test for exceptions on remove tags
+    with pytest.raises(ClientError) as err:
+        elbv2.remove_tags(ResourceArns=[BAD_ARN], TagKeys=["a"])
+
+    assert err.value.response["Error"]["Code"] == "ListenerNotFound"
+    assert (
+        err.value.response["Error"]["Message"]
+        == "The specified listener does not exist."
+    )
diff --git a/tests/test_elbv2/test_elbv2_target_groups.py b/tests/test_elbv2/test_elbv2_target_groups.py
index eefbd4544..d125d1f8c 100644
--- a/tests/test_elbv2/test_elbv2_target_groups.py
+++ b/tests/test_elbv2/test_elbv2_target_groups.py
@@ -75,6 +75,41 @@ def test_create_target_group_with_tags():
     assert tags == [{"Key": "key2", "Value": "val2"}]
 
 
+@mock_aws
+def test_remove_tags_to_invalid_target_group():
+    response, vpc, _, _, _, elbv2 = create_load_balancer()
+
+    response = elbv2.create_target_group(
+        Name="a-target",
+        Protocol="HTTP",
+        Port=8080,
+        VpcId=vpc.id,
+        HealthCheckProtocol="HTTP",
+        HealthCheckPort="8080",
+        HealthCheckPath="/",
+        HealthCheckIntervalSeconds=5,
+        HealthCheckTimeoutSeconds=3,
+        HealthyThresholdCount=5,
+        UnhealthyThresholdCount=2,
+        Matcher={"HttpCode": "200"},
+        Tags=[{"Key": "key1", "Value": "val1"}],
+    )
+    target_group = response["TargetGroups"][0]
+    target_group_arn = target_group["TargetGroupArn"]
+
+    # add a random string in the ARN to make the resource non-existent
+    BAD_ARN = target_group_arn + "randomstring"
+
+    # test for exceptions on remove tags
+    with pytest.raises(ClientError) as err:
+        elbv2.remove_tags(ResourceArns=[BAD_ARN], TagKeys=["a"])
+
+    assert err.value.response["Error"]["Code"] == "TargetGroupNotFound"
+    assert (
+        err.value.response["Error"]["Message"] == "One or more target groups not found"
+    )
+
+
 @mock_aws
 def test_create_target_group_and_listeners():
     response, vpc, _, _, _, conn = create_load_balancer()
diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py
index cab288905..f103c4cb6 100644
--- a/tests/test_iam/test_iam.py
+++ b/tests/test_iam/test_iam.py
@@ -209,8 +209,11 @@ def test_get_instance_profile__should_throw__when_instance_profile_does_not_exis
 def test_create_role_and_instance_profile():
     conn = boto3.client("iam", region_name="us-east-1")
     conn.create_instance_profile(InstanceProfileName="my-profile", Path="my-path")
+    assume_role_policy_document = {"value": "some policy"}
     conn.create_role(
-        RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/"
+        RoleName="my-role",
+        AssumeRolePolicyDocument=json.dumps(assume_role_policy_document),
+        Path="/my-path/",
     )
 
     conn.add_role_to_instance_profile(
@@ -219,7 +222,7 @@ def test_create_role_and_instance_profile():
 
     role = conn.get_role(RoleName="my-role")["Role"]
     assert role["Path"] == "/my-path/"
-    assert role["AssumeRolePolicyDocument"] == "some policy"
+    assert role["AssumeRolePolicyDocument"] == assume_role_policy_document
 
     profile = conn.get_instance_profile(InstanceProfileName="my-profile")[
         "InstanceProfile"
@@ -3209,6 +3212,98 @@ def test_create_role_no_path():
     assert resp["Role"]["Description"] == "test"
 
 
+@mock_aws()
+def test_role_policy_encoding():
+    role_name = "my-role"
+    policy_name = "my-policy"
+    conn = boto3.client("iam", region_name="us-east-1")
+    assume_policy_document = {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Action": "sts:AssumeRole",
+                "Principal": {"Service": "lambda.amazonaws.com"},
+                "Effect": "Allow",
+                "Condition": {
+                    "StringEquals": {"aws:SourceArn": "arn:aws:test%3Aencoded%3Astring"}
+                },
+            }
+        ],
+    }
+    policy_document = {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": ["apigatway:PUT"],
+                "Resource": ["arn:aws:test%3Aencoded%3Astring"],
+            }
+        ],
+    }
+    resp = conn.create_role(
+        RoleName=role_name, AssumeRolePolicyDocument=json.dumps(assume_policy_document)
+    )
+    assert resp["Role"]["AssumeRolePolicyDocument"] == assume_policy_document
+    conn.put_role_policy(
+        RoleName=role_name,
+        PolicyName=policy_name,
+        PolicyDocument=json.dumps(policy_document),
+    )
+    resp = conn.get_role_policy(RoleName=role_name, PolicyName=policy_name)
+    assert resp["PolicyDocument"] == policy_document
+
+
+@mock_aws()
+def test_user_policy_encoding():
+    user_name = "my-user"
+    policy_name = "my-policy"
+    conn = boto3.client("iam", region_name="us-east-1")
+
+    policy_document = {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": ["apigatway:PUT"],
+                "Resource": ["arn:aws:test%3Aencoded%3Astring"],
+            }
+        ],
+    }
+    conn.create_user(UserName=user_name)
+    conn.put_user_policy(
+        UserName=user_name,
+        PolicyName=policy_name,
+        PolicyDocument=json.dumps(policy_document),
+    )
+    resp = conn.get_user_policy(UserName=user_name, PolicyName=policy_name)
+    assert resp["PolicyDocument"] == policy_document
+
+
+@mock_aws()
+def test_group_policy_encoding():
+    group_name = "my-group"
+    policy_name = "my-policy"
+    conn = boto3.client("iam", region_name="us-east-1")
+    policy_document = {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": ["apigatway:PUT"],
+                "Resource": ["arn:aws:test%3Aencoded%3Astring"],
+            }
+        ],
+    }
+    conn.create_group(GroupName=group_name)
+    conn.put_group_policy(
+        GroupName=group_name,
+        PolicyName=policy_name,
+        PolicyDocument=json.dumps(policy_document),
+    )
+    resp = conn.get_group_policy(GroupName=group_name, PolicyName=policy_name)
+    assert resp["PolicyDocument"] == policy_document
+
+
 @mock_aws()
 @pytest.mark.parametrize(
     "region,partition", [("us-west-2", "aws"), ("cn-north-1", "aws-cn")]
diff --git a/tests/test_secretsmanager/test_secretsmanager.py b/tests/test_secretsmanager/test_secretsmanager.py
index 8879b64f0..734fcf384 100644
--- a/tests/test_secretsmanager/test_secretsmanager.py
+++ b/tests/test_secretsmanager/test_secretsmanager.py
@@ -13,6 +13,7 @@ from freezegun import freeze_time
 
 from moto import mock_aws, settings
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+from moto.secretsmanager.utils import SecretsManagerSecretIdentifier
 
 from . import secretsmanager_aws_verified
 
@@ -1961,3 +1962,23 @@ def test_update_secret_version_stage_dont_specify_current_stage(secret_arn=None)
         err["Message"]
         == f"The parameter RemoveFromVersionId can't be empty. Staging label AWSCURRENT is currently attached to version {current_version}, so you must explicitly reference that version in RemoveFromVersionId."
     )
+
+
+@mock_aws
+@pytest.mark.skipif(
+    not settings.TEST_DECORATOR_MODE, reason="Can't access the id manager in proxy mode"
+)
+def test_create_secret_custom_id(account_id, set_custom_id):
+    secret_suffix = "randomSuffix"
+    secret_name = "secret-name"
+    region_name = "us-east-1"
+
+    client = boto3.client("secretsmanager", region_name=region_name)
+
+    set_custom_id(
+        SecretsManagerSecretIdentifier(account_id, region_name, secret_name),
+        secret_suffix,
+    )
+    secret = client.create_secret(Name=secret_name, SecretString="my secret")
+
+    assert secret["ARN"].split(":")[-1] == f"{secret_name}-{secret_suffix}"
diff --git a/tests/test_utilities/test_id_generator.py b/tests/test_utilities/test_id_generator.py
new file mode 100644
index 000000000..7ee85fd32
--- /dev/null
+++ b/tests/test_utilities/test_id_generator.py
@@ -0,0 +1,134 @@
+from moto.utilities.id_generator import (
+    TAG_KEY_CUSTOM_ID,
+    ExistingIds,
+    ResourceIdentifier,
+    Tags,
+    moto_id,
+    moto_id_manager,
+)
+
+ACCOUNT = "account"
+REGION = "us-east-1"
+RESOURCE_NAME = "my-resource"
+
+CUSTOM_ID = "custom"
+GENERATED_ID = "generated"
+TAG_ID = "fromTag"
+SERVICE = "test-service"
+RESOURCE = "test-resource"
+
+
+@moto_id
+def generate_test_id(
+    resource_identifier: ResourceIdentifier,
+    existing_ids: ExistingIds = None,
+    tags: Tags = None,
+):
+    return GENERATED_ID
+
+
+class TestResourceIdentifier(ResourceIdentifier):
+    service = SERVICE
+    resource = RESOURCE
+
+    def generate(self, existing_ids: ExistingIds = None, tags: Tags = None) -> str:
+        return generate_test_id(
+            resource_identifier=self, existing_ids=existing_ids, tags=tags
+        )
+
+
+def test_generate_with_no_resource_identifier():
+    generated_id = generate_test_id(None)
+    assert generated_id == GENERATED_ID
+
+
+def test_generate_with_matching_resource_identifier(set_custom_id):
+    resource_identifier = TestResourceIdentifier(ACCOUNT, REGION, RESOURCE_NAME)
+
+    set_custom_id(resource_identifier, CUSTOM_ID)
+
+    generated_id = generate_test_id(resource_identifier=resource_identifier)
+    assert generated_id == CUSTOM_ID
+
+
+def test_generate_with_non_matching_resource_identifier(set_custom_id):
+    resource_identifier = TestResourceIdentifier(ACCOUNT, REGION, RESOURCE_NAME)
+    resource_identifier_2 = TestResourceIdentifier(ACCOUNT, REGION, "non-matching")
+
+    set_custom_id(resource_identifier, CUSTOM_ID)
+
+    generated_id = generate_test_id(resource_identifier=resource_identifier_2)
+    assert generated_id == GENERATED_ID
+
+
+def test_generate_with_custom_id_tag():
+    resource_identifier = TestResourceIdentifier(ACCOUNT, REGION, RESOURCE_NAME)
+
+    generated_id = generate_test_id(
+        resource_identifier=resource_identifier, tags={TAG_KEY_CUSTOM_ID: TAG_ID}
+    )
+    assert generated_id == TAG_ID
+
+
+def test_generate_with_custom_id_tag_has_priority(set_custom_id):
+    resource_identifier = TestResourceIdentifier(ACCOUNT, REGION, RESOURCE_NAME)
+
+    set_custom_id(resource_identifier, CUSTOM_ID)
+    generated_id = generate_test_id(
+        resource_identifier=resource_identifier, tags={TAG_KEY_CUSTOM_ID: TAG_ID}
+    )
+    assert generated_id == TAG_ID
+
+
+def test_generate_with_existing_id(set_custom_id):
+    resource_identifier = TestResourceIdentifier(ACCOUNT, REGION, RESOURCE_NAME)
+
+    set_custom_id(resource_identifier, CUSTOM_ID)
+    generated_id = generate_test_id(
+        resource_identifier=resource_identifier, existing_ids=[CUSTOM_ID]
+    )
+    assert generated_id == GENERATED_ID
+
+
+def test_generate_with_tags_and_existing_id(set_custom_id):
+    resource_identifier = TestResourceIdentifier(ACCOUNT, REGION, RESOURCE_NAME)
+
+    generated_id = generate_test_id(
+        resource_identifier=resource_identifier,
+        existing_ids=[TAG_ID],
+        tags={TAG_KEY_CUSTOM_ID: TAG_ID},
+    )
+    assert generated_id == GENERATED_ID
+
+
+def test_generate_with_tags_fallback(set_custom_id):
+    resource_identifier = TestResourceIdentifier(ACCOUNT, REGION, RESOURCE_NAME)
+
+    set_custom_id(resource_identifier, CUSTOM_ID)
+    generated_id = generate_test_id(
+        resource_identifier=resource_identifier,
+        existing_ids=[TAG_ID],
+        tags={TAG_KEY_CUSTOM_ID: TAG_ID},
+    )
+    assert generated_id == CUSTOM_ID
+
+
+def test_set_custom_id_lifecycle():
+    resource_identifier = TestResourceIdentifier(ACCOUNT, REGION, RESOURCE_NAME)
+
+    moto_id_manager.set_custom_id(resource_identifier, CUSTOM_ID)
+
+    found_id = moto_id_manager.get_custom_id(resource_identifier)
+    assert found_id == CUSTOM_ID
+
+    moto_id_manager.unset_custom_id(resource_identifier)
+
+    found_id = moto_id_manager.get_custom_id(resource_identifier)
+    assert found_id is None
+
+
+def test_set_custom_id_name_is_not_set():
+    resource_identifier = TestResourceIdentifier(ACCOUNT, REGION, None)
+    moto_id_manager.set_custom_id(resource_identifier, CUSTOM_ID)
+
+    assert moto_id_manager._custom_ids == {}