Repository: getmoto/moto (python)

Issue:
Bug in RDS modify_db_instance with CloudwatchLogsExportConfiguration
## how to reproduce the issue

```
import boto3
from moto import mock_aws

@mock_aws
def rds():
    client = boto3.client("rds")
    response = client.create_db_instance(
        DBInstanceIdentifier="test",
        DBInstanceClass="db.m5.large",
        Engine="sqlserver-se",
        MasterUsername='string',
        MasterUserPassword='string123',
        LicenseModel="license-included",
        AllocatedStorage=32
    )
    print(response)
    print("---")

    response = client.modify_db_instance(
        DBInstanceIdentifier="test",
        CloudwatchLogsExportConfiguration={'EnableLogTypes':['error']}
    )
    print(response)
    print("---")

    response = client.describe_db_instances(DBInstanceIdentifier="test")
    print(response)
    print("---")

rds()
```

## what you expected to happen
After calling the `modify_db_instance` with the `CloudwatchLogsExportConfiguration` it should have the enabled log types

## what actually happens
The response is the same, nothing shows on the CW logs

## what version of Moto you're using
Latest ([5.0.12](https://github.com/getmoto/moto/releases/tag/5.0.12))

## how you installed it
`pip install 'moto[all]`


Pull request description:
RDS: update_db_instance() now supports the CloudwatchLogsExportConfig…
…uration-parameter

Fixes #7990 

Patch:
diff --git a/moto/events/models.py b/moto/events/models.py
index 9b43ba579..dc54f52e8 100644
--- a/moto/events/models.py
+++ b/moto/events/models.py
@@ -38,7 +38,12 @@ from moto.utilities.utils import (
     get_partition,
 )
 
-from .utils import _BASE_EVENT_MESSAGE, PAGINATION_MODEL, EventMessageType
+from .utils import (
+    _BASE_EVENT_MESSAGE,
+    PAGINATION_MODEL,
+    EventMessageType,
+    EventTemplateParser,
+)
 
 if TYPE_CHECKING:
     from moto.secretsmanager.models import SecretsManagerBackend
@@ -110,7 +115,8 @@ class Rule(CloudFormationModel):
         self.state = "DISABLED"
 
     def delete(self, account_id: str, region_name: str) -> None:
-        event_backend = events_backends[account_id][region_name]
+        event_backend: EventsBackend = events_backends[account_id][region_name]
+        self.remove_targets([t["Id"] for t in self.targets])
         event_backend.delete_rule(name=self.name, event_bus_arn=self.event_bus_name)
 
     def put_targets(self, targets: List[Dict[str, Any]]) -> None:
@@ -128,8 +134,10 @@ class Rule(CloudFormationModel):
             if index is not None:
                 self.targets.pop(index)
 
-    def send_to_targets(self, event: EventMessageType) -> None:
-        if not self.event_pattern.matches_event(event):
+    def send_to_targets(
+        self, original_event: EventMessageType, transform_input: bool = True
+    ) -> None:
+        if not self.event_pattern.matches_event(original_event):
             return
 
         # supported targets
@@ -140,13 +148,22 @@ class Rule(CloudFormationModel):
         for target in self.targets:
             arn = parse_arn(target["Arn"])
 
+            if transform_input:
+                input_transformer = target.get("InputTransformer", {})
+                event = EventTemplateParser.parse(
+                    input_template=input_transformer.get("InputTemplate"),
+                    input_paths_map=input_transformer.get("InputPathsMap", {}),
+                    event=original_event,
+                )
+            else:
+                event = original_event.copy()  # type: ignore[assignment]
+
             if arn.service == "logs" and arn.resource_type == "log-group":
                 self._send_to_cw_log_group(arn.resource_id, event)
             elif arn.service == "events" and not arn.resource_type:
-                input_template = json.loads(target["InputTransformer"]["InputTemplate"])
-                archive_arn = parse_arn(input_template["archive-arn"])
+                archive_arn = parse_arn(event["archive-arn"])
 
-                self._send_to_events_archive(archive_arn.resource_id, event)
+                self._send_to_events_archive(archive_arn.resource_id, original_event)
             elif arn.service == "sqs":
                 group_id = target.get("SqsParameters", {}).get("MessageGroupId")
                 self._send_to_sqs_queue(arn.resource_id, event, group_id)
@@ -181,18 +198,15 @@ class Rule(CloudFormationModel):
             else:
                 raise NotImplementedError(f"Expr not defined for {type(self)}")
 
-    def _send_to_cw_log_group(self, name: str, event: EventMessageType) -> None:
+    def _send_to_cw_log_group(self, name: str, event: Dict[str, Any]) -> None:
         from moto.logs import logs_backends
 
-        event_copy = copy.deepcopy(event)
-        event_copy["time"] = iso_8601_datetime_without_milliseconds(
-            utcfromtimestamp(event_copy["time"])  # type: ignore[arg-type]
+        event["time"] = iso_8601_datetime_without_milliseconds(
+            utcfromtimestamp(event["time"])  # type: ignore[arg-type]
         )
 
         log_stream_name = str(random.uuid4())
-        log_events = [
-            {"timestamp": unix_time_millis(), "message": json.dumps(event_copy)}
-        ]
+        log_events = [{"timestamp": unix_time_millis(), "message": json.dumps(event)}]
 
         log_backend = logs_backends[self.account_id][self.region_name]
         log_backend.create_log_stream(name, log_stream_name)
@@ -214,13 +228,12 @@ class Rule(CloudFormationModel):
         return backend.destinations[destination_name]
 
     def _send_to_sqs_queue(
-        self, resource_id: str, event: EventMessageType, group_id: Optional[str] = None
+        self, resource_id: str, event: Dict[str, Any], group_id: Optional[str] = None
     ) -> None:
         from moto.sqs import sqs_backends
 
-        event_copy = copy.deepcopy(event)
-        event_copy["time"] = iso_8601_datetime_without_milliseconds(
-            utcfromtimestamp(event_copy["time"])  # type: ignore[arg-type]
+        event["time"] = iso_8601_datetime_without_milliseconds(
+            utcfromtimestamp(float(event["time"]))  # type: ignore[arg-type]
         )
 
         if group_id:
@@ -238,7 +251,7 @@ class Rule(CloudFormationModel):
 
         sqs_backends[self.account_id][self.region_name].send_message(
             queue_name=resource_id,
-            message_body=json.dumps(event_copy),
+            message_body=json.dumps(event),
             group_id=group_id,
         )
 
@@ -288,8 +301,8 @@ class Rule(CloudFormationModel):
         event_bus_arn = properties.get("EventBusName")
         tags = properties.get("Tags")
 
-        backend = events_backends[account_id][region_name]
-        return backend.put_rule(
+        backend: "EventsBackend" = events_backends[account_id][region_name]
+        rule = backend.put_rule(
             event_name,
             scheduled_expression=scheduled_expression,
             event_pattern=event_pattern,
@@ -300,6 +313,16 @@ class Rule(CloudFormationModel):
             tags=tags,
         )
 
+        targets = properties.get("Targets", [])
+        if targets:
+            backend.put_targets(
+                name=rule.name,
+                event_bus_arn=event_bus_arn,
+                targets=targets,
+            )
+
+        return rule
+
     @classmethod
     def update_from_cloudformation_json(  # type: ignore[misc]
         cls,
@@ -322,9 +345,10 @@ class Rule(CloudFormationModel):
         account_id: str,
         region_name: str,
     ) -> None:
-        event_backend = events_backends[account_id][region_name]
+        event_backend: EventsBackend = events_backends[account_id][region_name]
         properties = cloudformation_json["Properties"]
         event_bus_arn = properties.get("EventBusName")
+
         event_backend.delete_rule(resource_name, event_bus_arn)
 
     def describe(self) -> Dict[str, Any]:
@@ -378,6 +402,8 @@ class EventBus(CloudFormationModel):
 
     def delete(self, account_id: str, region_name: str) -> None:
         event_backend = events_backends[account_id][region_name]
+        for rule in self.rules.values():
+            rule.delete(account_id, region_name)
         event_backend.delete_event_bus(name=self.name)
 
     @classmethod
@@ -576,6 +602,7 @@ class Archive(CloudFormationModel):
 
         self.events: List[EventMessageType] = []
         self.event_bus_name = source_arn.split("/")[-1]
+        self.rule: Optional[Rule] = None
 
     def describe_short(self) -> Dict[str, Any]:
         return {
@@ -612,7 +639,11 @@ class Archive(CloudFormationModel):
             self.retention = retention
 
     def delete(self, account_id: str, region_name: str) -> None:
-        event_backend = events_backends[account_id][region_name]
+        event_backend: EventsBackend = events_backends[account_id][region_name]
+
+        if self.rule:
+            self.rule.delete(account_id, region_name)
+
         event_backend.archives.pop(self.name)
 
     @classmethod
@@ -756,6 +787,7 @@ class Replay(BaseModel):
                         event,
                         **{"id": str(random.uuid4()), "replay-name": self.name},  # type: ignore
                     ),
+                    transform_input=False,
                 )
 
         self.state = ReplayState.COMPLETED
@@ -1148,7 +1180,11 @@ class EventsBackend(BaseBackend):
 
     def delete_rule(self, name: str, event_bus_arn: Optional[str]) -> None:
         event_bus_name = self._normalize_event_bus_arn(event_bus_arn)
-        event_bus = self._get_event_bus(event_bus_name)
+        try:
+            event_bus = self._get_event_bus(event_bus_name)
+        except ResourceNotFoundException:
+            # If the EventBus is deleted, the Rule is also gone
+            return
         rule = event_bus.rules.get(name)
         if not rule:
             return
@@ -1350,6 +1386,7 @@ class EventsBackend(BaseBackend):
                     event_msg["region"] = self.region_name
                     event_msg["resources"] = event.get("Resources", [])
                     event_msg["detail"] = json.loads(event["Detail"])
+                    event_msg["ingestion-time"] = unix_time()
                     rule.send_to_targets(event_msg)
 
         return entries
@@ -1610,6 +1647,7 @@ class EventsBackend(BaseBackend):
             event_bus_arn=event_bus.name,
             managed_by="prod.vhs.events.aws.internal",
         )
+        archive.rule = rule
         self.put_targets(
             rule.name,
             rule.event_bus_name,
@@ -1619,13 +1657,8 @@ class EventsBackend(BaseBackend):
                     "Arn": f"arn:{get_partition(self.region_name)}:events:{self.region_name}:::",
                     "InputTransformer": {
                         "InputPathsMap": {},
-                        "InputTemplate": json.dumps(
-                            {
-                                "archive-arn": f"{archive.arn}:{archive.uuid}",
-                                "event": "<aws.events.event.json>",
-                                "ingestion-time": "<aws.events.event.ingestion-time>",
-                            }
-                        ),
+                        # Template is not valid JSON, so we can't json.dump it
+                        "InputTemplate": f'{{"archive-arn": "{archive.arn}:{archive.uuid}", "event": <aws.events.event.json>, "ingestion-time": <aws.events.event.ingestion-time>}}',
                     },
                 }
             ],
diff --git a/moto/events/utils.py b/moto/events/utils.py
index e84c68548..6c4640f29 100644
--- a/moto/events/utils.py
+++ b/moto/events/utils.py
@@ -1,4 +1,5 @@
-from typing import TYPE_CHECKING, List, TypedDict
+import json
+from typing import TYPE_CHECKING, Any, Dict, List, TypedDict
 
 if TYPE_CHECKING:
     from typing_extensions import Any, Dict, Required, Union
@@ -19,6 +20,7 @@ EventMessageType = TypedDict(
         "region": str,
         "resources": List[str],
         "detail": "Required[Dict[str, Any]]",
+        "ingestion-time": float,
     },
     total=False,
 )
@@ -58,3 +60,58 @@ _BASE_EVENT_MESSAGE: EventMessageType = {
     "resources": [],
     "detail": {},
 }
+
+
+class EventTemplateParser:
+    DEFAULT_EVENT_INPUT_TEMPLATE = '{"id": "<id>", "time": <time>, "version": "0", "detail-type": "<detail-type>", "source": "<source>", "region": "<region>", "resources": <resources>, "detail": <detail>}'
+    DEFAULT_EVENT_INPUT_PATHS_MAP = {
+        "account": "$.account",
+        "detail": "$.detail",
+        "detail-type": "$.detail-type",
+        "source": "$.source",
+        "id": "$.id",
+        "region": "$.region",
+        "resources": "$.resources",
+        "time": "$.time",
+    }
+
+    @staticmethod
+    def _stringify(result: Any) -> str:  # type: ignore[misc]
+        if isinstance(result, dict):
+            result = json.dumps(result)
+        elif isinstance(result, list):
+            result = json.dumps([EventTemplateParser._stringify(x) for x in result])
+        elif isinstance(result, (int, float)):
+            result = str(result)
+        return result
+
+    @staticmethod
+    def parse(  # type: ignore[misc]
+        input_template: str, input_paths_map: Dict[str, Any], event: EventMessageType
+    ) -> Dict[str, Any]:
+        from jsonpath_ng.ext import parse
+
+        template_to_use = (
+            input_template or EventTemplateParser.DEFAULT_EVENT_INPUT_TEMPLATE
+        )
+        input_paths_map = (
+            input_paths_map or EventTemplateParser.DEFAULT_EVENT_INPUT_PATHS_MAP
+        )
+        for input_path in input_paths_map:
+            input_expr = parse(input_paths_map[input_path])
+            matches = input_expr.find(event)
+            result = (
+                EventTemplateParser._stringify(matches[0].value) if matches else None
+            )
+            if result:
+                template_to_use = template_to_use.replace(f"<{input_path}>", result)
+
+        default_inputs_map = {
+            "aws.events.event.json": event,
+            "aws.events.event.ingestion-time": event["ingestion-time"],
+        }
+        for input_path in default_inputs_map:
+            result = EventTemplateParser._stringify(default_inputs_map[input_path])
+            template_to_use = template_to_use.replace(f"<{input_path}>", result)
+
+        return json.loads(template_to_use)
diff --git a/moto/rds/models.py b/moto/rds/models.py
index e8d56b279..a03630690 100644
--- a/moto/rds/models.py
+++ b/moto/rds/models.py
@@ -999,6 +999,13 @@ class DBInstance(CloudFormationModel, RDSBaseModel):
             if value is not None:
                 setattr(self, key, value)
 
+        cwl_exports = db_kwargs.get("cloudwatch_logs_exports_config") or {}
+        for exp in cwl_exports.get("DisableLogTypes", []):
+            self.enabled_cloudwatch_logs_exports.remove(exp)
+        self.enabled_cloudwatch_logs_exports.extend(
+            cwl_exports.get("EnableLogTypes", [])
+        )
+
     @classmethod
     def has_cfn_attr(cls, attr: str) -> bool:
         return attr in ["Endpoint.Address", "Endpoint.Port"]
diff --git a/moto/rds/responses.py b/moto/rds/responses.py
index 632d2327e..53a7bcbf9 100644
--- a/moto/rds/responses.py
+++ b/moto/rds/responses.py
@@ -37,6 +37,9 @@ class RDSResponse(BaseResponse):
             "enable_cloudwatch_logs_exports": self._get_params().get(
                 "EnableCloudwatchLogsExports"
             ),
+            "cloudwatch_logs_exports_config": self._get_params().get(
+                "CloudwatchLogsExportConfiguration"
+            ),
             "enable_iam_database_authentication": self._get_bool_param(
                 "EnableIAMDatabaseAuthentication"
             ),
diff --git a/moto/s3/notifications.py b/moto/s3/notifications.py
index 9f935a8f2..6dcae0007 100644
--- a/moto/s3/notifications.py
+++ b/moto/s3/notifications.py
@@ -203,7 +203,7 @@ def _send_event_bridge_message(
         events_backend = events_backends[account_id][bucket.region_name]
         for event_bus in events_backend.event_buses.values():
             for rule in event_bus.rules.values():
-                rule.send_to_targets(event)
+                rule.send_to_targets(event, transform_input=False)
 
     except:  # noqa
         # This is an async action in AWS.
diff --git a/setup.cfg b/setup.cfg
index 03d5fdb5c..31a38c272 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -162,7 +162,7 @@ emr =
 emrcontainers =
 emrserverless =
 es =
-events =
+events = jsonpath_ng
 firehose =
 forecast =
 glacier =


Test patch:
diff --git a/.github/workflows/tests_real_aws.yml b/.github/workflows/tests_real_aws.yml
index 736b47a55..f3d204bbb 100644
--- a/.github/workflows/tests_real_aws.yml
+++ b/.github/workflows/tests_real_aws.yml
@@ -44,4 +44,4 @@ jobs:
       env:
         MOTO_TEST_ALLOW_AWS_REQUEST: ${{ true }}
       run: |
-        pytest -sv -n auto tests/test_applicationautoscaling/ tests/test_athena/ tests/test_cloudformation/ tests/test_dynamodb/ tests/test_ec2/ tests/test_iam/ tests/test_iot/ tests/test_lakeformation/ tests/test_logs/ tests/test_sqs/ tests/test_ses/ tests/test_s3* tests/test_stepfunctions/ tests/test_sns/ tests/test_timestreamwrite/ -m aws_verified
+        pytest -sv -n auto tests/test_applicationautoscaling/ tests/test_athena/ tests/test_cloudformation/ tests/test_dynamodb/ tests/test_ec2/ tests/test_events/ tests/test_iam/ tests/test_iot/ tests/test_lakeformation/ tests/test_logs/ tests/test_sqs/ tests/test_ses/ tests/test_s3* tests/test_stepfunctions/ tests/test_sns/ tests/test_timestreamwrite/ -m aws_verified
diff --git a/tests/test_events/test_events.py b/tests/test_events/test_events.py
index b2fe8dd78..babba5bee 100644
--- a/tests/test_events/test_events.py
+++ b/tests/test_events/test_events.py
@@ -13,7 +13,7 @@ from botocore.exceptions import ClientError
 from moto import mock_aws, settings
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
 from moto.core.utils import iso_8601_datetime_without_milliseconds
-from tests import aws_verified
+from tests import allow_aws_request, aws_verified
 
 RULES = [
     {"Name": "test1", "ScheduleExpression": "rate(5 minutes)"},
@@ -1584,14 +1584,17 @@ def test_delete_archive_error_unknown_archive():
     assert ex.response["Error"]["Message"] == f"Archive {name} does not exist."
 
 
-@mock_aws
+@aws_verified
+@pytest.mark.aws_verified
 def test_archive_actual_events():
     # given
+    sts = boto3.client("sts", "us-east-1")
+    account_id = sts.get_caller_identity()["Account"]
     client = boto3.client("events", "eu-central-1")
     name = "test-archive"
     name_2 = "test-archive-no-match"
     name_3 = "test-archive-matches"
-    event_bus_arn = f"arn:aws:events:eu-central-1:{ACCOUNT_ID}:event-bus/default"
+    event_bus_arn = f"arn:aws:events:eu-central-1:{account_id}:event-bus/default"
     event = {
         "Source": "source",
         "DetailType": "type",
@@ -1609,24 +1612,72 @@ def test_archive_actual_events():
         EventPattern=json.dumps({"detail-type": ["type"], "source": ["source"]}),
     )
 
+    # then
+    rules = client.list_rules()["Rules"]
+    assert len(rules) >= 3
+    for rule in rules:
+        assert rule["Name"] in [
+            f"Events-Archive-{name}",
+            f"Events-Archive-{name_2}",
+            f"Events-Archive-{name_3}",
+        ]
+        assert rule["ManagedBy"] == "prod.vhs.events.aws.internal"
+        if rule["Name"] == f"Events-Archive-{name}":
+            assert json.loads(rule["EventPattern"]) == {
+                "replay-name": [{"exists": False}]
+            }
+        if rule["Name"] == f"Events-Archive-{name_2}":
+            assert json.loads(rule["EventPattern"]) == {
+                "detail-type": ["type"],
+                "replay-name": [{"exists": False}],
+                "source": ["test"],
+            }
+        if rule["Name"] == f"Events-Archive-{name_3}":
+            assert json.loads(rule["EventPattern"]) == {
+                "detail-type": ["type"],
+                "replay-name": [{"exists": False}],
+                "source": ["source"],
+            }
+
+        targets = client.list_targets_by_rule(Rule=rule["Name"])["Targets"]
+        assert len(targets) == 1
+        assert targets[0]["Arn"] == "arn:aws:events:eu-central-1:::"
+
+        transformer = targets[0]["InputTransformer"]
+        assert transformer["InputPathsMap"] == {}
+
+        template = transformer["InputTemplate"]
+        assert '"archive-arn"' in template
+        assert '"event": <aws.events.event.json>' in template
+        assert '"ingestion-time": <aws.events.event.ingestion-time>' in template
+
     # when
     response = client.put_events(Entries=[event])
-
-    # then
     assert response["FailedEntryCount"] == 0
     assert len(response["Entries"]) == 1
 
-    response = client.describe_archive(ArchiveName=name)
-    assert response["EventCount"] == 1
-    assert response["SizeBytes"] > 0
+    # then
+    if not allow_aws_request():
+        # AWS doesn't (immediately) update the EventCount
+        # Only test this against Moto
+        response = client.describe_archive(ArchiveName=name)
+        assert response["EventCount"] == 1
+        assert response["SizeBytes"] > 0
 
-    response = client.describe_archive(ArchiveName=name_2)
-    assert response["EventCount"] == 0
-    assert response["SizeBytes"] == 0
+        response = client.describe_archive(ArchiveName=name_2)
+        assert response["EventCount"] == 0
+        assert response["SizeBytes"] == 0
 
-    response = client.describe_archive(ArchiveName=name_3)
-    assert response["EventCount"] == 1
-    assert response["SizeBytes"] > 0
+        response = client.describe_archive(ArchiveName=name_3)
+        assert response["EventCount"] == 1
+        assert response["SizeBytes"] > 0
+
+    client.delete_archive(ArchiveName=name)
+    client.delete_archive(ArchiveName=name_2)
+    client.delete_archive(ArchiveName=name_3)
+
+    rules = client.list_rules()["Rules"]
+    assert rules == []
 
 
 @mock_aws
diff --git a/tests/test_events/test_events_cloudformation.py b/tests/test_events/test_events_cloudformation.py
index f9f808ea1..c643d9b63 100644
--- a/tests/test_events/test_events_cloudformation.py
+++ b/tests/test_events/test_events_cloudformation.py
@@ -1,6 +1,7 @@
 import copy
 import json
 from string import Template
+from uuid import uuid4
 
 import boto3
 import pytest
@@ -8,6 +9,7 @@ from botocore.exceptions import ClientError
 
 from moto import mock_aws
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+from tests import aws_verified
 
 archive_template = Template(
     json.dumps(
@@ -69,6 +71,64 @@ empty = json.dumps(
     }
 )
 
+rule_with_targets = """---
+AWSTemplateFormatVersion: 2010-09-09
+Resources:
+  MyEventBus:
+    Type: AWS::Events::EventBus
+    Properties:
+      Name: "TestEventBusName"
+
+  DestQueue:
+    Type: AWS::SQS::Queue
+
+  MyQueuePolicy:
+    Type: AWS::SQS::QueuePolicy
+    Properties:
+      PolicyDocument:
+          Version: 2012-10-17
+          Statement:
+            - Effect: Allow
+              Principal:
+                Service: events.amazonaws.com
+              Action: sqs:SendMessage
+              Resource: !GetAtt DestQueue.Arn
+              Condition:
+                ArnEquals:
+                  aws:SourceArn: !GetAtt MyRule.Arn
+            - Effect: Allow
+              Principal:
+                Service: events.amazonaws.com
+              Action: sqs:SendMessage
+              Resource: !GetAtt DestQueue.Arn
+      Queues:
+        - !Ref DestQueue
+
+  MyRule:
+    Type: AWS::Events::Rule
+    Properties:
+      EventBusName: !GetAtt MyEventBus.Name
+      EventPattern:
+        source:
+          - my.source
+        detail-type:
+          - Malicious
+      State: ENABLED
+      Targets:
+        - Id: meh
+          Arn: !GetAtt DestQueue.Arn
+          InputTransformer:
+            InputPathsMap:
+              account: $.account
+              detail: $.detail
+              detail-type: $.detail-type
+              id: $.id
+              region: $.region
+              resources: $.resources
+              time: $.time
+            InputTemplate: '{\"id\": \"<id>\",\"detail-type\": \"<detail-type>\",\"time\":\"<time>\",\"region\": \"<region>\",\"account\": \"<account>\",\"resources\":<resources>,\"http_method\": \"POST\",\"http_endpoint\":\"/test/endpoint\",\"detail\":<detail>}'
+"""
+
 
 @mock_aws
 def test_create_archive():
@@ -183,3 +243,71 @@ def test_delete_rule():
     err = exc.value.response["Error"]
     assert err["Code"] == "ResourceNotFoundException"
     assert err["Message"] == "Rule test-rule does not exist."
+
+
+@aws_verified
+@pytest.mark.aws_verified
+def test_create_rule_with_targets():
+    # given
+    cfn_client = boto3.client("cloudformation", region_name="eu-central-1")
+    sqs_client = boto3.client("sqs", region_name="eu-central-1")
+    sts = boto3.client("sts", "us-east-1")
+
+    account_id = sts.get_caller_identity()["Account"]
+    stack_name = f"test-stack-{str(uuid4())[0:6]}"
+
+    # when
+    cfn_client.create_stack(StackName=stack_name, TemplateBody=rule_with_targets)
+    cfn_client.get_waiter("stack_create_complete").wait(StackName=stack_name)
+    try:
+        # then
+        events_client = boto3.client("events", region_name="eu-central-1")
+        rule = events_client.list_rules(EventBusName="TestEventBusName")["Rules"][0]
+
+        targets = events_client.list_targets_by_rule(
+            Rule=rule["Name"], EventBusName="TestEventBusName"
+        )["Targets"]
+        assert len(targets) == 1
+        assert targets[0]["Id"] == "meh"
+        assert targets[0]["InputTransformer"]["InputPathsMap"]["account"] == "$.account"
+
+        resources = cfn_client.list_stack_resources(StackName=stack_name)[
+            "StackResourceSummaries"
+        ]
+        queue = next((r for r in resources if r["LogicalResourceId"] == "DestQueue"))
+        q_url = queue["PhysicalResourceId"]
+
+        entry = dict(
+            Detail='{"a": "b"}',
+            DetailType="Malicious",
+            EventBusName="TestEventBusName",
+            Resources=["path/to/file"],
+            Source="my.source",
+        )
+        entry_id = events_client.put_events(Entries=[entry])["Entries"][0]["EventId"]
+
+        messages = sqs_client.receive_message(QueueUrl=q_url, WaitTimeSeconds=20)[
+            "Messages"
+        ]
+        msg_body = json.loads(messages[0]["Body"])
+
+        msg_body.pop("time")
+        assert msg_body == {
+            "id": entry_id,
+            "detail-type": "Malicious",
+            "region": "eu-central-1",
+            "account": account_id,
+            "resources": ["path/to/file"],
+            "http_method": "POST",
+            "http_endpoint": "/test/endpoint",
+            "detail": {"a": "b"},
+        }
+
+    finally:
+        cfn_client.delete_stack(StackName=stack_name)
+        cfn_client.get_waiter("stack_delete_complete").wait(StackName=stack_name)
+
+        with pytest.raises(ClientError) as exc:
+            events_client.list_rules(EventBusName="TestEventBusName")
+        err = exc.value.response["Error"]
+        assert err["Code"] == "ResourceNotFoundException"
diff --git a/tests/test_rds/test_rds.py b/tests/test_rds/test_rds.py
index 7b95748a9..6ed50fb0e 100644
--- a/tests/test_rds/test_rds.py
+++ b/tests/test_rds/test_rds.py
@@ -551,7 +551,7 @@ def test_describe_non_existent_database():
 def test_modify_db_instance():
     conn = boto3.client("rds", region_name=DEFAULT_REGION)
     conn.create_db_instance(
-        DBInstanceIdentifier="db-master-1",
+        DBInstanceIdentifier="db-id",
         AllocatedStorage=10,
         DBInstanceClass="postgres",
         Engine="postgres",
@@ -560,23 +560,22 @@ def test_modify_db_instance():
         Port=1234,
         DBSecurityGroups=["my_sg"],
     )
-    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
-    assert instances["DBInstances"][0]["AllocatedStorage"] == 10
+    inst = conn.describe_db_instances(DBInstanceIdentifier="db-id")["DBInstances"][0]
+    assert inst["AllocatedStorage"] == 10
+    assert inst["EnabledCloudwatchLogsExports"] == []
+
     conn.modify_db_instance(
-        DBInstanceIdentifier="db-master-1",
+        DBInstanceIdentifier="db-id",
         AllocatedStorage=20,
         ApplyImmediately=True,
         VpcSecurityGroupIds=["sg-123456"],
+        CloudwatchLogsExportConfiguration={"EnableLogTypes": ["error"]},
     )
-    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
-    assert instances["DBInstances"][0]["AllocatedStorage"] == 20
-    assert instances["DBInstances"][0]["PreferredMaintenanceWindow"] == (
-        "wed:06:38-wed:07:08"
-    )
-    assert (
-        instances["DBInstances"][0]["VpcSecurityGroups"][0]["VpcSecurityGroupId"]
-        == "sg-123456"
-    )
+    inst = conn.describe_db_instances(DBInstanceIdentifier="db-id")["DBInstances"][0]
+    assert inst["AllocatedStorage"] == 20
+    assert inst["PreferredMaintenanceWindow"] == "wed:06:38-wed:07:08"
+    assert inst["VpcSecurityGroups"][0]["VpcSecurityGroupId"] == "sg-123456"
+    assert inst["EnabledCloudwatchLogsExports"] == ["error"]
 
 
 @mock_aws