Repository: getmoto/moto
Pull request #8922 (2025-05-23 16:18:50)

[EC2] Add modify_instance_metadata_options support
This PR supports the following added capabilities:

- modify_instance_metadata_options

In the models/instances.py a MetadataOptions class was added. Figured it was cleaner to add a class rather a dict in the Instance's init function

Added "DryRun" to the modify_instance_metadata_options in case a user wants to pass it in. Mock support of it doesn't make a lot of sense so I didn't add handling for it. If wanted in the future it can be implemented.


Patch:
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index ec9c35299..38c0c03d9 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -22,7 +22,7 @@
 
 ## acm-pca
 <details>
-<summary>69% implemented</summary>
+<summary>73% implemented</summary>
 
 - [X] create_certificate_authority
 - [ ] create_certificate_authority_audit_report
@@ -38,7 +38,7 @@
 - [X] get_policy
 - [X] import_certificate_authority_certificate
 - [X] issue_certificate
-- [ ] list_certificate_authorities
+- [X] list_certificate_authorities
 - [ ] list_permissions
 - [X] list_tags
 - [X] put_policy
@@ -51,7 +51,7 @@
 
 ## amp
 <details>
-<summary>62% implemented</summary>
+<summary>58% implemented</summary>
 
 - [ ] create_alert_manager_definition
 - [X] create_logging_configuration
@@ -68,6 +68,7 @@
 - [X] describe_rule_groups_namespace
 - [ ] describe_scraper
 - [X] describe_workspace
+- [ ] describe_workspace_configuration
 - [ ] get_default_scraper_configuration
 - [X] list_rule_groups_namespaces
 - [ ] list_scrapers
@@ -80,6 +81,7 @@
 - [X] update_logging_configuration
 - [ ] update_scraper
 - [X] update_workspace_alias
+- [ ] update_workspace_configuration
 </details>
 
 ## apigateway
@@ -1169,20 +1171,25 @@
 
 ## cloudfront
 <details>
-<summary>18% implemented</summary>
+<summary>15% implemented</summary>
 
 - [ ] associate_alias
+- [ ] associate_distribution_tenant_web_acl
+- [ ] associate_distribution_web_acl
 - [ ] copy_distribution
 - [ ] create_anycast_ip_list
 - [ ] create_cache_policy
 - [ ] create_cloud_front_origin_access_identity
+- [ ] create_connection_group
 - [ ] create_continuous_deployment_policy
 - [X] create_distribution
+- [ ] create_distribution_tenant
 - [X] create_distribution_with_tags
 - [ ] create_field_level_encryption_config
 - [ ] create_field_level_encryption_profile
 - [ ] create_function
 - [X] create_invalidation
+- [ ] create_invalidation_for_distribution_tenant
 - [X] create_key_group
 - [ ] create_key_value_store
 - [ ] create_monitoring_subscription
@@ -1197,8 +1204,10 @@
 - [ ] delete_anycast_ip_list
 - [ ] delete_cache_policy
 - [ ] delete_cloud_front_origin_access_identity
+- [ ] delete_connection_group
 - [ ] delete_continuous_deployment_policy
 - [X] delete_distribution
+- [ ] delete_distribution_tenant
 - [ ] delete_field_level_encryption_config
 - [ ] delete_field_level_encryption_profile
 - [ ] delete_function
@@ -1214,23 +1223,31 @@
 - [ ] delete_vpc_origin
 - [ ] describe_function
 - [ ] describe_key_value_store
+- [ ] disassociate_distribution_tenant_web_acl
+- [ ] disassociate_distribution_web_acl
 - [ ] get_anycast_ip_list
 - [ ] get_cache_policy
 - [ ] get_cache_policy_config
 - [ ] get_cloud_front_origin_access_identity
 - [ ] get_cloud_front_origin_access_identity_config
+- [ ] get_connection_group
+- [ ] get_connection_group_by_routing_endpoint
 - [ ] get_continuous_deployment_policy
 - [ ] get_continuous_deployment_policy_config
 - [X] get_distribution
 - [X] get_distribution_config
+- [ ] get_distribution_tenant
+- [ ] get_distribution_tenant_by_domain
 - [ ] get_field_level_encryption
 - [ ] get_field_level_encryption_config
 - [ ] get_field_level_encryption_profile
 - [ ] get_field_level_encryption_profile_config
 - [ ] get_function
 - [X] get_invalidation
+- [ ] get_invalidation_for_distribution_tenant
 - [X] get_key_group
 - [ ] get_key_group_config
+- [ ] get_managed_certificate_details
 - [ ] get_monitoring_subscription
 - [X] get_origin_access_control
 - [ ] get_origin_access_control_config
@@ -1248,20 +1265,26 @@
 - [ ] list_cache_policies
 - [ ] list_cloud_front_origin_access_identities
 - [ ] list_conflicting_aliases
+- [ ] list_connection_groups
 - [ ] list_continuous_deployment_policies
+- [ ] list_distribution_tenants
+- [ ] list_distribution_tenants_by_customization
 - [X] list_distributions
 - [ ] list_distributions_by_anycast_ip_list_id
 - [ ] list_distributions_by_cache_policy_id
+- [ ] list_distributions_by_connection_mode
 - [ ] list_distributions_by_key_group
 - [ ] list_distributions_by_origin_request_policy_id
 - [ ] list_distributions_by_realtime_log_config
 - [ ] list_distributions_by_response_headers_policy_id
 - [ ] list_distributions_by_vpc_origin_id
 - [ ] list_distributions_by_web_acl_id
+- [ ] list_domain_conflicts
 - [ ] list_field_level_encryption_configs
 - [ ] list_field_level_encryption_profiles
 - [ ] list_functions
 - [X] list_invalidations
+- [ ] list_invalidations_for_distribution_tenant
 - [X] list_key_groups
 - [ ] list_key_value_stores
 - [X] list_origin_access_controls
@@ -1278,9 +1301,12 @@
 - [ ] untag_resource
 - [ ] update_cache_policy
 - [ ] update_cloud_front_origin_access_identity
+- [ ] update_connection_group
 - [ ] update_continuous_deployment_policy
 - [X] update_distribution
+- [ ] update_distribution_tenant
 - [ ] update_distribution_with_staging_config
+- [ ] update_domain_association
 - [ ] update_field_level_encryption_config
 - [ ] update_field_level_encryption_profile
 - [ ] update_function
@@ -1293,6 +1319,7 @@
 - [ ] update_response_headers_policy
 - [ ] update_streaming_distribution
 - [ ] update_vpc_origin
+- [ ] verify_dns_configuration
 </details>
 
 ## cloudhsmv2
@@ -1779,6 +1806,7 @@
 - [ ] get_identity_provider_by_identifier
 - [ ] get_log_delivery_configuration
 - [ ] get_signing_certificate
+- [ ] get_tokens_from_refresh_token
 - [ ] get_ui_customization
 - [X] get_user
 - [ ] get_user_attribute_verification_code
@@ -2020,6 +2048,34 @@
 - [X] untag_resource
 </details>
 
+## connectcampaigns
+<details>
+<summary>22% implemented</summary>
+
+- [X] create_campaign
+- [X] delete_campaign
+- [ ] delete_connect_instance_config
+- [ ] delete_instance_onboarding_job
+- [X] describe_campaign
+- [ ] get_campaign_state
+- [ ] get_campaign_state_batch
+- [X] get_connect_instance_config
+- [ ] get_instance_onboarding_job_status
+- [ ] list_campaigns
+- [ ] list_tags_for_resource
+- [ ] pause_campaign
+- [ ] put_dial_request_batch
+- [ ] resume_campaign
+- [ ] start_campaign
+- [X] start_instance_onboarding_job
+- [ ] stop_campaign
+- [ ] tag_resource
+- [ ] untag_resource
+- [ ] update_campaign_dialer_config
+- [ ] update_campaign_name
+- [ ] update_campaign_outbound_call_config
+</details>
+
 ## databrew
 <details>
 <summary>54% implemented</summary>
@@ -2459,13 +2515,14 @@
 
 ## dsql
 <details>
-<summary>20% implemented</summary>
+<summary>18% implemented</summary>
 
 - [X] create_cluster
 - [ ] create_multi_region_clusters
 - [ ] delete_cluster
 - [ ] delete_multi_region_clusters
 - [X] get_cluster
+- [ ] get_vpc_endpoint_service_name
 - [ ] list_clusters
 - [ ] list_tags_for_resource
 - [ ] tag_resource
@@ -2560,7 +2617,7 @@
 
 ## ec2
 <details>
-<summary>31% implemented</summary>
+<summary>30% implemented</summary>
 
 - [ ] accept_address_transfer
 - [ ] accept_capacity_reservation_billing_ownership
@@ -2655,6 +2712,8 @@
 - [ ] create_local_gateway_route_table
 - [ ] create_local_gateway_route_table_virtual_interface_group_association
 - [ ] create_local_gateway_route_table_vpc_association
+- [ ] create_local_gateway_virtual_interface
+- [ ] create_local_gateway_virtual_interface_group
 - [X] create_managed_prefix_list
 - [X] create_nat_gateway
 - [X] create_network_acl
@@ -2736,6 +2795,8 @@
 - [ ] delete_local_gateway_route_table
 - [ ] delete_local_gateway_route_table_virtual_interface_group_association
 - [ ] delete_local_gateway_route_table_vpc_association
+- [ ] delete_local_gateway_virtual_interface
+- [ ] delete_local_gateway_virtual_interface_group
 - [X] delete_managed_prefix_list
 - [X] delete_nat_gateway
 - [X] delete_network_acl
@@ -2888,6 +2949,7 @@
 - [ ] describe_network_interface_attribute
 - [ ] describe_network_interface_permissions
 - [X] describe_network_interfaces
+- [ ] describe_outpost_lags
 - [ ] describe_placement_groups
 - [ ] describe_prefix_lists
 - [ ] describe_principal_id_format
@@ -2908,6 +2970,7 @@
 - [X] describe_security_group_rules
 - [ ] describe_security_group_vpc_associations
 - [X] describe_security_groups
+- [ ] describe_service_link_virtual_interfaces
 - [ ] describe_snapshot_attribute
 - [ ] describe_snapshot_tier_status
 - [X] describe_snapshots
@@ -3304,7 +3367,7 @@
 
 ## ecs
 <details>
-<summary>74% implemented</summary>
+<summary>73% implemented</summary>
 
 - [X] create_capacity_provider
 - [X] create_cluster
@@ -3350,6 +3413,7 @@
 - [X] register_task_definition
 - [X] run_task
 - [X] start_task
+- [ ] stop_service_deployment
 - [X] stop_task
 - [ ] submit_attachment_state_changes
 - [ ] submit_container_state_change
@@ -5213,7 +5277,7 @@
 
 ## kinesis
 <details>
-<summary>93% implemented</summary>
+<summary>85% implemented</summary>
 
 - [X] add_tags_to_stream
 - [X] create_stream
@@ -5234,6 +5298,7 @@
 - [X] list_shards
 - [X] list_stream_consumers
 - [X] list_streams
+- [ ] list_tags_for_resource
 - [X] list_tags_for_stream
 - [X] merge_shards
 - [X] put_record
@@ -5245,6 +5310,8 @@
 - [X] start_stream_encryption
 - [X] stop_stream_encryption
 - [ ] subscribe_to_shard
+- [ ] tag_resource
+- [ ] untag_resource
 - [X] update_shard_count
 - [X] update_stream_mode
 </details>
@@ -6079,13 +6146,14 @@
 
 ## mq
 <details>
-<summary>82% implemented</summary>
+<summary>79% implemented</summary>
 
 - [X] create_broker
 - [X] create_configuration
 - [X] create_tags
 - [X] create_user
 - [X] delete_broker
+- [ ] delete_configuration
 - [X] delete_tags
 - [X] delete_user
 - [X] describe_broker
@@ -7115,7 +7183,7 @@
 
 ## ram
 <details>
-<summary>14% implemented</summary>
+<summary>17% implemented</summary>
 
 - [ ] accept_resource_share_invitation
 - [ ] associate_resource_share
@@ -7131,7 +7199,7 @@
 - [X] enable_sharing_with_aws_organization
 - [ ] get_permission
 - [ ] get_resource_policies
-- [ ] get_resource_share_associations
+- [X] get_resource_share_associations
 - [ ] get_resource_share_invitations
 - [X] get_resource_shares
 - [ ] list_pending_invitation_resources
@@ -8156,7 +8224,7 @@
 
 ## s3tables
 <details>
-<summary>53% implemented</summary>
+<summary>46% implemented</summary>
 
 - [X] create_namespace
 - [X] create_table
@@ -8164,13 +8232,16 @@
 - [X] delete_namespace
 - [X] delete_table
 - [X] delete_table_bucket
+- [ ] delete_table_bucket_encryption
 - [ ] delete_table_bucket_policy
 - [ ] delete_table_policy
 - [X] get_namespace
 - [X] get_table
 - [X] get_table_bucket
+- [ ] get_table_bucket_encryption
 - [ ] get_table_bucket_maintenance_configuration
 - [ ] get_table_bucket_policy
+- [ ] get_table_encryption
 - [ ] get_table_maintenance_configuration
 - [ ] get_table_maintenance_job_status
 - [ ] get_table_metadata_location
@@ -8178,6 +8249,7 @@
 - [X] list_namespaces
 - [X] list_table_buckets
 - [X] list_tables
+- [ ] put_table_bucket_encryption
 - [ ] put_table_bucket_maintenance_configuration
 - [ ] put_table_bucket_policy
 - [ ] put_table_maintenance_configuration
@@ -9293,6 +9365,7 @@
 - [ ] describe_patch_properties
 - [ ] describe_sessions
 - [ ] disassociate_ops_item_related_item
+- [ ] get_access_token
 - [ ] get_automation_execution
 - [ ] get_calendar_state
 - [X] get_command_invocation
@@ -9352,6 +9425,7 @@
 - [ ] resume_session
 - [ ] send_automation_signal
 - [X] send_command
+- [ ] start_access_request
 - [ ] start_associations_once
 - [ ] start_automation_execution
 - [ ] start_change_request_execution
@@ -10098,7 +10172,6 @@
 - compute-optimizer
 - connect
 - connect-contact-lens
-- connectcampaigns
 - connectcampaignsv2
 - connectcases
 - connectparticipant
@@ -10264,6 +10337,7 @@
 - snowball
 - socialmessaging
 - ssm-contacts
+- ssm-guiconnect
 - ssm-incidents
 - ssm-quicksetup
 - ssm-sap
diff --git a/docs/docs/services/acm-pca.rst b/docs/docs/services/acm-pca.rst
index ab2cf0d00..1079e8fdb 100644
--- a/docs/docs/services/acm-pca.rst
+++ b/docs/docs/services/acm-pca.rst
@@ -51,7 +51,11 @@ acm-pca
         Some fields of the resulting certificate will have default values, instead of using the CSR
         
 
-- [ ] list_certificate_authorities
+- [X] list_certificate_authorities
+  
+        Lists the private certificate authorities that you created by using the CreateCertificateAuthority action.
+        
+
 - [ ] list_permissions
 - [X] list_tags
   
diff --git a/docs/docs/services/amp.rst b/docs/docs/services/amp.rst
index 016e4dbfb..471e3ec1a 100644
--- a/docs/docs/services/amp.rst
+++ b/docs/docs/services/amp.rst
@@ -47,6 +47,7 @@ amp
 - [X] describe_rule_groups_namespace
 - [ ] describe_scraper
 - [X] describe_workspace
+- [ ] describe_workspace_configuration
 - [ ] get_default_scraper_configuration
 - [X] list_rule_groups_namespaces
 - [ ] list_scrapers
@@ -67,4 +68,5 @@ amp
         The ClientToken-parameter is not yet implemented
         
 
+- [ ] update_workspace_configuration
 
diff --git a/docs/docs/services/cloudfront.rst b/docs/docs/services/cloudfront.rst
index 4a9c0f636..93bdaf7bf 100644
--- a/docs/docs/services/cloudfront.rst
+++ b/docs/docs/services/cloudfront.rst
@@ -15,10 +15,13 @@ cloudfront
 |start-h3| Implemented features for this service |end-h3|
 
 - [ ] associate_alias
+- [ ] associate_distribution_tenant_web_acl
+- [ ] associate_distribution_web_acl
 - [ ] copy_distribution
 - [ ] create_anycast_ip_list
 - [ ] create_cache_policy
 - [ ] create_cloud_front_origin_access_identity
+- [ ] create_connection_group
 - [ ] create_continuous_deployment_policy
 - [X] create_distribution
   
@@ -27,11 +30,13 @@ cloudfront
         use-case.
         
 
+- [ ] create_distribution_tenant
 - [X] create_distribution_with_tags
 - [ ] create_field_level_encryption_config
 - [ ] create_field_level_encryption_profile
 - [ ] create_function
 - [X] create_invalidation
+- [ ] create_invalidation_for_distribution_tenant
 - [X] create_key_group
 - [ ] create_key_value_store
 - [ ] create_monitoring_subscription
@@ -46,6 +51,7 @@ cloudfront
 - [ ] delete_anycast_ip_list
 - [ ] delete_cache_policy
 - [ ] delete_cloud_front_origin_access_identity
+- [ ] delete_connection_group
 - [ ] delete_continuous_deployment_policy
 - [X] delete_distribution
   
@@ -53,6 +59,7 @@ cloudfront
         Calling this function without a value is invalid, per AWS' behaviour
         
 
+- [ ] delete_distribution_tenant
 - [ ] delete_field_level_encryption_config
 - [ ] delete_field_level_encryption_profile
 - [ ] delete_function
@@ -76,23 +83,31 @@ cloudfront
 - [ ] delete_vpc_origin
 - [ ] describe_function
 - [ ] describe_key_value_store
+- [ ] disassociate_distribution_tenant_web_acl
+- [ ] disassociate_distribution_web_acl
 - [ ] get_anycast_ip_list
 - [ ] get_cache_policy
 - [ ] get_cache_policy_config
 - [ ] get_cloud_front_origin_access_identity
 - [ ] get_cloud_front_origin_access_identity_config
+- [ ] get_connection_group
+- [ ] get_connection_group_by_routing_endpoint
 - [ ] get_continuous_deployment_policy
 - [ ] get_continuous_deployment_policy_config
 - [X] get_distribution
 - [X] get_distribution_config
+- [ ] get_distribution_tenant
+- [ ] get_distribution_tenant_by_domain
 - [ ] get_field_level_encryption
 - [ ] get_field_level_encryption_config
 - [ ] get_field_level_encryption_profile
 - [ ] get_field_level_encryption_profile_config
 - [ ] get_function
 - [X] get_invalidation
+- [ ] get_invalidation_for_distribution_tenant
 - [X] get_key_group
 - [ ] get_key_group_config
+- [ ] get_managed_certificate_details
 - [ ] get_monitoring_subscription
 - [X] get_origin_access_control
 - [ ] get_origin_access_control_config
@@ -110,7 +125,10 @@ cloudfront
 - [ ] list_cache_policies
 - [ ] list_cloud_front_origin_access_identities
 - [ ] list_conflicting_aliases
+- [ ] list_connection_groups
 - [ ] list_continuous_deployment_policies
+- [ ] list_distribution_tenants
+- [ ] list_distribution_tenants_by_customization
 - [X] list_distributions
   
         Pagination is not supported yet.
@@ -118,12 +136,14 @@ cloudfront
 
 - [ ] list_distributions_by_anycast_ip_list_id
 - [ ] list_distributions_by_cache_policy_id
+- [ ] list_distributions_by_connection_mode
 - [ ] list_distributions_by_key_group
 - [ ] list_distributions_by_origin_request_policy_id
 - [ ] list_distributions_by_realtime_log_config
 - [ ] list_distributions_by_response_headers_policy_id
 - [ ] list_distributions_by_vpc_origin_id
 - [ ] list_distributions_by_web_acl_id
+- [ ] list_domain_conflicts
 - [ ] list_field_level_encryption_configs
 - [ ] list_field_level_encryption_profiles
 - [ ] list_functions
@@ -132,6 +152,7 @@ cloudfront
         Pagination is not yet implemented
         
 
+- [ ] list_invalidations_for_distribution_tenant
 - [X] list_key_groups
   
         Pagination is not yet implemented
@@ -160,6 +181,7 @@ cloudfront
 - [ ] untag_resource
 - [ ] update_cache_policy
 - [ ] update_cloud_front_origin_access_identity
+- [ ] update_connection_group
 - [ ] update_continuous_deployment_policy
 - [X] update_distribution
   
@@ -167,7 +189,9 @@ cloudfront
         Calling this function without a value is invalid, per AWS' behaviour
         
 
+- [ ] update_distribution_tenant
 - [ ] update_distribution_with_staging_config
+- [ ] update_domain_association
 - [ ] update_field_level_encryption_config
 - [ ] update_field_level_encryption_profile
 - [ ] update_function
@@ -184,4 +208,5 @@ cloudfront
 - [ ] update_response_headers_policy
 - [ ] update_streaming_distribution
 - [ ] update_vpc_origin
+- [ ] verify_dns_configuration
 
