# Buggy code
def tg_healthy(asg_connection, elbv2_connection, module, group_name):
    healthy_instances = set()
    as_group = describe_autoscaling_groups(asg_connection, group_name)[0]
    props = get_properties(as_group, module)
    # get healthy, inservice instances from ASG
    instances = []
    for instance, settings in props["instance_facts"].items():
        if (
            settings["lifecycle_state"] == "InService"
            and settings["health_status"] == "Healthy"
        ):
            instances.append(dict(Id=instance))
    log.debug(
        "ASG considers the following instances InService and Healthy: %s", instances
    )
    log.debug("Target Group instance status:")
    tg_instances = list()
    for tg in as_group.get("TargetGroupARNs"):
        # we catch a race condition that sometimes happens if the instance exists in the ASG
        # 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(
                msg="Failed to get target group.",
                exception=traceback.format_exc(),
                **camel_dict_to_snake_dict(e.response),
            )
        except botocore.exceptions.BotoCoreError as e:
            module.fail_json(
                msg="Failed to get target group.",
                exception=traceback.format_exc(),
                **camel_dict_to_snake_dict(e.response),
            )

        for i in tg_instances.get("TargetHealthDescriptions"):
            if i["TargetHealth"]["State"] == "healthy":
                healthy_instances.add(i["Target"]["Id"])
            log.debug(
                "Target Group Health State %s: %s",
                i["Target"]["Id"],
                i["TargetHealth"]["State"],
            )
    return len(healthy_instances)


# Traceback (AttributeError)
ansible-playbook 2.5.0
config file = /Users/redacted/code/infrastructure/ansible/ansible.cfg
configured module search path = [u'/Users/redacted/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python2.7/site-packages/ansible
executable location = /usr/local/bin/ansible-playbook
python version = 2.7.13 (default, Sep 11 2017, 09:13:23) [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)]
Using /Users/redacted/code/infrastructure/ansible/ansible.cfg as config file
setting up inventory plugins
statically imported: /Users/redacted/code/infrastructure/ansible/roles/elasticsearch/tasks/java.yml
Loading callback plugin default of type stdout, v2.0 from /usr/local/lib/python2.7/site-packages/ansible/plugins/callback/__init__.pyc

<TRUNCATED>

TASK [Create Auto Scaling Group for logstash nodes] *************************************************************************************************************************************************************************
task path: /Users/redacted/code/infrastructure/ansible/elastic.yml:106
Using module_utils file /usr/local/lib/python2.7/site-packages/ansible/module_utils/basic.py
Using module_utils file /usr/local/lib/python2.7/site-packages/ansible/module_utils/ec2.py
Using module_utils file /usr/local/lib/python2.7/site-packages/ansible/module_utils/_text.py
Using module_utils file /usr/local/lib/python2.7/site-packages/ansible/module_utils/parsing/convert_bool.py
Using module_utils file /usr/local/lib/python2.7/site-packages/ansible/module_utils/parsing/__init__.py
Using module_utils file /usr/local/lib/python2.7/site-packages/ansible/module_utils/pycompat24.py
Using module_utils file /usr/local/lib/python2.7/site-packages/ansible/module_utils/six/__init__.py
Using module_utils file /usr/local/lib/python2.7/site-packages/ansible/module_utils/cloud.py
Using module file /usr/local/lib/python2.7/site-packages/ansible/modules/cloud/amazon/ec2_asg.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: redacted
<127.0.0.1> EXEC /bin/sh -c 'echo ~ &amp;&amp; sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 &amp;&amp; mkdir -p "` echo /Users/redacted/.ansible/tmp/ansible-tmp-1505233014.94-265789443111897 `" &amp;&amp; echo ansible-tmp-1505233014.94-265789443111897="` echo /Users/redacted/.ansible/tmp/ansible-tmp-1505233014.94-265789443111897 `" ) &amp;&amp; sleep 0'
<127.0.0.1> PUT /var/folders/dz/8c2jdqws34d0ddwkzp21cq2dqx1h_5/T/tmp6RNZgu TO /Users/redacted/.ansible/tmp/ansible-tmp-1505233014.94-265789443111897/ec2_asg.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /Users/redacted/.ansible/tmp/ansible-tmp-1505233014.94-265789443111897/ /Users/redacted/.ansible/tmp/ansible-tmp-1505233014.94-265789443111897/ec2_asg.py &amp;&amp; sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/local/opt/python/bin/python2.7 /Users/redacted/.ansible/tmp/ansible-tmp-1505233014.94-265789443111897/ec2_asg.py; rm -rf "/Users/redacted/.ansible/tmp/ansible-tmp-1505233014.94-265789443111897/" > /dev/null 2>&amp;1 &amp;&amp; sleep 0'
The full traceback is:
Traceback (most recent call last):
File "/var/folders/dz/8c2jdqws34d0ddwkzp21cq2dqx1h_5/T/ansible_BIe43C/ansible_module_ec2_asg.py", line 1354, in <module>
main()
File "/var/folders/dz/8c2jdqws34d0ddwkzp21cq2dqx1h_5/T/ansible_BIe43C/ansible_module_ec2_asg.py", line 1343, in main
create_changed, asg_properties = create_autoscaling_group(connection, module)
File "/var/folders/dz/8c2jdqws34d0ddwkzp21cq2dqx1h_5/T/ansible_BIe43C/ansible_modlib.zip/ansible/module_utils/cloud.py", line 153, in retry_func
AttributeError: 'ParamValidationError' object has no attribute 'response'
fatal: [localhost]: FAILED! => {
"changed": false,
"failed": true,
"module_stderr": "Traceback (most recent call last):\n  File \"/var/folders/dz/8c2jdqws34d0ddwkzp21cq2dqx1h_5/T/ansible_BIe43C/ansible_module_ec2_asg.py\", line 1354, in <module>\n    main()\n  File \"/var/folders/dz/8c2jdqws34d0ddwkzp21cq2dqx1h_5/T/ansible_BIe43C/ansible_module_ec2_asg.py\", line 1343, in main\n    create_changed, asg_properties = create_autoscaling_group(connection, module)\n  File \"/var/folders/dz/8c2jdqws34d0ddwkzp21cq2dqx1h_5/T/ansible_BIe43C/ansible_modlib.zip/ansible/module_utils/cloud.py\", line 153, in retry_func\nAttributeError: 'ParamValidationError' object has no attribute 'response'\n",
"module_stdout": "",
"msg": "MODULE FAILURE",
"rc": 0
}

PLAY RECAP ******************************************************************************************************************************************************************************************************************
localhost                  : ok=7    changed=0    unreachable=0    failed=1

# Fixed code
def tg_healthy(asg_connection, elbv2_connection, module, group_name):
    healthy_instances = set()
    as_group = describe_autoscaling_groups(asg_connection, group_name)[0]
    props = get_properties(as_group, module)
    # get healthy, inservice instances from ASG
    instances = []
    for instance, settings in props["instance_facts"].items():
        if (
            settings["lifecycle_state"] == "InService"
            and settings["health_status"] == "Healthy"
        ):
            instances.append(dict(Id=instance))
    log.debug(
        "ASG considers the following instances InService and Healthy: %s", instances
    )
    log.debug("Target Group instance status:")
    tg_instances = list()
    for tg in as_group.get("TargetGroupARNs"):
        # we catch a race condition that sometimes happens if the instance exists in the ASG
        # 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(
                msg="Failed to get target group.",
                exception=traceback.format_exc(),
                **camel_dict_to_snake_dict(e.response),
            )
        except botocore.exceptions.BotoCoreError as e:
            module.fail_json(
                msg="Failed to get target group.", exception=traceback.format_exc()
            )

        for i in tg_instances.get("TargetHealthDescriptions"):
            if i["TargetHealth"]["State"] == "healthy":
                healthy_instances.add(i["Target"]["Id"])
            log.debug(
                "Target Group Health State %s: %s",
                i["Target"]["Id"],
                i["TargetHealth"]["State"],
            )
    return len(healthy_instances)