From daa5ed0e7c7ad72a88a20f017b2bd3629cc9d239 Mon Sep 17 00:00:00 2001 From: ichekaldin <39010411+ichekaldin@users.noreply.github.com> Date: Fri, 12 Mar 2021 06:08:49 -0500 Subject: [PATCH] Make "unit" parameter optional and add support for check mode (#470) * Make "unit" parameter optional and add support for check mode boto3 documentation explicitly suggests omitting this parameter: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudwatch.html#CloudWatch.Client.put_metric_alarm Creating an alarm without the "unit" parameter specified fails: ``` - community.aws.ec2_metric_alarm: name: My alarm description: My description namespace: AWS/CertificateManager metric: DaysToExpiry statistic: Average comparison: LessThanOrEqualToThreshold threshold: 45 period: 86400 evaluation_periods: 1 dimensions: CertificateArn: "arn:aws:acm:us-east-1:123412341234:certificate/example" alarm_actions: - arn:aws:sns:us-east-1:123412341234:my-sns-topic ok_actions: - arn:aws:sns:us-east-1:123412341234:my-sns-topic treat_missing_data: ignore state: present ``` with the following error: ``` Invalid type for parameter Unit, value: None, type: , valid types: ``` Apparently specifying `unit: None` in the example above is not the same as omitting the unit - it causes the alarm to be in "Insufficient data" state. * Fix module output for tests * Add tests for idempotency and check mode * Fix an error when the module creates a new alarm in check mode Alarm is not actuall created in check mode, and therefore `describe_alarms` returns an empty list. * Add tests for alarm creation with no unit attribute specified * Fix typo - MetricAlarms vs MetricsAlarms * Fix variable name - alarm_info_query_check vs alarm_info_check * Fix variable names in tests * Fix tests by ensuring that alarm doesn't exist before we begin * Fix variable name * Fix assertion * Ensure check mode is enabled when it is supposed to be * Enable check mode for alarm deletion * Fix variable name - alarm_info_no_unit vs alarm_info * Fix the test of creating the alarm without unit attribute * Fix variable name - alarm_info_query_no_unit vs alarm_info * Update changelogs/fragments/470-ec2_metric_alarm-unit-optional.yml * Update changelogs/fragments/470-ec2_metric_alarm-unit-optional.yml Co-authored-by: Mark Chappell --- .../470-ec2_metric_alarm-unit-optional.yml | 3 + plugins/modules/ec2_metric_alarm.py | 190 +++++------ .../targets/ec2_metric_alarm/tasks/main.yml | 308 ++++++++++++++++-- 3 files changed, 357 insertions(+), 144 deletions(-) create mode 100644 changelogs/fragments/470-ec2_metric_alarm-unit-optional.yml diff --git a/changelogs/fragments/470-ec2_metric_alarm-unit-optional.yml b/changelogs/fragments/470-ec2_metric_alarm-unit-optional.yml new file mode 100644 index 00000000000..2216016def2 --- /dev/null +++ b/changelogs/fragments/470-ec2_metric_alarm-unit-optional.yml @@ -0,0 +1,3 @@ +minor_changes: + - ec2_metric_alarm - Made ``unit`` parameter optional (https://github.com/ansible-collections/community.aws/pull/470). + - ec2_metric_alarm - Added support for check mode (https://github.com/ansible-collections/community.aws/pull/470). diff --git a/plugins/modules/ec2_metric_alarm.py b/plugins/modules/ec2_metric_alarm.py index c7b4c28a8ad..effa9bd5c4e 100644 --- a/plugins/modules/ec2_metric_alarm.py +++ b/plugins/modules/ec2_metric_alarm.py @@ -211,63 +211,30 @@ from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -def create_metric_alarm(connection, module): - - name = module.params.get('name') - metric = module.params.get('metric') - namespace = module.params.get('namespace') - statistic = module.params.get('statistic') - comparison = module.params.get('comparison') - threshold = module.params.get('threshold') - period = module.params.get('period') - evaluation_periods = module.params.get('evaluation_periods') - unit = module.params.get('unit') - description = module.params.get('description') - dimensions = module.params.get('dimensions') - alarm_actions = module.params.get('alarm_actions') - insufficient_data_actions = module.params.get('insufficient_data_actions') - ok_actions = module.params.get('ok_actions') - treat_missing_data = module.params.get('treat_missing_data') - - warnings = [] - - alarms = connection.describe_alarms(AlarmNames=[name]) +def create_metric_alarm(connection, module, params): + alarms = connection.describe_alarms(AlarmNames=[params['AlarmName']]) comparisons = {'<=': 'LessThanOrEqualToThreshold', '<': 'LessThanThreshold', '>=': 'GreaterThanOrEqualToThreshold', '>': 'GreaterThanThreshold'} - if comparison in ('<=', '<', '>', '>='): + if params['ComparisonOperator'] in ('<=', '<', '>', '>='): module.deprecate('Using the <=, <, > and >= operators for comparison has been deprecated. Please use LessThanOrEqualToThreshold, ' 'LessThanThreshold, GreaterThanThreshold or GreaterThanOrEqualToThreshold instead.', date='2022-06-01', collection_name='community.aws') - comparison = comparisons[comparison] + params['ComparisonOperator'] = comparisons[params['ComparisonOperator']] - if not isinstance(dimensions, list): + if not isinstance(params['Dimensions'], list): fixed_dimensions = [] - for key, value in dimensions.items(): + for key, value in params['Dimensions'].items(): fixed_dimensions.append({'Name': key, 'Value': value}) - dimensions = fixed_dimensions + params['Dimensions'] = fixed_dimensions if not alarms['MetricAlarms']: try: - connection.put_metric_alarm(AlarmName=name, - MetricName=metric, - Namespace=namespace, - Statistic=statistic, - ComparisonOperator=comparison, - Threshold=threshold, - Period=period, - EvaluationPeriods=evaluation_periods, - Unit=unit, - AlarmDescription=description, - Dimensions=dimensions, - AlarmActions=alarm_actions, - InsufficientDataActions=insufficient_data_actions, - OKActions=ok_actions, - TreatMissingData=treat_missing_data) + if not module.check_mode: + connection.put_metric_alarm(**params) changed = True - alarms = connection.describe_alarms(AlarmNames=[name]) except ClientError as e: module.fail_json_aws(e) @@ -279,85 +246,60 @@ def create_metric_alarm(connection, module): if 'TreatMissingData' not in alarm.keys(): alarm['TreatMissingData'] = 'missing' - for key, value in {'MetricName': metric, - 'Namespace': namespace, - 'Statistic': statistic, - 'ComparisonOperator': comparison, - 'Threshold': threshold, - 'Period': period, - 'EvaluationPeriods': evaluation_periods, - 'Unit': unit, - 'AlarmDescription': description, - 'Dimensions': dimensions, - 'TreatMissingData': treat_missing_data}.items(): - try: - if alarm[key] != value: - changed = True - except KeyError: - if value is not None: - changed = True - - alarm[key] = value - - for key, value in {'AlarmActions': alarm_actions, - 'InsufficientDataActions': insufficient_data_actions, - 'OKActions': ok_actions}.items(): - action = value or [] - if alarm[key] != action: - changed = True - alarm[key] = value + for key in ['ActionsEnabled', 'StateValue', 'StateReason', + 'StateReasonData', 'StateUpdatedTimestamp', + 'AlarmArn', 'AlarmConfigurationUpdatedTimestamp']: + alarm.pop(key, None) + if alarm != params: + changed = True + alarm = params try: if changed: - connection.put_metric_alarm(AlarmName=alarm['AlarmName'], - MetricName=alarm['MetricName'], - Namespace=alarm['Namespace'], - Statistic=alarm['Statistic'], - ComparisonOperator=alarm['ComparisonOperator'], - Threshold=alarm['Threshold'], - Period=alarm['Period'], - EvaluationPeriods=alarm['EvaluationPeriods'], - Unit=alarm['Unit'], - AlarmDescription=alarm['AlarmDescription'], - Dimensions=alarm['Dimensions'], - AlarmActions=alarm['AlarmActions'], - InsufficientDataActions=alarm['InsufficientDataActions'], - OKActions=alarm['OKActions'], - TreatMissingData=alarm['TreatMissingData']) + if not module.check_mode: + connection.put_metric_alarm(**alarm) except ClientError as e: module.fail_json_aws(e) - result = alarms['MetricAlarms'][0] - module.exit_json(changed=changed, warnings=warnings, - name=result['AlarmName'], - actions_enabled=result['ActionsEnabled'], - alarm_actions=result['AlarmActions'], - alarm_arn=result['AlarmArn'], - comparison=result['ComparisonOperator'], - description=result['AlarmDescription'], - dimensions=result['Dimensions'], - evaluation_periods=result['EvaluationPeriods'], - insufficient_data_actions=result['InsufficientDataActions'], - last_updated=result['AlarmConfigurationUpdatedTimestamp'], - metric=result['MetricName'], - namespace=result['Namespace'], - ok_actions=result['OKActions'], - period=result['Period'], - state_reason=result['StateReason'], - state_value=result['StateValue'], - statistic=result['Statistic'], - threshold=result['Threshold'], - treat_missing_data=result['TreatMissingData'], - unit=result['Unit']) - - -def delete_metric_alarm(connection, module): - name = module.params.get('name') - alarms = connection.describe_alarms(AlarmNames=[name]) + try: + alarms = connection.describe_alarms(AlarmNames=[params['AlarmName']]) + except ClientError as e: + module.fail_json_aws(e) + + result = {} + if alarms['MetricAlarms']: + result = alarms['MetricAlarms'][0] + + module.exit_json(changed=changed, + name=result.get('AlarmName'), + actions_enabled=result.get('ActionsEnabled'), + alarm_actions=result.get('AlarmActions'), + alarm_arn=result.get('AlarmArn'), + comparison=result.get('ComparisonOperator'), + description=result.get('AlarmDescription'), + dimensions=result.get('Dimensions'), + evaluation_periods=result.get('EvaluationPeriods'), + insufficient_data_actions=result.get('InsufficientDataActions'), + last_updated=result.get('AlarmConfigurationUpdatedTimestamp'), + metric=result.get('MetricName'), + namespace=result.get('Namespace'), + ok_actions=result.get('OKActions'), + period=result.get('Period'), + state_reason=result.get('StateReason'), + state_value=result.get('StateValue'), + statistic=result.get('Statistic'), + threshold=result.get('Threshold'), + treat_missing_data=result.get('TreatMissingData'), + unit=result.get('Unit')) + + +def delete_metric_alarm(connection, module, params): + alarms = connection.describe_alarms(AlarmNames=[params['AlarmName']]) if alarms['MetricAlarms']: try: - connection.delete_alarms(AlarmNames=[name]) + if not module.check_mode: + connection.delete_alarms(AlarmNames=[params['AlarmName']]) module.exit_json(changed=True) except (ClientError) as e: module.fail_json_aws(e) @@ -390,16 +332,34 @@ def main(): state=dict(default='present', choices=['present', 'absent']), ) - module = AnsibleAWSModule(argument_spec=argument_spec) + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) state = module.params.get('state') + params = dict() + params['AlarmName'] = module.params.get('name') + params['MetricName'] = module.params.get('metric') + params['Namespace'] = module.params.get('namespace') + params['Statistic'] = module.params.get('statistic') + params['ComparisonOperator'] = module.params.get('comparison') + params['Threshold'] = module.params.get('threshold') + params['Period'] = module.params.get('period') + params['EvaluationPeriods'] = module.params.get('evaluation_periods') + if module.params.get('unit'): + params['Unit'] = module.params.get('unit') + params['AlarmDescription'] = module.params.get('description') + params['Dimensions'] = module.params.get('dimensions') + params['AlarmActions'] = module.params.get('alarm_actions', []) + params['InsufficientDataActions'] = module.params.get('insufficient_data_actions', []) + params['OKActions'] = module.params.get('ok_actions', []) + params['TreatMissingData'] = module.params.get('treat_missing_data') + connection = module.client('cloudwatch') if state == 'present': - create_metric_alarm(connection, module) + create_metric_alarm(connection, module, params) elif state == 'absent': - delete_metric_alarm(connection, module) + delete_metric_alarm(connection, module, params) if __name__ == '__main__': diff --git a/tests/integration/targets/ec2_metric_alarm/tasks/main.yml b/tests/integration/targets/ec2_metric_alarm/tasks/main.yml index 102747f48a8..e2980616ca3 100644 --- a/tests/integration/targets/ec2_metric_alarm/tasks/main.yml +++ b/tests/integration/targets/ec2_metric_alarm/tasks/main.yml @@ -12,7 +12,7 @@ - set_fact: alarm_full_name: "{{ alarm_prefix }}-{{ resource_prefix }}-cpu-low" - # until there's a module to get info about alarms, awscli is needed + # until there's a module to get info about alarms, awscli is needed - name: install awscli pip: state: present @@ -32,12 +32,12 @@ - name: Find AMI to use ec2_ami_info: - owners: 'amazon' + owners: "amazon" filters: - name: '{{ ec2_ami_name }}' + name: "{{ ec2_ami_name }}" register: ec2_amis - set_fact: - ec2_ami_image: '{{ ec2_amis.images[0].image_id }}' + ec2_ami_image: "{{ ec2_amis.images[0].image_id }}" - name: Make instance in a default subnet of the VPC ec2_instance: @@ -51,6 +51,50 @@ wait: true register: ec2_instance_results + - name: ensure alarm doesn't exist for a clean test + ec2_metric_alarm: + state: absent + name: "{{ alarm_full_name }}" + + - name: create ec2 metric alarm on ec2 instance (check mode) + ec2_metric_alarm: + dimensions: + InstanceId: "{{ ec2_instance_results.instances[0].instance_id }}" + state: present + name: "{{ alarm_full_name }}" + metric: "CPUUtilization" + namespace: "AWS/EC2" + treat_missing_data: missing + statistic: Average + comparison: "<=" + threshold: 5.0 + period: 300 + evaluation_periods: 3 + unit: "Percent" + description: "This will alarm when an instance's cpu usage average is lower than 5% for 15 minutes" + check_mode: true + register: ec2_instance_metric_alarm_check + + - name: get info on alarms + command: aws cloudwatch describe-alarms --alarm-names {{ alarm_full_name }} + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: alarm_info_query_check + + - name: convert it to an object + set_fact: + alarm_info_check: "{{ alarm_info_query_check.stdout | from_json }}" + + - name: "verify that an alarm was not created in check mode" + assert: + that: + - 'ec2_instance_metric_alarm_check.changed' + - 'not ec2_instance_metric_alarm_check.alarm_arn' + - 'alarm_info_check["MetricAlarms"] | length == 0' + - name: create ec2 metric alarm on ec2 instance ec2_metric_alarm: dimensions: @@ -66,7 +110,7 @@ period: 300 evaluation_periods: 3 unit: "Percent" - description: "This will alarm when an instance's cpu usage average is lower than 5% for 15 minutes " + description: "This will alarm when an instance's cpu usage average is lower than 5% for 15 minutes" register: ec2_instance_metric_alarm - name: get info on alarms @@ -80,7 +124,7 @@ - name: convert it to an object set_fact: - alarm_info: "{{ alarm_info_query.stdout |from_json }}" + alarm_info: "{{ alarm_info_query.stdout | from_json }}" - name: "verify that an alarm was created" assert: @@ -89,10 +133,9 @@ - 'ec2_instance_metric_alarm.alarm_arn' - 'ec2_instance_metric_alarm.statistic == alarm_info["MetricAlarms"][0].Statistic' - 'ec2_instance_metric_alarm.name == alarm_info["MetricAlarms"][0].AlarmName' - - 'ec2_instance_metric_alarm.metric== alarm_info["MetricAlarms"][0].MetricName' + - 'ec2_instance_metric_alarm.metric == alarm_info["MetricAlarms"][0].MetricName' - 'ec2_instance_metric_alarm.namespace == alarm_info["MetricAlarms"][0].Namespace' - 'ec2_instance_metric_alarm.comparison == alarm_info["MetricAlarms"][0].ComparisonOperator' - - 'ec2_instance_metric_alarm.comparison == alarm_info["MetricAlarms"][0].ComparisonOperator' - 'ec2_instance_metric_alarm.threshold == alarm_info["MetricAlarms"][0].Threshold' - 'ec2_instance_metric_alarm.period == alarm_info["MetricAlarms"][0].Period' - 'ec2_instance_metric_alarm.unit == alarm_info["MetricAlarms"][0].Unit' @@ -100,6 +143,61 @@ - 'ec2_instance_metric_alarm.description == alarm_info["MetricAlarms"][0].AlarmDescription' - 'ec2_instance_metric_alarm.treat_missing_data == alarm_info["MetricAlarms"][0].TreatMissingData' + - name: create ec2 metric alarm on ec2 instance (idempotent) (check mode) + ec2_metric_alarm: + dimensions: + InstanceId: "{{ ec2_instance_results.instances[0].instance_id }}" + state: present + name: "{{ alarm_full_name }}" + metric: "CPUUtilization" + namespace: "AWS/EC2" + treat_missing_data: missing + statistic: Average + comparison: "<=" + threshold: 5.0 + period: 300 + evaluation_periods: 3 + unit: "Percent" + description: "This will alarm when an instance's cpu usage average is lower than 5% for 15 minutes" + check_mode: true + register: ec2_instance_metric_alarm_idempotent_check + + - name: get info on alarms + command: aws cloudwatch describe-alarms --alarm-names {{ alarm_full_name }} + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: alarm_info_query_idempotent_check + + - name: convert it to an object + set_fact: + alarm_info_idempotent_check: "{{ alarm_info_query_idempotent_check.stdout | from_json }}" + + - name: "Verify alarm does not register as changed after update in check mode" + assert: + that: + - not ec2_instance_metric_alarm_idempotent_check.changed + + - name: "Verify alarm did not change after updating in check mode" + assert: + that: + - "alarm_info['MetricAlarms'][0]['{{item}}'] == alarm_info_idempotent_check['MetricAlarms'][0]['{{ item }}']" + with_items: + - AlarmArn + - Statistic + - AlarmName + - MetricName + - Namespace + - ComparisonOperator + - Threshold + - Period + - Unit + - EvaluationPeriods + - AlarmDescription + - TreatMissingData + - name: create ec2 metric alarm on ec2 instance (idempotent) ec2_metric_alarm: dimensions: @@ -115,7 +213,7 @@ period: 300 evaluation_periods: 3 unit: "Percent" - description: "This will alarm when an instance's cpu usage average is lower than 5% for 15 minutes " + description: "This will alarm when an instance's cpu usage average is lower than 5% for 15 minutes" register: ec2_instance_metric_alarm_idempotent - name: get info on alarms @@ -129,7 +227,7 @@ - name: convert it to an object set_fact: - alarm_info_idempotent: "{{ alarm_info_query_idempotent.stdout |from_json }}" + alarm_info_idempotent: "{{ alarm_info_query_idempotent.stdout | from_json }}" - name: "Verify alarm does not register as changed after update" assert: @@ -141,18 +239,58 @@ that: - "alarm_info['MetricAlarms'][0]['{{item}}'] == alarm_info_idempotent['MetricAlarms'][0]['{{ item }}']" with_items: - - AlarmArn - - Statistic - - AlarmName - - MetricName - - Namespace - - ComparisonOperator - - Threshold - - Period - - Unit - - EvaluationPeriods - - AlarmDescription - - TreatMissingData + - AlarmArn + - Statistic + - AlarmName + - MetricName + - Namespace + - ComparisonOperator + - Threshold + - Period + - Unit + - EvaluationPeriods + - AlarmDescription + - TreatMissingData + + - name: update alarm (check mode) + ec2_metric_alarm: + dimensions: + InstanceId: "{{ ec2_instance_results.instances[0].instance_id }}" + state: present + name: "{{ alarm_full_name }}" + metric: "CPUUtilization" + namespace: "AWS/EC2" + statistic: Average + comparison: "<=" + threshold: 5.0 + period: 60 + evaluation_periods: 3 + unit: "Percent" + description: "This will alarm when an instance's cpu usage average is lower than 5% for 3 minutes" + check_mode: true + register: ec2_instance_metric_alarm_update_check + + - name: "verify that alarm registers as updated in check mode" + assert: + that: + - 'ec2_instance_metric_alarm_check.changed' + + - name: "verify that properties were not changed in check mode" + assert: + that: + - 'ec2_instance_metric_alarm_update_check.changed' + - 'ec2_instance_metric_alarm_update_check.period == alarm_info["MetricAlarms"][0].Period' # Period of actual alarm should not change + - 'ec2_instance_metric_alarm_update_check.alarm_arn == ec2_instance_metric_alarm.alarm_arn' + - 'ec2_instance_metric_alarm_update_check.statistic == alarm_info["MetricAlarms"][0].Statistic' + - 'ec2_instance_metric_alarm_update_check.name == alarm_info["MetricAlarms"][0].AlarmName' + - 'ec2_instance_metric_alarm_update_check.metric == alarm_info["MetricAlarms"][0].MetricName' + - 'ec2_instance_metric_alarm_update_check.namespace == alarm_info["MetricAlarms"][0].Namespace' + - 'ec2_instance_metric_alarm_update_check.statistic == alarm_info["MetricAlarms"][0].Statistic' + - 'ec2_instance_metric_alarm_update_check.comparison == alarm_info["MetricAlarms"][0].ComparisonOperator' + - 'ec2_instance_metric_alarm_update_check.threshold == alarm_info["MetricAlarms"][0].Threshold' + - 'ec2_instance_metric_alarm_update_check.unit == alarm_info["MetricAlarms"][0].Unit' + - 'ec2_instance_metric_alarm_update_check.evaluation_periods == alarm_info["MetricAlarms"][0].EvaluationPeriods' + - 'ec2_instance_metric_alarm_update_check.treat_missing_data == alarm_info["MetricAlarms"][0].TreatMissingData' - name: update alarm ec2_metric_alarm: @@ -168,7 +306,7 @@ period: 60 evaluation_periods: 3 unit: "Percent" - description: "This will alarm when an instance's cpu usage average is lower than 5% for 3 minutes " + description: "This will alarm when an instance's cpu usage average is lower than 5% for 3 minutes" register: ec2_instance_metric_alarm_update - name: "verify that alarm registers as updated" @@ -180,11 +318,11 @@ assert: that: - 'ec2_instance_metric_alarm_update.changed' - - 'ec2_instance_metric_alarm_update.period == 60' #Period should be 60, not matching old value + - 'ec2_instance_metric_alarm_update.period == 60' # Period should be 60, not matching old value - 'ec2_instance_metric_alarm_update.alarm_arn == ec2_instance_metric_alarm.alarm_arn' - 'ec2_instance_metric_alarm_update.statistic == alarm_info["MetricAlarms"][0].Statistic' - 'ec2_instance_metric_alarm_update.name == alarm_info["MetricAlarms"][0].AlarmName' - - 'ec2_instance_metric_alarm_update.metric== alarm_info["MetricAlarms"][0].MetricName' + - 'ec2_instance_metric_alarm_update.metric == alarm_info["MetricAlarms"][0].MetricName' - 'ec2_instance_metric_alarm_update.namespace == alarm_info["MetricAlarms"][0].Namespace' - 'ec2_instance_metric_alarm_update.statistic == alarm_info["MetricAlarms"][0].Statistic' - 'ec2_instance_metric_alarm_update.comparison == alarm_info["MetricAlarms"][0].ComparisonOperator' @@ -193,11 +331,40 @@ - 'ec2_instance_metric_alarm_update.evaluation_periods == alarm_info["MetricAlarms"][0].EvaluationPeriods' - 'ec2_instance_metric_alarm_update.treat_missing_data == alarm_info["MetricAlarms"][0].TreatMissingData' - - name: try to remove the alarm + - name: try to remove the alarm (check mode) ec2_metric_alarm: state: absent name: "{{ alarm_full_name }}" + check_mode: true + register: ec2_instance_metric_alarm_deletion_check + + - name: Verify that the alarm reports deleted/changed + assert: + that: + - 'ec2_instance_metric_alarm_deletion_check.changed' + + - name: get info on alarms + command: aws cloudwatch describe-alarms --alarm-names {{ alarm_full_name }} + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: alarm_info_query_check + + - name: convert it to an object + set_fact: + alarm_info: "{{ alarm_info_query_check.stdout | from_json }}" + + - name: Verify that the alarm was not deleted in check mode using cli + assert: + that: + - 'alarm_info["MetricAlarms"] | length > 0' + - name: try to remove the alarm + ec2_metric_alarm: + state: absent + name: "{{ alarm_full_name }}" register: ec2_instance_metric_alarm_deletion - name: Verify that the alarm reports deleted/changed @@ -216,17 +383,100 @@ - name: convert it to an object set_fact: - alarm_info: "{{ alarm_info_query.stdout |from_json }}" + alarm_info: "{{ alarm_info_query.stdout | from_json }}" + + - name: Verify that the alarm was deleted using cli + assert: + that: + - 'alarm_info["MetricAlarms"] | length == 0' + + - name: create ec2 metric alarm with no unit on ec2 instance + ec2_metric_alarm: + dimensions: + InstanceId: "{{ ec2_instance_results.instances[0].instance_id }}" + state: present + name: "{{ alarm_full_name }}" + metric: "CPUUtilization" + namespace: "AWS/EC2" + treat_missing_data: missing + statistic: Average + comparison: "<=" + threshold: 5.0 + period: 300 + evaluation_periods: 3 + description: "This will alarm when an instance's cpu usage average is lower than 5% for 15 minutes" + register: ec2_instance_metric_alarm_no_unit + + - name: get info on alarms + command: aws cloudwatch describe-alarms --alarm-names {{ alarm_full_name }} + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: alarm_info_query_no_unit + + - name: convert it to an object + set_fact: + alarm_info_no_unit: "{{ alarm_info_query_no_unit.stdout | from_json }}" + + - name: "verify that an alarm was created" + assert: + that: + - 'ec2_instance_metric_alarm_no_unit.changed' + - 'ec2_instance_metric_alarm_no_unit.alarm_arn' + - 'ec2_instance_metric_alarm_no_unit.statistic == alarm_info_no_unit["MetricAlarms"][0].Statistic' + - 'ec2_instance_metric_alarm_no_unit.name == alarm_info_no_unit["MetricAlarms"][0].AlarmName' + - 'ec2_instance_metric_alarm_no_unit.metric == alarm_info_no_unit["MetricAlarms"][0].MetricName' + - 'ec2_instance_metric_alarm_no_unit.namespace == alarm_info_no_unit["MetricAlarms"][0].Namespace' + - 'ec2_instance_metric_alarm_no_unit.comparison == alarm_info_no_unit["MetricAlarms"][0].ComparisonOperator' + - 'ec2_instance_metric_alarm_no_unit.threshold == alarm_info_no_unit["MetricAlarms"][0].Threshold' + - 'ec2_instance_metric_alarm_no_unit.period == alarm_info_no_unit["MetricAlarms"][0].Period' + - 'alarm_info_no_unit["MetricAlarms"][0].Unit is not defined' + - 'ec2_instance_metric_alarm_no_unit.evaluation_periods == alarm_info_no_unit["MetricAlarms"][0].EvaluationPeriods' + - 'ec2_instance_metric_alarm_no_unit.description == alarm_info_no_unit["MetricAlarms"][0].AlarmDescription' + - 'ec2_instance_metric_alarm_no_unit.treat_missing_data == alarm_info_no_unit["MetricAlarms"][0].TreatMissingData' + + - name: try to remove the alarm + ec2_metric_alarm: + state: absent + name: "{{ alarm_full_name }}" + register: ec2_instance_metric_alarm_deletion_no_unit + + - name: Verify that the alarm reports deleted/changed + assert: + that: + - 'ec2_instance_metric_alarm_deletion_no_unit.changed' + + - name: get info on alarms + command: aws cloudwatch describe-alarms --alarm-names {{ alarm_full_name }} + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: alarm_info_query_no_unit + + - name: convert it to an object + set_fact: + alarm_info_no_unit: "{{ alarm_info_query_no_unit.stdout | from_json }}" - name: Verify that the alarm was deleted using cli assert: that: - - 'alarm_info["MetricAlarms"]|length == 0' + - 'alarm_info_no_unit["MetricAlarms"] | length == 0' + always: + - name: try to delete the alarm + ec2_metric_alarm: + state: absent + name: "{{ alarm_full_name }}" + ignore_errors: true + - name: try to stop the ec2 instance ec2_instance: instance_ids: "{{ ec2_instance_results.instances[0].instance_id }}" state: terminated - ignore_errors: yes + ignore_errors: true - include_tasks: env_cleanup.yml