Repository: getmoto/moto
Pull request #8893 (2025-05-13 00:10:16)

EventBridge: Update describe_event_bus schema
1. Updated the event bus schema to match boto3 API for describe_event_bus and create_event_bus: https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_DescribeEventBus.html
- Added timestamp fields (CreationTime and LastModifiedTime) to the EventBus class
- Added support for optional fields (DeadLetterConfig, Description, and KmsKeyIdentifier) to match the full AWS API schema

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/getmoto/moto/pull/8893?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) Report
Attention: Patch coverage is `76.92308%` with `3 lines` in your changes missing coverage. Please review.
> Project coverage is 92.85%. Comparing base [(`648a829`)](https://app.codecov.io/gh/getmoto/moto/commit/648a829d56f15502aa8ed46ab8a20ed2ac847195?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) to head [(`ea48400`)](https://app.codecov.io/gh/getmoto/moto/commit/ea484001f395d5027da954f76a26f98c7c09be75?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).

| [Files with missing lines](https://app.codecov.io/gh/getmoto/moto/pull/8893?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | Patch % | Lines |
|---|---|---|
| [moto/events/responses.py](https://app.codecov.io/gh/getmoto/moto/pull/8893?src=pr&el=tree&filepath=moto%2Fevents%2Fresponses.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto#diff-bW90by9ldmVudHMvcmVzcG9uc2VzLnB5) | 57.14% | [3 Missing :warning: ](https://app.codecov.io/gh/getmoto/moto/pull/8893?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) |

:x: Your patch check has failed because the patch coverage (76.92%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the [target](https://docs.codecov.com/docs/commit-status#target) coverage.

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


```diff
@@            Coverage Diff             @@
##           master    #8893      +/-   ##
==========================================
- Coverage   92.85%   92.85%   -0.01%     
==========================================
  Files        1282     1282              
  Lines      111878   111887       +9     
==========================================
+ Hits       103885   103890       +5     
- Misses       7993     7997       +4     
```

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

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/8893?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
ghorondo: @bblommers I added the default event bus back to the response, it was a request from a teammate that I should have looked further into. My apologies!

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/apigateway/models.py b/moto/apigateway/models.py
index 438f126f2..2f8562b87 100644
--- a/moto/apigateway/models.py
+++ b/moto/apigateway/models.py
@@ -2198,10 +2198,14 @@ class APIGatewayBackend(BaseBackend):
         return self.keys[api_key_id]
 
     def update_api_key(self, api_key_id: str, patch_operations: Any) -> ApiKey:
+        if api_key_id not in self.keys:
+            raise ApiKeyNotFoundException()
         key = self.keys[api_key_id]
         return key.update_operations(patch_operations)
 
     def delete_api_key(self, api_key_id: str) -> None:
+        if api_key_id not in self.keys:
+            raise ApiKeyNotFoundException()
         self.keys.pop(api_key_id)
 
     def create_usage_plan(self, payload: Any) -> UsagePlan:
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/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


Test patch:
diff --git a/tests/test_apigateway/test_apigateway.py b/tests/test_apigateway/test_apigateway.py
index ba2ca674e..f678c8586 100644
--- a/tests/test_apigateway/test_apigateway.py
+++ b/tests/test_apigateway/test_apigateway.py
@@ -1953,6 +1953,27 @@ def test_get_api_key_unknown_apikey():
     assert err["Code"] == "NotFoundException"
 
 
+@mock_aws
+def test_update_api_key_unknown_apikey():
+    client = boto3.client("apigateway", region_name="us-east-1")
+    patch_operations = [{"op": "replace", "path": "/name", "value": "test"}]
+    with pytest.raises(ClientError) as ex:
+        client.update_api_key(apiKey="unknown", patchOperations=patch_operations)
+    err = ex.value.response["Error"]
+    assert err["Message"] == "Invalid API Key identifier specified"
+    assert err["Code"] == "NotFoundException"
+
+
+@mock_aws
+def test_delete_api_key_unknown_apikey():
+    client = boto3.client("apigateway", region_name="us-east-1")
+    with pytest.raises(ClientError) as ex:
+        client.delete_api_key(apiKey="unknown")
+    err = ex.value.response["Error"]
+    assert err["Message"] == "Invalid API Key identifier specified"
+    assert err["Code"] == "NotFoundException"
+
+
 @mock_aws
 def test_get_rest_api_without_id():
     client = boto3.client("apigateway", region_name="us-east-1")
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_elastic_network_interfaces.py b/tests/test_ec2/test_elastic_network_interfaces.py
index e6e869a3d..1198e0b6f 100644
--- a/tests/test_ec2/test_elastic_network_interfaces.py
+++ b/tests/test_ec2/test_elastic_network_interfaces.py
@@ -499,22 +499,28 @@ def test_elastic_network_interfaces_describe_network_interfaces_with_filter():
     waiter = ec2client.get_waiter("network_interface_available")
     waiter.wait(NetworkInterfaceIds=[eni1.id])
 
+    def assert_network_interface_found(resp):
+        assert len(resp["NetworkInterfaces"]) >= 1
+        for ni in resp["NetworkInterfaces"]:
+            if eni1.id == ni["NetworkInterfaceId"]:
+                break
+        else:
+            raise AssertionError("NetworkInterfaceId not found in response!")
+        assert ni["NetworkInterfaceId"] == eni1.id
+        assert ni["PrivateIpAddress"] == eni1.private_ip_address
+        assert ni["Description"] == eni1.description
+
     # Filter by network-interface-id
     response = ec2client.describe_network_interfaces(
         Filters=[{"Name": "network-interface-id", "Values": [eni1.id]}]
     )
-    assert len(response["NetworkInterfaces"]) == 1
-    interface = response["NetworkInterfaces"][0]
-    assert interface["NetworkInterfaceId"] == eni1.id
-    assert interface["PrivateIpAddress"] == eni1.private_ip_address
-    assert interface["Description"] == eni1.description
+    assert_network_interface_found(response)
 
     # Filter by network-interface-id
     response = ec2client.describe_network_interfaces(
         Filters=[{"Name": "group-id", "Values": [sg_id]}]
     )
-    assert len(response["NetworkInterfaces"]) == 1
-    assert response["NetworkInterfaces"][0]["NetworkInterfaceId"] == eni1.id
+    assert_network_interface_found(response)
 
     response = ec2client.describe_network_interfaces(
         Filters=[{"Name": "network-interface-id", "Values": ["bad-id"]}]
@@ -525,27 +531,18 @@ def test_elastic_network_interfaces_describe_network_interfaces_with_filter():
     response = ec2client.describe_network_interfaces(
         Filters=[{"Name": "private-ip-address", "Values": [eni1.private_ip_address]}]
     )
-    assert len(response["NetworkInterfaces"]) == 1
-    interface = response["NetworkInterfaces"][0]
-    assert interface["NetworkInterfaceId"] == eni1.id
-    assert interface["PrivateIpAddress"] == eni1.private_ip_address
-    assert interface["Description"] == eni1.description
+    assert_network_interface_found(response)
 
     response = ec2client.describe_network_interfaces(
         Filters=[{"Name": "private-ip-address", "Values": ["11.11.11.11"]}]
     )
     assert len(response["NetworkInterfaces"]) == 0
 
-    # Filter by sunet-id
+    # Filter by subnet-id
     response = ec2client.describe_network_interfaces(
         Filters=[{"Name": "subnet-id", "Values": [eni1.subnet.id]}]
     )
-    assert len(response["NetworkInterfaces"]) == 1
-    assert response["NetworkInterfaces"][0]["NetworkInterfaceId"] == eni1.id
-    assert (
-        response["NetworkInterfaces"][0]["PrivateIpAddress"] == eni1.private_ip_address
-    )
-    assert response["NetworkInterfaces"][0]["Description"] == eni1.description
+    assert_network_interface_found(response)
 
     response = ec2client.describe_network_interfaces(
         Filters=[{"Name": "subnet-id", "Values": ["sn-bad-id"]}]
@@ -556,12 +553,7 @@ def test_elastic_network_interfaces_describe_network_interfaces_with_filter():
     response = ec2client.describe_network_interfaces(
         Filters=[{"Name": "description", "Values": [eni1.description]}]
     )
-    assert len(response["NetworkInterfaces"]) == 1
-    assert response["NetworkInterfaces"][0]["NetworkInterfaceId"] == eni1.id
-    assert (
-        response["NetworkInterfaces"][0]["PrivateIpAddress"] == eni1.private_ip_address
-    )
-    assert response["NetworkInterfaces"][0]["Description"] == eni1.description
+    assert_network_interface_found(response)
 
     response = ec2client.describe_network_interfaces(
         Filters=[{"Name": "description", "Values": ["bad description"]}]
@@ -576,12 +568,7 @@ def test_elastic_network_interfaces_describe_network_interfaces_with_filter():
             {"Name": "subnet-id", "Values": [eni1.subnet.id]},
         ]
     )
-    assert len(response["NetworkInterfaces"]) == 1
-    assert response["NetworkInterfaces"][0]["NetworkInterfaceId"] == eni1.id
-    assert (
-        response["NetworkInterfaces"][0]["PrivateIpAddress"] == eni1.private_ip_address
-    )
-    assert response["NetworkInterfaces"][0]["Description"] == eni1.description
+    assert_network_interface_found(response)
 
 
 @mock_aws
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