From 4cf52ef67682fd4f9ed2a707adfbafffe7b88f15 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 5 Feb 2021 09:43:09 +0100 Subject: [PATCH] Cleanup - use is_boto3_error_(message|code) (#268) * Reorder imports * Make use of is_boto3_error_message * Mass-migration over to is_boto3_error_code * Remove unused imports * unused vars in exception * Improve consistency around catching BotoCoreError and ClientError * Remove unused imports * Remove unused 'PolicyError' from iam_policy_info * Avoid catching botocore.exceptions.ClientError when we only want some error codes * Import camel_dict_to_snake_dict/snake_dict_to_camel_dict from ansible.module_utils.common.dict_transformations --- changelogs/fragments/268-is_boto3_error.yml | 3 + plugins/modules/aws_elasticbeanstalk_app.py | 18 ++--- plugins/modules/aws_glue_connection.py | 24 +++--- plugins/modules/aws_glue_job.py | 27 +++---- plugins/modules/aws_kms_info.py | 20 +++-- plugins/modules/aws_ssm_parameter_store.py | 17 +++-- ..._step_functions_state_machine_execution.py | 20 ++--- plugins/modules/aws_waf_condition.py | 19 +++-- plugins/modules/cloudfront_invalidation.py | 36 ++++----- plugins/modules/cloudwatchevent_rule.py | 22 +++--- plugins/modules/data_pipeline.py | 9 +-- plugins/modules/ec2_asg.py | 23 +++--- plugins/modules/ec2_asg_info.py | 19 +++-- plugins/modules/ec2_instance.py | 37 +++++---- plugins/modules/ec2_placement_group.py | 26 ++++--- plugins/modules/ec2_transit_gateway_info.py | 24 +++--- plugins/modules/ec2_vpc_egress_igw.py | 33 ++++---- plugins/modules/ec2_vpc_nacl_info.py | 21 +++-- plugins/modules/ec2_vpc_route_table.py | 26 ++++--- plugins/modules/ecs_ecr.py | 26 +++---- plugins/modules/ecs_service_info.py | 8 +- plugins/modules/efs.py | 25 +++--- plugins/modules/elasticache.py | 14 ++-- plugins/modules/elasticache_info.py | 9 +-- plugins/modules/elasticache_snapshot.py | 29 ++++--- plugins/modules/elb_target_group.py | 5 +- plugins/modules/execute_lambda.py | 13 ++-- plugins/modules/iam_group.py | 52 ++++++------- plugins/modules/iam_managed_policy.py | 19 +++-- plugins/modules/iam_password_policy.py | 13 ++-- plugins/modules/iam_policy_info.py | 15 ++-- plugins/modules/iam_role.py | 76 +++++++++---------- plugins/modules/iam_role_info.py | 15 ++-- plugins/modules/iam_user.py | 30 ++++---- plugins/modules/lambda_alias.py | 26 +++---- plugins/modules/lambda_facts.py | 65 ++++++++-------- plugins/modules/lambda_info.py | 65 ++++++++-------- plugins/modules/lambda_policy.py | 23 +++--- plugins/modules/lightsail.py | 12 ++- plugins/modules/rds_instance.py | 38 +++++----- plugins/modules/rds_param_group.py | 19 +++-- plugins/modules/s3_lifecycle.py | 31 +++----- plugins/modules/sqs_queue.py | 28 +++---- 43 files changed, 513 insertions(+), 567 deletions(-) create mode 100644 changelogs/fragments/268-is_boto3_error.yml diff --git a/changelogs/fragments/268-is_boto3_error.yml b/changelogs/fragments/268-is_boto3_error.yml new file mode 100644 index 00000000000..ad7b2ff1559 --- /dev/null +++ b/changelogs/fragments/268-is_boto3_error.yml @@ -0,0 +1,3 @@ +minor_changes: +- various community.aws modules - cleanup error handling to use ``is_boto3_error_code`` and ``is_boto3_error_message`` helpers (https://github.com/ansible-collections/community.aws/pull/268). +- various community.aws modules - improve consistency of handling Boto3 exceptions (https://github.com/ansible-collections/community.aws/pull/268). diff --git a/plugins/modules/aws_elasticbeanstalk_app.py b/plugins/modules/aws_elasticbeanstalk_app.py index bab889f0b07..19110282d0e 100644 --- a/plugins/modules/aws_elasticbeanstalk_app.py +++ b/plugins/modules/aws_elasticbeanstalk_app.py @@ -85,11 +85,12 @@ ''' try: - from botocore.exceptions import BotoCoreError, ClientError + import botocore except ImportError: pass # handled by AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_message def describe_app(ebs, app_name, module): @@ -104,7 +105,7 @@ def list_apps(ebs, app_name, module): apps = ebs.describe_applications(ApplicationNames=[app_name]) else: apps = ebs.describe_applications() - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Could not describe application") return apps.get("Applications", []) @@ -175,7 +176,7 @@ def main(): try: create_app = ebs.create_application(**filter_empty(ApplicationName=app_name, Description=description)) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Could not create application") app = describe_app(ebs, app_name, module) @@ -188,7 +189,7 @@ def main(): ebs.update_application(ApplicationName=app_name) else: ebs.update_application(ApplicationName=app_name, Description=description) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Could not update application") app = describe_app(ebs, app_name, module) @@ -208,13 +209,10 @@ def main(): else: ebs.delete_application(ApplicationName=app_name) changed = True - except BotoCoreError as e: + except is_boto3_error_message('It is currently pending deletion'): + changed = False + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Cannot terminate app") - except ClientError as e: - if 'It is currently pending deletion.' not in e.response['Error']['Message']: - module.fail_json_aws(e, msg="Cannot terminate app") - else: - changed = False result = dict(changed=changed, app=app) diff --git a/plugins/modules/aws_glue_connection.py b/plugins/modules/aws_glue_connection.py index 0df4ab915d1..41bc99816a0 100644 --- a/plugins/modules/aws_glue_connection.py +++ b/plugins/modules/aws_glue_connection.py @@ -130,17 +130,20 @@ sample: {'subnet-id':'subnet-aabbccddee'} ''' -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict, get_ec2_security_group_ids_from_names - # Non-ansible imports import copy import time try: - from botocore.exceptions import BotoCoreError, ClientError + import botocore except ImportError: pass +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_ec2_security_group_ids_from_names + def _get_glue_connection(connection, module): """ @@ -160,11 +163,8 @@ def _get_glue_connection(connection, module): try: return connection.get_connection(**params)['Connection'] - except (BotoCoreError, ClientError) as e: - if e.response['Error']['Code'] == 'EntityNotFoundException': - return None - else: - raise e + except is_boto3_error_code('EntityNotFoundException'): + return None def _compare_glue_connection_params(user_params, current_params): @@ -251,13 +251,13 @@ def create_or_update_glue_connection(connection, connection_ec2, module, glue_co update_params['Name'] = update_params['ConnectionInput']['Name'] connection.update_connection(**update_params) changed = True - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e) else: try: connection.create_connection(**params) changed = True - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e) # If changed, get the Glue connection again @@ -292,7 +292,7 @@ def delete_glue_connection(connection, module, glue_connection): try: connection.delete_connection(**params) changed = True - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e) module.exit_json(changed=changed) diff --git a/plugins/modules/aws_glue_job.py b/plugins/modules/aws_glue_job.py index 1d991f52f41..7f6af1f4d0c 100644 --- a/plugins/modules/aws_glue_job.py +++ b/plugins/modules/aws_glue_job.py @@ -185,15 +185,17 @@ sample: 300 ''' -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict - # Non-ansible imports import copy try: - from botocore.exceptions import BotoCoreError, ClientError + import botocore except ImportError: - pass + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code def _get_glue_job(connection, module, glue_job_name): @@ -208,11 +210,10 @@ def _get_glue_job(connection, module, glue_job_name): try: return connection.get_job(JobName=glue_job_name)['Job'] - except (BotoCoreError, ClientError) as e: - if e.response['Error']['Code'] == 'EntityNotFoundException': - return None - else: - module.fail_json_aws(e) + except is_boto3_error_code('EntityNotFoundException'): + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e) def _compare_glue_job_params(user_params, current_params): @@ -292,13 +293,13 @@ def create_or_update_glue_job(connection, module, glue_job): del update_params['JobUpdate']['Name'] connection.update_job(**update_params) changed = True - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e) else: try: connection.create_job(**params) changed = True - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e) # If changed, get the Glue job again @@ -324,7 +325,7 @@ def delete_glue_job(connection, module, glue_job): try: connection.delete_job(JobName=glue_job['Name']) changed = True - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e) module.exit_json(changed=changed) diff --git a/plugins/modules/aws_kms_info.py b/plugins/modules/aws_kms_info.py index 978ed804ec2..2366c5d0f45 100644 --- a/plugins/modules/aws_kms_info.py +++ b/plugins/modules/aws_kms_info.py @@ -290,7 +290,7 @@ def get_key_policy_with_backoff(connection, key_id, policy_name): def get_enable_key_rotation_with_backoff(connection, key_id): try: current_rotation_status = connection.get_key_rotation_status(KeyId=key_id) - except is_boto3_error_code('AccessDeniedException') as e: + except is_boto3_error_code('AccessDeniedException'): return None return current_rotation_status.get('KeyRotationEnabled') @@ -306,11 +306,10 @@ def get_kms_tags(connection, module, key_id): try: tag_response = get_kms_tags_with_backoff(connection, key_id, **kwargs) tags.extend(tag_response['Tags']) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] != 'AccessDeniedException': - module.fail_json_aws(e, msg="Failed to obtain key tags") - else: - tag_response = {} + except is_boto3_error_code('AccessDeniedException'): + tag_response = {} + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Failed to obtain key tags") if tag_response.get('NextMarker'): kwargs['Marker'] = tag_response['NextMarker'] else: @@ -323,11 +322,10 @@ def get_kms_policies(connection, module, key_id): policies = list_key_policies_with_backoff(connection, key_id)['PolicyNames'] return [get_key_policy_with_backoff(connection, key_id, policy)['Policy'] for policy in policies] - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] != 'AccessDeniedException': - module.fail_json_aws(e, msg="Failed to obtain key policies") - else: - return [] + except is_boto3_error_code('AccessDeniedException'): + return [] + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Failed to obtain key policies") def key_matches_filter(key, filtr): diff --git a/plugins/modules/aws_ssm_parameter_store.py b/plugins/modules/aws_ssm_parameter_store.py index 32c1df62536..d31a79b2bef 100644 --- a/plugins/modules/aws_ssm_parameter_store.py +++ b/plugins/modules/aws_ssm_parameter_store.py @@ -127,13 +127,14 @@ type: dict ''' -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule - try: - from botocore.exceptions import ClientError + import botocore except ImportError: pass # Handled by AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code + def update_parameter(client, module, args): changed = False @@ -142,7 +143,7 @@ def update_parameter(client, module, args): try: response = client.put_parameter(**args) changed = True - except ClientError as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="setting parameter") return changed, response @@ -195,7 +196,7 @@ def create_update_parameter(client, module): describe_existing_parameter = describe_existing_parameter_paginator.paginate( Filters=[{"Key": "Name", "Values": [args['Name']]}]).build_full_result() - except ClientError as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="getting description value") if describe_existing_parameter['Parameters'][0]['Description'] != args['Description']: @@ -213,9 +214,9 @@ def delete_parameter(client, module): response = client.delete_parameter( Name=module.params.get('name') ) - except ClientError as e: - if e.response['Error']['Code'] == 'ParameterNotFound': - return False, {} + except is_boto3_error_code('ParameterNotFound'): + return False, {} + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="deleting parameter") return True, response diff --git a/plugins/modules/aws_step_functions_state_machine_execution.py b/plugins/modules/aws_step_functions_state_machine_execution.py index f9e1d3fa44c..8ecc2a1272d 100644 --- a/plugins/modules/aws_step_functions_state_machine_execution.py +++ b/plugins/modules/aws_step_functions_state_machine_execution.py @@ -89,14 +89,16 @@ ''' -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict - try: - from botocore.exceptions import ClientError, BotoCoreError + import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code + def start_execution(module, sfn_client): ''' @@ -123,10 +125,10 @@ def start_execution(module, sfn_client): name=name, input=execution_input ) - except (ClientError, BotoCoreError) as e: - if e.response['Error']['Code'] == 'ExecutionAlreadyExists': - # this will never be executed anymore - module.exit_json(changed=False) + except is_boto3_error_code('ExecutionAlreadyExists'): + # this will never be executed anymore + module.exit_json(changed=False) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to start execution.") module.exit_json(changed=True, **camel_dict_to_snake_dict(res_execution)) @@ -151,7 +153,7 @@ def stop_execution(module, sfn_client): cause=cause, error=error ) - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Failed to stop execution.") module.exit_json(changed=True, **camel_dict_to_snake_dict(res)) diff --git a/plugins/modules/aws_waf_condition.py b/plugins/modules/aws_waf_condition.py index 006caaad7cd..77f66f9f767 100644 --- a/plugins/modules/aws_waf_condition.py +++ b/plugins/modules/aws_waf_condition.py @@ -402,10 +402,17 @@ except ImportError: pass # handled by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict, AWSRetry, compare_policies -from ansible_collections.amazon.aws.plugins.module_utils.waf import run_func_with_change_token_backoff, MATCH_LOOKUP -from ansible_collections.amazon.aws.plugins.module_utils.waf import get_rule_with_backoff, list_rules_with_backoff, list_regional_rules_with_backoff +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies +from ansible_collections.amazon.aws.plugins.module_utils.waf import MATCH_LOOKUP +from ansible_collections.amazon.aws.plugins.module_utils.waf import run_func_with_change_token_backoff +from ansible_collections.amazon.aws.plugins.module_utils.waf import get_rule_with_backoff +from ansible_collections.amazon.aws.plugins.module_utils.waf import list_regional_rules_with_backoff +from ansible_collections.amazon.aws.plugins.module_utils.waf import list_rules_with_backoff class Condition(object): @@ -540,9 +547,9 @@ def delete_unused_regex_pattern(self, regex_pattern_set_id): run_func_with_change_token_backoff(self.client, self.module, {'RegexPatternSetId': regex_pattern_set_id}, self.client.delete_regex_pattern_set, wait=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - if e.response['Error']['Code'] == 'WAFNonexistentItemException': - return + except is_boto3_error_code('WAFNonexistentItemException'): + return + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except self.module.fail_json_aws(e, msg='Could not delete regex pattern') def get_condition_by_name(self, name): diff --git a/plugins/modules/cloudfront_invalidation.py b/plugins/modules/cloudfront_invalidation.py index 52e3aea1873..974358f3967 100644 --- a/plugins/modules/cloudfront_invalidation.py +++ b/plugins/modules/cloudfront_invalidation.py @@ -136,17 +136,20 @@ sample: https://cloudfront.amazonaws.com/2017-03-25/distribution/E1ZID6KZJECZY7/invalidation/I2G9MOWJZFV622 ''' -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import snake_dict_to_camel_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.cloudfront_facts import CloudFrontFactsServiceManager import datetime try: - from botocore.exceptions import ClientError, BotoCoreError + import botocore except ImportError: pass # caught by imported AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_message +from ansible_collections.amazon.aws.plugins.module_utils.cloudfront_facts import CloudFrontFactsServiceManager + class CloudFrontInvalidationServiceManager(object): """ @@ -166,16 +169,13 @@ def create_invalidation(self, distribution_id, invalidation_batch): return response, False else: return response, True - except BotoCoreError as e: + except is_boto3_error_message('Your request contains a caller reference that was used for a previous invalidation ' + 'batch for the same distribution.'): + self.module.warn("InvalidationBatch target paths are not modifiable. " + "To make a new invalidation please update caller_reference.") + return current_invalidation_response, False + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except self.module.fail_json_aws(e, msg="Error creating CloudFront invalidations.") - except ClientError as e: - if ('Your request contains a caller reference that was used for a previous invalidation batch ' - 'for the same distribution.' in e.response['Error']['Message']): - self.module.warn("InvalidationBatch target paths are not modifiable. " - "To make a new invalidation please update caller_reference.") - return current_invalidation_response, False - else: - self.module.fail_json_aws(e, msg="Error creating CloudFront invalidations.") def get_invalidation(self, distribution_id, caller_reference): current_invalidation = {} @@ -184,7 +184,7 @@ def get_invalidation(self, distribution_id, caller_reference): paginator = self.client.get_paginator('list_invalidations') invalidations = paginator.paginate(DistributionId=distribution_id).build_full_result().get('InvalidationList', {}).get('Items', []) invalidation_ids = [inv['Id'] for inv in invalidations] - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg="Error listing CloudFront invalidations.") # check if there is an invalidation with the same caller reference @@ -192,7 +192,7 @@ def get_invalidation(self, distribution_id, caller_reference): try: invalidation = self.client.get_invalidation(DistributionId=distribution_id, Id=inv_id)['Invalidation'] caller_ref = invalidation.get('InvalidationBatch', {}).get('CallerReference') - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg="Error getting CloudFront invalidation {0}".format(inv_id)) if caller_ref == caller_reference: current_invalidation = invalidation @@ -218,7 +218,7 @@ def validate_distribution_id(self, distribution_id, alias): if distribution_id is None: distribution_id = self.__cloudfront_facts_mgr.get_distribution_id_from_domain_name(alias) return distribution_id - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg="Error validating parameters.") def create_aws_list(self, invalidation_batch): @@ -238,7 +238,7 @@ def validate_invalidation_batch(self, invalidation_batch, caller_reference): 'caller_reference': valid_caller_reference } return valid_invalidation_batch - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg="Error validating invalidation batch.") diff --git a/plugins/modules/cloudwatchevent_rule.py b/plugins/modules/cloudwatchevent_rule.py index 0ba66909d25..8de7dc7d291 100644 --- a/plugins/modules/cloudwatchevent_rule.py +++ b/plugins/modules/cloudwatchevent_rule.py @@ -154,8 +154,10 @@ except ImportError: pass # handled by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code class CloudWatchEventRule(object): @@ -174,12 +176,9 @@ def describe(self): """Returns the existing details of the rule in AWS""" try: rule_info = self.client.describe_rule(Name=self.name) - except botocore.exceptions.ClientError as e: - error_code = e.response.get('Error', {}).get('Code') - if error_code == 'ResourceNotFoundException': - return {} - self.module.fail_json_aws(e, msg="Could not describe rule %s" % self.name) - except botocore.exceptions.BotoCoreError as e: + except is_boto3_error_code('ResourceNotFoundException'): + return {} + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except self.module.fail_json_aws(e, msg="Could not describe rule %s" % self.name) return self._snakify(rule_info) @@ -237,12 +236,9 @@ def list_targets(self): """Lists the existing targets for the rule in AWS""" try: targets = self.client.list_targets_by_rule(Rule=self.name) - except botocore.exceptions.ClientError as e: - error_code = e.response.get('Error', {}).get('Code') - if error_code == 'ResourceNotFoundException': - return [] - self.module.fail_json_aws(e, msg="Could not find target for rule %s" % self.name) - except botocore.exceptions.BotoCoreError as e: + except is_boto3_error_code('ResourceNotFoundException'): + return [] + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except self.module.fail_json_aws(e, msg="Could not find target for rule %s" % self.name) return self._snakify(targets)['targets'] diff --git a/plugins/modules/data_pipeline.py b/plugins/modules/data_pipeline.py index 54a4cd6f39a..4874388c733 100644 --- a/plugins/modules/data_pipeline.py +++ b/plugins/modules/data_pipeline.py @@ -203,7 +203,6 @@ try: import botocore - from botocore.exceptions import ClientError except ImportError: pass # Handled by AnsibleAWSModule @@ -211,6 +210,7 @@ from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code DP_ACTIVE_STATES = ['ACTIVE', 'SCHEDULED'] @@ -254,7 +254,7 @@ def pipeline_description(client, dp_id): """ try: return client.describe_pipelines(pipelineIds=[dp_id]) - except ClientError as e: + except is_boto3_error_code('PipelineNotFoundException', 'PipelineDeletedException'): raise DataPipelineNotFound @@ -361,9 +361,8 @@ def activate_pipeline(client, module): else: try: client.activate_pipeline(pipelineId=dp_id) - except ClientError as e: - if e.response["Error"]["Code"] == "InvalidRequestException": - module.fail_json(msg="You need to populate your pipeline before activation.") + except is_boto3_error_code('InvalidRequestException'): + module.fail_json(msg="You need to populate your pipeline before activation.") try: pipeline_status_timeout(client, dp_id, status=DP_ACTIVE_STATES, timeout=timeout) diff --git a/plugins/modules/ec2_asg.py b/plugins/modules/ec2_asg.py index ee07b68f516..152918b6d6c 100644 --- a/plugins/modules/ec2_asg.py +++ b/plugins/modules/ec2_asg.py @@ -535,6 +535,7 @@ from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry ASG_ATTRIBUTES = ('AvailabilityZones', 'DefaultCooldown', 'DesiredCapacity', @@ -594,13 +595,13 @@ def describe_launch_templates(connection, launch_template): try: lt = connection.describe_launch_templates(LaunchTemplateIds=[launch_template['launch_template_id']]) return lt - except (botocore.exceptions.ClientError) as e: + except is_boto3_error_code('InvalidLaunchTemplateName.NotFoundException'): module.fail_json(msg="No launch template found matching: %s" % launch_template) else: try: lt = connection.describe_launch_templates(LaunchTemplateNames=[launch_template['launch_template_name']]) return lt - except (botocore.exceptions.ClientError) as e: + except is_boto3_error_code('InvalidLaunchTemplateName.NotFoundException'): module.fail_json(msg="No launch template found matching: %s" % launch_template) @@ -851,12 +852,9 @@ def elb_healthy(asg_connection, elb_connection, group_name): # but has not yet show up in the ELB try: lb_instances = describe_instance_health(elb_connection, lb, instances) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'InvalidInstance': - return None - - module.fail_json_aws(e, msg="Failed to get load balancer.") - except botocore.exceptions.BotoCoreError as e: + except is_boto3_error_code('InvalidInstance'): + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to get load balancer.") for i in lb_instances.get('InstanceStates'): @@ -883,12 +881,9 @@ def tg_healthy(asg_connection, elbv2_connection, group_name): # but has not yet show up in the ELB try: tg_instances = describe_target_health(elbv2_connection, tg, instances) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'InvalidInstance': - return None - - module.fail_json_aws(e, msg="Failed to get target group.") - except botocore.exceptions.BotoCoreError as e: + except is_boto3_error_code('InvalidInstance'): + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to get target group.") for i in tg_instances.get('TargetHealthDescriptions'): diff --git a/plugins/modules/ec2_asg_info.py b/plugins/modules/ec2_asg_info.py index 07df498968a..819bf6e5ab3 100644 --- a/plugins/modules/ec2_asg_info.py +++ b/plugins/modules/ec2_asg_info.py @@ -219,12 +219,14 @@ import re try: - from botocore.exceptions import BotoCoreError, ClientError + import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code def match_asg_tags(tags_to_match, asg): @@ -330,7 +332,7 @@ def find_asgs(conn, module, name=None, tags=None): try: asgs_paginator = conn.get_paginator('describe_auto_scaling_groups') asgs = asgs_paginator.paginate().build_full_result() - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Failed to describe AutoScalingGroups') if not asgs: @@ -338,7 +340,7 @@ def find_asgs(conn, module, name=None, tags=None): try: elbv2 = module.client('elbv2') - except ClientError as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # This is nice to have, not essential elbv2 = None matched_asgs = [] @@ -373,12 +375,9 @@ def find_asgs(conn, module, name=None, tags=None): tg_paginator = elbv2.get_paginator('describe_target_groups') tg_result = tg_paginator.paginate(TargetGroupArns=asg['target_group_arns']).build_full_result() asg['target_group_names'] = [tg['TargetGroupName'] for tg in tg_result['TargetGroups']] - except ClientError as e: - if e.response['Error']['Code'] == 'TargetGroupNotFound': - asg['target_group_names'] = [] - else: - module.fail_json_aws(e, msg="Failed to describe Target Groups") - except BotoCoreError as e: + except is_boto3_error_code('TargetGroupNotFound'): + asg['target_group_names'] = [] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to describe Target Groups") else: asg['target_group_names'] = [] diff --git a/plugins/modules/ec2_instance.py b/plugins/modules/ec2_instance.py index a13b00c680b..06ebda60341 100644 --- a/plugins/modules/ec2_instance.py +++ b/plugins/modules/ec2_instance.py @@ -814,6 +814,8 @@ from ansible.module_utils.six.moves.urllib import parse as urlparse from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_message from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list @@ -1119,15 +1121,13 @@ def discover_security_groups(group, groups, parent_vpc_id=None, subnet_id=None, if subnet_id is not None: try: sub = ec2.describe_subnets(aws_retry=True, SubnetIds=[subnet_id]) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'InvalidGroup.NotFound': - module.fail_json( - "Could not find subnet {0} to associate security groups. Please check the vpc_subnet_id and security_groups parameters.".format( - subnet_id - ) + except is_boto3_error_code('InvalidGroup.NotFound'): + module.fail_json( + "Could not find subnet {0} to associate security groups. Please check the vpc_subnet_id and security_groups parameters.".format( + subnet_id ) - module.fail_json_aws(e, msg="Error while searching for subnet {0} parent VPC.".format(subnet_id)) - except botocore.exceptions.BotoCoreError as e: + ) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Error while searching for subnet {0} parent VPC.".format(subnet_id)) parent_vpc_id = sub['Subnets'][0]['VpcId'] @@ -1615,9 +1615,9 @@ def determine_iam_role(name_or_arn): try: role = iam.get_instance_profile(InstanceProfileName=name_or_arn, aws_retry=True) return role['InstanceProfile']['Arn'] - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'NoSuchEntity': - module.fail_json_aws(e, msg="Could not find instance_role {0}".format(name_or_arn)) + except is_boto3_error_code('NoSuchEntity'): + module.fail_json_aws(e, msg="Could not find instance_role {0}".format(name_or_arn)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="An error occurred while searching for instance_role {0}. Please try supplying the full ARN.".format(name_or_arn)) @@ -1697,15 +1697,12 @@ def ensure_present(existing_matches, changed, ec2, state): def run_instances(ec2, **instance_spec): try: - return ec2.run_instances(aws_retry=True, **instance_spec) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'InvalidParameterValue' and "Invalid IAM Instance Profile ARN" in e.response['Error']['Message']: - # If the instance profile has just been created, it takes some time to be visible by ec2 - # So we wait 10 second and retry the run_instances - time.sleep(10) - return ec2.run_instances(**instance_spec) - else: - raise e + return ec2.run_instances(**instance_spec) + except is_boto3_error_message('Invalid IAM Instance Profile ARN'): + # If the instance profile has just been created, it takes some time to be visible by ec2 + # So we wait 10 second and retry the run_instances + time.sleep(10) + return ec2.run_instances(**instance_spec) def main(): diff --git a/plugins/modules/ec2_placement_group.py b/plugins/modules/ec2_placement_group.py index 7d9a8004544..3ccb2c00802 100644 --- a/plugins/modules/ec2_placement_group.py +++ b/plugins/modules/ec2_placement_group.py @@ -87,13 +87,15 @@ ''' -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry try: - from botocore.exceptions import (BotoCoreError, ClientError) + import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry + @AWSRetry.exponential_backoff() def get_placement_group_details(connection, module): @@ -104,7 +106,7 @@ def get_placement_group_details(connection, module): "Name": "group-name", "Values": [name] }]) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, msg="Couldn't find placement group named [%s]" % name) @@ -128,13 +130,13 @@ def create_placement_group(connection, module): try: connection.create_placement_group( GroupName=name, Strategy=strategy, DryRun=module.check_mode) - except (BotoCoreError, ClientError) as e: - if e.response['Error']['Code'] == "DryRunOperation": - module.exit_json(changed=True, placement_group={ - "name": name, - "state": 'DryRun', - "strategy": strategy, - }) + except is_boto3_error_code('DryRunOperation'): + module.exit_json(changed=True, placement_group={ + "name": name, + "state": 'DryRun', + "strategy": strategy, + }) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg="Couldn't create placement group [%s]" % name) @@ -152,7 +154,7 @@ def delete_placement_group(connection, module): try: connection.delete_placement_group( GroupName=name, DryRun=module.check_mode) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, msg="Couldn't delete placement group [%s]" % name) diff --git a/plugins/modules/ec2_transit_gateway_info.py b/plugins/modules/ec2_transit_gateway_info.py index 2eacf01cd96..707e375a7ee 100644 --- a/plugins/modules/ec2_transit_gateway_info.py +++ b/plugins/modules/ec2_transit_gateway_info.py @@ -165,17 +165,17 @@ ''' try: - from botocore.exceptions import BotoCoreError, ClientError + import botocore except ImportError: pass # handled by imported AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ( - AWSRetry, - boto3_tag_list_to_ansible_dict, - camel_dict_to_snake_dict, - ansible_dict_to_boto3_filter_list -) +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict class AnsibleEc2TgwInfo(object): @@ -208,11 +208,9 @@ def describe_transit_gateways(self): try: response = self._connection.describe_transit_gateways( TransitGatewayIds=transit_gateway_ids, Filters=filters) - except ClientError as e: - if e.response['Error']['Code'] == 'InvalidTransitGatewayID.NotFound': - self._results['transit_gateways'] = [] - return - raise + except is_boto3_error_code('InvalidTransitGatewayID.NotFound'): + self._results['transit_gateways'] = [] + return for transit_gateway in response['TransitGateways']: transit_gateway_info.append(camel_dict_to_snake_dict(transit_gateway, ignore_list=['Tags'])) @@ -253,7 +251,7 @@ def main(): tgwf_manager = AnsibleEc2TgwInfo(module=module, results=results) try: tgwf_manager.describe_transit_gateways() - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e) module.exit_json(**results) diff --git a/plugins/modules/ec2_vpc_egress_igw.py b/plugins/modules/ec2_vpc_egress_igw.py index d462696d0af..0026ade65ad 100644 --- a/plugins/modules/ec2_vpc_egress_igw.py +++ b/plugins/modules/ec2_vpc_egress_igw.py @@ -57,15 +57,16 @@ sample: vpc-012345678 ''' - -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict - try: import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code + def delete_eigw(module, conn, eigw_id): """ @@ -79,14 +80,9 @@ def delete_eigw(module, conn, eigw_id): try: response = conn.delete_egress_only_internet_gateway(DryRun=module.check_mode, EgressOnlyInternetGatewayId=eigw_id) - except botocore.exceptions.ClientError as e: - # When boto3 method is run with DryRun=True it returns an error on success - # We need to catch the error and return something valid - if e.response.get('Error', {}).get('Code') == "DryRunOperation": - changed = True - else: - module.fail_json_aws(e, msg="Could not delete Egress-Only Internet Gateway {0} from VPC {1}".format(eigw_id, module.vpc_id)) - except botocore.exceptions.BotoCoreError as e: + except is_boto3_error_code('DryRunOperation'): + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Could not delete Egress-Only Internet Gateway {0} from VPC {1}".format(eigw_id, module.vpc_id)) if not module.check_mode: @@ -108,16 +104,13 @@ def create_eigw(module, conn, vpc_id): try: response = conn.create_egress_only_internet_gateway(DryRun=module.check_mode, VpcId=vpc_id) - except botocore.exceptions.ClientError as e: + except is_boto3_error_code('DryRunOperation'): # When boto3 method is run with DryRun=True it returns an error on success # We need to catch the error and return something valid - if e.response.get('Error', {}).get('Code') == "DryRunOperation": - changed = True - elif e.response.get('Error', {}).get('Code') == "InvalidVpcID.NotFound": - module.fail_json_aws(e, msg="invalid vpc ID '{0}' provided".format(vpc_id)) - else: - module.fail_json_aws(e, msg="Could not create Egress-Only Internet Gateway for vpc ID {0}".format(vpc_id)) - except botocore.exceptions.BotoCoreError as e: + changed = True + except is_boto3_error_code('InvalidVpcID.NotFound') as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="invalid vpc ID '{0}' provided".format(vpc_id)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Could not create Egress-Only Internet Gateway for vpc ID {0}".format(vpc_id)) if not module.check_mode: diff --git a/plugins/modules/ec2_vpc_nacl_info.py b/plugins/modules/ec2_vpc_nacl_info.py index 1e42e486cea..8c905f67e58 100644 --- a/plugins/modules/ec2_vpc_nacl_info.py +++ b/plugins/modules/ec2_vpc_nacl_info.py @@ -105,16 +105,17 @@ ''' try: - from botocore.exceptions import ClientError, BotoCoreError + import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (AWSRetry, - ansible_dict_to_boto3_filter_list, - camel_dict_to_snake_dict, - boto3_tag_list_to_ansible_dict, - ) +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict # VPC-supported IANA protocol numbers @@ -132,11 +133,9 @@ def list_ec2_vpc_nacls(connection, module): try: nacls = connection.describe_network_acls(aws_retry=True, NetworkAclIds=nacl_ids, Filters=filters) - except ClientError as e: - if e.response['Error']['Code'] == 'InvalidNetworkAclID.NotFound': - module.fail_json(msg='Unable to describe ACL. NetworkAcl does not exist') - module.fail_json_aws(e, msg="Unable to describe network ACLs {0}".format(nacl_ids)) - except BotoCoreError as e: + except is_boto3_error_code('InvalidNetworkAclID.NotFound'): + module.fail_json(msg='Unable to describe ACL. NetworkAcl does not exist') + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Unable to describe network ACLs {0}".format(nacl_ids)) # Turn the boto3 result in to ansible_friendly_snaked_names diff --git a/plugins/modules/ec2_vpc_route_table.py b/plugins/modules/ec2_vpc_route_table.py index ca5d586b2e7..cebbed38f8b 100644 --- a/plugins/modules/ec2_vpc_route_table.py +++ b/plugins/modules/ec2_vpc_route_table.py @@ -226,19 +226,24 @@ import re from time import sleep -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.waiters import get_waiter -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict, snake_dict_to_camel_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list, boto3_tag_list_to_ansible_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags, AWSRetry - try: import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags +from ansible_collections.amazon.aws.plugins.module_utils.waiters import get_waiter + CIDR_RE = re.compile(r'^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$') SUBNET_RE = re.compile(r'^subnet-[A-z0-9]+$') @@ -370,11 +375,8 @@ def ensure_tags(connection=None, module=None, resource_id=None, tags=None, purge def describe_route_tables_with_backoff(connection, **params): try: return connection.describe_route_tables(**params)['RouteTables'] - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'InvalidRouteTableID.NotFound': - return None - else: - raise + except is_boto3_error_code('InvalidRouteTableID.NotFound'): + return None def get_route_table_by_id(connection, module, route_table_id): diff --git a/plugins/modules/ecs_ecr.py b/plugins/modules/ecs_ecr.py index 5b7ddd261f4..768589dbdff 100644 --- a/plugins/modules/ecs_ecr.py +++ b/plugins/modules/ecs_ecr.py @@ -188,13 +188,14 @@ import traceback try: - from botocore.exceptions import ClientError + import botocore except ImportError: pass # Handled by AnsibleAWSModule from ansible.module_utils.six import string_types from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto_exception from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies from ansible_collections.amazon.aws.plugins.module_utils.ec2 import sort_json_policy_dict @@ -227,11 +228,8 @@ def get_repository(self, registry_id, name): repositoryNames=[name], **build_kwargs(registry_id)) repos = res.get('repositories') return repos and repos[0] - except ClientError as err: - code = err.response['Error'].get('Code', 'Unknown') - if code == 'RepositoryNotFoundException': - return None - raise + except is_boto3_error_code('RepositoryNotFoundException'): + return None def get_repository_policy(self, registry_id, name): try: @@ -239,11 +237,8 @@ def get_repository_policy(self, registry_id, name): repositoryName=name, **build_kwargs(registry_id)) text = res.get('policyText') return text and json.loads(text) - except ClientError as err: - code = err.response['Error'].get('Code', 'Unknown') - if code == 'RepositoryPolicyNotFoundException': - return None - raise + except is_boto3_error_code('RepositoryPolicyNotFoundException'): + return None def create_repository(self, registry_id, name, image_tag_mutability): if registry_id: @@ -330,11 +325,8 @@ def get_lifecycle_policy(self, registry_id, name): repositoryName=name, **build_kwargs(registry_id)) text = res.get('lifecyclePolicyText') return text and json.loads(text) - except ClientError as err: - code = err.response['Error'].get('Code', 'Unknown') - if code == 'LifecyclePolicyNotFoundException': - return None - raise + except is_boto3_error_code('LifecyclePolicyNotFoundException'): + return None def put_lifecycle_policy(self, registry_id, name, policy_text): if not self.check_mode: @@ -521,7 +513,7 @@ def run(ecr, params): except Exception as err: msg = str(err) - if isinstance(err, ClientError): + if isinstance(err, botocore.exceptions.ClientError): msg = boto_exception(err) result['msg'] = msg result['exception'] = traceback.format_exc() diff --git a/plugins/modules/ecs_service_info.py b/plugins/modules/ecs_service_info.py index d428dde8835..2d64a89e6dd 100644 --- a/plugins/modules/ecs_service_info.py +++ b/plugins/modules/ecs_service_info.py @@ -138,6 +138,7 @@ pass # caught by AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry @@ -153,11 +154,8 @@ def list_services_with_backoff(self, **kwargs): paginator = self.ecs.get_paginator('list_services') try: return paginator.paginate(**kwargs).build_full_result() - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'ClusterNotFoundException': - self.module.fail_json_aws(e, "Could not find cluster to list services") - else: - raise + except is_boto3_error_code('ClusterNotFoundException') as e: + self.module.fail_json_aws(e, "Could not find cluster to list services") @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) def describe_services_with_backoff(self, **kwargs): diff --git a/plugins/modules/efs.py b/plugins/modules/efs.py index 56ec6980e56..f46c3f588c4 100644 --- a/plugins/modules/efs.py +++ b/plugins/modules/efs.py @@ -230,16 +230,17 @@ from time import time as timestamp try: - from botocore.exceptions import ClientError, BotoCoreError + import botocore except ImportError as e: pass # Handled by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (compare_aws_tags, - camel_dict_to_snake_dict, - ansible_dict_to_boto3_tag_list, - boto3_tag_list_to_ansible_dict, - ) +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags def _index_by_key(key, items): @@ -431,7 +432,7 @@ def create_file_system(self, name, performance_mode, encrypt, kms_key_id, throug try: self.connection.create_file_system(**params) changed = True - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg="Unable to create file system.") # we always wait for the state to be available when creating. @@ -469,7 +470,7 @@ def update_file_system(self, name, throughput_mode, provisioned_throughput_in_mi try: self.connection.update_file_system(FileSystemId=fs_id, **params) changed = True - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg="Unable to update file system.") return changed @@ -489,7 +490,7 @@ def converge_file_system(self, name, tags, purge_tags, targets, throughput_mode, FileSystemId=fs_id, TagKeys=tags_to_delete ) - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg="Unable to delete tags.") result = True @@ -500,7 +501,7 @@ def converge_file_system(self, name, tags, purge_tags, targets, throughput_mode, FileSystemId=fs_id, Tags=ansible_dict_to_boto3_tag_list(tags_need_modify) ) - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg="Unable to create tags.") result = True @@ -624,8 +625,8 @@ def iterate_all(attr, map_method, **kwargs): args['Marker'] = data['Nextmarker'] continue break - except ClientError as e: - if e.response['Error']['Code'] == "ThrottlingException" and wait < 600: + except is_boto3_error_code('ThrottlingException'): + if wait < 600: sleep(wait) wait = wait * 2 continue diff --git a/plugins/modules/elasticache.py b/plugins/modules/elasticache.py index 5fb45a8883b..93804562f2e 100644 --- a/plugins/modules/elasticache.py +++ b/plugins/modules/elasticache.py @@ -134,6 +134,7 @@ pass # Handled by AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info @@ -439,13 +440,12 @@ def _refresh_data(self, cache_cluster_data=None): if cache_cluster_data is None: try: response = self.conn.describe_cache_clusters(CacheClusterId=self.name, ShowCacheNodeInfo=True) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'CacheClusterNotFound': - self.data = None - self.status = 'gone' - return - else: - self.module.fail_json_aws(e, msg="Failed to describe cache clusters") + except is_boto3_error_code('CacheClusterNotFound'): + self.data = None + self.status = 'gone' + return + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + self.module.fail_json_aws(e, msg="Failed to describe cache clusters") cache_cluster_data = response['CacheClusters'][0] self.data = cache_cluster_data self.status = self.data['CacheClusterStatus'] diff --git a/plugins/modules/elasticache_info.py b/plugins/modules/elasticache_info.py index 5b22c5cec1c..026337e3350 100644 --- a/plugins/modules/elasticache_info.py +++ b/plugins/modules/elasticache_info.py @@ -225,6 +225,7 @@ from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict @@ -243,12 +244,8 @@ def describe_cache_clusters_with_backoff(client, cluster_id=None): params['CacheClusterId'] = cluster_id try: response = paginator.paginate(**params).build_full_result() - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'CacheClusterNotFound': - return [] - raise - except botocore.exceptions.BotoCoreError: - raise + except is_boto3_error_code('CacheClusterNotFound'): + return [] return response['CacheClusters'] diff --git a/plugins/modules/elasticache_snapshot.py b/plugins/modules/elasticache_snapshot.py index d07125023bd..42920a3c2c4 100644 --- a/plugins/modules/elasticache_snapshot.py +++ b/plugins/modules/elasticache_snapshot.py @@ -119,6 +119,7 @@ from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code def create(module, connection, replication_id, cluster_id, name): @@ -128,12 +129,11 @@ def create(module, connection, replication_id, cluster_id, name): CacheClusterId=cluster_id, SnapshotName=name) changed = True - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == "SnapshotAlreadyExistsFault": - response = {} - changed = False - else: - module.fail_json_aws(e, msg="Unable to create the snapshot.") + except is_boto3_error_code('SnapshotAlreadyExistsFault'): + response = {} + changed = False + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Unable to create the snapshot.") return response, changed @@ -154,15 +154,14 @@ def delete(module, connection, name): try: response = connection.delete_snapshot(SnapshotName=name) changed = True - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == "SnapshotNotFoundFault": - response = {} - changed = False - elif e.response['Error']['Code'] == "InvalidSnapshotState": - module.fail_json(msg="Error: InvalidSnapshotState. The snapshot is not in an available state or failed state to allow deletion." - "You may need to wait a few minutes.") - else: - module.fail_json_aws(e, msg="Unable to delete the snapshot.") + except is_boto3_error_code('SnapshotNotFoundFault'): + response = {} + changed = False + except is_boto3_error_code('InvalidSnapshotState'): # pylint: disable=duplicate-except + module.fail_json(msg="Error: InvalidSnapshotState. The snapshot is not in an available state or failed state to allow deletion." + "You may need to wait a few minutes.") + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Unable to delete the snapshot.") return response, changed diff --git a/plugins/modules/elb_target_group.py b/plugins/modules/elb_target_group.py index e6c94f06286..4980fc797ad 100644 --- a/plugins/modules/elb_target_group.py +++ b/plugins/modules/elb_target_group.py @@ -379,13 +379,14 @@ except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list def get_tg_attributes(connection, module, tg_arn): diff --git a/plugins/modules/execute_lambda.py b/plugins/modules/execute_lambda.py index 199a50fd0a7..e5e21eacb61 100644 --- a/plugins/modules/execute_lambda.py +++ b/plugins/modules/execute_lambda.py @@ -136,6 +136,7 @@ pass # Handled by AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code def main(): @@ -206,13 +207,13 @@ def main(): try: response = client.invoke(**invoke_params) - except botocore.exceptions.ClientError as ce: - if ce.response['Error']['Code'] == 'ResourceNotFoundException': - module.fail_json_aws(ce, msg="Could not find Lambda to execute. Make sure " - "the ARN is correct and your profile has " - "permissions to execute this function.") + except is_boto3_error_code('ResourceNotFoundException') as nfe: + module.fail_json_aws(nfe, msg="Could not find Lambda to execute. Make sure " + "the ARN is correct and your profile has " + "permissions to execute this function.") + except botocore.exceptions.ClientError as ce: # pylint: disable=duplicate-except module.fail_json_aws(ce, msg="Client-side error when invoking Lambda, check inputs and specific error") - except botocore.exceptions.ParamValidationError as ve: + except botocore.exceptions.ParamValidationError as ve: # pylint: disable=duplicate-except module.fail_json_aws(ve, msg="Parameters to `invoke` failed to validate") except Exception as e: module.fail_json_aws(e, msg="Unexpected failure while invoking Lambda function") diff --git a/plugins/modules/iam_group.py b/plugins/modules/iam_group.py index b55e32218a2..af9d781a92f 100644 --- a/plugins/modules/iam_group.py +++ b/plugins/modules/iam_group.py @@ -177,15 +177,17 @@ sample: / ''' -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry - try: - from botocore.exceptions import BotoCoreError, ClientError + import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry + def compare_attached_group_policies(current_attached_policies, new_attached_policies): @@ -246,7 +248,7 @@ def create_or_update_group(connection, module): # Get group try: group = get_group(connection, module, params['GroupName']) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get group") # If group is None, create it @@ -258,7 +260,7 @@ def create_or_update_group(connection, module): try: group = connection.create_group(**params) changed = True - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't create group") # Manage managed policies @@ -276,7 +278,7 @@ def create_or_update_group(connection, module): if not module.check_mode: try: connection.detach_group_policy(GroupName=params['GroupName'], PolicyArn=policy_arn) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't detach policy from group %s" % params['GroupName']) # If there are policies to adjust that aren't in the current list, then things have changed # Otherwise the only changes were in purging above @@ -287,13 +289,13 @@ def create_or_update_group(connection, module): for policy_arn in managed_policies: try: connection.attach_group_policy(GroupName=params['GroupName'], PolicyArn=policy_arn) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't attach policy to group %s" % params['GroupName']) # Manage group memberships try: current_group_members = get_group(connection, module, params['GroupName'])['Users'] - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't get group %s" % params['GroupName']) current_group_members_list = [] @@ -310,7 +312,7 @@ def create_or_update_group(connection, module): if not module.check_mode: try: connection.remove_user_from_group(GroupName=params['GroupName'], UserName=user) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't remove user %s from group %s" % (user, params['GroupName'])) # If there are users to adjust that aren't in the current list, then things have changed # Otherwise the only changes were in purging above @@ -321,7 +323,7 @@ def create_or_update_group(connection, module): for user in users: try: connection.add_user_to_group(GroupName=params['GroupName'], UserName=user) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't add user %s to group %s" % (user, params['GroupName'])) if module.check_mode: module.exit_json(changed=changed) @@ -329,7 +331,7 @@ def create_or_update_group(connection, module): # Get the group again try: group = get_group(connection, module, params['GroupName']) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't get group %s" % params['GroupName']) module.exit_json(changed=changed, iam_group=camel_dict_to_snake_dict(group)) @@ -342,7 +344,7 @@ def destroy_group(connection, module): try: group = get_group(connection, module, params['GroupName']) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't get group %s" % params['GroupName']) if group: # Check mode means we would remove this group @@ -353,26 +355,26 @@ def destroy_group(connection, module): try: for policy in get_attached_policy_list(connection, module, params['GroupName']): connection.detach_group_policy(GroupName=params['GroupName'], PolicyArn=policy['PolicyArn']) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't remove policy from group %s" % params['GroupName']) # Remove any users in the group otherwise deletion fails current_group_members_list = [] try: current_group_members = get_group(connection, module, params['GroupName'])['Users'] - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't get group %s" % params['GroupName']) for member in current_group_members: current_group_members_list.append(member['UserName']) for user in current_group_members_list: try: connection.remove_user_from_group(GroupName=params['GroupName'], UserName=user) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't remove user %s from group %s" % (user, params['GroupName'])) try: connection.delete_group(**params) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't delete group %s" % params['GroupName']) else: @@ -386,11 +388,8 @@ def get_group(connection, module, name): try: paginator = connection.get_paginator('get_group') return paginator.paginate(GroupName=name).build_full_result() - except ClientError as e: - if e.response['Error']['Code'] == 'NoSuchEntity': - return None - else: - raise + except is_boto3_error_code('NoSuchEntity'): + return None @AWSRetry.exponential_backoff() @@ -399,11 +398,8 @@ def get_attached_policy_list(connection, module, name): try: paginator = connection.get_paginator('list_attached_group_policies') return paginator.paginate(GroupName=name).build_full_result()['AttachedPolicies'] - except ClientError as e: - if e.response['Error']['Code'] == 'NoSuchEntity': - return None - else: - raise + except is_boto3_error_code('NoSuchEntity'): + return None def main(): diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index aa668498ad1..f0fa588c44e 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -140,6 +140,7 @@ from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies @@ -200,16 +201,14 @@ def get_or_create_policy_version(module, iam, policy, policy_document): try: version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] return version, True - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'LimitExceeded': - delete_oldest_non_default_version(module, iam, policy) - try: - version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] - return version, True - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as second_e: - e = second_e - # Handle both when the exception isn't LimitExceeded or - # the second attempt still failed + except is_boto3_error_code('LimitExceeded'): + delete_oldest_non_default_version(module, iam, policy) + try: + version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] + return version, True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as second_e: + module.fail_json_aws(second_e, msg="Couldn't create policy version") + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Couldn't create policy version") diff --git a/plugins/modules/iam_password_policy.py b/plugins/modules/iam_password_policy.py index d654a846cfd..852deb0d10b 100644 --- a/plugins/modules/iam_password_policy.py +++ b/plugins/modules/iam_password_policy.py @@ -103,8 +103,10 @@ except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code class IAMConnection(object): @@ -169,11 +171,10 @@ def update_password_policy(self, module, policy): def delete_password_policy(self, policy): try: results = policy.delete() - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - if e.response['Error']['Code'] == 'NoSuchEntity': - self.module.exit_json(changed=False, task_status={'IAM': "Couldn't find IAM Password Policy"}) - else: - self.module.fail_json_aws(e, msg="Couldn't delete IAM Password Policy") + except is_boto3_error_code('NoSuchEntity'): + self.module.exit_json(changed=False, task_status={'IAM': "Couldn't find IAM Password Policy"}) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + self.module.fail_json_aws(e, msg="Couldn't delete IAM Password Policy") return camel_dict_to_snake_dict(results) diff --git a/plugins/modules/iam_policy_info.py b/plugins/modules/iam_policy_info.py index e934e09a621..19c5a01885b 100644 --- a/plugins/modules/iam_policy_info.py +++ b/plugins/modules/iam_policy_info.py @@ -78,18 +78,15 @@ ''' try: - from botocore.exceptions import BotoCoreError, ClientError + import botocore except ImportError: pass from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -class PolicyError(Exception): - pass - - class Policy: def __init__(self, client, name, policy_name): @@ -202,12 +199,10 @@ def main(): policy = GroupPolicy(**args) module.exit_json(**(policy.run())) - except (BotoCoreError, ClientError) as e: - if e.response['Error']['Code'] == 'NoSuchEntity': - module.exit_json(changed=False, msg=e.response['Error']['Message']) + except is_boto3_error_code('NoSuchEntity') as e: + module.exit_json(changed=False, msg=e.response['Error']['Message']) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e) - except PolicyError as e: - module.fail_json(msg=str(e)) if __name__ == '__main__': diff --git a/plugins/modules/iam_role.py b/plugins/modules/iam_role.py index 9a2eaca8cfe..ddc8ad23041 100644 --- a/plugins/modules/iam_role.py +++ b/plugins/modules/iam_role.py @@ -194,19 +194,21 @@ import json -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict, compare_policies -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (AWSRetry, - ansible_dict_to_boto3_tag_list, - boto3_tag_list_to_ansible_dict, - compare_aws_tags, - ) - try: - from botocore.exceptions import ClientError, BotoCoreError + import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies + def compare_assume_role_policy_doc(current_policy_doc, new_policy_doc): if not compare_policies(current_policy_doc, json.loads(new_policy_doc)): @@ -242,7 +244,7 @@ def attach_policies(connection, module, policies_to_attach, params): try: if not module.check_mode: connection.attach_role_policy(RoleName=params['RoleName'], PolicyArn=policy_arn, aws_retry=True) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to attach policy {0} to role {1}".format(policy_arn, params['RoleName'])) changed = True return changed @@ -254,7 +256,7 @@ def remove_policies(connection, module, policies_to_remove, params): try: if not module.check_mode: connection.detach_role_policy(RoleName=params['RoleName'], PolicyArn=policy, aws_retry=True) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to detach policy {0} from {1}".format(policy, params['RoleName'])) changed = True return changed @@ -293,7 +295,7 @@ def create_basic_role(connection, module, params): else: role = {'MadeInCheckMode': True} role['AssumeRolePolicyDocument'] = json.loads(params['AssumeRolePolicyDocument']) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to create role") return role @@ -312,7 +314,7 @@ def update_role_assumed_policy(connection, module, params, role): RoleName=params['RoleName'], PolicyDocument=json.dumps(json.loads(params['AssumeRolePolicyDocument'])), aws_retry=True) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to update assume role policy for role {0}".format(params['RoleName'])) return True @@ -329,7 +331,7 @@ def update_role_description(connection, module, params, role): try: connection.update_role_description(RoleName=params['RoleName'], Description=params['Description'], aws_retry=True) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to update description for role {0}".format(params['RoleName'])) return True @@ -346,7 +348,7 @@ def update_role_max_session_duration(connection, module, params, role): try: connection.update_role(RoleName=params['RoleName'], MaxSessionDuration=params['MaxSessionDuration'], aws_retry=True) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to update maximum session duration for role {0}".format(params['RoleName'])) return True @@ -364,12 +366,12 @@ def update_role_permissions_boundary(connection, module, params, role): if params.get('PermissionsBoundary') == '': try: connection.delete_role_permissions_boundary(RoleName=params['RoleName'], aws_retry=True) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to remove permission boundary for role {0}".format(params['RoleName'])) else: try: connection.put_role_permissions_boundary(RoleName=params['RoleName'], PermissionsBoundary=params['PermissionsBoundary'], aws_retry=True) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to update permission boundary for role {0}".format(params['RoleName'])) return True @@ -457,7 +459,7 @@ def create_instance_profiles(connection, module, params, role): # Fetch existing Profiles try: instance_profiles = connection.list_instance_profiles_for_role(RoleName=params['RoleName'], aws_retry=True)['InstanceProfiles'] - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to list instance profiles for role {0}".format(params['RoleName'])) # Profile already exists @@ -470,20 +472,17 @@ def create_instance_profiles(connection, module, params, role): # Make sure an instance profile is created try: connection.create_instance_profile(InstanceProfileName=params['RoleName'], Path=params['Path'], aws_retry=True) - except ClientError as e: + except is_boto3_error_code('EntityAlreadyExists'): # If the profile already exists, no problem, move on. # Implies someone's changing things at the same time... - if e.response['Error']['Code'] == 'EntityAlreadyExists': - return False - else: - module.fail_json_aws(e, msg="Unable to create instance profile for role {0}".format(params['RoleName'])) - except BotoCoreError as e: + return False + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Unable to create instance profile for role {0}".format(params['RoleName'])) # And attach the role to the profile try: connection.add_role_to_instance_profile(InstanceProfileName=params['RoleName'], RoleName=params['RoleName'], aws_retry=True) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to attach role {0} to instance profile {0}".format(params['RoleName'])) return True @@ -495,7 +494,7 @@ def remove_instance_profiles(connection, module, role_params, role): try: instance_profiles = connection.list_instance_profiles_for_role(aws_retry=True, **role_params)['InstanceProfiles'] - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to list instance profiles for role {0}".format(role_name)) # Remove the role from the instance profile(s) @@ -508,9 +507,9 @@ def remove_instance_profiles(connection, module, role_params, role): if delete_profiles: try: connection.delete_instance_profile(InstanceProfileName=profile_name, aws_retry=True) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to remove instance profile {0}".format(profile_name)) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to remove role {0} from instance profile {1}".format(role_name, profile_name)) @@ -537,7 +536,7 @@ def destroy_role(connection, module): try: if not module.check_mode: connection.delete_role(aws_retry=True, **role_params) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to delete role") module.exit_json(changed=True) @@ -546,26 +545,23 @@ def destroy_role(connection, module): def get_role_with_backoff(connection, module, name): try: return AWSRetry.jittered_backoff(catch_extra_error_codes=['NoSuchEntity'])(connection.get_role)(RoleName=name)['Role'] - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to get role {0}".format(name)) def get_role(connection, module, name): try: return connection.get_role(RoleName=name, aws_retry=True)['Role'] - except ClientError as e: - if e.response['Error']['Code'] == 'NoSuchEntity': - return None - else: - module.fail_json_aws(e, msg="Unable to get role {0}".format(name)) - except BotoCoreError as e: + except is_boto3_error_code('NoSuchEntity'): + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Unable to get role {0}".format(name)) def get_attached_policy_list(connection, module, name): try: return connection.list_attached_role_policies(RoleName=name, aws_retry=True)['AttachedPolicies'] - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to list attached policies for role {0}".format(name)) @@ -575,7 +571,7 @@ def get_role_tags(connection, module): return {} try: return boto3_tag_list_to_ansible_dict(connection.list_role_tags(RoleName=role_name, aws_retry=True)['Tags']) - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to list tags for role {0}".format(role_name)) @@ -590,7 +586,7 @@ def update_role_tags(connection, module, params, role): try: existing_tags = boto3_tag_list_to_ansible_dict(connection.list_role_tags(RoleName=role_name, aws_retry=True)['Tags']) - except (ClientError, KeyError): + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError, KeyError): existing_tags = {} tags_to_add, tags_to_remove = compare_aws_tags(existing_tags, new_tags, purge_tags=purge_tags) @@ -601,7 +597,7 @@ def update_role_tags(connection, module, params, role): connection.untag_role(RoleName=role_name, TagKeys=tags_to_remove, aws_retry=True) if tags_to_add: connection.tag_role(RoleName=role_name, Tags=ansible_dict_to_boto3_tag_list(tags_to_add), aws_retry=True) - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Unable to set tags for role %s' % role_name) changed = bool(tags_to_add) or bool(tags_to_remove) diff --git a/plugins/modules/iam_role_info.py b/plugins/modules/iam_role_info.py index 95eabdb95ab..132bdeedcc9 100644 --- a/plugins/modules/iam_role_info.py +++ b/plugins/modules/iam_role_info.py @@ -152,8 +152,12 @@ except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict, camel_dict_to_snake_dict, AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict @AWSRetry.exponential_backoff() @@ -208,12 +212,9 @@ def describe_iam_roles(module, client): if name: try: roles = [client.get_role(RoleName=name)['Role']] - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'NoSuchEntity': - return [] - else: - module.fail_json_aws(e, msg="Couldn't get IAM role %s" % name) - except botocore.exceptions.BotoCoreError as e: + except is_boto3_error_code('NoSuchEntity'): + return [] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Couldn't get IAM role %s" % name) else: params = dict() diff --git a/plugins/modules/iam_user.py b/plugins/modules/iam_user.py index 7bd8ebda423..531ae6ba9ae 100644 --- a/plugins/modules/iam_user.py +++ b/plugins/modules/iam_user.py @@ -111,10 +111,10 @@ except ImportError: pass # caught by AnsibleAWSModule -from ansible.module_utils._text import to_native from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code def compare_attached_policies(current_attached_policies, new_attached_policies): @@ -297,34 +297,30 @@ def get_user(connection, module, name): try: return connection.get_user(**params) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'NoSuchEntity': - return None - else: - module.fail_json(msg="Unable to get user {0}: {1}".format(name, to_native(e)), - **camel_dict_to_snake_dict(e.response)) + except is_boto3_error_code('NoSuchEntity'): + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Unable to get user {0}".format(name)) def get_attached_policy_list(connection, module, name): try: return connection.list_attached_user_policies(UserName=name)['AttachedPolicies'] - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'NoSuchEntity': - return None - else: - module.fail_json_aws(e, msg="Unable to get policies for user {0}".format(name)) + except is_boto3_error_code('NoSuchEntity'): + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Unable to get policies for user {0}".format(name)) def delete_user_login_profile(connection, module, user_name): try: return connection.delete_login_profile(UserName=user_name) - except botocore.exceptions.ClientError as e: - if e.response["Error"]["Code"] == "NoSuchEntity": - return None - else: - module.fail_json_aws(e, msg="Unable to delete login profile for user {0}".format(user_name)) + except is_boto3_error_code('NoSuchEntity'): + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Unable to delete login profile for user {0}".format(user_name)) def main(): diff --git a/plugins/modules/lambda_alias.py b/plugins/modules/lambda_alias.py index 8cd8a891289..aeacb6e3b75 100644 --- a/plugins/modules/lambda_alias.py +++ b/plugins/modules/lambda_alias.py @@ -144,13 +144,15 @@ import re try: - from botocore.exceptions import ClientError, ParamValidationError, MissingParametersError + import botocore except ImportError: pass # Handled by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info @@ -182,12 +184,12 @@ def __init__(self, ansible_obj, resources, boto3_=True): if not self.region: self.region = self.resource_client['lambda'].meta.region_name - except (ClientError, ParamValidationError, MissingParametersError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: ansible_obj.fail_json(msg="Unable to connect, authorize or access resource: {0}".format(e)) try: self.account_id = self.resource_client['iam'].get_user()['User']['Arn'].split(':')[4] - except (ClientError, ValueError, KeyError, IndexError): + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError, ValueError, KeyError, IndexError): self.account_id = '' def client(self, resource='lambda'): @@ -269,12 +271,10 @@ def get_lambda_alias(module, aws): # check if alias exists and get facts try: results = client.get_alias(**api_params) - - except (ClientError, ParamValidationError, MissingParametersError) as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - results = None - else: - module.fail_json(msg='Error retrieving function alias: {0}'.format(e)) + except is_boto3_error_code('ResourceNotFoundException'): + results = None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Error retrieving function alias') return results @@ -314,7 +314,7 @@ def lambda_alias(module, aws): if not module.check_mode: try: results = client.update_alias(**api_params) - except (ClientError, ParamValidationError, MissingParametersError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json(msg='Error updating function alias: {0}'.format(e)) else: @@ -325,7 +325,7 @@ def lambda_alias(module, aws): if not module.check_mode: results = client.create_alias(**api_params) changed = True - except (ClientError, ParamValidationError, MissingParametersError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json(msg='Error creating function alias: {0}'.format(e)) else: # state = 'absent' @@ -337,7 +337,7 @@ def lambda_alias(module, aws): if not module.check_mode: results = client.delete_alias(**api_params) changed = True - except (ClientError, ParamValidationError, MissingParametersError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json(msg='Error deleting function alias: {0}'.format(e)) return dict(changed=changed, **dict(results or facts)) diff --git a/plugins/modules/lambda_facts.py b/plugins/modules/lambda_facts.py index 4c02947c998..b1a223b61db 100644 --- a/plugins/modules/lambda_facts.py +++ b/plugins/modules/lambda_facts.py @@ -88,20 +88,21 @@ returned: success type: dict ''' - -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict import json import datetime import sys import re - try: - from botocore.exceptions import ClientError + import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code + def fix_return(node): """ @@ -147,11 +148,10 @@ def alias_details(client, module): params['Marker'] = module.params.get('next_marker') try: lambda_facts.update(aliases=client.list_aliases(FunctionName=function_name, **params)['Aliases']) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_facts.update(aliases=[]) - else: - module.fail_json_aws(e, msg="Trying to get aliases") + except is_boto3_error_code('ResourceNotFoundException'): + lambda_facts.update(aliases=[]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get aliases") else: module.fail_json(msg='Parameter function_name required for query=aliases.') @@ -201,11 +201,10 @@ def config_details(client, module): if function_name: try: lambda_facts.update(client.get_function_configuration(FunctionName=function_name)) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_facts.update(function={}) - else: - module.fail_json_aws(e, msg="Trying to get {0} configuration".format(function_name)) + except is_boto3_error_code('ResourceNotFoundException'): + lambda_facts.update(function={}) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get {0} configuration".format(function_name)) else: params = dict() if module.params.get('max_items'): @@ -216,11 +215,10 @@ def config_details(client, module): try: lambda_facts.update(function_list=client.list_functions(**params)['Functions']) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_facts.update(function_list=[]) - else: - module.fail_json_aws(e, msg="Trying to get function list") + except is_boto3_error_code('ResourceNotFoundException'): + lambda_facts.update(function_list=[]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get function list") functions = dict() for func in lambda_facts.pop('function_list', []): @@ -257,11 +255,10 @@ def mapping_details(client, module): try: lambda_facts.update(mappings=client.list_event_source_mappings(**params)['EventSourceMappings']) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_facts.update(mappings=[]) - else: - module.fail_json_aws(e, msg="Trying to get source event mappings") + except is_boto3_error_code('ResourceNotFoundException'): + lambda_facts.update(mappings=[]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get source event mappings") if function_name: return {function_name: camel_dict_to_snake_dict(lambda_facts)} @@ -288,11 +285,10 @@ def policy_details(client, module): try: # get_policy returns a JSON string so must convert to dict before reassigning to its key lambda_facts.update(policy=json.loads(client.get_policy(FunctionName=function_name)['Policy'])) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_facts.update(policy={}) - else: - module.fail_json_aws(e, msg="Trying to get {0} policy".format(function_name)) + except is_boto3_error_code('ResourceNotFoundException'): + lambda_facts.update(policy={}) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get {0} policy".format(function_name)) else: module.fail_json(msg='Parameter function_name required for query=policy.') @@ -321,11 +317,10 @@ def version_details(client, module): try: lambda_facts.update(versions=client.list_versions_by_function(FunctionName=function_name, **params)['Versions']) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_facts.update(versions=[]) - else: - module.fail_json_aws(e, msg="Trying to get {0} versions".format(function_name)) + except is_boto3_error_code('ResourceNotFoundException'): + lambda_facts.update(versions=[]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get {0} versions".format(function_name)) else: module.fail_json(msg='Parameter function_name required for query=versions.') diff --git a/plugins/modules/lambda_info.py b/plugins/modules/lambda_info.py index 1e40aec4ca1..725149d9c3b 100644 --- a/plugins/modules/lambda_info.py +++ b/plugins/modules/lambda_info.py @@ -78,19 +78,20 @@ returned: success type: dict ''' - -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict import json import datetime import re - try: - from botocore.exceptions import ClientError + import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code + def fix_return(node): """ @@ -136,11 +137,10 @@ def alias_details(client, module): params['Marker'] = module.params.get('next_marker') try: lambda_info.update(aliases=client.list_aliases(FunctionName=function_name, **params)['Aliases']) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_info.update(aliases=[]) - else: - module.fail_json_aws(e, msg="Trying to get aliases") + except is_boto3_error_code('ResourceNotFoundException'): + lambda_info.update(aliases=[]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get aliases") else: module.fail_json(msg='Parameter function_name required for query=aliases.') @@ -190,11 +190,10 @@ def config_details(client, module): if function_name: try: lambda_info.update(client.get_function_configuration(FunctionName=function_name)) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_info.update(function={}) - else: - module.fail_json_aws(e, msg="Trying to get {0} configuration".format(function_name)) + except is_boto3_error_code('ResourceNotFoundException'): + lambda_info.update(function={}) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get {0} configuration".format(function_name)) else: params = dict() if module.params.get('max_items'): @@ -205,11 +204,10 @@ def config_details(client, module): try: lambda_info.update(function_list=client.list_functions(**params)['Functions']) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_info.update(function_list=[]) - else: - module.fail_json_aws(e, msg="Trying to get function list") + except is_boto3_error_code('ResourceNotFoundException'): + lambda_info.update(function_list=[]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get function list") functions = dict() for func in lambda_info.pop('function_list', []): @@ -246,11 +244,10 @@ def mapping_details(client, module): try: lambda_info.update(mappings=client.list_event_source_mappings(**params)['EventSourceMappings']) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_info.update(mappings=[]) - else: - module.fail_json_aws(e, msg="Trying to get source event mappings") + except is_boto3_error_code('ResourceNotFoundException'): + lambda_info.update(mappings=[]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get source event mappings") if function_name: return {function_name: camel_dict_to_snake_dict(lambda_info)} @@ -277,11 +274,10 @@ def policy_details(client, module): try: # get_policy returns a JSON string so must convert to dict before reassigning to its key lambda_info.update(policy=json.loads(client.get_policy(FunctionName=function_name)['Policy'])) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_info.update(policy={}) - else: - module.fail_json_aws(e, msg="Trying to get {0} policy".format(function_name)) + except is_boto3_error_code('ResourceNotFoundException'): + lambda_info.update(policy={}) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get {0} policy".format(function_name)) else: module.fail_json(msg='Parameter function_name required for query=policy.') @@ -310,11 +306,10 @@ def version_details(client, module): try: lambda_info.update(versions=client.list_versions_by_function(FunctionName=function_name, **params)['Versions']) - except ClientError as e: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - lambda_info.update(versions=[]) - else: - module.fail_json_aws(e, msg="Trying to get {0} versions".format(function_name)) + except is_boto3_error_code('ResourceNotFoundException'): + lambda_info.update(versions=[]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Trying to get {0} versions".format(function_name)) else: module.fail_json(msg='Parameter function_name required for query=versions.') diff --git a/plugins/modules/lambda_policy.py b/plugins/modules/lambda_policy.py index 2fb4b4ddead..ff091a8beaa 100644 --- a/plugins/modules/lambda_policy.py +++ b/plugins/modules/lambda_policy.py @@ -134,14 +134,16 @@ import json import re -from ansible.module_utils._text import to_native -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule try: - from botocore.exceptions import ClientError + import botocore except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils._text import to_native +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code + def pc(key): """ @@ -285,14 +287,9 @@ def get_policy_statement(module, client): # check if function policy exists try: policy_results = client.get_policy(**api_params) - except ClientError as e: - try: - if e.response['Error']['Code'] == 'ResourceNotFoundException': - return {} - except AttributeError: # catches ClientErrors without response, e.g. fail before connect - pass - module.fail_json_aws(e, msg="retrieving function policy") - except Exception as e: + except is_boto3_error_code('ResourceNotFoundException'): + return {} + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="retrieving function policy") # get_policy returns a JSON string so must convert to dict before reassigning to its key @@ -328,7 +325,7 @@ def add_policy_permission(module, client): if not module.check_mode: try: client.add_permission(**api_params) - except Exception as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="adding permission to policy") changed = True @@ -356,7 +353,7 @@ def remove_policy_permission(module, client): if not module.check_mode: client.remove_permission(**api_params) changed = True - except Exception as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="removing permission from policy") return changed diff --git a/plugins/modules/lightsail.py b/plugins/modules/lightsail.py index 4be2fc3f458..a996edc5e85 100644 --- a/plugins/modules/lightsail.py +++ b/plugins/modules/lightsail.py @@ -160,17 +160,21 @@ # will be caught by AnsibleAWSModule pass +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code def find_instance_info(module, client, instance_name, fail_if_not_found=False): try: res = client.get_instance(instanceName=instance_name) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'NotFoundException' and not fail_if_not_found: - return None + except is_boto3_error_code('NotFoundException') as e: + if fail_if_not_found: + module.fail_json_aws(e) + return None + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except module.fail_json_aws(e) return res['instance'] diff --git a/plugins/modules/rds_instance.py b/plugins/modules/rds_instance.py index 169ace0e2fa..0dd763c369f 100644 --- a/plugins/modules/rds_instance.py +++ b/plugins/modules/rds_instance.py @@ -745,20 +745,6 @@ sample: sg-12345678 ''' -from ansible.module_utils._text import to_text -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule, is_boto3_error_code, get_boto3_client_method_parameters -from ansible_collections.amazon.aws.plugins.module_utils.rds import ( - arg_spec_to_rds_params, - call_method, - ensure_tags, - get_final_identifier, - get_rds_method_attribute, - get_tags, -) -from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list, AWSRetry -from ansible.module_utils.six import string_types - from time import sleep try: @@ -766,6 +752,23 @@ except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils._text import to_text +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible.module_utils.six import string_types + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_message +from ansible_collections.amazon.aws.plugins.module_utils.core import get_boto3_client_method_parameters +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.rds import arg_spec_to_rds_params +from ansible_collections.amazon.aws.plugins.module_utils.rds import call_method +from ansible_collections.amazon.aws.plugins.module_utils.rds import ensure_tags +from ansible_collections.amazon.aws.plugins.module_utils.rds import get_final_identifier +from ansible_collections.amazon.aws.plugins.module_utils.rds import get_rds_method_attribute +from ansible_collections.amazon.aws.plugins.module_utils.rds import get_tags + def get_rds_method_attribute_name(instance, state, creation_source, read_replica): method_name = None @@ -1034,11 +1037,8 @@ def promote_replication_instance(client, module, instance, read_replica): try: call_method(client, module, method_name='promote_read_replica', parameters={'DBInstanceIdentifier': instance['DBInstanceIdentifier']}) changed = True - except is_boto3_error_code('InvalidDBInstanceState') as e: - if 'DB Instance is not a read replica' in e.response['Error']['Message']: - pass - else: - raise e + except is_boto3_error_message('DB Instance is not a read replica'): + pass return changed diff --git a/plugins/modules/rds_param_group.py b/plugins/modules/rds_param_group.py index ff18fc98300..30aa814de67 100644 --- a/plugins/modules/rds_param_group.py +++ b/plugins/modules/rds_param_group.py @@ -124,6 +124,7 @@ from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags @@ -230,11 +231,10 @@ def ensure_present(module, connection): errors = [] try: response = connection.describe_db_parameter_groups(DBParameterGroupName=groupname) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'DBParameterGroupNotFound': - response = None - else: - module.fail_json_aws(e, msg="Couldn't access parameter group information") + except is_boto3_error_code('DBParameterGroupNotFound'): + response = None + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Couldn't access parameter group information") if not response: params = dict(DBParameterGroupName=groupname, DBParameterGroupFamily=module.params['engine'], @@ -273,11 +273,10 @@ def ensure_absent(module, connection): group = module.params['name'] try: response = connection.describe_db_parameter_groups(DBParameterGroupName=group) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'DBParameterGroupNotFound': - module.exit_json(changed=False) - else: - module.fail_json_aws(e, msg="Couldn't access parameter group information") + except is_boto3_error_code('DBParameterGroupNotFound'): + module.exit_json(changed=False) + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Couldn't access parameter group information") try: response = connection.delete_db_parameter_group(DBParameterGroupName=group) module.exit_json(changed=True) diff --git a/plugins/modules/s3_lifecycle.py b/plugins/modules/s3_lifecycle.py index 967be374219..0bc4a328680 100644 --- a/plugins/modules/s3_lifecycle.py +++ b/plugins/modules/s3_lifecycle.py @@ -17,8 +17,6 @@ notes: - If specifying expiration time as days then transition time must also be specified in days - If specifying expiration time as a date then transition time must also be specified as a date -requirements: - - python-dateutil options: name: description: @@ -196,11 +194,12 @@ import datetime try: - from botocore.exceptions import BotoCoreError, ClientError + import botocore except ImportError: pass # handled by AnsibleAwsModule from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code def create_lifecycle_rule(client, module): @@ -226,12 +225,9 @@ def create_lifecycle_rule(client, module): try: current_lifecycle = client.get_bucket_lifecycle_configuration(Bucket=name) current_lifecycle_rules = current_lifecycle['Rules'] - except ClientError as e: - if e.response['Error']['Code'] == 'NoSuchLifecycleConfiguration': - current_lifecycle_rules = [] - else: - module.fail_json_aws(e) - except BotoCoreError as e: + except is_boto3_error_code('NoSuchLifecycleConfiguration'): + current_lifecycle_rules = [] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e) rule = dict(Filter=dict(Prefix=prefix), Status=status.title()) @@ -307,7 +303,7 @@ def create_lifecycle_rule(client, module): # Write lifecycle to bucket try: client.put_bucket_lifecycle_configuration(Bucket=name, LifecycleConfiguration=lifecycle_configuration) - except (BotoCoreError, ClientError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e) module.exit_json(changed=changed) @@ -390,12 +386,9 @@ def destroy_lifecycle_rule(client, module): # Get the bucket's current lifecycle rules try: current_lifecycle_rules = client.get_bucket_lifecycle_configuration(Bucket=name)['Rules'] - except ClientError as e: - if e.response['Error']['Code'] == 'NoSuchLifecycleConfiguration': - current_lifecycle_rules = [] - else: - module.fail_json_aws(e) - except BotoCoreError as e: + except is_boto3_error_code('NoSuchLifecycleConfiguration'): + current_lifecycle_rules = [] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e) # Create lifecycle @@ -425,7 +418,7 @@ def destroy_lifecycle_rule(client, module): elif current_lifecycle_rules: changed = True client.delete_bucket_lifecycle(Bucket=name) - except (ClientError, BotoCoreError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e) module.exit_json(changed=changed) @@ -485,13 +478,13 @@ def main(): if expiration_date is not None: try: datetime.datetime.strptime(expiration_date, "%Y-%m-%dT%H:%M:%S.000Z") - except ValueError as e: + except ValueError: module.fail_json(msg="expiration_date is not a valid ISO-8601 format. The time must be midnight and a timezone of GMT must be included") if transition_date is not None: try: datetime.datetime.strptime(transition_date, "%Y-%m-%dT%H:%M:%S.000Z") - except ValueError as e: + except ValueError: module.fail_json(msg="expiration_date is not a valid ISO-8601 format. The time must be midnight and a timezone of GMT must be included") if state == 'present': diff --git a/plugins/modules/sqs_queue.py b/plugins/modules/sqs_queue.py index 5d65967974a..0a93909f021 100644 --- a/plugins/modules/sqs_queue.py +++ b/plugins/modules/sqs_queue.py @@ -220,19 +220,21 @@ ''' import json -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (AWSRetry, - camel_dict_to_snake_dict, - compare_aws_tags, - snake_dict_to_camel_dict, - compare_policies, - ) try: - from botocore.exceptions import BotoCoreError, ClientError, ParamValidationError + import botocore except ImportError: pass # handled by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies + def get_queue_name(module, is_fifo=False): name = module.params.get('name') @@ -246,10 +248,8 @@ def get_queue_name(module, is_fifo=False): def get_queue_url(client, name): try: return client.get_queue_url(QueueName=name)['QueueUrl'] - except ClientError as e: - if e.response['Error']['Code'] == 'AWS.SimpleQueueService.NonExistentQueue': - return None - raise + except is_boto3_error_code('AWS.SimpleQueueService.NonExistentQueue'): + return None def describe_queue(client, queue_url): @@ -418,7 +418,7 @@ def update_tags(client, queue_url, module): try: existing_tags = client.list_queue_tags(QueueUrl=queue_url, aws_retry=True)['Tags'] - except (ClientError, KeyError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError, KeyError) as e: existing_tags = {} tags_to_add, tags_to_remove = compare_aws_tags(existing_tags, new_tags, purge_tags=purge_tags) @@ -465,7 +465,7 @@ def main(): result = create_or_update_sqs_queue(client, module) elif state == 'absent': result = delete_sqs_queue(client, module) - except (BotoCoreError, ClientError, ParamValidationError) as e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Failed to control sqs queue') else: module.exit_json(**result)