diff --git a/docs/docs/services/cognito-idp.rst b/docs/docs/services/cognito-idp.rst
index b36f71d62..564919126 100644
--- a/docs/docs/services/cognito-idp.rst
+++ b/docs/docs/services/cognito-idp.rst
@@ -94,6 +94,7 @@ cognito-idp
 - [ ] get_identity_provider_by_identifier
 - [ ] get_log_delivery_configuration
 - [ ] get_signing_certificate
+- [ ] get_tokens_from_refresh_token
 - [ ] get_ui_customization
 - [X] get_user
 - [ ] get_user_attribute_verification_code
diff --git a/docs/docs/services/connectcampaigns.rst b/docs/docs/services/connectcampaigns.rst
new file mode 100644
index 000000000..3b10d80e7
--- /dev/null
+++ b/docs/docs/services/connectcampaigns.rst
@@ -0,0 +1,39 @@
+.. _implementedservice_connectcampaigns:
+
+.. |start-h3| raw:: html
+
+    <h3>
+
+.. |end-h3| raw:: html
+
+    </h3>
+
+================
+connectcampaigns
+================
+
+|start-h3| Implemented features for this service |end-h3|
+
+- [X] create_campaign
+- [X] delete_campaign
+- [ ] delete_connect_instance_config
+- [ ] delete_instance_onboarding_job
+- [X] describe_campaign
+- [ ] get_campaign_state
+- [ ] get_campaign_state_batch
+- [X] get_connect_instance_config
+- [ ] get_instance_onboarding_job_status
+- [ ] list_campaigns
+- [ ] list_tags_for_resource
+- [ ] pause_campaign
+- [ ] put_dial_request_batch
+- [ ] resume_campaign
+- [ ] start_campaign
+- [X] start_instance_onboarding_job
+- [ ] stop_campaign
+- [ ] tag_resource
+- [ ] untag_resource
+- [ ] update_campaign_dialer_config
+- [ ] update_campaign_name
+- [ ] update_campaign_outbound_call_config
+
diff --git a/docs/docs/services/dsql.rst b/docs/docs/services/dsql.rst
index 02bdadf65..06a589c17 100644
--- a/docs/docs/services/dsql.rst
+++ b/docs/docs/services/dsql.rst
@@ -21,6 +21,7 @@ dsql
 - [ ] delete_cluster
 - [ ] delete_multi_region_clusters
 - [X] get_cluster
+- [ ] get_vpc_endpoint_service_name
 - [ ] list_clusters
 - [ ] list_tags_for_resource
 - [ ] tag_resource
diff --git a/docs/docs/services/ec2.rst b/docs/docs/services/ec2.rst
index 70dbc12ad..96ae2907b 100644
--- a/docs/docs/services/ec2.rst
+++ b/docs/docs/services/ec2.rst
@@ -109,6 +109,8 @@ ec2
 - [ ] create_local_gateway_route_table
 - [ ] create_local_gateway_route_table_virtual_interface_group_association
 - [ ] create_local_gateway_route_table_vpc_association
+- [ ] create_local_gateway_virtual_interface
+- [ ] create_local_gateway_virtual_interface_group
 - [X] create_managed_prefix_list
 - [X] create_nat_gateway
 - [X] create_network_acl
@@ -194,6 +196,8 @@ ec2
 - [ ] delete_local_gateway_route_table
 - [ ] delete_local_gateway_route_table_virtual_interface_group_association
 - [ ] delete_local_gateway_route_table_vpc_association
+- [ ] delete_local_gateway_virtual_interface
+- [ ] delete_local_gateway_virtual_interface_group
 - [X] delete_managed_prefix_list
 - [X] delete_nat_gateway
 - [X] delete_network_acl
@@ -359,6 +363,7 @@ ec2
 - [ ] describe_network_interface_attribute
 - [ ] describe_network_interface_permissions
 - [X] describe_network_interfaces
+- [ ] describe_outpost_lags
 - [ ] describe_placement_groups
 - [ ] describe_prefix_lists
 - [ ] describe_principal_id_format
@@ -379,6 +384,7 @@ ec2
 - [X] describe_security_group_rules
 - [ ] describe_security_group_vpc_associations
 - [X] describe_security_groups
+- [ ] describe_service_link_virtual_interfaces
 - [ ] describe_snapshot_attribute
 - [ ] describe_snapshot_tier_status
 - [X] describe_snapshots
diff --git a/docs/docs/services/ecs.rst b/docs/docs/services/ecs.rst
index 325e1fd41..91f6b1a5e 100644
--- a/docs/docs/services/ecs.rst
+++ b/docs/docs/services/ecs.rst
@@ -86,6 +86,7 @@ ecs
 - [X] register_task_definition
 - [X] run_task
 - [X] start_task
+- [ ] stop_service_deployment
 - [X] stop_task
 - [ ] submit_attachment_state_changes
 - [ ] submit_container_state_change
diff --git a/docs/docs/services/kinesis.rst b/docs/docs/services/kinesis.rst
index cae31fc63..eff51d9b6 100644
--- a/docs/docs/services/kinesis.rst
+++ b/docs/docs/services/kinesis.rst
@@ -45,6 +45,7 @@ kinesis
         
 
 - [X] list_streams
+- [ ] list_tags_for_resource
 - [X] list_tags_for_stream
 - [X] merge_shards
 - [X] put_record
@@ -60,6 +61,8 @@ kinesis
 - [X] start_stream_encryption
 - [X] stop_stream_encryption
 - [ ] subscribe_to_shard
+- [ ] tag_resource
+- [ ] untag_resource
 - [X] update_shard_count
 - [X] update_stream_mode
 
diff --git a/docs/docs/services/mq.rst b/docs/docs/services/mq.rst
index 41b08faaf..a3970df21 100644
--- a/docs/docs/services/mq.rst
+++ b/docs/docs/services/mq.rst
@@ -21,6 +21,7 @@ mq
 - [X] create_tags
 - [X] create_user
 - [X] delete_broker
+- [ ] delete_configuration
 - [X] delete_tags
 - [X] delete_user
 - [X] describe_broker
diff --git a/docs/docs/services/ram.rst b/docs/docs/services/ram.rst
index f78561123..546f9704d 100644
--- a/docs/docs/services/ram.rst
+++ b/docs/docs/services/ram.rst
@@ -28,7 +28,7 @@ ram
 - [X] enable_sharing_with_aws_organization
 - [ ] get_permission
 - [ ] get_resource_policies
-- [ ] get_resource_share_associations
+- [X] get_resource_share_associations
 - [ ] get_resource_share_invitations
 - [X] get_resource_shares
 - [ ] list_pending_invitation_resources
diff --git a/docs/docs/services/s3tables.rst b/docs/docs/services/s3tables.rst
index 4ad3d48ca..03e0a72fd 100644
--- a/docs/docs/services/s3tables.rst
+++ b/docs/docs/services/s3tables.rst
@@ -22,13 +22,16 @@ s3tables
 - [X] delete_namespace
 - [X] delete_table
 - [X] delete_table_bucket
+- [ ] delete_table_bucket_encryption
 - [ ] delete_table_bucket_policy
 - [ ] delete_table_policy
 - [X] get_namespace
 - [X] get_table
 - [X] get_table_bucket
+- [ ] get_table_bucket_encryption
 - [ ] get_table_bucket_maintenance_configuration
 - [ ] get_table_bucket_policy
+- [ ] get_table_encryption
 - [ ] get_table_maintenance_configuration
 - [ ] get_table_maintenance_job_status
 - [ ] get_table_metadata_location
@@ -36,6 +39,7 @@ s3tables
 - [X] list_namespaces
 - [X] list_table_buckets
 - [X] list_tables
+- [ ] put_table_bucket_encryption
 - [ ] put_table_bucket_maintenance_configuration
 - [ ] put_table_bucket_policy
 - [ ] put_table_maintenance_configuration
diff --git a/docs/docs/services/ssm.rst b/docs/docs/services/ssm.rst
index 89ad40030..6a710fce6 100644
--- a/docs/docs/services/ssm.rst
+++ b/docs/docs/services/ssm.rst
@@ -117,6 +117,7 @@ ssm
 - [ ] describe_patch_properties
 - [ ] describe_sessions
 - [ ] disassociate_ops_item_related_item
+- [ ] get_access_token
 - [ ] get_automation_execution
 - [ ] get_calendar_state
 - [X] get_command_invocation
@@ -190,6 +191,7 @@ ssm
 - [ ] resume_session
 - [ ] send_automation_signal
 - [X] send_command
+- [ ] start_access_request
 - [ ] start_associations_once
 - [ ] start_automation_execution
 - [ ] start_change_request_execution
diff --git a/docs/docs/services/textract.rst b/docs/docs/services/textract.rst
index df84f8847..7ca7df723 100644
--- a/docs/docs/services/textract.rst
+++ b/docs/docs/services/textract.rst
@@ -45,7 +45,7 @@ textract
 
 - [X] start_document_text_detection
   
-        The following parameters have not yet been implemented: ClientRequestToken, JobTag, NotificationChannel, OutputConfig, KmsKeyID
+        The following parameters have not yet been implemented: ClientRequestToken, JobTag, OutputConfig, KmsKeyID
         
 
 - [ ] start_expense_analysis
