# Buggy code
def destroy_lifecycle_rule(client, module):
    name = module.params.get("name")
    prefix = module.params.get("prefix")
    rule_id = module.params.get("rule_id")
    changed = False

    if prefix is None:
        prefix = ""

    # 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:
        module.fail_json_aws(e)

    # Create lifecycle
    lifecycle_obj = dict(Rules=[])

    # Check if rule exists
    # If an ID exists, use that otherwise compare based on prefix
    if rule_id is not None:
        for existing_rule in current_lifecycle_rules:
            if rule_id == existing_rule["ID"]:
                # We're not keeping the rule (i.e. deleting) so mark as changed
                changed = True
            else:
                lifecycle_obj["Rules"].append(existing_rule)
    else:
        for existing_rule in current_lifecycle_rules:
            if prefix == existing_rule["Filter"]["Prefix"]:
                # We're not keeping the rule (i.e. deleting) so mark as changed
                changed = True
            else:
                lifecycle_obj["Rules"].append(existing_rule)

    # Write lifecycle to bucket or, if there no rules left, delete lifecycle configuration
    try:
        if lifecycle_obj["Rules"]:
            client.put_bucket_lifecycle_configuration(
                Bucket=name, LifecycleConfiguration=lifecycle_obj
            )
        else:
            client.delete_lifecycle_configuration(Bucket=name)
    except (ClientError, BotoCoreError) as e:
        module.fail_json_aws(e)
    module.exit_json(changed=changed)


# Traceback (AttributeError)
TASK [Manage bucket lifecycle for 'some-test-bucket'] **********************************************************************************************************************************************************************************************************************
task path: /home/benp/workspace/devops/testcase.yml:14
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/cloud/amazon/s3_lifecycle.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: benp
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python &amp;&amp; sleep 0'
The full traceback is:
Traceback (most recent call last):
File "/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py", line 498, in <module>
main()
File "/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py", line 494, in main
destroy_lifecycle_rule(client, module)
File "/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py", line 419, in destroy_lifecycle_rule
client.delete_lifecycle_configuration(Bucket=name)
File "/usr/local/lib/python2.7/dist-packages/botocore/client.py", line 555, in __getattr__
self.__class__.__name__, item)
AttributeError: 'S3' object has no attribute 'delete_lifecycle_configuration'

fatal: [localhost]: FAILED! => {
"changed": false,
"module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py\", line 498, in <module>\n    main()\n  File \"/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py\", line 494, in main\n    destroy_lifecycle_rule(client, module)\n  File \"/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py\", line 419, in destroy_lifecycle_rule\n    client.delete_lifecycle_configuration(Bucket=name)\n  File \"/usr/local/lib/python2.7/dist-packages/botocore/client.py\", line 555, in __getattr__\n    self.__class__.__name__, item)\nAttributeError: 'S3' object has no attribute 'delete_lifecycle_configuration'\n",
"module_stdout": "",
"msg": "MODULE FAILURE",
"rc": 1
}

# Fixed code
def destroy_lifecycle_rule(client, module):
    name = module.params.get("name")
    prefix = module.params.get("prefix")
    rule_id = module.params.get("rule_id")
    changed = False

    if prefix is None:
        prefix = ""

    # 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:
        module.fail_json_aws(e)

    # Create lifecycle
    lifecycle_obj = dict(Rules=[])

    # Check if rule exists
    # If an ID exists, use that otherwise compare based on prefix
    if rule_id is not None:
        for existing_rule in current_lifecycle_rules:
            if rule_id == existing_rule["ID"]:
                # We're not keeping the rule (i.e. deleting) so mark as changed
                changed = True
            else:
                lifecycle_obj["Rules"].append(existing_rule)
    else:
        for existing_rule in current_lifecycle_rules:
            if prefix == existing_rule["Filter"]["Prefix"]:
                # We're not keeping the rule (i.e. deleting) so mark as changed
                changed = True
            else:
                lifecycle_obj["Rules"].append(existing_rule)

    # Write lifecycle to bucket or, if there no rules left, delete lifecycle configuration
    try:
        if lifecycle_obj["Rules"]:
            client.put_bucket_lifecycle_configuration(
                Bucket=name, LifecycleConfiguration=lifecycle_obj
            )
        elif current_lifecycle_rules:
            changed = True
            client.delete_bucket_lifecycle(Bucket=name)
    except (ClientError, BotoCoreError) as e:
        module.fail_json_aws(e)
    module.exit_json(changed=changed)