Repository: ansible-collections/amazon.aws

Issue:
rds_instance cannot clear cloudwatch log exports
### Summary

rds_instance cannot clear all existing cloudwatch log exports by setting `enable_cloudwatch_logs_exports: []`.

The root cause appears to be [this test](https://github.com/ansible-collections/amazon.aws/blob/stable-5/plugins/modules/rds_instance.py#L995) which evaluates to False for empty lists. I believe this should be `if cloudwatch_logs_enabled is not None:` instead. I've linked to version 5 but all are the same, including the main branch.

### Issue Type

Bug Report

### Component Name

rds_instance

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.14.7]
  config file = /home/<user>/.../ansible.cfg
  configured module search path = ['/home/<user>/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/<user>/venv/ansible-2.14/lib/python3.9/site-packages/ansible
  ansible collection location = /home/<user>/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/<user>/venv/ansible-2.14/bin/ansible
  python version = 3.9.5 (default, Nov 23 2021, 15:27:38) [GCC 9.3.0] (/home/<user>/venv/ansible-2.14/bin/python3.9)
  jinja version = 3.0.3
  libyaml = True
```


### Collection Versions

```console (paste below)
$ ansible-galaxy collection list
Collection                    Version
----------------------------- -------
amazon.aws                    5.4.0
ansible.netcommon             4.1.0
ansible.posix                 1.5.2
ansible.utils                 2.9.0
ansible.windows               1.13.0
arista.eos                    6.0.1
awx.awx                       21.14.0
azure.azcollection            1.15.0
check_point.mgmt              4.0.0
chocolatey.chocolatey         1.4.0
cisco.aci                     2.6.0
cisco.asa                     4.0.0
cisco.dnac                    6.7.1
cisco.intersight              1.0.27
cisco.ios                     4.5.0
cisco.iosxr                   4.1.0
cisco.ise                     2.5.12
cisco.meraki                  2.15.1
cisco.mso                     2.4.0
cisco.nso                     1.0.3
cisco.nxos                    4.3.0
cisco.ucs                     1.8.0
cloud.common                  2.1.3
cloudscale_ch.cloud           2.2.4
community.aws                 5.4.0
community.azure               2.0.0
community.ciscosmb            1.0.5
community.crypto              2.12.0
community.digitalocean        1.23.0
community.dns                 2.5.3
community.docker              3.4.3
community.fortios             1.0.0
community.general             6.6.0
community.google              1.0.0
community.grafana             1.5.4
community.hashi_vault         4.2.0
community.hrobot              1.8.0
community.libvirt             1.2.0
community.mongodb             1.5.2
community.mysql               3.6.0
community.network             5.0.0
community.okd                 2.3.0
community.postgresql          2.3.2
community.proxysql            1.5.1
community.rabbitmq            1.2.3
community.routeros            2.8.0
community.sap                 1.0.0
community.sap_libs            1.4.1
community.skydive             1.0.0
community.sops                1.6.1
community.vmware              3.5.0
community.windows             1.12.0
community.zabbix              1.9.3
containers.podman             1.10.1
cyberark.conjur               1.2.0
cyberark.pas                  1.0.17
dellemc.enterprise_sonic      2.0.0
dellemc.openmanage            6.3.0
dellemc.os10                  1.1.1
dellemc.os6                   1.0.7
dellemc.os9                   1.0.4
dellemc.powerflex             1.6.0
dellemc.unity                 1.6.0
f5networks.f5_modules         1.23.0
fortinet.fortimanager         2.1.7
fortinet.fortios              2.2.3
frr.frr                       2.0.2
gluster.gluster               1.0.2
google.cloud                  1.1.3
grafana.grafana               1.1.1
hetzner.hcloud                1.11.0
hpe.nimble                    1.1.4
ibm.qradar                    2.1.0
ibm.spectrum_virtualize       1.11.0
infinidat.infinibox           1.3.12
infoblox.nios_modules         1.4.1
inspur.ispim                  1.3.0
inspur.sm                     2.3.0
junipernetworks.junos         4.1.0
kubernetes.core               2.4.0
lowlydba.sqlserver            1.3.1
mellanox.onyx                 1.0.0
microsoft.ad                  1.0.0
netapp.aws                    21.7.0
netapp.azure                  21.10.0
netapp.cloudmanager           21.22.0
netapp.elementsw              21.7.0
netapp.ontap                  22.5.0
netapp.storagegrid            21.11.1
netapp.um_info                21.8.0
netapp_eseries.santricity     1.4.0
netbox.netbox                 3.12.0
ngine_io.cloudstack           2.3.0
ngine_io.exoscale             1.0.0
ngine_io.vultr                1.1.3
openstack.cloud               1.10.0
openvswitch.openvswitch       2.1.0
ovirt.ovirt                   2.4.1
purestorage.flasharray        1.17.2
purestorage.flashblade        1.11.0
purestorage.fusion            1.4.2
sensu.sensu_go                1.13.2
splunk.es                     2.1.0
t_systems_mms.icinga_director 1.32.2
theforeman.foreman            3.10.0
vmware.vmware_rest            2.3.1
vultr.cloud                   1.7.0
vyos.vyos                     4.0.2
wti.remote                    1.0.4
```


### AWS SDK versions

```console (paste below)
$ pip show boto boto3 botocore
Name: boto
Version: 2.49.0
Summary: Amazon Web Services Library
Home-page: https://github.com/boto/boto/
Author: Mitch Garnaat
Author-email: mitch@garnaat.com
License: MIT
Location: /home/<user>/venv/ansible-2.14/lib/python3.9/site-packages
Requires:
Required-by:
---
Name: boto3
Version: 1.28.15
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email:
License: Apache License 2.0
Location: /home/<user>/venv/ansible-2.14/lib/python3.9/site-packages
Requires: botocore, jmespath, s3transfer
Required-by:
---
Name: botocore
Version: 1.31.15
Summary: Low-level, data-driven core of boto 3.
Home-page: https://github.com/boto/botocore
Author: Amazon Web Services
Author-email:
License: Apache License 2.0
Location: /home/<user>/venv/ansible-2.14/lib/python3.9/site-packages
Requires: jmespath, python-dateutil, urllib3
Required-by: awscli, boto3, s3transfer

```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed
CALLBACKS_ENABLED(/home/<user>/.../ansible.cfg) = ['profile_tasks']
CONFIG_FILE() = /home/<user>/.../ansible.cfg
```


### OS / Environment

Ubuntu

### Steps to Reproduce

Create database with some cloudwatch log exports, for example `enable_cloudwatch_logs_exports: ['postgresql', 'upgrade']`.
Try to delete them
```yaml (paste below)
    - name: Disable log exports
      amazon.aws.rds_instance:
        db_instance_identifier: "example"
        enable_cloudwatch_logs_exports: []
```

Running with something else, like `['upgrade']` removes `postgresql` as expected.

### Expected Results

Changed status, log exports to be deleted.

### Actual Results

Cropped results from task
```console (paste below)
"enabled_cloudwatch_logs_exports": [
            "postgresql",
            "upgrade"
        ],
"pending_modified_values": {},
```
No changed status, no changes attempted.

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Thanks @andreiboost for submitting this! Would you be willing to open a PR to fix this bug?
Perhaps, my only reservation is that I'd like to add tests for this but those can be time consuming to add, especially for a one liner change like this appears to be.

Resolving patch:
diff --git a/plugins/modules/rds_instance.py b/plugins/modules/rds_instance.py
--- a/plugins/modules/rds_instance.py
+++ b/plugins/modules/rds_instance.py
@@ -43,7 +43,9 @@
         type: bool
         default: false
     purge_cloudwatch_logs_exports:
-        description: Set to C(false) to retain any enabled cloudwatch logs that aren't specified in the task and are associated with the instance.
+        description:
+          - Set to C(false) to retain any enabled cloudwatch logs that aren't specified in the task and are associated with the instance.
+          - Set I(enable_cloudwatch_logs_exports) to an empty list to disable all.
         type: bool
         default: true
     read_replica:
@@ -1028,7 +1030,7 @@ def get_options_with_changing_values(client, module, parameters):
         parameters["DBPortNumber"] = port
     if not force_update_password:
         parameters.pop("MasterUserPassword", None)
-    if cloudwatch_logs_enabled:
+    if cloudwatch_logs_enabled is not None:
         parameters["CloudwatchLogsExportConfiguration"] = cloudwatch_logs_enabled
     if not module.params["storage_type"]:
         parameters.pop("Iops", None)


Test patch:
diff --git a/tests/integration/targets/rds_instance_modify/tasks/main.yml b/tests/integration/targets/rds_instance_modify/tasks/main.yml
--- a/tests/integration/targets/rds_instance_modify/tasks/main.yml
+++ b/tests/integration/targets/rds_instance_modify/tasks/main.yml
@@ -82,7 +82,7 @@
           - '"is not valid for adding IAM roles" in result.msg'
 
     # TODO: test modifying db_subnet_group_name, db_security_groups, db_parameter_group_name, option_group_name,
-    # monitoring_role_arn, monitoring_interval, domain, domain_iam_role_name, cloudwatch_logs_export_configuration
+    # monitoring_role_arn, monitoring_interval, domain, domain_iam_role_name
 
     # ------------------------------------------------------------------------------------------
     - name: Modify the storage type without immediate application - check_mode
@@ -306,6 +306,193 @@
           - db_info.instances[0].ca_certificate_identifier == "rds-ca-ecc384-g1"
   # Test modifying CA certificate identifier Complete-------------------------------------------
 
+  # Test modifying cloudwatch log exports -------------------------------------------
+    - name: Enable all cloudwatch log exports - check_mode
+      amazon.aws.rds_instance:
+        state: present
+        db_instance_identifier: "{{ modified_instance_id }}"
+        enable_cloudwatch_logs_exports: ["audit", "error", "general", "slowquery"]
+      register: result
+      check_mode: true
+      vars:
+        ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}"
+
+    - name: Get current cloudwatch log exports
+      amazon.aws.rds_instance_info:
+        db_instance_identifier: "{{ modified_instance_id }}"
+      register: db_info
+    - name: Assert that cloudwatch log exports has been modified - check_mode
+      ansible.builtin.assert:
+        that:
+          - result is changed
+          - result is not failed
+          - db_info.instances[0].enabled_cloudwatch_logs_exports is not defined
+
+    - name: Enable all cloudwatch log exports
+      amazon.aws.rds_instance:
+        state: present
+        db_instance_identifier: "{{ modified_instance_id }}"
+        enable_cloudwatch_logs_exports: ["audit", "error", "general", "slowquery"]
+      register: result
+      vars:
+        ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}"
+
+    - name: Get current cloudwatch log exports
+      amazon.aws.rds_instance_info:
+        db_instance_identifier: "{{ modified_instance_id }}"
+      register: db_info
+      # It applies immediately but takes a couple seconds
+      until:
+        - db_info.instances[0].db_instance_status == 'available'
+        - not db_info.instances[0].pending_modified_values
+      retries: 10
+      delay: 20
+
+    - name: Assert that cloudwatch log exports has been modified
+      ansible.builtin.assert:
+        that:
+          - result is changed
+          - result is not failed
+          - db_info.instances[0].enabled_cloudwatch_logs_exports | length == 4
+
+    - name: Enable all cloudwatch log exports - idempotent
+      amazon.aws.rds_instance:
+        state: present
+        db_instance_identifier: "{{ modified_instance_id }}"
+        enable_cloudwatch_logs_exports: ["audit", "error", "general", "slowquery"]
+      register: result
+      vars:
+        ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}"
+
+    - name: Get current cloudwatch log exports
+      amazon.aws.rds_instance_info:
+        db_instance_identifier: "{{ modified_instance_id }}"
+      register: db_info
+    - name: Assert that cloudwatch log exports has not been modified
+      ansible.builtin.assert:
+        that:
+          - result is not changed
+          - result is not failed
+          - not result.pending_modified_values
+          - db_info.instances[0].enabled_cloudwatch_logs_exports | length == 4
+
+    - name: Disable some cloudwatch log exports - check_mode
+      amazon.aws.rds_instance:
+        state: present
+        db_instance_identifier: "{{ modified_instance_id }}"
+        enable_cloudwatch_logs_exports: ["audit", "error"]
+      register: result
+      check_mode: true
+      vars:
+        ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}"
+
+    - name: Get current cloudwatch log exports
+      amazon.aws.rds_instance_info:
+        db_instance_identifier: "{{ modified_instance_id }}"
+      register: db_info
+    - name: Assert that cloudwatch log exports has been modified - check_mode
+      ansible.builtin.assert:
+        that:
+          - result is changed
+          - result is not failed
+          - db_info.instances[0].enabled_cloudwatch_logs_exports | length == 4
+
+    - name: Disable some cloudwatch log exports
+      amazon.aws.rds_instance:
+        state: present
+        db_instance_identifier: "{{ modified_instance_id }}"
+        enable_cloudwatch_logs_exports: ["audit", "error"]
+      register: result
+      vars:
+        ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}"
+
+    - name: Get current cloudwatch log exports
+      amazon.aws.rds_instance_info:
+        db_instance_identifier: "{{ modified_instance_id }}"
+      register: db_info
+      until:
+        - db_info.instances[0].db_instance_status == 'available'
+        - not db_info.instances[0].pending_modified_values
+      retries: 10
+      delay: 20
+
+    - name: Assert that cloudwatch log exports has been modified
+      ansible.builtin.assert:
+        that:
+          - result is changed
+          - result is not failed
+          - db_info.instances[0].enabled_cloudwatch_logs_exports | length == 2
+
+    - name: Disable all cloudwatch log exports - no purge
+      amazon.aws.rds_instance:
+        state: present
+        db_instance_identifier: "{{ modified_instance_id }}"
+        enable_cloudwatch_logs_exports: []
+        purge_cloudwatch_logs_exports: false
+      register: result
+      vars:
+        ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}"
+
+    - name: Get current cloudwatch log exports
+      amazon.aws.rds_instance_info:
+        db_instance_identifier: "{{ modified_instance_id }}"
+      register: db_info
+    - name: Assert that cloudwatch log exports has not been modified
+      ansible.builtin.assert:
+        that:
+          - result is not changed
+          - result is not failed
+          - not result.pending_modified_values
+          - db_info.instances[0].enabled_cloudwatch_logs_exports | length == 2
+
+    - name: Disable all cloudwatch log exports - check_mode
+      amazon.aws.rds_instance:
+        state: present
+        db_instance_identifier: "{{ modified_instance_id }}"
+        enable_cloudwatch_logs_exports: []
+      register: result
+      check_mode: true
+      vars:
+        ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}"
+
+    - name: Get current cloudwatch log exports
+      amazon.aws.rds_instance_info:
+        db_instance_identifier: "{{ modified_instance_id }}"
+      register: db_info
+    - name: Assert that cloudwatch log exports has been modified - check_mode
+      ansible.builtin.assert:
+        that:
+          - result is changed
+          - result is not failed
+          - db_info.instances[0].enabled_cloudwatch_logs_exports | length == 2
+
+    - name: Disable all cloudwatch log exports
+      amazon.aws.rds_instance:
+        state: present
+        db_instance_identifier: "{{ modified_instance_id }}"
+        enable_cloudwatch_logs_exports: []
+      register: result
+      vars:
+        ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}"
+
+    - name: Get current cloudwatch log exports
+      amazon.aws.rds_instance_info:
+        db_instance_identifier: "{{ modified_instance_id }}"
+      register: db_info
+      until:
+        - db_info.instances[0].db_instance_status == 'available'
+        - not db_info.instances[0].pending_modified_values
+      retries: 10
+      delay: 20
+
+    - name: Assert that cloudwatch log exports has been modified
+      ansible.builtin.assert:
+        that:
+          - result is changed
+          - result is not failed
+          - db_info.instances[0].enabled_cloudwatch_logs_exports is not defined
+  # Test modifying cloudwatch log exports Complete-------------------------------------------
+
   always:
     - name: Delete the instance
       amazon.aws.rds_instance: