Repository: ansible-collections/amazon.aws

Issue:
cloudwatch_metric_alarm - Existing alarms always report 'changed' even when no changes have been made
### Summary

When running CloudWatch metric alarm playbooks, existing alarms always report 'changed' even when no changes have been made to the alarm settings/props.

After digging into the *cloudwatch_metric_alarm* module code, looks like the prop **StateTransitionedTimestamp** is included when determining any changes when it most likely should not be. (Note: other state props are already excluded - 'StateValue', 'StateReason', 'StateReasonData', 'StateUpdatedTimestamp')

### Issue Type

Bug Report

### Component Name

cloudwatch-metric-alarm

### Ansible Version

```console (paste below)
ansible [core 2.14.3]
  config file = None
  configured module search path = ['/home/davidovp/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/davidovp/.local/lib/python3.10/site-packages/ansible
  ansible collection location = /home/davidovp/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/davidovp/.local/bin/ansible
  python version = 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True
```


### Collection Versions

```console (paste below)
# /home/davidovp/.local/lib/python3.10/site-packages/ansible_collections
Collection                    Version
----------------------------- -------
amazon.aws                    5.2.0  
ansible.netcommon             4.1.0  
ansible.posix                 1.5.1  
ansible.utils                 2.9.0  
ansible.windows               1.13.0 
arista.eos                    6.0.0  
awx.awx                       21.12.0
azure.azcollection            1.14.0 
check_point.mgmt              4.0.0  
chocolatey.chocolatey         1.4.0  
cisco.aci                     2.4.0  
cisco.asa                     4.0.0  
cisco.dnac                    6.6.3  
cisco.intersight              1.0.23 
cisco.ios                     4.3.1  
cisco.iosxr                   4.1.0
cisco.ise                     2.5.12
cisco.meraki                  2.15.1
cisco.mso                     2.2.1
cisco.nso                     1.0.3
cisco.nxos                    4.1.0
cisco.ucs                     1.8.0
cloud.common                  2.1.2
cloudscale_ch.cloud           2.2.4
community.aws                 5.2.0
community.azure               2.0.0
community.ciscosmb            1.0.5
community.crypto              2.11.0
community.digitalocean        1.23.0
community.dns                 2.5.1
community.docker              3.4.2
community.fortios             1.0.0
community.general             6.4.0
community.google              1.0.0
community.grafana             1.5.4
community.hashi_vault         4.1.0
community.hrobot              1.7.0
community.libvirt             1.2.0
community.mongodb             1.5.1
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.7.0
community.sap                 1.0.0
community.sap_libs            1.4.0
community.skydive             1.0.0
community.sops                1.6.1
community.vmware              3.4.0
community.windows             1.12.0
community.zabbix              1.9.2
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.5.0
dellemc.unity                 1.5.0
f5networks.f5_modules         1.22.1
fortinet.fortimanager         2.1.7
fortinet.fortios              2.2.2
frr.frr                       2.0.0
gluster.gluster               1.0.2
google.cloud                  1.1.2
grafana.grafana               1.1.1
hetzner.hcloud                1.10.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
netapp.aws                    21.7.0
netapp.azure                  21.10.0
netapp.cloudmanager           21.22.0
netapp.elementsw              21.7.0
netapp.ontap                  22.3.0
netapp.storagegrid            21.11.1
netapp.um_info                21.8.0
netapp_eseries.santricity     1.4.0
netbox.netbox                 3.11.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.0
purestorage.flashblade        1.10.0
purestorage.fusion            1.3.0
sensu.sensu_go                1.13.2
splunk.es                     2.1.0
t_systems_mms.icinga_director 1.32.0
theforeman.foreman            3.9.0
vmware.vmware_rest            2.2.0
vultr.cloud                   1.7.0
vyos.vyos                     4.0.0
wti.remote                    1.0.4
```


### AWS SDK versions

```console (paste below)
WARNING: Package(s) not found: boto
Name: boto3
Version: 1.26.86
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/davidovp/.local/lib/python3.10/site-packages
Requires: botocore, jmespath, s3transfer
Required-by:
---
Name: botocore
Version: 1.29.86
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/davidovp/.local/lib/python3.10/site-packages
Requires: jmespath, python-dateutil, urllib3
Required-by: boto3, s3transfer
```


### Configuration

```console (paste below)
CONFIG_FILE() = None
```


### OS / Environment

Windows 11 Pro / WSL - Ubuntu 22.04.2 LTS

### Steps to Reproduce

#### Steps to reproduce:

1. Run any cloudwatch metric alarm task/playbook more than once in a row (example below)

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
- hosts: localhost
  become: False
  gather_facts: False
  connection: local
  vars:
    tables:
      - name: Product_1
        read: 6000
        action: "{{ sns.NonCriticalAlerts }}"
  tasks:
    - name: create read alarm
      ec2_metric_alarm:
        state: present
        region: "{{ aws_region }}"
        name: "{{ item.name }}-dynamo-read-iops"
        metric: "ConsumedReadCapacityUnits"
        namespace: "AWS/DynamoDB"
        statistic: Sum
        comparison: GreaterThanOrEqualToThreshold
        threshold: "{{ item.read }}"
        period: 300
        evaluation_periods: 3
        unit: "Count"
        description: "This will alarm when read IOPS are sustained high"
        dimensions:
          TableName: "{{ item.name }}"
        alarm_actions: ["{{ item.action }}"]
        ok_actions: ["{{ item.action }}"]
        treat_missing_data: notBreaching
      with_items: "{{ tables }}"
```


### Expected Results

After the initial run of above playbook, future runs should report no changes (changed=0) unless settings/props are modified.

### Actual Results

After initial playbook run, future playbook runs always report that changes are necessary and makes AWS call to create alarm again.

```console (paste below)
PLAY RECAP **********************************************************************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```


### Code of Conduct

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


Discussion:

Resolving patch:
diff --git a/plugins/modules/cloudwatch_metric_alarm.py b/plugins/modules/cloudwatch_metric_alarm.py
--- a/plugins/modules/cloudwatch_metric_alarm.py
+++ b/plugins/modules/cloudwatch_metric_alarm.py
@@ -333,8 +333,10 @@ def create_metric_alarm(connection, module, params):
         if 'TreatMissingData' not in alarm.keys():
             alarm['TreatMissingData'] = 'missing'
 
+        # Exclude certain props from change detection
         for key in ['ActionsEnabled', 'StateValue', 'StateReason',
                     'StateReasonData', 'StateUpdatedTimestamp',
+                    'StateTransitionedTimestamp',
                     'AlarmArn', 'AlarmConfigurationUpdatedTimestamp', 'Metrics']:
             alarm.pop(key, None)
         if alarm != params:


Test patch: