# Buggy code
def copy_snapshot(module, ec2):
    """
    Copies an EC2 Snapshot to another region

    module : AnsibleModule object
    ec2: ec2 connection object
    """

    params = {
        "SourceRegion": module.params.get("source_region"),
        "SourceSnapshotId": module.params.get("source_snapshot_id"),
        "Description": module.params.get("description"),
    }

    if module.params.get("encrypted"):
        params["Encrypted"] = True

    if module.params.get("kms_key_id"):
        params["KmsKeyId"] = module.params.get("kms_key_id")

    try:
        snapshot_id = ec2.copy_snapshot(**params)["SnapshotId"]
        if module.params.get("wait"):
            ec2.get_waiter("snapshot_completed").wait(SnapshotIds=[snapshot_id])
        if module.params.get("tags"):
            ec2.create_tags(
                Resources=[snapshot_id],
                Tags=[
                    {"Key": k, "Value": v} for k, v in module.params.get("tags").items()
                ],
            )

    except WaiterError as we:
        module.fail_json(
            msg="An error occurred waiting for the snapshot to become available. (%s)"
            % str(we),
            exception=traceback.format_exc(),
        )
    except ClientError as ce:
        module.fail_json(
            msg=str(ce),
            exception=traceback.format_exc(),
            **camel_dict_to_snake_dict(ce.response),
        )

    module.exit_json(changed=True, snapshot_id=snapshot_id)


# Traceback (AttributeError)
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: 'WaiterError' object has no attribute 'reason'
failed: [127.0.0.1 -> 127.0.0.1] (item={'_ansible_parsed': True, u'changed': True, '_ansible_no_log': False, u'tags': {u'Device': u'/dev/sdf', u'CostReference': u'I-DUMMY', u'Type': u'st1', u'Name': u's4h-db-001', u'Zone': u'eu-central-1b'}, '_ansible_item_result': True, 'item': {u'status': u'in-use', u'zone': u'eu-central-1b', u'tags': {u'Device': u'/dev/sdf', u'CostReference': u'I-DUMMY', u'Name': u's4h-db-001', u'SID': u'S4H'}, u'encrypted': True, u'iops': None, u'create_time': u'2017-07-14T13:39:46.714Z', u'snapshot_id': u'snap-0a1c59f1131cb509d', u'attachment_set': {u'device': u'/dev/sdf', u'instance_id': u'i-07d9f83a461aa1635', u'deleteOnTermination': u'false', u'status': u'attached', u'attach_time': u'2017-07-14T13:39:46.000Z'}, u'type': u'st1', u'id': u'vol-0d472f99bd0e6e145', u'size': 2000}, u'snapshot_id': u'snap-01ee7bdf877172cfa', u'volume_id': u'vol-0d472f99bd0e6e145', u'invocation': {u'module_args': {u'profile': None, u'aws_secret_key': None, u'aws_access_key': None, u'description': u'snapshot of "vol-0d472f99bd0e6e145" attached to "s4h-db-001" "/dev/sdf"', u'security_token': None, u'snapshot_tags': {u'Device': u'/dev/sdf', u'CostReference': u'I-DUMMY', u'Type': u'st1', u'Name': u's4h-db-001', u'Zone': u'eu-central-1b'}, u'region': u'eu-central-1', u'snapshot_id': None, u'device_name': None, u'instance_id': None, u'state': u'present', u'wait_timeout': 0, u'ec2_url': None, u'last_snapshot_min_age': 0, u'validate_certs': True, u'volume_id': u'vol-0d472f99bd0e6e145', u'wait': True}}, '_ansible_delegated_vars': {'ansible_delegated_host': u'127.0.0.1', 'ansible_host': u'127.0.0.1'}, u'volume_size': 2000, '_ansible_ignore_errors': None, 'failed': False}) => {"changed": false, "item": {"changed": true, "failed": false, "invocation": {"module_args": {"aws_access_key": null, "aws_secret_key": null, "description": "snapshot of \"vol-0d472f99bd0e6e145\" attached to \"s4h-db-001\" \"/dev/sdf\"", "device_name": null, "ec2_url": null, "instance_id": null, "last_snapshot_min_age": 0, "profile": null, "region": "eu-central-1", "security_token": null, "snapshot_id": null, "snapshot_tags": {"CostReference": "I-DUMMY", "Device": "/dev/sdf", "Name": "s4h-db-001", "Type": "st1", "Zone": "eu-central-1b"}, "state": "present", "validate_certs": true, "volume_id": "vol-0d472f99bd0e6e145", "wait": true, "wait_timeout": 0}}, "item": {"attachment_set": {"attach_time": "2017-07-14T13:39:46.000Z", "deleteOnTermination": "false", "device": "/dev/sdf", "instance_id": "i-07d9f83a461aa1635", "status": "attached"}, "create_time": "2017-07-14T13:39:46.714Z", "encrypted": true, "id": "vol-0d472f99bd0e6e145", "iops": null, "size": 2000, "snapshot_id": "snap-0a1c59f1131cb509d", "status": "in-use", "tags": {"CostReference": "I-DUMMY", "Device": "/dev/sdf", "Name": "s4h-db-001", "SID": "S4H"}, "type": "st1", "zone": "eu-central-1b"}, "snapshot_id": "snap-01ee7bdf877172cfa", "tags": {"CostReference": "I-DUMMY", "Device": "/dev/sdf", "Name": "s4h-db-001", "Type": "st1", "Zone": "eu-central-1b"}, "volume_id": "vol-0d472f99bd0e6e145", "volume_size": 2000}, "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_mwRHjF/ansible_module_ec2_snapshot_copy.py\", line 188, in <module>\n    main()\n  File \"/tmp/ansible_mwRHjF/ansible_module_ec2_snapshot_copy.py\", line 184, in main\n    copy_snapshot(module, client)\n  File \"/tmp/ansible_mwRHjF/ansible_module_ec2_snapshot_copy.py\", line 153, in copy_snapshot\n    module.fail_json(msg='An error occurred (%s) waiting for the snapshot to become available. (%s)' % (we.message, we.reason))\nAttributeError: 'WaiterError' object has no attribute 'reason'\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 0}

The full traceback is:
Traceback (most recent call last):
File "/tmp/ansible_8ado4e/ansible_module_ec2_snapshot_copy.py", line 188, in <module>
main()
File "/tmp/ansible_8ado4e/ansible_module_ec2_snapshot_copy.py", line 184, in main
copy_snapshot(module, client)
File "/tmp/ansible_8ado4e/ansible_module_ec2_snapshot_copy.py", line 153, in copy_snapshot
module.fail_json(msg='An error occurred (%s) waiting for the snapshot to become available. (%s)' % (we.message, we.reason))
AttributeError: 'WaiterError' object has no attribute 'reason'

# Fixed code
def copy_snapshot(module, ec2):
    """
    Copies an EC2 Snapshot to another region

    module : AnsibleModule object
    ec2: ec2 connection object
    """

    params = {
        "SourceRegion": module.params.get("source_region"),
        "SourceSnapshotId": module.params.get("source_snapshot_id"),
        "Description": module.params.get("description"),
    }

    if module.params.get("encrypted"):
        params["Encrypted"] = True

    if module.params.get("kms_key_id"):
        params["KmsKeyId"] = module.params.get("kms_key_id")

    try:
        snapshot_id = ec2.copy_snapshot(**params)["SnapshotId"]
        if module.params.get("wait"):
            delay = 15
            # Add one to max_attempts as wait() increment
            # its counter before assessing it for time.sleep()
            max_attempts = (module.params.get("wait_timeout") // delay) + 1
            ec2.get_waiter("snapshot_completed").wait(
                SnapshotIds=[snapshot_id],
                WaiterConfig=dict(Delay=delay, MaxAttempts=max_attempts),
            )
        if module.params.get("tags"):
            ec2.create_tags(
                Resources=[snapshot_id],
                Tags=[
                    {"Key": k, "Value": v} for k, v in module.params.get("tags").items()
                ],
            )

    except WaiterError as we:
        module.fail_json(
            msg="An error occurred waiting for the snapshot to become available. (%s)"
            % str(we),
            exception=traceback.format_exc(),
        )
    except ClientError as ce:
        module.fail_json(
            msg=str(ce),
            exception=traceback.format_exc(),
            **camel_dict_to_snake_dict(ce.response),
        )

    module.exit_json(changed=True, snapshot_id=snapshot_id)