diff --git a/moto/backend_index.py b/moto/backend_index.py
index 6fa4b41e2..4ce527433 100644
--- a/moto/backend_index.py
+++ b/moto/backend_index.py
@@ -42,6 +42,10 @@ backend_url_patterns = [
     ("cognitoidp", re.compile("https?://cognito-idp\\.(.+)\\.amazonaws.com")),
     ("comprehend", re.compile("https?://comprehend\\.(.+)\\.amazonaws\\.com")),
     ("config", re.compile("https?://config\\.(.+)\\.amazonaws\\.com")),
+    (
+        "connectcampaigns",
+        re.compile("https?://connect-campaigns\\.(.+)\\.amazonaws\\.com"),
+    ),
     ("databrew", re.compile("https?://databrew\\.(.+)\\.amazonaws.com")),
     ("datapipeline", re.compile("https?://datapipeline\\.(.+)\\.amazonaws\\.com")),
     ("datasync", re.compile("https?://(.*\\.)?(datasync)\\.(.+)\\.amazonaws.com")),
diff --git a/moto/codedeploy/models.py b/moto/codedeploy/models.py
index 14976f5e6..80199a9e0 100644
--- a/moto/codedeploy/models.py
+++ b/moto/codedeploy/models.py
@@ -7,6 +7,7 @@ from typing import Any, Dict, List, Optional
 from moto.core.base_backend import BackendDict, BaseBackend
 from moto.core.common_models import BaseModel
 from moto.core.utils import iso_8601_datetime_with_milliseconds
+from moto.utilities.tagging_service import TaggingService
 
 from .exceptions import (
     ApplicationAlreadyExistsException,
@@ -109,7 +110,7 @@ class DeploymentGroup(BaseModel):
         self.ec2_tag_set = ec2_tag_set or {}
         self.ecs_services = ecs_services or []
         self.on_premises_tag_set = on_premises_tag_set or {}
-        self.tags = tags
+        self.tags = tags or []
         self.termination_hook_enabled = termination_hook_enabled
         self.deployment_group_id = str(uuid.uuid4())
 
@@ -242,6 +243,7 @@ class CodeDeployBackend(BaseBackend):
         self.applications: Dict[str, Application] = {}
         self.deployments: Dict[str, DeploymentInfo] = {}
         self.deployment_groups: Dict[str, Dict[str, DeploymentGroup]] = {}
+        self.tagger = TaggingService()
 
     def get_application(self, application_name: str) -> Application:
         if application_name not in self.applications:
@@ -302,6 +304,11 @@ class CodeDeployBackend(BaseBackend):
 
         app = Application(application_name, compute_platform, tags)
         self.applications[app.application_name] = app
+
+        if tags:
+            app_arn = f"arn:aws:codedeploy:{self.region_name}:{self.account_id}:application:{application_name}"
+            self.tagger.tag_resource(app_arn, tags)
+
         return app.id
 
     def create_deployment(
@@ -362,6 +369,14 @@ class CodeDeployBackend(BaseBackend):
         )
 
         self.deployments[deployment.deployment_id] = deployment
+
+        deployment_arn = f"arn:aws:codedeploy:{self.region_name}:{self.account_id}:deployment:{deployment.deployment_id}"
+        if self.deployment_groups[application_name][deployment_group_name].tags:
+            self.tagger.tag_resource(
+                deployment_arn,
+                self.deployment_groups[application_name][deployment_group_name].tags,
+            )
+
         return deployment.deployment_id
 
     # TODO support all optional fields
@@ -427,6 +442,10 @@ class CodeDeployBackend(BaseBackend):
             self.deployment_groups[application_name] = {}
         self.deployment_groups[application_name][dg.deployment_group_name] = dg
 
+        if tags:
+            dg_arn = f"arn:aws:codedeploy:{self.region_name}:{self.account_id}:deploymentgroup:{application_name}/{deployment_group_name}"
+            self.tagger.tag_resource(dg_arn, tags)
+
         return dg.deployment_group_id
 
     # TODO: implement pagination
@@ -492,5 +511,20 @@ class CodeDeployBackend(BaseBackend):
             for deployment_group in self.deployment_groups[application_name].values()
         ]
 
+    def list_tags_for_resource(
+        self, resource_arn: str
+    ) -> Dict[str, List[Dict[str, str]]]:
+        return self.tagger.list_tags_for_resource(resource_arn)
+
+    def tag_resource(
+        self, resource_arn: str, tags: List[Dict[str, str]]
+    ) -> Dict[str, Any]:
+        self.tagger.tag_resource(resource_arn, tags)
+        return {}
+
+    def untag_resource(self, resource_arn: str, tag_keys: List[str]) -> Dict[str, Any]:
+        self.tagger.untag_resource_using_names(resource_arn, tag_keys)
+        return {}
+
 
 codedeploy_backends = BackendDict(CodeDeployBackend, "codedeploy")
diff --git a/moto/codedeploy/responses.py b/moto/codedeploy/responses.py
index 24f4df486..5d523dabb 100644
--- a/moto/codedeploy/responses.py
+++ b/moto/codedeploy/responses.py
@@ -185,3 +185,23 @@ class CodeDeployResponse(BaseResponse):
                 nextToken=next_token,
             )
         )
