Repository: getmoto/moto
Pull request #7251 (2024-01-26 10:05:09)

SNS: allow SetSubscriptionAttributes to unset FilterPolicy
We've had a report that a user could not "unset" a FilterPolicy by setting it to an empty string like they could in AWS.
This was due to always trying to decode the FilterPolicy as JSON, raising a `JSONDecodeError`. 

I've introduce a conditional check for the value, and manually set the filter policy and the matcher to None to not have a filter in place anymore. 

I've also sneaked a little change to what is returned from `GetSubscriptionAttributes`: SNS does not return the FilterPolicy if it is not set (None or empty string), and will not return the FilterPolicyScope either in that case. Also, if a FilterPolicy is set without manually setting a FilterPolicyScope, it will return the default filter policy scope as well in the fields. This was validated against AWS. 

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/getmoto/moto/pull/7251?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:
> Comparison is base [(`1c3c495`)](https://app.codecov.io/gh/getmoto/moto/commit/1c3c4953cf87098a6b51891c7fb3ee140c0da2e3?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) 95.90% compared to head [(`ccfb98f`)](https://app.codecov.io/gh/getmoto/moto/pull/7251?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) 95.91%.
> Report is 4 commits behind head on master.


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


```diff
@@           Coverage Diff           @@
##           master    #7251   +/-   ##
=======================================
  Coverage   95.90%   95.91%           
=======================================
  Files         840      840           
  Lines       82749    82798   +49     
=======================================
+ Hits        79362    79412   +50     
+ Misses       3387     3386    -1     
```

| [Flag](https://app.codecov.io/gh/getmoto/moto/pull/7251/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/7251/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `35.49% <0.00%> (+0.01%)` | :arrow_up: |
| [unittests](https://app.codecov.io/gh/getmoto/moto/pull/7251/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `95.85% <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/7251?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/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index c45abd1ba..63b41d97c 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -364,7 +364,7 @@
 
 ## appsync
 <details>
-<summary>24% implemented</summary>
+<summary>25% implemented</summary>
 
 - [ ] associate_api
 - [ ] associate_merged_graphql_api
@@ -394,7 +394,6 @@
 - [ ] get_api_association
 - [ ] get_api_cache
 - [ ] get_data_source
-- [ ] get_data_source_introspection
 - [ ] get_domain_name
 - [ ] get_function
 - [X] get_graphql_api
@@ -414,7 +413,6 @@
 - [X] list_tags_for_resource
 - [ ] list_types
 - [ ] list_types_by_association
-- [ ] start_data_source_introspection
 - [X] start_schema_creation
 - [ ] start_schema_merge
 - [X] tag_resource
@@ -575,6 +573,89 @@
 - [X] update_auto_scaling_group
 </details>
 
+## backup
+<details>
+<summary>11% implemented</summary>
+
+- [ ] cancel_legal_hold
+- [X] create_backup_plan
+- [ ] create_backup_selection
+- [X] create_backup_vault
+- [ ] create_framework
+- [ ] create_legal_hold
+- [ ] create_logically_air_gapped_backup_vault
+- [ ] create_report_plan
+- [X] delete_backup_plan
+- [ ] delete_backup_selection
+- [ ] delete_backup_vault
+- [ ] delete_backup_vault_access_policy
+- [ ] delete_backup_vault_lock_configuration
+- [ ] delete_backup_vault_notifications
+- [ ] delete_framework
+- [ ] delete_recovery_point
+- [ ] delete_report_plan
+- [ ] describe_backup_job
+- [ ] describe_backup_vault
+- [ ] describe_copy_job
+- [ ] describe_framework
+- [ ] describe_global_settings
+- [ ] describe_protected_resource
+- [ ] describe_recovery_point
+- [ ] describe_region_settings
+- [ ] describe_report_job
+- [ ] describe_report_plan
+- [ ] describe_restore_job
+- [ ] disassociate_recovery_point
+- [ ] disassociate_recovery_point_from_parent
+- [ ] export_backup_plan_template
+- [X] get_backup_plan
+- [ ] get_backup_plan_from_json
+- [ ] get_backup_plan_from_template
+- [ ] get_backup_selection
+- [ ] get_backup_vault_access_policy
+- [ ] get_backup_vault_notifications
+- [ ] get_legal_hold
+- [ ] get_recovery_point_restore_metadata
+- [ ] get_supported_resource_types
+- [ ] list_backup_job_summaries
+- [ ] list_backup_jobs
+- [ ] list_backup_plan_templates
+- [ ] list_backup_plan_versions
+- [X] list_backup_plans
+- [ ] list_backup_selections
+- [X] list_backup_vaults
+- [ ] list_copy_job_summaries
+- [ ] list_copy_jobs
+- [ ] list_frameworks
+- [ ] list_legal_holds
+- [ ] list_protected_resources
+- [ ] list_protected_resources_by_backup_vault
+- [ ] list_recovery_points_by_backup_vault
+- [ ] list_recovery_points_by_legal_hold
+- [ ] list_recovery_points_by_resource
+- [ ] list_report_jobs
+- [ ] list_report_plans
+- [ ] list_restore_job_summaries
+- [ ] list_restore_jobs
+- [X] list_tags
+- [ ] put_backup_vault_access_policy
+- [ ] put_backup_vault_lock_configuration
+- [ ] put_backup_vault_notifications
+- [ ] start_backup_job
+- [ ] start_copy_job
+- [ ] start_report_job
+- [ ] start_restore_job
+- [ ] stop_backup_job
+- [X] tag_resource
+- [X] untag_resource
+- [ ] update_backup_plan
+- [ ] update_framework
+- [ ] update_global_settings
+- [ ] update_recovery_point_lifecycle
+- [ ] update_region_settings
+- [ ] update_report_plan
+</details>
+
 ## batch
 <details>
 <summary>100% implemented</summary>
@@ -1101,7 +1182,6 @@
 - [ ] update_pull_request_status
 - [ ] update_pull_request_title
 - [ ] update_repository_description
-- [ ] update_repository_encryption_key
 - [ ] update_repository_name
 </details>
 
@@ -2653,7 +2733,7 @@
 
 ## efs
 <details>
-<summary>54% implemented</summary>
+<summary>56% implemented</summary>
 
 - [X] create_access_point
 - [X] create_file_system
@@ -2685,24 +2765,20 @@
 - [X] tag_resource
 - [X] untag_resource
 - [ ] update_file_system
-- [ ] update_file_system_protection
 </details>
 
 ## eks
 <details>
-<summary>26% implemented</summary>
+<summary>33% implemented</summary>
 
-- [ ] associate_access_policy
 - [ ] associate_encryption_config
 - [ ] associate_identity_provider_config
-- [ ] create_access_entry
 - [ ] create_addon
 - [X] create_cluster
 - [ ] create_eks_anywhere_subscription
 - [X] create_fargate_profile
 - [X] create_nodegroup
 - [ ] create_pod_identity_association
-- [ ] delete_access_entry
 - [ ] delete_addon
 - [X] delete_cluster
 - [ ] delete_eks_anywhere_subscription
@@ -2710,7 +2786,6 @@
 - [X] delete_nodegroup
 - [ ] delete_pod_identity_association
 - [ ] deregister_cluster
-- [ ] describe_access_entry
 - [ ] describe_addon
 - [ ] describe_addon_configuration
 - [ ] describe_addon_versions
@@ -2718,21 +2793,15 @@
 - [ ] describe_eks_anywhere_subscription
 - [X] describe_fargate_profile
 - [ ] describe_identity_provider_config
-- [ ] describe_insight
 - [X] describe_nodegroup
 - [ ] describe_pod_identity_association
 - [ ] describe_update
-- [ ] disassociate_access_policy
 - [ ] disassociate_identity_provider_config
-- [ ] list_access_entries
-- [ ] list_access_policies
 - [ ] list_addons
-- [ ] list_associated_access_policies
 - [X] list_clusters
 - [ ] list_eks_anywhere_subscriptions
 - [X] list_fargate_profiles
 - [ ] list_identity_provider_configs
-- [ ] list_insights
 - [X] list_nodegroups
 - [ ] list_pod_identity_associations
 - [X] list_tags_for_resource
@@ -2740,7 +2809,6 @@
 - [ ] register_cluster
 - [X] tag_resource
 - [X] untag_resource
-- [ ] update_access_entry
 - [ ] update_addon
 - [ ] update_cluster_config
 - [ ] update_cluster_version
@@ -2752,14 +2820,13 @@
 
 ## elasticache
 <details>
-<summary>8% implemented</summary>
+<summary>9% implemented</summary>
 
 - [ ] add_tags_to_resource
 - [ ] authorize_cache_security_group_ingress
 - [ ] batch_apply_update_action
 - [ ] batch_stop_update_action
 - [ ] complete_migration
-- [ ] copy_serverless_cache_snapshot
 - [ ] copy_snapshot
 - [X] create_cache_cluster
 - [ ] create_cache_parameter_group
@@ -2767,8 +2834,6 @@
 - [ ] create_cache_subnet_group
 - [ ] create_global_replication_group
 - [ ] create_replication_group
-- [ ] create_serverless_cache
-- [ ] create_serverless_cache_snapshot
 - [ ] create_snapshot
 - [X] create_user
 - [ ] create_user_group
@@ -2780,8 +2845,6 @@
 - [ ] delete_cache_subnet_group
 - [ ] delete_global_replication_group
 - [ ] delete_replication_group
-- [ ] delete_serverless_cache
-- [ ] delete_serverless_cache_snapshot
 - [ ] delete_snapshot
 - [X] delete_user
 - [ ] delete_user_group
@@ -2797,15 +2860,12 @@
 - [ ] describe_replication_groups
 - [ ] describe_reserved_cache_nodes
 - [ ] describe_reserved_cache_nodes_offerings
-- [ ] describe_serverless_cache_snapshots
-- [ ] describe_serverless_caches
 - [ ] describe_service_updates
 - [ ] describe_snapshots
 - [ ] describe_update_actions
 - [ ] describe_user_groups
 - [X] describe_users
 - [ ] disassociate_global_replication_group
-- [ ] export_serverless_cache_snapshot
 - [ ] failover_global_replication_group
 - [ ] increase_node_groups_in_global_replication_group
 - [ ] increase_replica_count
@@ -2817,7 +2877,6 @@
 - [ ] modify_global_replication_group
 - [ ] modify_replication_group
 - [ ] modify_replication_group_shard_configuration
-- [ ] modify_serverless_cache
 - [ ] modify_user
 - [ ] modify_user_group
 - [ ] purchase_reserved_cache_nodes_offering
@@ -3043,7 +3102,6 @@
 - [ ] remove_managed_scaling_policy
 - [X] remove_tags
 - [X] run_job_flow
-- [ ] set_keep_job_flow_alive_when_no_steps
 - [X] set_termination_protection
 - [X] set_visible_to_all_users
 - [ ] start_notebook_execution
@@ -3663,7 +3721,7 @@
 
 ## guardduty
 <details>
-<summary>15% implemented</summary>
+<summary>16% implemented</summary>
 
 - [ ] accept_administrator_invitation
 - [ ] accept_invitation
@@ -3703,7 +3761,6 @@
 - [ ] get_master_account
 - [ ] get_member_detectors
 - [ ] get_members
-- [ ] get_organization_statistics
 - [ ] get_remaining_free_trial_days
 - [ ] get_threat_intel_set
 - [ ] get_usage_statistics
@@ -3980,7 +4037,7 @@
 
 ## iot
 <details>
-<summary>31% implemented</summary>
+<summary>32% implemented</summary>
 
 - [ ] accept_certificate_transfer
 - [ ] add_thing_to_billing_group
@@ -4002,7 +4059,6 @@
 - [ ] create_authorizer
 - [ ] create_billing_group
 - [X] create_certificate_from_csr
-- [ ] create_certificate_provider
 - [ ] create_custom_metric
 - [ ] create_dimension
 - [X] create_domain_configuration
@@ -4035,7 +4091,6 @@
 - [ ] delete_billing_group
 - [X] delete_ca_certificate
 - [X] delete_certificate
-- [ ] delete_certificate_provider
 - [ ] delete_custom_metric
 - [ ] delete_dimension
 - [X] delete_domain_configuration
@@ -4073,7 +4128,6 @@
 - [ ] describe_billing_group
 - [X] describe_ca_certificate
 - [X] describe_certificate
-- [ ] describe_certificate_provider
 - [ ] describe_custom_metric
 - [ ] describe_default_authorizer
 - [ ] describe_detect_mitigation_actions_task
@@ -4133,7 +4187,6 @@
 - [ ] list_authorizers
 - [ ] list_billing_groups
 - [ ] list_ca_certificates
-- [ ] list_certificate_providers
 - [X] list_certificates
 - [X] list_certificates_by_ca
 - [ ] list_custom_metrics
@@ -4214,7 +4267,6 @@
 - [ ] update_billing_group
 - [X] update_ca_certificate
 - [X] update_certificate
-- [ ] update_certificate_provider
 - [ ] update_custom_metric
 - [ ] update_dimension
 - [X] update_domain_configuration
@@ -4630,7 +4682,6 @@
 - [X] put_resource_policy
 - [X] put_retention_policy
 - [X] put_subscription_filter
-- [ ] start_live_tail
 - [X] start_query
 - [ ] stop_query
 - [X] tag_log_group
@@ -4677,7 +4728,7 @@
 
 ## mediaconnect
 <details>
-<summary>35% implemented</summary>
+<summary>36% implemented</summary>
 
 - [ ] add_bridge_outputs
 - [ ] add_bridge_sources
@@ -4694,7 +4745,6 @@
 - [ ] deregister_gateway_instance
 - [ ] describe_bridge
 - [X] describe_flow
-- [ ] describe_flow_source_metadata
 - [ ] describe_gateway
 - [ ] describe_gateway_instance
 - [ ] describe_offering
@@ -4981,10 +5031,9 @@
 
 ## opensearch
 <details>
-<summary>16% implemented</summary>
+<summary>17% implemented</summary>
 
 - [ ] accept_inbound_connection
-- [ ] add_data_source
 - [X] add_tags
 - [ ] associate_package
 - [ ] authorize_vpc_endpoint_access
@@ -4993,7 +5042,6 @@
 - [ ] create_outbound_connection
 - [ ] create_package
 - [ ] create_vpc_endpoint
-- [ ] delete_data_source
 - [X] delete_domain
 - [ ] delete_inbound_connection
 - [ ] delete_outbound_connection
@@ -5016,12 +5064,10 @@
 - [ ] describe_vpc_endpoints
 - [ ] dissociate_package
 - [X] get_compatible_versions
-- [ ] get_data_source
 - [ ] get_domain_maintenance_status
 - [ ] get_package_version_history
 - [ ] get_upgrade_history
 - [ ] get_upgrade_status
-- [ ] list_data_sources
 - [ ] list_domain_maintenances
 - [X] list_domain_names
 - [ ] list_domains_for_package
@@ -5039,7 +5085,6 @@
 - [ ] revoke_vpc_endpoint_access
 - [ ] start_domain_maintenance
 - [ ] start_service_software_update
-- [ ] update_data_source
 - [X] update_domain_config
 - [ ] update_package
 - [ ] update_scheduled_action
@@ -5592,7 +5637,6 @@
 - [ ] update_analysis
 - [ ] update_analysis_permissions
 - [ ] update_dashboard
-- [ ] update_dashboard_links
 - [ ] update_dashboard_permissions
 - [ ] update_dashboard_published_version
 - [ ] update_data_set
@@ -5663,7 +5707,7 @@
 
 ## rds
 <details>
-<summary>35% implemented</summary>
+<summary>37% implemented</summary>
 
 - [ ] add_role_to_db_cluster
 - [ ] add_role_to_db_instance
@@ -5739,7 +5783,6 @@
 - [ ] describe_db_proxy_endpoints
 - [ ] describe_db_proxy_target_groups
 - [ ] describe_db_proxy_targets
-- [ ] describe_db_recommendations
 - [ ] describe_db_security_groups
 - [ ] describe_db_snapshot_attributes
 - [ ] describe_db_snapshot_tenant_databases
@@ -5762,9 +5805,7 @@
 - [ ] describe_source_regions
 - [ ] describe_tenant_databases
 - [ ] describe_valid_db_instance_modifications
-- [ ] disable_http_endpoint
 - [ ] download_db_log_file_portion
-- [ ] enable_http_endpoint
 - [ ] failover_db_cluster
 - [ ] failover_global_cluster
 - [X] list_tags_for_resource
@@ -5781,7 +5822,6 @@
 - [ ] modify_db_proxy
 - [ ] modify_db_proxy_endpoint
 - [ ] modify_db_proxy_target_group
-- [ ] modify_db_recommendation
 - [ ] modify_db_snapshot
 - [ ] modify_db_snapshot_attribute
 - [X] modify_db_subnet_group
@@ -6323,14 +6363,13 @@
 
 ## s3
 <details>
-<summary>66% implemented</summary>
+<summary>68% implemented</summary>
 
 - [X] abort_multipart_upload
 - [X] complete_multipart_upload
 - [X] copy_object
 - [X] create_bucket
 - [X] create_multipart_upload
-- [ ] create_session
 - [X] delete_bucket
 - [ ] delete_bucket_analytics_configuration
 - [X] delete_bucket_cors
@@ -6386,7 +6425,6 @@
 - [ ] list_bucket_inventory_configurations
 - [ ] list_bucket_metrics_configurations
 - [X] list_buckets
-- [ ] list_directory_buckets
 - [ ] list_multipart_uploads
 - [X] list_object_versions
 - [X] list_objects
@@ -6526,7 +6564,7 @@
 
 ## sagemaker
 <details>
-<summary>19% implemented</summary>
+<summary>20% implemented</summary>
 
 - [ ] add_association
 - [X] add_tags
@@ -6539,7 +6577,6 @@
 - [ ] create_artifact
 - [ ] create_auto_ml_job
 - [ ] create_auto_ml_job_v2
-- [ ] create_cluster
 - [ ] create_code_repository
 - [ ] create_compilation_job
 - [ ] create_context
@@ -6559,7 +6596,6 @@
 - [ ] create_hyper_parameter_tuning_job
 - [ ] create_image
 - [ ] create_image_version
-- [ ] create_inference_component
 - [ ] create_inference_experiment
 - [ ] create_inference_recommendations_job
 - [ ] create_labeling_job
@@ -6594,9 +6630,7 @@
 - [ ] delete_app_image_config
 - [ ] delete_artifact
 - [ ] delete_association
-- [ ] delete_cluster
 - [ ] delete_code_repository
-- [ ] delete_compilation_job
 - [ ] delete_context
 - [ ] delete_data_quality_job_definition
 - [ ] delete_device_fleet
@@ -6613,7 +6647,6 @@
 - [ ] delete_human_task_ui
 - [ ] delete_image
 - [ ] delete_image_version
-- [ ] delete_inference_component
 - [ ] delete_inference_experiment
 - [X] delete_model
 - [ ] delete_model_bias_job_definition
@@ -6644,8 +6677,6 @@
 - [ ] describe_artifact
 - [ ] describe_auto_ml_job
 - [ ] describe_auto_ml_job_v2
-- [ ] describe_cluster
-- [ ] describe_cluster_node
 - [ ] describe_code_repository
 - [ ] describe_compilation_job
 - [ ] describe_context
@@ -6667,7 +6698,6 @@
 - [ ] describe_hyper_parameter_tuning_job
 - [ ] describe_image
 - [ ] describe_image_version
-- [ ] describe_inference_component
 - [ ] describe_inference_experiment
 - [ ] describe_inference_recommendations_job
 - [ ] describe_labeling_job
@@ -6717,8 +6747,6 @@
 - [ ] list_associations
 - [ ] list_auto_ml_jobs
 - [ ] list_candidates_for_auto_ml_job
-- [ ] list_cluster_nodes
-- [ ] list_clusters
 - [ ] list_code_repositories
 - [ ] list_compilation_jobs
 - [ ] list_contexts
@@ -6740,7 +6768,6 @@
 - [ ] list_hyper_parameter_tuning_jobs
 - [ ] list_image_versions
 - [ ] list_images
-- [ ] list_inference_components
 - [ ] list_inference_experiments
 - [ ] list_inference_recommendations_job_steps
 - [ ] list_inference_recommendations_jobs
@@ -6813,7 +6840,6 @@
 - [ ] update_action
 - [ ] update_app_image_config
 - [ ] update_artifact
-- [ ] update_cluster
 - [ ] update_code_repository
 - [ ] update_context
 - [ ] update_device_fleet
@@ -6827,8 +6853,6 @@
 - [ ] update_hub
 - [ ] update_image
 - [ ] update_image_version
-- [ ] update_inference_component
-- [ ] update_inference_component_runtime_config
 - [ ] update_inference_experiment
 - [ ] update_model_card
 - [X] update_model_package
@@ -7658,7 +7682,7 @@
 
 ## transcribe
 <details>
-<summary>37% implemented</summary>
+<summary>41% implemented</summary>
 
 - [ ] create_call_analytics_category
 - [ ] create_language_model
@@ -7668,7 +7692,6 @@
 - [ ] delete_call_analytics_category
 - [ ] delete_call_analytics_job
 - [ ] delete_language_model
-- [ ] delete_medical_scribe_job
 - [X] delete_medical_transcription_job
 - [X] delete_medical_vocabulary
 - [X] delete_transcription_job
@@ -7677,7 +7700,6 @@
 - [ ] describe_language_model
 - [ ] get_call_analytics_category
 - [ ] get_call_analytics_job
-- [ ] get_medical_scribe_job
 - [X] get_medical_transcription_job
 - [X] get_medical_vocabulary
 - [X] get_transcription_job
@@ -7686,7 +7708,6 @@
 - [ ] list_call_analytics_categories
 - [ ] list_call_analytics_jobs
 - [ ] list_language_models
-- [ ] list_medical_scribe_jobs
 - [X] list_medical_transcription_jobs
 - [X] list_medical_vocabularies
 - [ ] list_tags_for_resource
@@ -7694,7 +7715,6 @@
 - [X] list_vocabularies
 - [ ] list_vocabulary_filters
 - [ ] start_call_analytics_job
-- [ ] start_medical_scribe_job
 - [X] start_medical_transcription_job
 - [X] start_transcription_job
 - [ ] tag_resource
@@ -7785,14 +7805,10 @@
 - arc-zonal-shift
 - auditmanager
 - autoscaling-plans
-- b2bi
-- backup
 - backup-gateway
 - backupstorage
 - bcm-data-exports
 - bedrock
-- bedrock-agent
-- bedrock-agent-runtime
 - bedrock-runtime
 - billingconductor
 - braket
@@ -7803,7 +7819,6 @@
 - chime-sdk-messaging
 - chime-sdk-voice
 - cleanrooms
-- cleanroomsml
 - cloud9
 - cloudcontrol
 - clouddirectory
@@ -7915,9 +7930,7 @@
 - machinelearning
 - macie2
 - managedblockchain-query
-- marketplace-agreement
 - marketplace-catalog
-- marketplace-deployment
 - marketplace-entitlement
 - marketplacecommerceanalytics
 - mediaconvert
@@ -7935,11 +7948,9 @@
 - mobile
 - mturk
 - mwaa
-- neptune-graph
 - neptunedata
 - network-firewall
 - networkmanager
-- networkmonitor
 - nimble
 - oam
 - omics
@@ -7960,8 +7971,6 @@
 - pricing
 - privatenetworks
 - proton
-- qbusiness
-- qconnect
 - qldb
 - qldb-session
 - rbin
diff --git a/docs/docs/services/appsync.rst b/docs/docs/services/appsync.rst
index 83d0540c7..bd2e16dd2 100644
--- a/docs/docs/services/appsync.rst
+++ b/docs/docs/services/appsync.rst
@@ -55,7 +55,6 @@ appsync
 - [ ] get_api_association
 - [ ] get_api_cache
 - [ ] get_data_source
-- [ ] get_data_source_introspection
 - [ ] get_domain_name
 - [ ] get_function
 - [X] get_graphql_api
@@ -83,7 +82,6 @@ appsync
 - [X] list_tags_for_resource
 - [ ] list_types
 - [ ] list_types_by_association
-- [ ] start_data_source_introspection
 - [X] start_schema_creation
 - [ ] start_schema_merge
 - [X] tag_resource
diff --git a/docs/docs/services/backup.rst b/docs/docs/services/backup.rst
new file mode 100644
index 000000000..108d2c522
--- /dev/null
+++ b/docs/docs/services/backup.rst
@@ -0,0 +1,119 @@
+.. _implementedservice_backup:
+
+.. |start-h3| raw:: html
+
+    <h3>
+
+.. |end-h3| raw:: html
+
+    </h3>
+
+======
+backup
+======
+
+.. autoclass:: moto.backup.models.BackupBackend
+
+|start-h3| Example usage |end-h3|
+
+.. sourcecode:: python
+
+            @mock_backup
+            def test_backup_behaviour:
+                boto3.client("backup")
+                ...
+
+
+
+|start-h3| Implemented features for this service |end-h3|
+
+- [ ] cancel_legal_hold
+- [X] create_backup_plan
+- [ ] create_backup_selection
+- [X] create_backup_vault
+- [ ] create_framework
+- [ ] create_legal_hold
+- [ ] create_logically_air_gapped_backup_vault
+- [ ] create_report_plan
+- [X] delete_backup_plan
+- [ ] delete_backup_selection
+- [ ] delete_backup_vault
+- [ ] delete_backup_vault_access_policy
+- [ ] delete_backup_vault_lock_configuration
+- [ ] delete_backup_vault_notifications
+- [ ] delete_framework
+- [ ] delete_recovery_point
+- [ ] delete_report_plan
+- [ ] describe_backup_job
+- [ ] describe_backup_vault
+- [ ] describe_copy_job
+- [ ] describe_framework
+- [ ] describe_global_settings
+- [ ] describe_protected_resource
+- [ ] describe_recovery_point
+- [ ] describe_region_settings
+- [ ] describe_report_job
+- [ ] describe_report_plan
+- [ ] describe_restore_job
+- [ ] disassociate_recovery_point
+- [ ] disassociate_recovery_point_from_parent
+- [ ] export_backup_plan_template
+- [X] get_backup_plan
+- [ ] get_backup_plan_from_json
+- [ ] get_backup_plan_from_template
+- [ ] get_backup_selection
+- [ ] get_backup_vault_access_policy
+- [ ] get_backup_vault_notifications
+- [ ] get_legal_hold
+- [ ] get_recovery_point_restore_metadata
+- [ ] get_supported_resource_types
+- [ ] list_backup_job_summaries
+- [ ] list_backup_jobs
+- [ ] list_backup_plan_templates
+- [ ] list_backup_plan_versions
+- [X] list_backup_plans
+  
+        Pagination is not yet implemented
+        
+
+- [ ] list_backup_selections
+- [X] list_backup_vaults
+  
+        Pagination is not yet implemented
+        
+
+- [ ] list_copy_job_summaries
+- [ ] list_copy_jobs
+- [ ] list_frameworks
+- [ ] list_legal_holds
+- [ ] list_protected_resources
+- [ ] list_protected_resources_by_backup_vault
+- [ ] list_recovery_points_by_backup_vault
+- [ ] list_recovery_points_by_legal_hold
+- [ ] list_recovery_points_by_resource
+- [ ] list_report_jobs
+- [ ] list_report_plans
+- [ ] list_restore_job_summaries
+- [ ] list_restore_jobs
+- [X] list_tags
+  
+        Pagination is not yet implemented
+        
+
+- [ ] put_backup_vault_access_policy
+- [ ] put_backup_vault_lock_configuration
+- [ ] put_backup_vault_notifications
+- [ ] start_backup_job
+- [ ] start_copy_job
+- [ ] start_report_job
+- [ ] start_restore_job
+- [ ] stop_backup_job
+- [X] tag_resource
+- [X] untag_resource
+- [ ] update_backup_plan
+- [ ] update_framework
+- [ ] update_global_settings
+- [ ] update_recovery_point_lifecycle
+- [ ] update_region_settings
+- [ ] update_report_plan
+
diff --git a/docs/docs/services/codecommit.rst b/docs/docs/services/codecommit.rst
index dd09e58fd..47368ba04 100644
--- a/docs/docs/services/codecommit.rst
+++ b/docs/docs/services/codecommit.rst
@@ -102,6 +102,5 @@ codecommit
 - [ ] update_pull_request_status
 - [ ] update_pull_request_title
 - [ ] update_repository_description
-- [ ] update_repository_encryption_key
 - [ ] update_repository_name
 
diff --git a/docs/docs/services/efs.rst b/docs/docs/services/efs.rst
index bad8c2223..47d76e667 100644
--- a/docs/docs/services/efs.rst
+++ b/docs/docs/services/efs.rst
@@ -98,5 +98,4 @@ efs
 - [X] tag_resource
 - [X] untag_resource
 - [ ] update_file_system
-- [ ] update_file_system_protection
 
diff --git a/docs/docs/services/eks.rst b/docs/docs/services/eks.rst
index ecb31c1bb..ac7c261be 100644
--- a/docs/docs/services/eks.rst
+++ b/docs/docs/services/eks.rst
@@ -25,17 +25,14 @@ eks
 
 |start-h3| Implemented features for this service |end-h3|
 
-- [ ] associate_access_policy
 - [ ] associate_encryption_config
 - [ ] associate_identity_provider_config
-- [ ] create_access_entry
 - [ ] create_addon
 - [X] create_cluster
 - [ ] create_eks_anywhere_subscription
 - [X] create_fargate_profile
 - [X] create_nodegroup
 - [ ] create_pod_identity_association
-- [ ] delete_access_entry
 - [ ] delete_addon
 - [X] delete_cluster
 - [ ] delete_eks_anywhere_subscription
@@ -43,7 +40,6 @@ eks
 - [X] delete_nodegroup
 - [ ] delete_pod_identity_association
 - [ ] deregister_cluster
-- [ ] describe_access_entry
 - [ ] describe_addon
 - [ ] describe_addon_configuration
 - [ ] describe_addon_versions
@@ -51,21 +47,15 @@ eks
 - [ ] describe_eks_anywhere_subscription
 - [X] describe_fargate_profile
 - [ ] describe_identity_provider_config
-- [ ] describe_insight
 - [X] describe_nodegroup
 - [ ] describe_pod_identity_association
 - [ ] describe_update
-- [ ] disassociate_access_policy
 - [ ] disassociate_identity_provider_config
-- [ ] list_access_entries
-- [ ] list_access_policies
 - [ ] list_addons
-- [ ] list_associated_access_policies
 - [X] list_clusters
 - [ ] list_eks_anywhere_subscriptions
 - [X] list_fargate_profiles
 - [ ] list_identity_provider_configs
-- [ ] list_insights
 - [X] list_nodegroups
 - [ ] list_pod_identity_associations
 - [X] list_tags_for_resource
@@ -85,7 +75,6 @@ eks
         This function currently will remove tags on an EKS cluster only.  It does not remove tags from a managed node group
         
 
-- [ ] update_access_entry
 - [ ] update_addon
 - [ ] update_cluster_config
 - [ ] update_cluster_version
diff --git a/docs/docs/services/elasticache.rst b/docs/docs/services/elasticache.rst
index ff44a3044..227bf1204 100644
--- a/docs/docs/services/elasticache.rst
+++ b/docs/docs/services/elasticache.rst
@@ -32,7 +32,6 @@ elasticache
 - [ ] batch_apply_update_action
 - [ ] batch_stop_update_action
 - [ ] complete_migration
-- [ ] copy_serverless_cache_snapshot
 - [ ] copy_snapshot
 - [X] create_cache_cluster
 - [ ] create_cache_parameter_group
@@ -40,8 +39,6 @@ elasticache
 - [ ] create_cache_subnet_group
 - [ ] create_global_replication_group
 - [ ] create_replication_group
-- [ ] create_serverless_cache
-- [ ] create_serverless_cache_snapshot
 - [ ] create_snapshot
 - [X] create_user
 - [ ] create_user_group
@@ -53,8 +50,6 @@ elasticache
 - [ ] delete_cache_subnet_group
 - [ ] delete_global_replication_group
 - [ ] delete_replication_group
-- [ ] delete_serverless_cache
-- [ ] delete_serverless_cache_snapshot
 - [ ] delete_snapshot
 - [X] delete_user
 - [ ] delete_user_group
@@ -70,8 +65,6 @@ elasticache
 - [ ] describe_replication_groups
 - [ ] describe_reserved_cache_nodes
 - [ ] describe_reserved_cache_nodes_offerings
-- [ ] describe_serverless_cache_snapshots
-- [ ] describe_serverless_caches
 - [ ] describe_service_updates
 - [ ] describe_snapshots
 - [ ] describe_update_actions
@@ -83,7 +76,6 @@ elasticache
         
 
 - [ ] disassociate_global_replication_group
-- [ ] export_serverless_cache_snapshot
 - [ ] failover_global_replication_group
 - [ ] increase_node_groups_in_global_replication_group
 - [ ] increase_replica_count
@@ -95,7 +87,6 @@ elasticache
 - [ ] modify_global_replication_group
 - [ ] modify_replication_group
 - [ ] modify_replication_group_shard_configuration
-- [ ] modify_serverless_cache
 - [ ] modify_user
 - [ ] modify_user_group
 - [ ] purchase_reserved_cache_nodes_offering
diff --git a/docs/docs/services/emr.rst b/docs/docs/services/emr.rst
index 595fb150c..4a1d8fc06 100644
--- a/docs/docs/services/emr.rst
+++ b/docs/docs/services/emr.rst
@@ -72,7 +72,6 @@ emr
 - [ ] remove_managed_scaling_policy
 - [X] remove_tags
 - [X] run_job_flow
-- [ ] set_keep_job_flow_alive_when_no_steps
 - [X] set_termination_protection
 - [X] set_visible_to_all_users
 - [ ] start_notebook_execution
diff --git a/docs/docs/services/guardduty.rst b/docs/docs/services/guardduty.rst
index c339fb57c..4dbcdd873 100644
--- a/docs/docs/services/guardduty.rst
+++ b/docs/docs/services/guardduty.rst
@@ -63,7 +63,6 @@ guardduty
 - [ ] get_master_account
 - [ ] get_member_detectors
 - [ ] get_members
-- [ ] get_organization_statistics
 - [ ] get_remaining_free_trial_days
 - [ ] get_threat_intel_set
 - [ ] get_usage_statistics
diff --git a/docs/docs/services/iot.rst b/docs/docs/services/iot.rst
index 5b9c1c847..b52b080f4 100644
--- a/docs/docs/services/iot.rst
+++ b/docs/docs/services/iot.rst
@@ -49,7 +49,6 @@ iot
 - [ ] create_authorizer
 - [ ] create_billing_group
 - [X] create_certificate_from_csr
-- [ ] create_certificate_provider
 - [ ] create_custom_metric
 - [ ] create_dimension
 - [X] create_domain_configuration
@@ -86,7 +85,6 @@ iot
 - [ ] delete_billing_group
 - [X] delete_ca_certificate
 - [X] delete_certificate
-- [ ] delete_certificate_provider
 - [ ] delete_custom_metric
 - [ ] delete_dimension
 - [X] delete_domain_configuration
@@ -132,7 +130,6 @@ iot
 - [ ] describe_billing_group
 - [X] describe_ca_certificate
 - [X] describe_certificate
-- [ ] describe_certificate_provider
 - [ ] describe_custom_metric
 - [ ] describe_default_authorizer
 - [ ] describe_detect_mitigation_actions_task
@@ -196,7 +193,6 @@ iot
 - [ ] list_authorizers
 - [ ] list_billing_groups
 - [ ] list_ca_certificates
-- [ ] list_certificate_providers
 - [X] list_certificates
   
         Pagination is not yet implemented
@@ -325,7 +321,6 @@ iot
         
 
 - [X] update_certificate
-- [ ] update_certificate_provider
 - [ ] update_custom_metric
 - [ ] update_dimension
 - [X] update_domain_configuration
diff --git a/docs/docs/services/logs.rst b/docs/docs/services/logs.rst
index 395c1e82d..cfb7f4bfd 100644
--- a/docs/docs/services/logs.rst
+++ b/docs/docs/services/logs.rst
@@ -126,7 +126,6 @@ logs
 
 - [X] put_retention_policy
 - [X] put_subscription_filter
-- [ ] start_live_tail
 - [X] start_query
 - [ ] stop_query
 - [X] tag_log_group
diff --git a/docs/docs/services/mediaconnect.rst b/docs/docs/services/mediaconnect.rst
index 4c415253d..08fc5177e 100644
--- a/docs/docs/services/mediaconnect.rst
+++ b/docs/docs/services/mediaconnect.rst
@@ -40,7 +40,6 @@ mediaconnect
 - [ ] deregister_gateway_instance
 - [ ] describe_bridge
 - [X] describe_flow
-- [ ] describe_flow_source_metadata
 - [ ] describe_gateway
 - [ ] describe_gateway_instance
 - [ ] describe_offering
diff --git a/docs/docs/services/opensearch.rst b/docs/docs/services/opensearch.rst
index 7402bf828..6a608a250 100644
--- a/docs/docs/services/opensearch.rst
+++ b/docs/docs/services/opensearch.rst
@@ -28,7 +28,6 @@ opensearch
 |start-h3| Implemented features for this service |end-h3|
 
 - [ ] accept_inbound_connection
-- [ ] add_data_source
 - [X] add_tags
 - [ ] associate_package
 - [ ] authorize_vpc_endpoint_access
@@ -37,7 +36,6 @@ opensearch
 - [ ] create_outbound_connection
 - [ ] create_package
 - [ ] create_vpc_endpoint
-- [ ] delete_data_source
 - [X] delete_domain
 - [ ] delete_inbound_connection
 - [ ] delete_outbound_connection
@@ -60,12 +58,10 @@ opensearch
 - [ ] describe_vpc_endpoints
 - [ ] dissociate_package
 - [X] get_compatible_versions
-- [ ] get_data_source
 - [ ] get_domain_maintenance_status
 - [ ] get_package_version_history
 - [ ] get_upgrade_history
 - [ ] get_upgrade_status
-- [ ] list_data_sources
 - [ ] list_domain_maintenances
 - [X] list_domain_names
 - [ ] list_domains_for_package
@@ -83,7 +79,6 @@ opensearch
 - [ ] revoke_vpc_endpoint_access
 - [ ] start_domain_maintenance
 - [ ] start_service_software_update
-- [ ] update_data_source
 - [X] update_domain_config
 - [ ] update_package
 - [ ] update_scheduled_action
diff --git a/docs/docs/services/quicksight.rst b/docs/docs/services/quicksight.rst
index 0690940e3..848a1bbfe 100644
--- a/docs/docs/services/quicksight.rst
+++ b/docs/docs/services/quicksight.rst
@@ -188,7 +188,6 @@ quicksight
 - [ ] update_analysis
 - [ ] update_analysis_permissions
 - [ ] update_dashboard
-- [ ] update_dashboard_links
 - [ ] update_dashboard_permissions
 - [ ] update_dashboard_published_version
 - [ ] update_data_set
diff --git a/docs/docs/services/rds.rst b/docs/docs/services/rds.rst
index 6542a8319..ac345da2b 100644
--- a/docs/docs/services/rds.rst
+++ b/docs/docs/services/rds.rst
@@ -99,7 +99,6 @@ rds
 - [ ] describe_db_proxy_endpoints
 - [ ] describe_db_proxy_target_groups
 - [ ] describe_db_proxy_targets
-- [ ] describe_db_recommendations
 - [ ] describe_db_security_groups
 - [ ] describe_db_snapshot_attributes
 - [ ] describe_db_snapshot_tenant_databases
@@ -126,9 +125,7 @@ rds
 - [ ] describe_source_regions
 - [ ] describe_tenant_databases
 - [ ] describe_valid_db_instance_modifications
-- [ ] disable_http_endpoint
 - [ ] download_db_log_file_portion
-- [ ] enable_http_endpoint
 - [ ] failover_db_cluster
 - [ ] failover_global_cluster
 - [X] list_tags_for_resource
@@ -145,7 +142,6 @@ rds
 - [ ] modify_db_proxy
 - [ ] modify_db_proxy_endpoint
 - [ ] modify_db_proxy_target_group
-- [ ] modify_db_recommendation
 - [ ] modify_db_snapshot
 - [ ] modify_db_snapshot_attribute
 - [X] modify_db_subnet_group
@@ -171,7 +167,7 @@ rds
 - [ ] restore_db_cluster_to_point_in_time
 - [X] restore_db_instance_from_db_snapshot
 - [ ] restore_db_instance_from_s3
-- [ ] restore_db_instance_to_point_in_time
+- [X] restore_db_instance_to_point_in_time
 - [ ] revoke_db_security_group_ingress
 - [ ] start_activity_stream
 - [X] start_db_cluster
diff --git a/docs/docs/services/s3.rst b/docs/docs/services/s3.rst
index 88eef2c87..969b9f7c2 100644
--- a/docs/docs/services/s3.rst
+++ b/docs/docs/services/s3.rst
@@ -32,7 +32,6 @@ s3
 - [X] copy_object
 - [X] create_bucket
 - [X] create_multipart_upload
-- [ ] create_session
 - [X] delete_bucket
 - [ ] delete_bucket_analytics_configuration
 - [X] delete_bucket_cors
@@ -92,7 +91,6 @@ s3
 - [ ] list_bucket_inventory_configurations
 - [ ] list_bucket_metrics_configurations
 - [X] list_buckets
-- [ ] list_directory_buckets
 - [ ] list_multipart_uploads
 - [X] list_object_versions
 - [X] list_objects
diff --git a/docs/docs/services/sagemaker.rst b/docs/docs/services/sagemaker.rst
index 87b2d2827..52d099cea 100644
--- a/docs/docs/services/sagemaker.rst
+++ b/docs/docs/services/sagemaker.rst
@@ -36,7 +36,6 @@ sagemaker
 - [ ] create_artifact
 - [ ] create_auto_ml_job
 - [ ] create_auto_ml_job_v2
-- [ ] create_cluster
 - [ ] create_code_repository
 - [ ] create_compilation_job
 - [ ] create_context
@@ -56,7 +55,6 @@ sagemaker
 - [ ] create_hyper_parameter_tuning_job
 - [ ] create_image
 - [ ] create_image_version
-- [ ] create_inference_component
 - [ ] create_inference_experiment
 - [ ] create_inference_recommendations_job
 - [ ] create_labeling_job
@@ -91,9 +89,7 @@ sagemaker
 - [ ] delete_app_image_config
 - [ ] delete_artifact
 - [ ] delete_association
-- [ ] delete_cluster
 - [ ] delete_code_repository
-- [ ] delete_compilation_job
 - [ ] delete_context
 - [ ] delete_data_quality_job_definition
 - [ ] delete_device_fleet
@@ -110,7 +106,6 @@ sagemaker
 - [ ] delete_human_task_ui
 - [ ] delete_image
 - [ ] delete_image_version
-- [ ] delete_inference_component
 - [ ] delete_inference_experiment
 - [X] delete_model
 - [ ] delete_model_bias_job_definition
@@ -141,8 +136,6 @@ sagemaker
 - [ ] describe_artifact
 - [ ] describe_auto_ml_job
 - [ ] describe_auto_ml_job_v2
-- [ ] describe_cluster
-- [ ] describe_cluster_node
 - [ ] describe_code_repository
 - [ ] describe_compilation_job
 - [ ] describe_context
@@ -164,7 +157,6 @@ sagemaker
 - [ ] describe_hyper_parameter_tuning_job
 - [ ] describe_image
 - [ ] describe_image_version
-- [ ] describe_inference_component
 - [ ] describe_inference_experiment
 - [ ] describe_inference_recommendations_job
 - [ ] describe_labeling_job
@@ -214,8 +206,6 @@ sagemaker
 - [ ] list_associations
 - [ ] list_auto_ml_jobs
 - [ ] list_candidates_for_auto_ml_job
-- [ ] list_cluster_nodes
-- [ ] list_clusters
 - [ ] list_code_repositories
 - [ ] list_compilation_jobs
 - [ ] list_contexts
@@ -237,7 +227,6 @@ sagemaker
 - [ ] list_hyper_parameter_tuning_jobs
 - [ ] list_image_versions
 - [ ] list_images
-- [ ] list_inference_components
 - [ ] list_inference_experiments
 - [ ] list_inference_recommendations_job_steps
 - [ ] list_inference_recommendations_jobs
@@ -315,7 +304,6 @@ sagemaker
 - [ ] update_action
 - [ ] update_app_image_config
 - [ ] update_artifact
-- [ ] update_cluster
 - [ ] update_code_repository
 - [ ] update_context
 - [ ] update_device_fleet
@@ -329,8 +317,6 @@ sagemaker
 - [ ] update_hub
 - [ ] update_image
 - [ ] update_image_version
-- [ ] update_inference_component
-- [ ] update_inference_component_runtime_config
 - [ ] update_inference_experiment
 - [ ] update_model_card
 - [X] update_model_package
diff --git a/docs/docs/services/transcribe.rst b/docs/docs/services/transcribe.rst
index 4d3536203..df61d80c0 100644
--- a/docs/docs/services/transcribe.rst
+++ b/docs/docs/services/transcribe.rst
@@ -33,7 +33,6 @@ transcribe
 - [ ] delete_call_analytics_category
 - [ ] delete_call_analytics_job
 - [ ] delete_language_model
-- [ ] delete_medical_scribe_job
 - [X] delete_medical_transcription_job
 - [X] delete_medical_vocabulary
 - [X] delete_transcription_job
@@ -42,7 +41,6 @@ transcribe
 - [ ] describe_language_model
 - [ ] get_call_analytics_category
 - [ ] get_call_analytics_job
-- [ ] get_medical_scribe_job
 - [X] get_medical_transcription_job
 - [X] get_medical_vocabulary
 - [X] get_transcription_job
@@ -51,7 +49,6 @@ transcribe
 - [ ] list_call_analytics_categories
 - [ ] list_call_analytics_jobs
 - [ ] list_language_models
-- [ ] list_medical_scribe_jobs
 - [X] list_medical_transcription_jobs
 - [X] list_medical_vocabularies
 - [ ] list_tags_for_resource
@@ -59,7 +56,6 @@ transcribe
 - [X] list_vocabularies
 - [ ] list_vocabulary_filters
 - [ ] start_call_analytics_job
-- [ ] start_medical_scribe_job
 - [X] start_medical_transcription_job
 - [X] start_transcription_job
 - [ ] tag_resource
diff --git a/moto/__init__.py b/moto/__init__.py
index 72865ad29..7c1b02e69 100644
--- a/moto/__init__.py
+++ b/moto/__init__.py
@@ -235,6 +235,7 @@ XRaySegment = load_xray_segment()
 mock_xray = lazy_load(".xray", "mock_xray")
 mock_xray_client = lazy_load(".xray", "mock_xray_client")
 mock_wafv2 = lazy_load(".wafv2", "mock_wafv2")
+mock_backup = lazy_load(".backup", "mock_backup", boto3_name="backup")
 
 
 class MockAll(ContextDecorator):
diff --git a/moto/backend_index.py b/moto/backend_index.py
index 300784891..91c8e707a 100644
--- a/moto/backend_index.py
+++ b/moto/backend_index.py
@@ -1,4 +1,4 @@
-# autogenerated by moto/scripts/update_backend_index.py
+# autogenerated by /Users/oxh252/workspace/moto_contribution/moto/scripts/update_backend_index.py
 import re
 
 backend_url_patterns = [
@@ -18,6 +18,7 @@ backend_url_patterns = [
     ("appsync", re.compile("https?://appsync\\.(.+)\\.amazonaws\\.com")),
     ("athena", re.compile("https?://athena\\.(.+)\\.amazonaws\\.com")),
     ("autoscaling", re.compile("https?://autoscaling\\.(.+)\\.amazonaws\\.com")),
+    ("backup", re.compile("https?://backup\\.(.+)\\.amazonaws\\.com")),
     ("batch", re.compile("https?://batch\\.(.+)\\.amazonaws.com")),
     ("budgets", re.compile("https?://budgets\\.amazonaws\\.com")),
     ("ce", re.compile("https?://ce\\.(.+)\\.amazonaws\\.com")),
diff --git a/moto/backup/__init__.py b/moto/backup/__init__.py
new file mode 100644
index 000000000..092556a72
--- /dev/null
+++ b/moto/backup/__init__.py
@@ -0,0 +1,5 @@
+"""backup module initialization; sets value for base decorator."""
+from ..core.models import base_decorator
+from .models import backup_backends
+
+mock_backup = base_decorator(backup_backends)
diff --git a/moto/backup/exceptions.py b/moto/backup/exceptions.py
new file mode 100644
index 000000000..dc0e1ad12
--- /dev/null
+++ b/moto/backup/exceptions.py
@@ -0,0 +1,16 @@
+"""Exceptions raised by the backup service."""
+from moto.core.exceptions import JsonRESTError
+
+
+class BackupClientError(JsonRESTError):
+    code = 400
+
+
+class AlreadyExistsException(BackupClientError):
+    def __init__(self, msg: str):
+        super().__init__("AlreadyExistsException", f"{msg}")
+
+
+class ResourceNotFoundException(JsonRESTError):
+    def __init__(self, msg: str):
+        super().__init__("ResourceNotFoundException", f"{msg}")
diff --git a/moto/backup/models.py b/moto/backup/models.py
new file mode 100644
index 000000000..f2f040aed
--- /dev/null
+++ b/moto/backup/models.py
@@ -0,0 +1,236 @@
+"""BackupBackend class with methods for supported APIs."""
+from copy import deepcopy
+from typing import Any, Dict, List, Optional, Tuple
+
+from moto.core import BackendDict, BaseBackend, BaseModel
+from moto.core.utils import unix_time
+from moto.moto_api._internal import mock_random
+from moto.utilities.tagging_service import TaggingService
+
+from .exceptions import AlreadyExistsException, ResourceNotFoundException
+
+
+class Plan(BaseModel):
+    def __init__(
+        self,
+        backup_plan: Dict[str, Any],
+        creator_request_id: str,
+        backend: "BackupBackend",
+    ):
+
+        self.backup_plan_id = str(mock_random.uuid4())
+        self.backup_plan_arn = f"arn:aws:backup:{backend.region_name}:{backend.account_id}:backup-plan:{self.backup_plan_id}"
+        self.creation_date = unix_time()
+        ran_str = mock_random.get_random_string(length=48)
+        self.version_id = ran_str
+        self.creator_request_id = creator_request_id
+        self.backup_plan = backup_plan
+        adv_settings = backup_plan.get("AdvancedBackupSettings")
+        self.advanced_backup_settings = adv_settings or []
+        self.deletion_date: Optional[float] = None
+        # Deletion Date is updated when the backup_plan is deleted
+        self.last_execution_date = None  # start_restore_job not yet supported
+        rules = backup_plan["Rules"]
+        for rule in rules:
+            rule["ScheduleExpression"] = rule.get(
+                "ScheduleExpression", "cron(0 5 ? * * *)"
+            )  # Default CRON expression in UTC
+            rule["StartWindowMinutes"] = rule.get(
+                "StartWindowMinutes", 480
+            )  # Default=480
+            rule["CompletionWindowMinutes"] = rule.get(
+                "CompletionWindowMinutes", 10080
+            )  # Default=10080
+            rule["ScheduleExpressionTimezone"] = rule.get(
+                "ScheduleExpressionTimezone", "Etc/UTC"
+            )  # set to Etc/UTc by default
+            rule["RuleId"] = str(mock_random.uuid4())
+
+    def to_dict(self) -> Dict[str, Any]:
+        dct = {
+            "BackupPlanId": self.backup_plan_id,
+            "BackupPlanArn": self.backup_plan_arn,
+            "CreationDate": self.creation_date,
+            "VersionId": self.version_id,
+            "AdvancedBackupSettings": self.advanced_backup_settings,
+        }
+        return {k: v for k, v in dct.items() if v}
+
+    def to_get_dict(self) -> Dict[str, Any]:
+        dct = self.to_dict()
+        dct_options = {
+            "BackupPlan": self.backup_plan,
+            "CreatorRequestId": self.creator_request_id,
+            "DeletionDate": self.deletion_date,
+            "LastExecutionDate": self.last_execution_date,
+        }
+        for key, value in dct_options.items():
+            if value is not None:
+                dct[key] = value
+        return dct
+
+    def to_list_dict(self) -> Dict[str, Any]:
+        dct = self.to_get_dict()
+        dct.pop("BackupPlan")
+        dct["BackupPlanName"] = self.backup_plan.get("BackupPlanName")
+        return dct
+
+
+class Vault(BaseModel):
+    def __init__(
+        self,
+        backup_vault_name: str,
+        encryption_key_arn: str,
+        creator_request_id: str,
+        backend: "BackupBackend",
+    ):
+        self.backup_vault_name = backup_vault_name
+        self.backup_vault_arn = f"arn:aws:backup:{backend.region_name}:{backend.account_id}:backup-vault:{backup_vault_name}"
+        self.creation_date = unix_time()
+        self.encryption_key_arn = encryption_key_arn
+        self.creator_request_id = creator_request_id
+        self.num_of_recovery_points = 0  # start_backup_job not yet supported
+        self.locked = False  # put_backup_vault_lock_configuration
+        self.min_retention_days = 0  # put_backup_vault_lock_configuration
+        self.max_retention_days = 0  # put_backup_vault_lock_configuration
+        self.lock_date = None  # put_backup_vault_lock_configuration
+
+    def to_dict(self) -> Dict[str, Any]:
+        dct = {
+            "BackupVaultName": self.backup_vault_name,
+            "BackupVaultArn": self.backup_vault_arn,
+            "CreationDate": self.creation_date,
+        }
+        return dct
+
+    def to_list_dict(self) -> Dict[str, Any]:
+        dct = self.to_dict()
+        dct_options: Dict[str, Any] = dict()
+        dct_options = {
+            "EncryptionKeyArn": self.encryption_key_arn,
+            "CreatorRequestId": self.creator_request_id,
+            "NumberOfRecoveryPoints": self.num_of_recovery_points,
+            "Locked": self.locked,
+            "MinRetentionDays": self.min_retention_days,
+            "MaxRetentionDays": self.max_retention_days,
+            "LockDate": self.lock_date,
+        }
+        for key, value in dct_options.items():
+            if value is not None:
+                dct[key] = value
+        return dct
+
+
+class BackupBackend(BaseBackend):
+    """Implementation of Backup APIs."""
+
+    def __init__(self, region_name: str, account_id: str):
+        super().__init__(region_name, account_id)
+
+        self.vaults: Dict[str, Vault] = dict()
+        self.plans: Dict[str, Plan] = dict()
+        self.tagger = TaggingService()
+
+    def create_backup_plan(
+        self,
+        backup_plan: Dict[str, Any],
+        backup_plan_tags: Dict[str, str],
+        creator_request_id: str,
+    ) -> Plan:
+
+        if backup_plan["BackupPlanName"] in list(
+            p.backup_plan["BackupPlanName"] for p in list(self.plans.values())
+        ):
+            raise AlreadyExistsException(
+                msg="Backup plan with the same plan document already exists"
+            )
+        plan = Plan(
+            backup_plan=backup_plan,
+            creator_request_id=creator_request_id,
+            backend=self,
+        )
+        if backup_plan_tags:
+            self.tag_resource(plan.backup_plan_arn, backup_plan_tags)
+        self.plans[plan.backup_plan_id] = plan
+        return plan
+
+    def get_backup_plan(self, backup_plan_id: str, version_id: Optional[Any]) -> Plan:
+        msg = "Failed reading Backup plan with provided version"
+        if backup_plan_id not in self.plans:
+            raise ResourceNotFoundException(msg=msg)
+        plan = self.plans[backup_plan_id]
+        if version_id:
+            if plan.version_id == version_id:
+                return plan
+            else:
+                raise ResourceNotFoundException(msg=msg)
+        return plan
+
+    def delete_backup_plan(self, backup_plan_id: str) -> Tuple[str, str, float, str]:
+        if backup_plan_id not in self.plans:
+            raise ResourceNotFoundException(
+                msg="Failed reading Backup plan with provided version"
+            )
+        deletion_date = unix_time()
+        res = self.plans[backup_plan_id]
+        res.deletion_date = deletion_date
+        return res.backup_plan_id, res.backup_plan_arn, deletion_date, res.version_id
+
+    def list_backup_plans(self, include_deleted: Any) -> List[Plan]:
+        """
+        Pagination is not yet implemented
+        """
+        plans_list = deepcopy(self.plans)
+
+        for plan in list(plans_list.values()):
+            backup_plan_id = plan.backup_plan_id
+            if plan.deletion_date is not None:
+                plans_list.pop(backup_plan_id)
+        if include_deleted:
+            return list(self.plans.values())
+        return list(plans_list.values())
+
+    def create_backup_vault(
+        self,
+        backup_vault_name: str,
+        backup_vault_tags: Dict[str, str],
+        encryption_key_arn: str,
+        creator_request_id: str,
+    ) -> Vault:
+
+        if backup_vault_name in self.vaults:
+            raise AlreadyExistsException(
+                msg="Backup vault with the same name already exists"
+            )
+        vault = Vault(
+            backup_vault_name=backup_vault_name,
+            encryption_key_arn=encryption_key_arn,
+            creator_request_id=creator_request_id,
+            backend=self,
+        )
+        if backup_vault_tags:
+            self.tag_resource(vault.backup_vault_arn, backup_vault_tags)
+        self.vaults[backup_vault_name] = vault
+        return vault
+
+    def list_backup_vaults(self) -> List[Vault]:
+        """
+        Pagination is not yet implemented
+        """
+        return list(self.vaults.values())
+
+    def list_tags(self, resource_arn: str) -> Dict[str, str]:
+        """
+        Pagination is not yet implemented
+        """
+        return self.tagger.get_tag_dict_for_resource(resource_arn)
+
+    def tag_resource(self, resource_arn: str, tags: Dict[str, str]) -> None:
+        tags_input = TaggingService.convert_dict_to_tags_input(tags or {})
+        self.tagger.tag_resource(resource_arn, tags_input)
+
+    def untag_resource(self, resource_arn: str, tag_key_list: List[str]) -> None:
+        self.tagger.untag_resource_using_names(resource_arn, tag_key_list)
+
+
+backup_backends = BackendDict(BackupBackend, "backup")
diff --git a/moto/backup/responses.py b/moto/backup/responses.py
new file mode 100644
index 000000000..42483740b
--- /dev/null
+++ b/moto/backup/responses.py
@@ -0,0 +1,116 @@
+"""Handles incoming backup requests, invokes methods, returns responses."""
+import json
+from urllib.parse import unquote
+
+from moto.core.responses import BaseResponse
+
+from .models import BackupBackend, backup_backends
+
+
+class BackupResponse(BaseResponse):
+    """Handler for Backup requests and responses."""
+
+    def __init__(self) -> None:
+        super().__init__(service_name="backup")
+
+    @property
+    def backup_backend(self) -> BackupBackend:
+        """Return backend instance specific for this region."""
+        return backup_backends[self.current_account][self.region]
+
+    def create_backup_plan(self) -> str:
+        params = json.loads(self.body)
+        backup_plan = params.get("BackupPlan")
+        backup_plan_tags = params.get("BackupPlanTags")
+        creator_request_id = params.get("CreatorRequestId")
+        plan = self.backup_backend.create_backup_plan(
+            backup_plan=backup_plan,
+            backup_plan_tags=backup_plan_tags,
+            creator_request_id=creator_request_id,
+        )
+        return json.dumps(dict(plan.to_dict()))
+
+    def get_backup_plan(self) -> str:
+        params = self._get_params()
+        backup_plan_id = self.path.split("/")[-2]
+        version_id = params.get("versionId")
+        plan = self.backup_backend.get_backup_plan(
+            backup_plan_id=backup_plan_id, version_id=version_id
+        )
+        return json.dumps(dict(plan.to_get_dict()))
+
+    def delete_backup_plan(self) -> str:
+        backup_plan_id = self.path.split("/")[-1]
+        (
+            backup_plan_id,
+            backup_plan_arn,
+            deletion_date,
+            version_id,
+        ) = self.backup_backend.delete_backup_plan(
+            backup_plan_id=backup_plan_id,
+        )
+        return json.dumps(
+            dict(
+                BackupPlanId=backup_plan_id,
+                BackupPlanArn=backup_plan_arn,
+                DeletionDate=deletion_date,
+                VersionId=version_id,
+            )
+        )
+
+    def list_backup_plans(self) -> str:
+        params = self._get_params()
+        include_deleted = params.get("includeDeleted")
+        backup_plans_list = self.backup_backend.list_backup_plans(
+            include_deleted=include_deleted
+        )
+        return json.dumps(
+            dict(BackupPlansList=[p.to_list_dict() for p in backup_plans_list])
+        )
+
+    def create_backup_vault(self) -> str:
+        params = json.loads(self.body)
+        backup_vault_name = self.path.split("/")[-1]
+        backup_vault_tags = params.get("BackupVaultTags")
+        encryption_key_arn = params.get("EncryptionKeyArn")
+        creator_request_id = params.get("CreatorRequestId")
+        backup_vault = self.backup_backend.create_backup_vault(
+            backup_vault_name=backup_vault_name,
+            backup_vault_tags=backup_vault_tags,
+            encryption_key_arn=encryption_key_arn,
+            creator_request_id=creator_request_id,
+        )
+        return json.dumps(dict(backup_vault.to_dict()))
+
+    def list_backup_vaults(self) -> str:
+        backup_vault_list = self.backup_backend.list_backup_vaults()
+        return json.dumps(
+            dict(BackupVaultList=[v.to_list_dict() for v in backup_vault_list])
+        )
+
+    def list_tags(self) -> str:
+        resource_arn = unquote(self.path.split("/")[-2])
+        tags = self.backup_backend.list_tags(
+            resource_arn=resource_arn,
+        )
+        return json.dumps(dict(Tags=tags))
+
+    def tag_resource(self) -> str:
+        params = json.loads(self.body)
+        resource_arn = unquote(self.path.split("/")[-1])
+        tags = params.get("Tags")
+        self.backup_backend.tag_resource(
+            resource_arn=resource_arn,
+            tags=tags,
+        )
+        return "{}"
+
+    def untag_resource(self) -> str:
+        params = json.loads(self.body)
+        resource_arn = unquote(self.path.split("/")[-1])
+        tag_key_list = params.get("TagKeyList")
+        self.backup_backend.untag_resource(
+            resource_arn=resource_arn,
+            tag_key_list=tag_key_list,
+        )
+        return "{}"
diff --git a/moto/backup/urls.py b/moto/backup/urls.py
new file mode 100644
index 000000000..5b4b7c84b
--- /dev/null
+++ b/moto/backup/urls.py
@@ -0,0 +1,19 @@
+"""backup base URL and path."""
+from .responses import BackupResponse
+
+url_bases = [
+    r"https?://backup\.(.+)\.amazonaws\.com",
+]
+
+
+response = BackupResponse()
+
+
+url_paths = {
+    "{0}/backup/plans/?$": response.dispatch,
+    "{0}/backup/plans/(?P<name>.+)/?$": response.dispatch,
+    "{0}/backup-vaults/$": response.dispatch,
+    "{0}/backup-vaults/(?P<name>[^/]+)$": response.dispatch,
+    "{0}/tags/(?P<resource_arn>.+)$": response.dispatch,
+    "{0}/untag/(?P<resource_arn>.+)$": response.dispatch,
+}
diff --git a/moto/core/utils.py b/moto/core/utils.py
index 8f3ddfce0..8e817dda2 100644
--- a/moto/core/utils.py
+++ b/moto/core/utils.py
@@ -170,10 +170,36 @@ def iso_8601_datetime_without_milliseconds_s3(
 
 
 RFC1123 = "%a, %d %b %Y %H:%M:%S GMT"
+EN_WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+EN_MONTHS = [
+    "Jan",
+    "Feb",
+    "Mar",
+    "Apr",
+    "May",
+    "Jun",
+    "Jul",
+    "Aug",
+    "Sep",
+    "Oct",
+    "Nov",
+    "Dec",
+]
 
 
 def rfc_1123_datetime(src: datetime.datetime) -> str:
-    return src.strftime(RFC1123)
+    """
+    Returns the datetime in the RFC-1123 format
+    Names of weekdays/months are in English
+    """
+    # strftime uses the current locale to translate values
+    # So weekday/month values may not be in English
+    # For our usecase, we need these values to always be in English, otherwise botocore is unable to parse it
+    # Having a hardcoded list ensures this always works, even if the user does not have an English locale installed
+    eng_weekday = EN_WEEKDAYS[src.isoweekday() - 1]
+    eng_month = EN_MONTHS[src.month - 1]
+    non_locallized_rfc1123 = RFC1123.replace("%a", "{}").replace("%b", "{}")
+    return src.strftime(non_locallized_rfc1123).format(eng_weekday, eng_month)
 
 
 def str_to_rfc_1123_datetime(value: str) -> datetime.datetime:
diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py
index 41219e4d5..90c7a76ab 100644
--- a/moto/resourcegroupstaggingapi/models.py
+++ b/moto/resourcegroupstaggingapi/models.py
@@ -2,6 +2,7 @@ from typing import Any, Dict, Iterator, List, Optional, Tuple
 
 from moto.acm.models import AWSCertificateManagerBackend, acm_backends
 from moto.awslambda.models import LambdaBackend, lambda_backends
+from moto.backup.models import BackupBackend, backup_backends
 from moto.core import BackendDict, BaseBackend
 from moto.core.exceptions import RESTError
 from moto.ec2 import ec2_backends
@@ -100,6 +101,10 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
     def sqs_backend(self) -> SQSBackend:
         return sqs_backends[self.account_id][self.region_name]
 
+    @property
+    def backup_backend(self) -> BackupBackend:
+        return backup_backends[self.account_id][self.region_name]
+
     def _get_resources_generator(
         self,
         tag_filters: Optional[List[Dict[str, Any]]] = None,
@@ -165,6 +170,19 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
                     continue
                 yield {"ResourceARN": f"{certificate.arn}", "Tags": tags}
 
+        # Backup
+        if not resource_type_filters or "backup" in resource_type_filters:
+            for vault in self.backup_backend.vaults.values():
+                tags = self.backup_backend.tagger.list_tags_for_resource(
+                    vault.backup_vault_arn
+                )["Tags"]
+                if not tags or not tag_filter(
+                    tags
+                ):  # Skip if no tags, or invalid filter
+                    continue
+
+                yield {"ResourceARN": f"{vault.backup_vault_arn}", "Tags": tags}
+
         # S3
         if not resource_type_filters or "s3" in resource_type_filters:
             for bucket in self.s3_backend.buckets.values():
diff --git a/moto/sns/models.py b/moto/sns/models.py
index e4a038977..c7f678a75 100644
--- a/moto/sns/models.py
+++ b/moto/sns/models.py
@@ -732,8 +732,17 @@ class SNSBackend(BaseBackend):
             raise SNSNotFoundError(
                 "Subscription does not exist", template="wrapped_single_error"
             )
+        # AWS does not return the FilterPolicy scope if the FilterPolicy is not set
+        # if the FilterPolicy is set and not the FilterPolicyScope, it returns the default value
+        attributes = {**subscription.attributes}
+        if "FilterPolicyScope" in attributes and not attributes.get("FilterPolicy"):
+            attributes.pop("FilterPolicyScope", None)
+            attributes.pop("FilterPolicy", None)
 
-        return subscription.attributes
+        elif "FilterPolicy" in attributes and "FilterPolicyScope" not in attributes:
+            attributes["FilterPolicyScope"] = "MessageAttributes"
+
+        return attributes
 
     def set_subscription_attributes(self, arn: str, name: str, value: Any) -> None:
         if name not in [
@@ -753,15 +762,19 @@ class SNSBackend(BaseBackend):
         subscription = _subscription[0]
 
         if name == "FilterPolicy":
-            filter_policy = json.loads(value)
-            # we validate the filter policy differently depending on the scope
-            # we need to always set the scope first
-            filter_policy_scope = subscription.attributes.get("FilterPolicyScope")
-            self._validate_filter_policy(filter_policy, scope=filter_policy_scope)
-            subscription._filter_policy = filter_policy
-            subscription._filter_policy_matcher = FilterPolicyMatcher(
-                filter_policy, filter_policy_scope
-            )
+            if value:
+                filter_policy = json.loads(value)
+                # we validate the filter policy differently depending on the scope
+                # we need to always set the scope first
+                filter_policy_scope = subscription.attributes.get("FilterPolicyScope")
+                self._validate_filter_policy(filter_policy, scope=filter_policy_scope)
+                subscription._filter_policy = filter_policy
+                subscription._filter_policy_matcher = FilterPolicyMatcher(
+                    filter_policy, filter_policy_scope
+                )
+            else:
+                subscription._filter_policy = None
+                subscription._filter_policy_matcher = None
 
         subscription.attributes[name] = value
 


Test patch:
diff --git a/tests/test_backup/__init__.py b/tests/test_backup/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_backup/test_backup.py b/tests/test_backup/test_backup.py
new file mode 100644
index 000000000..030bd9eb9
--- /dev/null
+++ b/tests/test_backup/test_backup.py
@@ -0,0 +1,359 @@
+"""Unit tests for backup-supported APIs."""
+import boto3
+import pytest
+from botocore.exceptions import ClientError
+
+from moto import mock_backup
+
+
+@mock_backup
+def test_create_backup_plan():
+    client = boto3.client("backup", region_name="eu-west-1")
+    response = client.create_backup_vault(
+        BackupVaultName="backupvault-foobar",
+    )
+    resp = client.create_backup_plan(
+        BackupPlan={
+            "BackupPlanName": "backupplan-foobar",
+            "Rules": [
+                {
+                    "RuleName": "foobar",
+                    "TargetBackupVaultName": response["BackupVaultName"],
+                },
+            ],
+        },
+    )
+    assert "BackupPlanId" in resp
+    assert "BackupPlanArn" in resp
+    assert "CreationDate" in resp
+    assert "VersionId" in resp
+
+
+@mock_backup
+def test_create_backup_plan_already_exists():
+    client = boto3.client("backup", region_name="eu-west-1")
+    backup_plan_name = "backup_plan_foobar"
+    rules = [
+        {
+            "RuleName": "foobar",
+            "TargetBackupVaultName": "backup-vault-foobar",
+        },
+    ]
+    client.create_backup_plan(
+        BackupPlan={"BackupPlanName": backup_plan_name, "Rules": rules}
+    )
+
+    with pytest.raises(ClientError) as exc:
+        client.create_backup_plan(
+            BackupPlan={"BackupPlanName": backup_plan_name, "Rules": rules}
+        )
+    err = exc.value.response["Error"]
+    assert err["Code"] == "AlreadyExistsException"
+
+
+@mock_backup
+def test_get_backup_plan():
+    client = boto3.client("backup", region_name="eu-west-1")
+    response = client.create_backup_vault(
+        BackupVaultName="backupvault-foobar",
+    )
+    plan = client.create_backup_plan(
+        BackupPlan={
+            "BackupPlanName": "backupplan-foobar",
+            "Rules": [
+                {
+                    "RuleName": "foobar",
+                    "TargetBackupVaultName": response["BackupVaultName"],
+                },
+            ],
+        },
+    )
+    resp = client.get_backup_plan(
+        BackupPlanId=plan["BackupPlanId"], VersionId=plan["VersionId"]
+    )
+    assert "BackupPlan" in resp
+
+
+@mock_backup
+def test_get_backup_plan_invalid_id():
+    client = boto3.client("backup", region_name="eu-west-1")
+
+    with pytest.raises(ClientError) as exc:
+        client.get_backup_plan(BackupPlanId="foobar")
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ResourceNotFoundException"
+
+
+@mock_backup
+def test_get_backup_plan_invalid_version_id():
+    client = boto3.client("backup", region_name="eu-west-1")
+
+    plan = client.create_backup_plan(
+        BackupPlan={
+            "BackupPlanName": "backupplan-foobar",
+            "Rules": [
+                {
+                    "RuleName": "foobar",
+                    "TargetBackupVaultName": "Backup-vault-foobar",
+                },
+            ],
+        },
+    )
+    with pytest.raises(ClientError) as exc:
+        client.get_backup_plan(BackupPlanId=plan["BackupPlanId"], VersionId="foobar")
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ResourceNotFoundException"
+
+
+@mock_backup
+def test_get_backup_plan_with_multiple_rules():
+    client = boto3.client("backup", region_name="eu-west-1")
+    plan = client.create_backup_plan(
+        BackupPlan={
+            "BackupPlanName": "backupplan-foobar",
+            "Rules": [
+                {
+                    "RuleName": "rule1",
+                    "TargetBackupVaultName": "backupvault-foobar",
+                    "ScheduleExpression": "cron(0 1 ? * * *)",
+                    "StartWindowMinutes": 60,
+                    "CompletionWindowMinutes": 120,
+                },
+                {
+                    "RuleName": "rule2",
+                    "TargetBackupVaultName": "backupvault-foobar",
+                },
+            ],
+        },
+    )
+    resp = client.get_backup_plan(BackupPlanId=plan["BackupPlanId"])
+    for rule in resp["BackupPlan"]["Rules"]:
+        assert "ScheduleExpression" in rule
+        assert "StartWindowMinutes" in rule
+        assert "CompletionWindowMinutes" in rule
+        assert "RuleId" in rule
+
+
+@mock_backup
+def test_delete_backup_plan():
+    client = boto3.client("backup", region_name="eu-west-1")
+    response = client.create_backup_vault(
+        BackupVaultName="backupvault-foobar",
+    )
+    plan = client.create_backup_plan(
+        BackupPlan={
+            "BackupPlanName": "backupplan-foobar",
+            "Rules": [
+                {
+                    "RuleName": "foobar",
+                    "TargetBackupVaultName": response["BackupVaultName"],
+                },
+            ],
+        },
+    )
+
+    resp = client.delete_backup_plan(BackupPlanId=plan["BackupPlanId"])
+    assert "BackupPlanId" in resp
+    assert "BackupPlanArn" in resp
+    assert "DeletionDate" in resp
+    assert "VersionId" in resp
+
+    resp = client.get_backup_plan(
+        BackupPlanId=plan["BackupPlanId"], VersionId=plan["VersionId"]
+    )
+    assert "DeletionDate" in resp
+
+
+@mock_backup
+def test_delete_backup_plan_invalid_id():
+    client = boto3.client("backup", region_name="eu-west-1")
+
+    with pytest.raises(ClientError) as exc:
+        client.delete_backup_plan(BackupPlanId="foobar")
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ResourceNotFoundException"
+
+
+@mock_backup
+def test_list_backup_plans():
+    client = boto3.client("backup", region_name="eu-west-1")
+    for i in range(1, 3):
+        client.create_backup_plan(
+            BackupPlan={
+                "BackupPlanName": f"backup-plan-{i}",
+                "Rules": [
+                    {
+                        "RuleName": "foobar",
+                        "TargetBackupVaultName": "backupvault-foobar",
+                    },
+                ],
+            },
+        )
+    resp = client.list_backup_plans()
+    backup_plans = resp["BackupPlansList"]
+    assert backup_plans[0]["BackupPlanName"] == "backup-plan-1"
+    assert backup_plans[1]["BackupPlanName"] == "backup-plan-2"
+    assert len(backup_plans) == 2
+    assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+
+@mock_backup
+def test_list_backup_plans_without_include_deleted():
+    client = boto3.client("backup", region_name="eu-west-1")
+
+    for i in range(1, 3):
+        client.create_backup_plan(
+            BackupPlan={
+                "BackupPlanName": f"backup-plan-{i}",
+                "Rules": [
+                    {
+                        "RuleName": "foobar",
+                        "TargetBackupVaultName": "backupvault-foobar",
+                    },
+                ],
+            },
+        )
+    resp = client.list_backup_plans()
+    client.delete_backup_plan(BackupPlanId=resp["BackupPlansList"][0]["BackupPlanId"])
+    resp_list = client.list_backup_plans()
+    backup_plans = resp_list["BackupPlansList"]
+    assert backup_plans[0]["BackupPlanName"] == "backup-plan-2"
+    assert len(backup_plans) == 1
+
+
+@mock_backup
+def test_list_backup_plans_with_include_deleted():
+    client = boto3.client("backup", region_name="eu-west-1")
+    for i in range(1, 3):
+        client.create_backup_plan(
+            BackupPlan={
+                "BackupPlanName": f"backup-plan-{i}",
+                "Rules": [
+                    {
+                        "RuleName": "foobar",
+                        "TargetBackupVaultName": "backupvault-foobar",
+                    },
+                ],
+            },
+        )
+    resp = client.list_backup_plans()
+    client.delete_backup_plan(BackupPlanId=resp["BackupPlansList"][0]["BackupPlanId"])
+    resp_list = client.list_backup_plans(IncludeDeleted=True)
+    backup_plans = resp_list["BackupPlansList"]
+    assert backup_plans[0]["BackupPlanName"] == "backup-plan-1"
+    assert backup_plans[1]["BackupPlanName"] == "backup-plan-2"
+    assert len(backup_plans) == 2
+
+
+@mock_backup
+def test_create_backup_vault():
+    client = boto3.client("backup", region_name="eu-west-1")
+    resp = client.create_backup_vault(
+        BackupVaultName="backupvault-foobar",
+        BackupVaultTags={
+            "foo": "bar",
+        },
+    )
+    assert "BackupVaultName" in resp
+    assert "BackupVaultArn" in resp
+    assert "CreationDate" in resp
+
+
+@mock_backup
+def test_create_backup_vault_already_exists():
+    client = boto3.client("backup", region_name="eu-west-1")
+    backup_vault_name = "backup_vault_foobar"
+    client.create_backup_vault(BackupVaultName=backup_vault_name)
+
+    with pytest.raises(ClientError) as exc:
+        client.create_backup_vault(BackupVaultName=backup_vault_name)
+    err = exc.value.response["Error"]
+    assert err["Code"] == "AlreadyExistsException"
+
+
+@mock_backup
+def test_list_backup_vaults():
+    client = boto3.client("backup", region_name="eu-west-1")
+    for i in range(1, 3):
+        client.create_backup_vault(
+            BackupVaultName=f"backup-vault-{i}",
+        )
+    resp = client.list_backup_vaults()
+    backup_plans = resp["BackupVaultList"]
+    assert backup_plans[0]["BackupVaultName"] == "backup-vault-1"
+    assert backup_plans[1]["BackupVaultName"] == "backup-vault-2"
+    assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+
+@mock_backup
+def test_list_tags_vault():
+    client = boto3.client("backup", region_name="eu-west-1")
+    vault = client.create_backup_vault(
+        BackupVaultName="backupvault-foobar",
+        BackupVaultTags={
+            "key1": "value1",
+            "key2": "value2",
+        },
+    )
+    resp = client.list_tags(ResourceArn=vault["BackupVaultArn"])
+    assert resp["Tags"] == {"key1": "value1", "key2": "value2"}
+
+
+@mock_backup
+def test_list_tags_plan():
+    client = boto3.client("backup", region_name="eu-west-1")
+    response = client.create_backup_vault(
+        BackupVaultName="backupvault-foobar",
+    )
+    plan = client.create_backup_plan(
+        BackupPlan={
+            "BackupPlanName": "backupplan-foobar",
+            "Rules": [
+                {
+                    "RuleName": "foobar",
+                    "TargetBackupVaultName": response["BackupVaultName"],
+                },
+            ],
+        },
+        BackupPlanTags={
+            "key1": "value1",
+            "key2": "value2",
+        },
+    )
+    resp = client.list_tags(ResourceArn=plan["BackupPlanArn"])
+    assert resp["Tags"] == {"key1": "value1", "key2": "value2"}
+
+
+@mock_backup
+def test_tag_resource():
+    client = boto3.client("backup", region_name="eu-west-1")
+    vault = client.create_backup_vault(
+        BackupVaultName="backupvault-foobar",
+        BackupVaultTags={
+            "key1": "value1",
+        },
+    )
+    resource_arn = vault["BackupVaultArn"]
+    client.tag_resource(
+        ResourceArn=resource_arn, Tags={"key2": "value2", "key3": "value3"}
+    )
+    resp = client.list_tags(ResourceArn=resource_arn)
+    assert resp["Tags"] == {"key1": "value1", "key2": "value2", "key3": "value3"}
+
+
+@mock_backup
+def test_untag_resource():
+    client = boto3.client("backup", region_name="eu-west-1")
+    vault = client.create_backup_vault(
+        BackupVaultName="backupvault-foobar",
+        BackupVaultTags={
+            "key1": "value1",
+        },
+    )
+    resource_arn = vault["BackupVaultArn"]
+    client.tag_resource(
+        ResourceArn=resource_arn, Tags={"key2": "value2", "key3": "value3"}
+    )
+    resp = client.untag_resource(ResourceArn=resource_arn, TagKeyList=["key2"])
+    resp = client.list_tags(ResourceArn=resource_arn)
+    assert resp["Tags"] == {"key1": "value1", "key3": "value3"}
diff --git a/tests/test_core/test_utils.py b/tests/test_core/test_utils.py
index cc2e86749..7a8915b91 100644
--- a/tests/test_core/test_utils.py
+++ b/tests/test_core/test_utils.py
@@ -1,3 +1,5 @@
+import datetime
+
 import pytest
 from freezegun import freeze_time
 
@@ -5,6 +7,8 @@ from moto.core.utils import (
     camelcase_to_pascal,
     camelcase_to_underscores,
     pascal_to_camelcase,
+    rfc_1123_datetime,
+    str_to_rfc_1123_datetime,
     underscores_to_camelcase,
     unix_time,
 )
@@ -50,3 +54,10 @@ def test_camelcase_to_pascal(_input: str, expected: str) -> None:
 @freeze_time("2015-01-01 12:00:00")
 def test_unix_time() -> None:  # type: ignore[misc]
     assert unix_time() == 1420113600.0
+
+
+def test_rfc1123_dates() -> None:
+    with freeze_time("2012-03-04 05:00:05"):
+        x = rfc_1123_datetime(datetime.datetime.now())
+        assert x == "Sun, 04 Mar 2012 05:00:05 GMT"
+        assert str_to_rfc_1123_datetime(x) == datetime.datetime.now()
diff --git a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
index 88fc908c1..079466116 100644
--- a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
+++ b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
@@ -5,6 +5,7 @@ from botocore.client import ClientError
 
 from moto import (
     mock_acm,
+    mock_backup,
     mock_cloudformation,
     mock_ec2,
     mock_ecs,
@@ -117,6 +118,37 @@ def test_get_resources_acm():
     )
 
 
+@mock_backup
+@mock_resourcegroupstaggingapi
+def test_get_resources_backup():
+    backup = boto3.client("backup", region_name="eu-central-1")
+
+    # Create two tagged Backup Vaults
+    for i in range(1, 3):
+        i_str = str(i)
+
+        backup.create_backup_vault(
+            BackupVaultName="backup-vault-tag-" + i_str,
+            BackupVaultTags={
+                "Test": i_str,
+            },
+        )
+
+    rtapi = boto3.client("resourcegroupstaggingapi", region_name="eu-central-1")
+
+    # Basic test
+    resp = rtapi.get_resources(ResourceTypeFilters=["backup"])
+    assert len(resp["ResourceTagMappingList"]) == 2
+
+    # Test tag filtering
+    resp = rtapi.get_resources(
+        ResourceTypeFilters=["backup"],
+        TagFilters=[{"Key": "Test", "Values": ["1"]}],
+    )
+    assert len(resp["ResourceTagMappingList"]) == 1
+    assert {"Key": "Test", "Value": "1"} in resp["ResourceTagMappingList"][0]["Tags"]
+
+
 @mock_ecs
 @mock_ec2
 @mock_resourcegroupstaggingapi
diff --git a/tests/test_s3/test_s3_locales.py b/tests/test_s3/test_s3_locales.py
new file mode 100644
index 000000000..06aca5c36
--- /dev/null
+++ b/tests/test_s3/test_s3_locales.py
@@ -0,0 +1,30 @@
+from datetime import datetime
+from unittest import SkipTest
+
+import boto3
+from freezegun import freeze_time
+
+from moto import mock_s3, settings
+
+
+@mock_s3
+def test_rfc_returns_valid_date_for_every_possible_weekday_and_month():
+    if not settings.TEST_DECORATOR_MODE:
+        raise SkipTest("Freezing time only possible in DecoratorMode")
+    client = boto3.client("s3", region_name="us-east-1")
+    client.create_bucket(Bucket="bucket_")
+    for weekday in range(1, 8):
+        with freeze_time(f"2023-02-{weekday} 12:00:00"):
+            client.put_object(Bucket="bucket_", Key="test.txt", Body=b"test")
+            obj = client.get_object(Bucket="bucket_", Key="test.txt")
+            assert obj["LastModified"].replace(tzinfo=None) == datetime.now()
+            # If we get here, the LastModified date will have been successfully parsed
+            # Regardless of which weekday it is
+
+    for month in range(1, 13):
+        with freeze_time(f"2023-{month}-02 12:00:00"):
+            client.put_object(Bucket="bucket_", Key="test.txt", Body=b"test")
+            obj = client.get_object(Bucket="bucket_", Key="test.txt")
+            assert obj["LastModified"].replace(tzinfo=None) == datetime.now()
+            # If we get here, the LastModified date will have been successfully parsed
+            # Regardless of which month it is
diff --git a/tests/test_sns/test_subscriptions_boto3.py b/tests/test_sns/test_subscriptions_boto3.py
index 700ccd32a..a63df5f50 100644
--- a/tests/test_sns/test_subscriptions_boto3.py
+++ b/tests/test_sns/test_subscriptions_boto3.py
@@ -393,6 +393,7 @@ def test_set_subscription_attributes():
     assert attrs["Attributes"]["RawMessageDelivery"] == "true"
     assert attrs["Attributes"]["DeliveryPolicy"] == delivery_policy
     assert attrs["Attributes"]["FilterPolicy"] == filter_policy
+    assert attrs["Attributes"]["FilterPolicyScope"] == "MessageAttributes"
 
     filter_policy_scope = "MessageBody"
     conn.set_subscription_attributes(
@@ -405,6 +406,17 @@ def test_set_subscription_attributes():
 
     assert attrs["Attributes"]["FilterPolicyScope"] == filter_policy_scope
 
+    # test unsetting a filter policy
+    conn.set_subscription_attributes(
+        SubscriptionArn=subscription_arn,
+        AttributeName="FilterPolicy",
+        AttributeValue="",
+    )
+
+    attrs = conn.get_subscription_attributes(SubscriptionArn=subscription_arn)
+    assert "FilterPolicy" not in attrs["Attributes"]
+    assert "FilterPolicyScope" not in attrs["Attributes"]
+
     # not existing subscription
     with pytest.raises(ClientError):
         conn.set_subscription_attributes(
@@ -413,7 +425,7 @@ def test_set_subscription_attributes():
             AttributeValue="true",
         )
     with pytest.raises(ClientError):
-        attrs = conn.get_subscription_attributes(SubscriptionArn="invalid")
+        conn.get_subscription_attributes(SubscriptionArn="invalid")
 
     # invalid attr name
     with pytest.raises(ClientError):