+
+    def list_tags_for_resource(self) -> str:
+        """Handler for list_tags_for_resource API call."""
+        resource_arn = self._get_param("ResourceArn")
+        tags_response = self.codedeploy_backend.list_tags_for_resource(resource_arn)
+        return json.dumps(tags_response)
+
+    def tag_resource(self) -> str:
+        """Handler for tag_resource API call."""
+        resource_arn = self._get_param("ResourceArn")
+        tags = self._get_param("Tags")
+        response = self.codedeploy_backend.tag_resource(resource_arn, tags)
+        return json.dumps(response)
+
+    def untag_resource(self) -> str:
+        """Handler for untag_resource API call."""
+        resource_arn = self._get_param("ResourceArn")
+        tag_keys = self._get_param("TagKeys")
+        response = self.codedeploy_backend.untag_resource(resource_arn, tag_keys)
+        return json.dumps(response)
diff --git a/moto/codedeploy/urls.py b/moto/codedeploy/urls.py
index afd141339..f37d6c967 100644
--- a/moto/codedeploy/urls.py
+++ b/moto/codedeploy/urls.py
@@ -8,4 +8,7 @@ url_bases = [
 
 url_paths = {
     "{0}/$": CodeDeployResponse.dispatch,
+    "{0}/list-tags-for-resource$": CodeDeployResponse.list_tags_for_resource,
+    "{0}/tag-resource$": CodeDeployResponse.tag_resource,
+    "{0}/untag-resource$": CodeDeployResponse.untag_resource,
 }
diff --git a/moto/connectcampaigns/__init__.py b/moto/connectcampaigns/__init__.py
new file mode 100644
index 000000000..d83b5f7d2
--- /dev/null
+++ b/moto/connectcampaigns/__init__.py
@@ -0,0 +1,1 @@
+from .models import connectcampaigns_backends  # noqa: F401
diff --git a/moto/connectcampaigns/exceptions.py b/moto/connectcampaigns/exceptions.py
new file mode 100644
index 000000000..06f065ea6
--- /dev/null
+++ b/moto/connectcampaigns/exceptions.py
@@ -0,0 +1,21 @@
+"""Exceptions raised by the connectcampaigns service."""
+
+from moto.core.exceptions import JsonRESTError
+
+
+class ResourceNotFoundException(JsonRESTError):
+    """When a resource is not found."""
+
+    code = 404
+
+    def __init__(self, message: str) -> None:
+        super().__init__("ResourceNotFoundException", message)
+
+
+class ValidationException(JsonRESTError):
+    """When validation fails on input parameters."""
+
+    code = 400
+
+    def __init__(self, message: str) -> None:
+        super().__init__("ValidationException", message)
diff --git a/moto/connectcampaigns/models.py b/moto/connectcampaigns/models.py
new file mode 100644
index 000000000..3dc5c29c5
--- /dev/null
+++ b/moto/connectcampaigns/models.py
@@ -0,0 +1,189 @@
+"""ConnectCampaignServiceBackend class with methods for supported APIs."""
+
+import uuid
+from typing import Any, Dict, Optional, Tuple
+
+from moto.core import DEFAULT_ACCOUNT_ID
+from moto.core.base_backend import BackendDict, BaseBackend
+from moto.core.common_models import BaseModel
+
+from .exceptions import ResourceNotFoundException, ValidationException
+
+
+class ConnectCampaign(BaseModel):
+    def __init__(
+        self,
+        name: str,
+        connect_instance_id: str,
+        dialer_config: Dict[str, Any],
+        outbound_call_config: Dict[str, Any],
+        region: str,
+        tags: Optional[Dict[str, str]] = None,
+    ) -> None:
+        self.id = str(uuid.uuid4())
+        self.name = name
+        self.connect_instance_id = connect_instance_id
+        self.dialer_config = dialer_config
+        self.outbound_call_config = outbound_call_config
+        self.region = region
+        self.tags = tags or {}
+        self.arn = f"arn:aws:connectcampaigns:{self.region}:{DEFAULT_ACCOUNT_ID}:campaign/{self.id}"
+
+    def to_dict(self) -> Dict[str, Any]:
+        return {
+            "id": self.id,
+            "arn": self.arn,
+            "name": self.name,
+            "connectInstanceId": self.connect_instance_id,
+            "dialerConfig": self.dialer_config,
+            "outboundCallConfig": self.outbound_call_config,
+            "tags": self.tags,
+        }
+
+
+class ConnectInstanceConfig(BaseModel):
+    def __init__(
+        self, connect_instance_id: str, region: str, encryption_enabled: bool = False
+    ) -> None:
+        self.connect_instance_id = connect_instance_id
+        self.region = region
+        self.service_linked_role_arn = f"arn:aws:iam::{DEFAULT_ACCOUNT_ID}:role/aws-service-role/connectcampaigns.amazonaws.com/AWSServiceRoleForConnectCampaigns"
+
+        self.encryption_config = {
+            "enabled": encryption_enabled,
+            "encryptionType": "KMS",
+        }
+
+        if encryption_enabled:
+            self.encryption_config["keyArn"] = (
+                f"arn:aws:kms:{region}:{DEFAULT_ACCOUNT_ID}:key/1234abcd-12ab-34cd-56ef-1234567890ab"
+            )
+
+    def to_dict(self) -> Dict[str, Any]:
+        return {
+            "connectInstanceId": self.connect_instance_id,
+            "serviceLinkedRoleArn": self.service_linked_role_arn,
+            "encryptionConfig": self.encryption_config,
+        }
+
+
+class ConnectInstanceOnboardingJobStatus(BaseModel):
+    def __init__(
+        self,
+        connect_instance_id: str,
+        status: str = "SUCCEEDED",
+        failure_code: Optional[str] = None,
+    ) -> None:
+        self.connect_instance_id = connect_instance_id
+        self.status = status
+        self.failure_code = failure_code
+
+    def to_dict(self) -> Dict[str, Any]:
+        result = {"connectInstanceId": self.connect_instance_id, "status": self.status}
+
+        if self.failure_code and self.status == "FAILED":
+            result["failureCode"] = self.failure_code
+
+        return result
+
+
+class ConnectCampaignServiceBackend(BaseBackend):
+    def __init__(self, region_name: str, account_id: str) -> None:
+        super().__init__(region_name, account_id)
+        self.campaigns: Dict[str, ConnectCampaign] = {}
+        self.instance_configs: Dict[str, ConnectInstanceConfig] = {}
+        self.onboarding_jobs: Dict[str, ConnectInstanceOnboardingJobStatus] = {}
+
+    def create_campaign(
+        self,
+        name: str,
+        connect_instance_id: str,
+        dialer_config: Dict[str, Any],
+        outbound_call_config: Dict[str, Any],
+        tags: Optional[Dict[str, str]],
+    ) -> Tuple[str, str, Dict[str, str]]:
+        campaign = ConnectCampaign(
+            name=name,
+            connect_instance_id=connect_instance_id,
+            dialer_config=dialer_config,
+            outbound_call_config=outbound_call_config,
+            region=self.region_name,
+            tags=tags,
+        )
+        self.campaigns[campaign.id] = campaign
+        return campaign.id, campaign.arn, campaign.tags
+
+    def delete_campaign(self, id: str) -> None:
+        del self.campaigns[id]
+
+    def describe_campaign(self, id: str) -> Dict[str, Any]:
+        if id not in self.campaigns:
+            raise ResourceNotFoundException(f"Campaign with id {id} not found")
+
+        campaign = self.campaigns[id]
+        return campaign.to_dict()
+
+    def get_connect_instance_config(self, connect_instance_id: str) -> Dict[str, Any]:
+        if not connect_instance_id:
+            raise ValidationException("connectInstanceId is a required parameter")
+
+        if connect_instance_id == "invalid-id":
+            raise ResourceNotFoundException(
+                f"Connect instance with id {connect_instance_id} not found"
+            )
+
+        if connect_instance_id in self.instance_configs:
+            return self.instance_configs[connect_instance_id].to_dict()
+
+        instance_config = ConnectInstanceConfig(
+            connect_instance_id=connect_instance_id, region=self.region_name
+        )
+        self.instance_configs[connect_instance_id] = instance_config
+
+        return instance_config.to_dict()
+
+    def start_instance_onboarding_job(
+        self, connect_instance_id: str, encryption_config: Dict[str, Any]
+    ) -> Dict[str, Any]:
+        if not connect_instance_id:
+            raise ValidationException("connectInstanceId is a required parameter")
+
+        if connect_instance_id == "invalid-id":
+            raise ResourceNotFoundException(
+                f"Connect instance with id {connect_instance_id} not found"
+            )
+
+        if not encryption_config:
+            raise ValidationException("encryptionConfig is a required parameter")
+
+        if "enabled" not in encryption_config:
+            raise ValidationException(
+                "enabled is a required parameter in encryptionConfig"
+            )
+
+        if encryption_config.get("enabled") and "keyArn" not in encryption_config:
+            raise ValidationException("keyArn is required when encryption is enabled")
+
+        job_status = ConnectInstanceOnboardingJobStatus(
+            connect_instance_id=connect_instance_id
+        )
+
+        self.onboarding_jobs[connect_instance_id] = job_status
+
+        instance_config = ConnectInstanceConfig(
+            connect_instance_id=connect_instance_id,
+            region=self.region_name,
+            encryption_enabled=encryption_config.get("enabled", False),
+        )
+
+        if encryption_config.get("enabled") and "keyArn" in encryption_config:
+            instance_config.encryption_config["keyArn"] = encryption_config["keyArn"]
+
+        self.instance_configs[connect_instance_id] = instance_config
+
+        return job_status.to_dict()
+
+
+connectcampaigns_backends = BackendDict(
+    ConnectCampaignServiceBackend, "connectcampaigns"
+)
diff --git a/moto/connectcampaigns/responses.py b/moto/connectcampaigns/responses.py
new file mode 100644
index 000000000..91bdc0336
--- /dev/null
+++ b/moto/connectcampaigns/responses.py
@@ -0,0 +1,87 @@
+"""Handles incoming connectcampaigns requests, invokes methods, returns responses."""
+
+import json
+
+from moto.core.responses import BaseResponse
+
+from .models import ConnectCampaignServiceBackend, connectcampaigns_backends
+
+
+class ConnectCampaignServiceResponse(BaseResponse):
+    """Handler for ConnectCampaignService requests and responses."""
+
+    def __init__(self) -> None:
+        super().__init__(service_name="connectcampaigns")
+
+    @property
+    def connectcampaigns_backend(self) -> ConnectCampaignServiceBackend:
+        """Return backend instance specific for this region."""
+        return connectcampaigns_backends[self.current_account][self.region]
+
+    def create_campaign(self) -> str:
+        params = json.loads(self.body)
+        name = params.get("name")
+        connect_instance_id = params.get("connectInstanceId")
+        dialer_config = params.get("dialerConfig")
+        outbound_call_config = params.get("outboundCallConfig")
+        tags = params.get("tags", {})
+
+        id, arn, tags = self.connectcampaigns_backend.create_campaign(
+            name=name,
+            connect_instance_id=connect_instance_id,
+            dialer_config=dialer_config,
+            outbound_call_config=outbound_call_config,
+            tags=tags,
+        )
+
+        response = {"id": id, "arn": arn, "tags": tags}
+
+        return json.dumps(response)
+
+    def delete_campaign(self) -> str:
+        id = self.path.split("/")[-1]
+
+        self.connectcampaigns_backend.delete_campaign(
+            id=id,
+        )
+
+        return "{}"
+
+    def describe_campaign(self) -> str:
+        id = self.path.split("/")[-1]
+
+        campaign_details = self.connectcampaigns_backend.describe_campaign(
+            id=id,
+        )
+
+        response = {"campaign": campaign_details}
+
+        return json.dumps(response)
+
+    def get_connect_instance_config(self) -> str:
+        connect_instance_id = self.path.split("/")[-2]
+
+        connect_instance_config = (
+            self.connectcampaigns_backend.get_connect_instance_config(
+                connect_instance_id=connect_instance_id,
+            )
+        )
+
+        response = {"connectInstanceConfig": connect_instance_config}
+
+        return json.dumps(response)
+
+    def start_instance_onboarding_job(self) -> str:
+        connect_instance_id = self.path.split("/")[-2]
+
+        params = json.loads(self.body) if self.body else {}
+        encryption_config = params.get("encryptionConfig", {})
+
+        job_status = self.connectcampaigns_backend.start_instance_onboarding_job(
+            connect_instance_id=connect_instance_id,
+            encryption_config=encryption_config,
+        )
+
+        response = {"connectInstanceOnboardingJobStatus": job_status}
+
+        return json.dumps(response)
diff --git a/moto/connectcampaigns/urls.py b/moto/connectcampaigns/urls.py
new file mode 100644
index 000000000..fd20423f7
--- /dev/null
+++ b/moto/connectcampaigns/urls.py
@@ -0,0 +1,14 @@
+"""connectcampaigns base URL and path."""
+
+from .responses import ConnectCampaignServiceResponse
+
+url_bases = [
+    r"https?://connect-campaigns\.(.+)\.amazonaws\.com",
+]
+
+url_paths = {
+    "{0}/campaigns$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/campaigns/(?P<id>[^/]+)$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/connect-instance/(?P<connectInstanceId>[^/]+)/config$": ConnectCampaignServiceResponse.dispatch,
+    "{0}/connect-instance/(?P<connectInstanceId>[^/]+)/onboarding$": ConnectCampaignServiceResponse.dispatch,
+}
diff --git a/moto/ec2/models/instances.py b/moto/ec2/models/instances.py
index 8af0a40d3..c4dbbbb5f 100644
--- a/moto/ec2/models/instances.py
+++ b/moto/ec2/models/instances.py
@@ -57,6 +57,27 @@ class StateReason:
         self.code = code
 
 
+class MetadataOptions:
+    def __init__(self, options: Optional[Dict[str, Any]] = None):
+        options = options or {}
+        self.state = options.get("State", "applied")
+        self.http_tokens = options.get("HttpTokens", "optional")
+        self.hop_limit = int(options.get("HttpPutResponseHopLimit", 1))
+        self.http_endpoint = options.get("HttpEndpoint", "enabled")
+        self.http_protocol = options.get("HttpProtocolIpv6", "disabled")
+        self.instance_metadata_tags = options.get("InstanceMetadataTags", "disabled")
+
+    def to_dict(self) -> Dict[str, Any]:
+        return {
+            "State": str(self.state),
+            "HttpTokens": str(self.http_tokens),
+            "HttpPutResponseHopLimit": int(self.hop_limit),
+            "HttpEndpoint": str(self.http_endpoint),
+            "HttpProtocolIpv6": str(self.http_protocol),
+            "InstanceMetadataTags": str(self.instance_metadata_tags),
+        }
+
+
 class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
     VALID_ATTRIBUTES = {
         "instanceType",
@@ -150,6 +171,10 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
         self.disable_api_stop = kwargs.get("disable_api_stop", False)
         self.iam_instance_profile = kwargs.get("iam_instance_profile")
 
+        self.metadata_options = MetadataOptions(
+            kwargs.get("metadata_options", {})
+        ).to_dict()
+
         # handle weird bug around user_data -- something grabs the repr(), so
         # it must be clean
         if isinstance(self.user_data, list) and len(self.user_data) > 0:
@@ -822,6 +847,33 @@ class InstanceBackend:
         setattr(instance, key, value)
         return instance
 
+    def modify_instance_metadata_options(
+        self,
+        instance_id: str,
+        http_tokens: Optional[str] = None,
+        hop_limit: Optional[int] = None,
+        http_endpoint: Optional[str] = None,
+        dry_run: Optional[bool] = False,
+        http_protocol: Optional[str] = None,
+        metadata_tags: Optional[str] = None,
+    ) -> MetadataOptions:
+        instance = self.get_instance(instance_id)
+
+        metadata_dict = {
+            "State": "applied",
+            "HttpTokens": http_tokens,
+            "HttpPutResponseHopLimit": hop_limit,
+            "HttpEndpoint": http_endpoint,
+            "HttpProtocolIpv6": http_protocol,
+            "InstanceMetadataTags": metadata_tags,
+        }
+
+        metadata_options = MetadataOptions(metadata_dict)
+
+        instance.metadata_options = metadata_options.to_dict()
+
+        return metadata_options
+
     def modify_instance_security_groups(
         self, instance_id: str, new_group_id_list: List[str]
     ) -> Instance:
diff --git a/moto/ec2/responses/instances.py b/moto/ec2/responses/instances.py
index 5c6139efb..3f8978d00 100644
--- a/moto/ec2/responses/instances.py
+++ b/moto/ec2/responses/instances.py
@@ -92,6 +92,7 @@ class InstanceResponse(EC2BaseResponse):
             if self._get_param("Monitoring.Enabled") == "true"
             else "disabled",
             "ipv6_address_count": self._get_int_param("Ipv6AddressCount"),
+            "metadata_options": self._get_multi_param_dict("MetadataOptions"),
         }
         if len(kwargs["nics"]) and kwargs["subnet_id"]:
             raise InvalidParameterCombination(
@@ -272,6 +273,28 @@ class InstanceResponse(EC2BaseResponse):
         )
         raise NotImplementedError(msg)
 
+    def modify_instance_metadata_options(self) -> str:
+        instance_id = self._get_param("InstanceId")
+        tokens = self._get_param("HttpTokens")
+        hop_limit = self._get_int_param("HttpPutResponseHopLimit")
+        endpoint = self._get_param("HttpEndpoint")
+        dry_run = False
+        http_protocol = self._get_param("HttpProtocolIpv6")
+        metadata_tags = self._get_param("InstanceMetadataTags")
+
+        options = self.ec2_backend.modify_instance_metadata_options(
+            instance_id=instance_id,
+            http_tokens=tokens,
+            hop_limit=hop_limit,
+            http_endpoint=endpoint,
+            dry_run=dry_run,
+            http_protocol=http_protocol,
+            metadata_tags=metadata_tags,
+        )
+
+        template = self.response_template(EC2_MODIFY_INSTANCE_METADATA_OPTIONS)
+        return template.render(instance_id=instance_id, options=options)
+
     def _block_device_mapping_handler(self) -> Optional[str]:
         """
         Handles requests which are generated by code similar to:
@@ -551,6 +574,15 @@ INSTANCE_TEMPLATE = """<item>
             <configured>{{ instance.hibernation_options.get("Configured") }}</configured>
           </hibernationOptions>
           {% endif %}
+          {% if instance.metadata_options %}
+          <metadataOptions>
+            <httpTokens>{{ instance.metadata_options.get("HttpTokens") }}</httpTokens>
+            <httpPutResponseHopLimit>{{ instance.metadata_options.get("HttpPutResponseHopLimit") }}</httpPutResponseHopLimit>
+            <httpEndpoint>{{ instance.metadata_options.get("HttpEndpoint") }}</httpEndpoint>
+            <httpProtocolIpv6>{{ instance.metadata_options.get("HttpProtocolIpv6") }}</httpProtocolIpv6>
+            <instanceMetadataTags>{{ instance.metadata_options.get("InstanceMetadataTags") }}</instanceMetadataTags>
+          </metadataOptions>
+          {% endif %}
           {% if instance.get_tags() %}
           <tagSet>
             {% for tag in instance.get_tags() %}
@@ -962,7 +994,6 @@ EC2_DESCRIBE_INSTANCE_TYPES = """<?xml version="1.0" encoding="UTF-8"?>
     </instanceTypeSet>
 </DescribeInstanceTypesResponse>"""
 
-
 EC2_DESCRIBE_INSTANCE_TYPE_OFFERINGS = """<?xml version="1.0" encoding="UTF-8"?>
 <DescribeInstanceTypeOfferingsResponse xmlns="http://api.outscale.com/wsdl/fcuext/2014-04-15/">
     <requestId>f8b86168-d034-4e65-b48d-3b84c78e64af</requestId>
@@ -976,3 +1007,16 @@ EC2_DESCRIBE_INSTANCE_TYPE_OFFERINGS = """<?xml version="1.0" encoding="UTF-8"?>
     {% endfor %}
     </instanceTypeOfferingSet>
 </DescribeInstanceTypeOfferingsResponse>"""
+
+EC2_MODIFY_INSTANCE_METADATA_OPTIONS = """<ModifyInstanceMetadataOptionsResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
+  <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
+  <instanceId>{{ instance_id }}</instanceId>
+  <instanceMetadataOptions>
+    <state>{{ options.state }}</state>
+    <httpTokens>{{ options.http_tokens }}</httpTokens>
+    <httpPutResponseHopLimit>{{ options.hop_limit }}</httpPutResponseHopLimit>
+    <httpEndpoint>{{ options.http_endpoint }}</httpEndpoint>
+    <httpProtocolIpv6>{{ options.http_protocol }}</httpProtocolIpv6>
+    <instanceMetadataTags>{{ options.instance_metadata_tags }}</instanceMetadataTags>
+  </instanceMetadataOptions>
+</ModifyInstanceMetadataOptionsResponse>"""
diff --git a/moto/events/models.py b/moto/events/models.py
index f0281837f..bbcc74c22 100644
--- a/moto/events/models.py
+++ b/moto/events/models.py
@@ -376,6 +376,9 @@ class EventBus(CloudFormationModel):
         account_id: str,
         region_name: str,
         name: str,
+        description: Optional[str] = None,
+        kms_key_identifier: Optional[str] = None,
+        dead_letter_config: Optional[Dict[str, str]] = None,
         tags: Optional[List[Dict[str, str]]] = None,
     ):
         self.account_id = account_id
@@ -384,6 +387,13 @@ class EventBus(CloudFormationModel):
         self.arn = f"arn:{get_partition(self.region)}:events:{self.region}:{account_id}:event-bus/{name}"
         self.tags = tags or []
 
+        self.description = description
+        self.kms_key_identifier = kms_key_identifier
+        self.dead_letter_config = dead_letter_config
+
+        self.creation_time = unix_time()
+        self.last_modified_time = self.creation_time
+
         self._statements: Dict[str, EventBusPolicyStatement] = {}
         self.rules: Dict[str, Rule] = OrderedDict()
 
@@ -1526,6 +1536,9 @@ class EventsBackend(BaseBackend):
         self,
         name: str,
         event_source_name: Optional[str] = None,
+        description: Optional[str] = None,
+        kms_key_identifier: Optional[str] = None,
+        dead_letter_config: Optional[Dict[str, str]] = None,
         tags: Optional[List[Dict[str, str]]] = None,
     ) -> EventBus:
         if name in self.event_buses:
@@ -1544,7 +1557,15 @@ class EventsBackend(BaseBackend):
                 f"Event source {event_source_name} does not exist.",
             )
 
-        event_bus = EventBus(self.account_id, self.region_name, name, tags=tags)
+        event_bus = EventBus(
+            self.account_id,
+            self.region_name,
+            name,
+            description=description,
+            kms_key_identifier=kms_key_identifier,
+            dead_letter_config=dead_letter_config,
+            tags=tags,
+        )
         self.event_buses[name] = event_bus
         if tags:
             self.tagger.tag_resource(event_bus.arn, tags)
diff --git a/moto/events/responses.py b/moto/events/responses.py
index 027687e74..c45cb0e26 100644
--- a/moto/events/responses.py
+++ b/moto/events/responses.py
@@ -239,7 +239,21 @@ class EventsHandler(BaseResponse):
         name = self._get_param("Name")
 
         event_bus = self.events_backend.describe_event_bus(name)
-        response = {"Name": event_bus.name, "Arn": event_bus.arn}
+        response = {
+            "Arn": event_bus.arn,
+            "CreationTime": event_bus.creation_time,
+            "Name": event_bus.name,
+            "LastModifiedTime": event_bus.last_modified_time,
+        }
+
+        if event_bus.dead_letter_config:
+            response["DeadLetterConfig"] = event_bus.dead_letter_config
+
+        if event_bus.description:
+            response["Description"] = event_bus.description
+
+        if event_bus.kms_key_identifier:
+            response["KmsKeyIdentifier"] = event_bus.kms_key_identifier
 
         if event_bus.policy:
             response["Policy"] = event_bus.policy
@@ -249,9 +263,19 @@ class EventsHandler(BaseResponse):
     def create_event_bus(self) -> Tuple[str, Dict[str, Any]]:
         name = self._get_param("Name")
         event_source_name = self._get_param("EventSourceName")
+        description = self._get_param("Description")
+        kms_key_identifier = self._get_param("KmsKeyIdentifier")
+        dead_letter_config = self._get_param("DeadLetterConfig")
         tags = self._get_param("Tags")
 
-        event_bus = self.events_backend.create_event_bus(name, event_source_name, tags)
+        event_bus = self.events_backend.create_event_bus(
+            name=name,
+            event_source_name=event_source_name,
+            description=description,
+            kms_key_identifier=kms_key_identifier,
+            dead_letter_config=dead_letter_config,
+            tags=tags,
+        )
         return json.dumps({"EventBusArn": event_bus.arn}), self.response_headers
 
     def list_event_buses(self) -> Tuple[str, Dict[str, Any]]:
diff --git a/moto/resourcegroups/exceptions.py b/moto/resourcegroups/exceptions.py
index 9a74f4651..63f084aae 100644
--- a/moto/resourcegroups/exceptions.py
+++ b/moto/resourcegroups/exceptions.py
@@ -6,3 +6,12 @@ class BadRequestException(JsonRESTError):
 
     def __init__(self, message: str):
         super().__init__(error_type="BadRequestException", message=message)
+
+
+class NotFoundException(JsonRESTError):
+    code = 404
+
+    def __init__(
+        self, message: str = "One or more of the specified resources don't exist."
+    ):
+        super().__init__(error_type="NotFoundException", message=message)
diff --git a/moto/resourcegroups/models.py b/moto/resourcegroups/models.py
index b4b45b7b3..98297bc34 100644
--- a/moto/resourcegroups/models.py
+++ b/moto/resourcegroups/models.py
@@ -6,7 +6,7 @@ from moto.core.base_backend import BackendDict, BaseBackend
 from moto.core.common_models import BaseModel
 from moto.utilities.utils import ARN_PARTITION_REGEX, get_partition
 
-from .exceptions import BadRequestException
+from .exceptions import BadRequestException, NotFoundException
 
 
 class FakeResourceGroup(BaseModel):
@@ -209,15 +209,22 @@ class ResourceGroups:
         self.by_arn: Dict[str, FakeResourceGroup] = {}
 
     def __contains__(self, item: str) -> bool:
-        return item in self.by_name
+        return item in self.by_name or item in self.by_arn
+
+    def __getitem__(self, item: str) -> FakeResourceGroup:
+        if item in self.by_name:
+            return self.by_name[item]
+        if item in self.by_arn:
+            return self.by_arn[item]
+        raise KeyError(f"Resource group '{item}' not found")
 
     def append(self, resource_group: FakeResourceGroup) -> None:
         self.by_name[resource_group.name] = resource_group
         self.by_arn[resource_group.arn] = resource_group
 
     def delete(self, name: str) -> FakeResourceGroup:
-        group = self.by_name[name]
-        del self.by_name[name]
+        group = self[name]
+        del self.by_name[group.name]
         del self.by_arn[group.arn]
         return group
 
@@ -330,10 +337,15 @@ class ResourceGroupsBackend(BaseBackend):
         return group
 
     def delete_group(self, group_name: str) -> FakeResourceGroup:
-        return self.groups.delete(name=group_name)
+        group = self.get_group(group_name)
+        return self.groups.delete(name=group.name)
 
     def get_group(self, group_name: str) -> FakeResourceGroup:
-        return self.groups.by_name[group_name]
+        try:
+            group = self.groups[group_name]
+        except KeyError:
+            raise NotFoundException()
+        return group
 
     def get_tags(self, arn: str) -> Dict[str, str]:
         return self.groups.by_arn[arn].tags
diff --git a/moto/sqs/responses.py b/moto/sqs/responses.py
index af546aa63..23ab7a6c2 100644
--- a/moto/sqs/responses.py
+++ b/moto/sqs/responses.py
@@ -531,6 +531,14 @@ class SQSResponse(BaseResponse):
             message_attributes = self._get_multi_param("message_attributes")
         if not message_attributes:
             message_attributes = extract_input_message_attributes(self.querystring)
+        if self.is_json():
+            message_system_attributes = self._get_param("MessageSystemAttributeNames")
+        else:
+            message_system_attributes = self._get_multi_param(
+                "message_system_attributes"
+            )
+        if not message_system_attributes:
+            message_system_attributes = set()
 
         if self.is_json():
             attribute_names = self._get_param("AttributeNames", [])
@@ -587,15 +595,29 @@ class SQSResponse(BaseResponse):
         )
 
         attributes = {
-            "approximate_first_receive_timestamp": False,
-            "approximate_receive_count": False,
-            "message_deduplication_id": False,
-            "message_group_id": False,
-            "sender_id": False,
-            "sent_timestamp": False,
-            "sequence_number": False,
+            "approximate_first_receive_timestamp": "ApproximateFirstReceiveTimestamp"
+            in message_system_attributes,
+            "approximate_receive_count": "ApproximateReceiveCount"
+            in message_system_attributes,
+            "message_deduplication_id": "MessageDeduplicationId"
+            in message_system_attributes,
+            "message_group_id": "MessageGroupId" in message_system_attributes,
+            "sender_id": "SenderId" in message_system_attributes,
+            "sent_timestamp": "SentTimestamp" in message_system_attributes,
+            "sequence_number": "SequenceNumber" in message_system_attributes,
         }
 
+        if "All" in message_system_attributes:
+            attributes = {
+                "approximate_first_receive_timestamp": True,
+                "approximate_receive_count": True,
+                "message_deduplication_id": True,
+                "message_group_id": True,
+                "sender_id": True,
+                "sent_timestamp": True,
+                "sequence_number": True,
+            }
+
         for attribute in attributes:
             pascalcase_name = camelcase_to_pascal(underscores_to_camelcase(attribute))
             if any(x in ["All", pascalcase_name] for x in attribute_names):


Test patch:
diff --git a/tests/test_codedeploy/test_codedeploy.py b/tests/test_codedeploy/test_codedeploy.py
index dce81e18d..a35fa52d6 100644
--- a/tests/test_codedeploy/test_codedeploy.py
+++ b/tests/test_codedeploy/test_codedeploy.py
@@ -553,3 +553,91 @@ def test_list_deployment_groups():
 
     resp = client.list_deployment_groups(applicationName=application_name)
     assert len(resp["deploymentGroups"]) == 2
+
+
+@mock_aws
+def test_application_tagging():
+    client = boto3.client("codedeploy", region_name="us-west-2")
+    app_name = "test-tag-application"
+    initial_tags = [{"Key": "Environment", "Value": "Test"}]
+
+    response = client.create_application(
+        applicationName=app_name, computePlatform="Server", tags=initial_tags
+    )
+
+    app_arn = f"arn:aws:codedeploy:us-west-2:123456789012:application:{app_name}"
+
+    response = client.list_tags_for_resource(ResourceArn=app_arn)
+    assert "Tags" in response
+    assert len(response["Tags"]) == 1
+    assert response["Tags"][0]["Key"] == "Environment"
+    assert response["Tags"][0]["Value"] == "Test"
+
+
+@mock_aws
+def test_deployment_group_tagging():
+    client = boto3.client("codedeploy", region_name="us-west-2")
+    app_name = "test-tag-dg-app"
+    dg_name = "test-tag-deployment-group"
+    service_role_arn = "arn:aws:iam::123456789012:role/CodeDeployDemoRole"
+
+    client.create_application(applicationName=app_name, computePlatform="Server")
+
+    initial_tags = [{"Key": "Team", "Value": "Platform"}]
+    response = client.create_deployment_group(
+        applicationName=app_name,
+        deploymentGroupName=dg_name,
+        serviceRoleArn=service_role_arn,
+        tags=initial_tags,
+    )
+
+    dg_arn = f"arn:aws:codedeploy:us-west-2:123456789012:deploymentgroup:{app_name}/{dg_name}"
+
+    response = client.list_tags_for_resource(ResourceArn=dg_arn)
+    assert "Tags" in response
+    assert len(response["Tags"]) == 1
+    assert response["Tags"][0]["Key"] == "Team"
+    assert response["Tags"][0]["Value"] == "Platform"
+
+
+@mock_aws
+def test_deployment_inherits_tags():
+    """Test that deployments inherit tags from deployment groups."""
+    client = boto3.client("codedeploy", region_name="us-west-2")
+    app_name = "test-tag-inherit-app"
+    dg_name = "test-tag-inherit-group"
+    service_role_arn = "arn:aws:iam::123456789012:role/CodeDeployDemoRole"
+
+    client.create_application(applicationName=app_name, computePlatform="Server")
+
+    dg_tags = [{"Key": "Environment", "Value": "Production"}]
+    client.create_deployment_group(
+        applicationName=app_name,
+        deploymentGroupName=dg_name,
+        serviceRoleArn=service_role_arn,
+        tags=dg_tags,
+    )
+
+    response = client.create_deployment(
+        applicationName=app_name,
+        deploymentGroupName=dg_name,
+        revision={
+            "revisionType": "S3",
+            "s3Location": {
+                "bucket": "my-bucket",
+                "key": "my-key",
+                "bundleType": "zip",
+                "version": "1",
+                "eTag": "my-etag",
+            },
+        },
+    )
+    deployment_id = response["deploymentId"]
+
+    deployment_arn = (
+        f"arn:aws:codedeploy:us-west-2:123456789012:deployment:{deployment_id}"
+    )
+
+    response = client.list_tags_for_resource(ResourceArn=deployment_arn)
+    assert "Tags" in response
+    assert len(response["Tags"]) == 1
diff --git a/tests/test_connectcampaigns/__init__.py b/tests/test_connectcampaigns/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_connectcampaigns/test_connectcampaigns.py b/tests/test_connectcampaigns/test_connectcampaigns.py
new file mode 100644
index 000000000..21da781db
--- /dev/null
+++ b/tests/test_connectcampaigns/test_connectcampaigns.py
@@ -0,0 +1,177 @@
+"""Unit tests for connectcampaigns-supported APIs."""
+
+import boto3
+import pytest
+
+from moto import mock_aws
+
+# See our Development Tips on writing tests for hints on how to write good tests:
+# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
+
+
+@mock_aws
+def test_create_campaign():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    response = client.create_campaign(
+        name="TestCampaign",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+            "connectSourcePhoneNumber": "+12065550100",
+            "connectQueueId": "12345678-1234-1234-1234-123456789012",
+            "answerMachineDetectionConfig": {
+                "enableAnswerMachineDetection": True,
+                "awaitAnswerMachinePrompt": False,
+            },
+        },
+        tags={"Department": "Marketing", "Project": "Outreach"},
+    )
+
+    assert "id" in response
+    assert "arn" in response
+    assert "tags" in response
+    assert response["tags"]["Department"] == "Marketing"
+    assert response["tags"]["Project"] == "Outreach"
+
+
+@mock_aws
+def test_delete_campaign():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignToDelete",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    campaign_id = create_response["id"]
+
+    client.delete_campaign(id=campaign_id)
+
+
+@mock_aws
+def test_describe_campaign():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    create_response = client.create_campaign(
+        name="CampaignToDescribe",
+        connectInstanceId="12345678-1234-1234-1234-123456789012",
+        dialerConfig={
+            "progressiveDialerConfig": {
+                "bandwidthAllocation": 1.0,
+                "dialingCapacity": 2.0,
+            }
+        },
+        outboundCallConfig={
+            "connectContactFlowId": "12345678-1234-1234-1234-123456789012",
+            "connectSourcePhoneNumber": "+12065550100",
+            "connectQueueId": "12345678-1234-1234-1234-123456789012",
+        },
+    )
+
+    campaign_id = create_response["id"]
+
+    describe_response = client.describe_campaign(id=campaign_id)
+
+    assert "campaign" in describe_response
+    campaign = describe_response["campaign"]
+
+    assert campaign["id"] == campaign_id
+    assert "arn" in campaign
+    assert campaign["name"] == "CampaignToDescribe"
+    assert campaign["connectInstanceId"] == "12345678-1234-1234-1234-123456789012"
+    assert "progressiveDialerConfig" in campaign["dialerConfig"]
+    assert (
+        campaign["dialerConfig"]["progressiveDialerConfig"]["bandwidthAllocation"]
+        == 1.0
+    )
+    assert (
+        campaign["outboundCallConfig"]["connectContactFlowId"]
+        == "12345678-1234-1234-1234-123456789012"
+    )
+
+    with pytest.raises(client.exceptions.ResourceNotFoundException):
+        client.describe_campaign(id="non-existent-id")
+
+
+@mock_aws
+def test_get_connect_instance_config():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    connect_instance_id = "12345678-1234-1234-1234-123456789012"
+
+    response = client.get_connect_instance_config(connectInstanceId=connect_instance_id)
+
+    assert "connectInstanceConfig" in response
+    config = response["connectInstanceConfig"]
+
+    assert config["connectInstanceId"] == connect_instance_id
+    assert "serviceLinkedRoleArn" in config
+    assert "encryptionConfig" in config
+    assert "enabled" in config["encryptionConfig"]
+    assert "encryptionType" in config["encryptionConfig"]
+    assert config["encryptionConfig"]["encryptionType"] == "KMS"
+
+    with pytest.raises(client.exceptions.ResourceNotFoundException):
+        client.get_connect_instance_config(connectInstanceId="invalid-id")
+
+
+@mock_aws
+def test_start_instance_onboarding_job():
+    client = boto3.client("connectcampaigns", region_name="us-east-1")
+
+    connect_instance_id = "12345678-1234-1234-1234-123456789012"
+
+    response = client.start_instance_onboarding_job(
+        connectInstanceId=connect_instance_id,
+        encryptionConfig={"enabled": False, "encryptionType": "KMS"},
+    )
+
+    assert "connectInstanceOnboardingJobStatus" in response
+    job_status = response["connectInstanceOnboardingJobStatus"]
+
+    assert job_status["connectInstanceId"] == connect_instance_id
+    assert job_status["status"] == "SUCCEEDED"
+    assert "failureCode" not in job_status
+
+    response = client.start_instance_onboarding_job(
+        connectInstanceId=connect_instance_id,
+        encryptionConfig={
+            "enabled": True,
+            "encryptionType": "KMS",
+            "keyArn": "arn:aws:kms:us-east-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab",
+        },
+    )
+
+    assert "connectInstanceOnboardingJobStatus" in response
+    job_status = response["connectInstanceOnboardingJobStatus"]
+
+    assert job_status["connectInstanceId"] == connect_instance_id
+    assert job_status["status"] == "SUCCEEDED"
+
+    with pytest.raises(client.exceptions.ResourceNotFoundException):
+        client.start_instance_onboarding_job(
+            connectInstanceId="invalid-id",
+            encryptionConfig={"enabled": False, "encryptionType": "KMS"},
+        )
+
+    with pytest.raises(client.exceptions.ValidationException):
+        client.start_instance_onboarding_job(
+            connectInstanceId=connect_instance_id,
+            encryptionConfig={"enabled": True, "encryptionType": "KMS"},
+        )
diff --git a/tests/test_ec2/test_instances.py b/tests/test_ec2/test_instances.py
index acf7b62be..3bf62db50 100644
--- a/tests/test_ec2/test_instances.py
+++ b/tests/test_ec2/test_instances.py
@@ -3018,3 +3018,52 @@ def test_instance_without_default_subnet():
     client.run_instances(
         ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1, SubnetId=subnet["SubnetId"]
     )
+
+
+@mock_aws
+def test_modify_instance_metadata_options():
+    ec2 = boto3.client("ec2", region_name="us-west-2")
+
+    instances = ec2.run_instances(
+        ImageId=EXAMPLE_AMI_ID,
+        MinCount=1,
+        MaxCount=1,
+        InstanceType="t2.micro",
+    ).get("Instances")
+
+    instance_id = [i["InstanceId"] for i in instances]
+
+    # Verify the original
+    metadata_options = instances[0]["MetadataOptions"]
+    assert metadata_options == {
+        "HttpTokens": "optional",
+        "HttpPutResponseHopLimit": 1,
+        "HttpEndpoint": "enabled",
+        "HttpProtocolIpv6": "disabled",
+        "InstanceMetadataTags": "disabled",
+    }
+
+    # Change the defaults
+    ec2.modify_instance_metadata_options(
+        InstanceId=instance_id[0],
+        HttpTokens="required",
+        HttpPutResponseHopLimit=2,
+        HttpEndpoint="disabled",
+        DryRun=False,
+        HttpProtocolIpv6="enabled",
+        InstanceMetadataTags="enabled",
+    )
+
+    response = ec2.describe_instances(InstanceIds=instance_id)
+
+    # Verify the updated fields
+    updated_metadata_options = response["Reservations"][0]["Instances"][0][
+        "MetadataOptions"
+    ]
+    assert updated_metadata_options == {
+        "HttpTokens": "required",
+        "HttpPutResponseHopLimit": 2,
+        "HttpEndpoint": "disabled",
+        "HttpProtocolIpv6": "enabled",
+        "InstanceMetadataTags": "enabled",
+    }
diff --git a/tests/test_events/test_events.py b/tests/test_events/test_events.py
index aeb6fa5a1..1ecb2b7ee 100644
--- a/tests/test_events/test_events.py
+++ b/tests/test_events/test_events.py
@@ -853,25 +853,35 @@ def test_describe_event_bus():
     client = boto3.client("events", "us-east-1")
 
     response = client.describe_event_bus()
-
     assert response["Name"] == "default"
     assert response["Arn"] == f"arn:aws:events:us-east-1:{ACCOUNT_ID}:event-bus/default"
     assert "Policy" not in response
+    assert "CreationTime" in response
+    assert "LastModifiedTime" in response
+
+    bus_name = "test-bus"
+    client.create_event_bus(
+        Name=bus_name,
+        Description="Test description",
+        KmsKeyIdentifier="arn:aws:kms:us-east-1:123456789012:key/test",
+        DeadLetterConfig={"Arn": "arn:aws:sqs:us-east-1:123456789012:dlq"},
+    )
 
-    client.create_event_bus(Name="test-bus")
     client.put_permission(
-        EventBusName="test-bus",
+        EventBusName=bus_name,
         Action="events:PutEvents",
         Principal="111111111111",
         StatementId="test",
     )
 
-    response = client.describe_event_bus(Name="test-bus")
+    response = client.describe_event_bus(Name=bus_name)
 
-    assert response["Name"] == "test-bus"
+    assert response["Name"] == bus_name
     assert (
-        response["Arn"] == f"arn:aws:events:us-east-1:{ACCOUNT_ID}:event-bus/test-bus"
+        response["Arn"] == f"arn:aws:events:us-east-1:{ACCOUNT_ID}:event-bus/{bus_name}"
     )
+    assert "CreationTime" in response
+    assert "LastModifiedTime" in response
     assert json.loads(response["Policy"]) == {
         "Version": "2012-10-17",
         "Statement": [
@@ -880,11 +890,17 @@ def test_describe_event_bus():
                 "Effect": "Allow",
                 "Principal": {"AWS": "arn:aws:iam::111111111111:root"},
                 "Action": "events:PutEvents",
-                "Resource": f"arn:aws:events:us-east-1:{ACCOUNT_ID}:event-bus/test-bus",
+                "Resource": f"arn:aws:events:us-east-1:{ACCOUNT_ID}:event-bus/{bus_name}",
             }
         ],
     }
 
+    assert response["Description"] == "Test description"
+    assert response["KmsKeyIdentifier"] == "arn:aws:kms:us-east-1:123456789012:key/test"
+    assert response["DeadLetterConfig"] == {
+        "Arn": "arn:aws:sqs:us-east-1:123456789012:dlq"
+    }
+
 
 @mock_aws
 def test_describe_event_bus_errors():
diff --git a/tests/test_resourcegroups/test_resourcegroups.py b/tests/test_resourcegroups/test_resourcegroups.py
index 800de6ddc..41e3f1b5a 100644
--- a/tests/test_resourcegroups/test_resourcegroups.py
+++ b/tests/test_resourcegroups/test_resourcegroups.py
@@ -1,6 +1,8 @@
 import json
 
 import boto3
+import pytest
+from botocore.exceptions import ClientError
 
 from moto import mock_aws
 
@@ -48,14 +50,35 @@ def test_delete_group():
     assert len(response["Groups"]) == 0
 
 
+@mock_aws
+def test_delete_group_by_arn():
+    resource_groups = boto3.client("resource-groups", region_name="us-east-1")
+
+    response = create_group(client=resource_groups)
+    group_arn = response["Group"]["GroupArn"]
+
+    response = resource_groups.delete_group(GroupName=group_arn)
+    assert group_arn == response["Group"]["GroupArn"]
+
+    with pytest.raises(ClientError) as exc:
+        resource_groups.get_group(GroupName=group_arn)
+    error = exc.value.response["Error"]
+    assert error["Code"] == "NotFoundException"
+
+
 @mock_aws
 def test_get_group():
     resource_groups = boto3.client("resource-groups", region_name="us-east-1")
 
-    create_group(client=resource_groups)
+    response = create_group(client=resource_groups)
+    group_name = response["Group"]["Name"]
+    group_arn = response["Group"]["GroupArn"]
 
-    response = resource_groups.get_group(GroupName="test_resource_group")
-    assert "description" in response["Group"]["Description"]
+    response = resource_groups.get_group(GroupName=group_name)
+    assert response["Group"]["GroupArn"] == group_arn
+
+    response = resource_groups.get_group(GroupName=group_arn)
+    assert response["Group"]["Name"] == group_name
 
 
 @mock_aws
diff --git a/tests/test_sqs/test_sqs.py b/tests/test_sqs/test_sqs.py
index 4f1b8fb4b..d8f1ace24 100644
--- a/tests/test_sqs/test_sqs.py
+++ b/tests/test_sqs/test_sqs.py
@@ -3138,6 +3138,84 @@ def test_message_attributes_contains_trace_header():
     )
 
 
+@mock_aws
+@pytest.mark.parametrize(
+    "attribute",
+    [
+        "MessageGroupId",
+        "MessageDeduplicationId",
+        "ApproximateFirstReceiveTimestamp",
+        "ApproximateReceiveCount",
+        "SenderId",
+        "SentTimestamp",
+        "SequenceNumber",
+    ],
+)
+def test_message_system_attributes_contains_system_attribute(attribute):
+    sqs = boto3.resource("sqs", region_name=REGION)
+    conn = boto3.client("sqs", region_name=REGION)
+    q_name = str(uuid4())[0:6] + ".fifo"
+    q_resp = conn.create_queue(
+        QueueName=q_name,
+        Attributes={
+            "FifoQueue": "true",
+            "ContentBasedDeduplication": "true",
+        },
+    )
+    queue = sqs.Queue(q_resp["QueueUrl"])
+    body_one = "this is a test message"
+
+    queue.send_message(
+        MessageBody=body_one,
+        MessageGroupId="group1",
+        MessageDeduplicationId="dedup1",
+    )
+
+    messages = conn.receive_message(
+        QueueUrl=queue.url,
+        MaxNumberOfMessages=2,
+        MessageSystemAttributeNames=[attribute],
+    )["Messages"]
+
+    assert messages[0]["Attributes"].get(attribute) is not None
+
+
+@mock_aws
+def test_message_system_attributes_contains_all_attributes():
+    sqs = boto3.resource("sqs", region_name=REGION)
+    conn = boto3.client("sqs", region_name=REGION)
+    q_name = str(uuid4())[0:6] + ".fifo"
+    q_resp = conn.create_queue(
+        QueueName=q_name,
+        Attributes={
+            "FifoQueue": "true",
+            "ContentBasedDeduplication": "true",
+        },
+    )
+    queue = sqs.Queue(q_resp["QueueUrl"])
+    body_one = "this is a test message"
+
+    queue.send_message(
+        MessageBody=body_one,
+        MessageGroupId="group1",
+        MessageDeduplicationId="dedup1",
+    )
+
+    messages = conn.receive_message(
+        QueueUrl=queue.url,
+        MaxNumberOfMessages=2,
+        MessageSystemAttributeNames=["All"],
+    )["Messages"]
+
+    assert messages[0]["Attributes"]["MessageGroupId"] == "group1"
+    assert messages[0]["Attributes"]["MessageDeduplicationId"] == "dedup1"
+    assert messages[0]["Attributes"]["ApproximateFirstReceiveTimestamp"] is not None
+    assert messages[0]["Attributes"]["ApproximateReceiveCount"] == "1"
+    assert messages[0]["Attributes"]["SenderId"] is not None
+    assert messages[0]["Attributes"]["SentTimestamp"] is not None
+    assert messages[0]["Attributes"]["SequenceNumber"] is not None
+
+
 @mock_aws
 def test_receive_message_again_preserves_attributes():
     sqs = boto3.resource("sqs", region_name=REGION)