# Buggy code
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(
        dict(
            name=dict(required=True),
            image_id=dict(),
            instance_id=dict(),
            key_name=dict(),
            security_groups=dict(default=[], type="list"),
            user_data=dict(),
            user_data_path=dict(type="path"),
            kernel_id=dict(),
            volumes=dict(type="list"),
            instance_type=dict(),
            state=dict(default="present", choices=["present", "absent"]),
            spot_price=dict(type="float"),
            ramdisk_id=dict(),
            instance_profile_name=dict(),
            ebs_optimized=dict(default=False, type="bool"),
            associate_public_ip_address=dict(type="bool"),
            instance_monitoring=dict(default=False, type="bool"),
            assign_public_ip=dict(type="bool"),
            classic_link_vpc_security_groups=dict(type="list"),
            classic_link_vpc_id=dict(),
            vpc_id=dict(),
            placement_tenancy=dict(default="default", choices=["default", "dedicated"]),
        )
    )

    module = AnsibleModule(
        argument_spec=argument_spec,
        mutually_exclusive=[["user_data", "user_data_path"]],
    )

    if not HAS_BOTO3:
        module.fail_json(msg="boto3 required for this module")

    try:
        region, ec2_url, aws_connect_kwargs = get_aws_connection_info(
            module, boto3=True
        )
        connection = boto3_conn(
            module,
            conn_type="client",
            resource="autoscaling",
            region=region,
            endpoint=ec2_url,
            **aws_connect_kwargs,
        )
    except botocore.exceptions.NoRegionError:
        module.fail_json(
            msg=(
                "region must be specified as a parameter in AWS_DEFAULT_REGION environment variable or in boto configuration file"
            )
        )
    except botocore.exceptions.ClientError as e:
        module.fail_json(
            msg="unable to establish connection - " + str(e),
            exception=traceback.format_exc(),
            **camel_dict_to_snake_dict(e.response),
        )

    state = module.params.get("state")

    if state == "present":
        create_launch_config(connection, module)
    elif state == "absent":
        delete_launch_config(connection, module)


# Traceback (botocore.exceptions.ClientError)
$ ansible-playbook -i hosts poc.yml -vvv
ansible-playbook 2.5.0
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/home/test/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /usr/bin/ansible-playbook
python version = 2.7.14 (default, Sep 20 2017, 01:25:59) [GCC 7.2.0]
Using /etc/ansible/ansible.cfg as config file
Parsed /home/test/git/ansible/hosts inventory source with ini plugin

PLAYBOOK: poc.yml ***************************************************************************************************************************************
1 plays in poc.yml
Read vars_file 'awssecrets'
Read vars_file 'awssecrets'

PLAY [POC] **********************************************************************************************************************************************
Read vars_file 'awssecrets'

TASK [Gathering Facts] **********************************************************************************************************************************
Using module file /usr/lib/python2.7/site-packages/ansible/modules/system/setup.py
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: test
<localhost> EXEC /bin/sh -c 'echo ~ &amp;&amp; sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 &amp;&amp; mkdir -p "` echo /home/test/.ansible/tmp/ansible-tmp-1507074847.68-27868486600757 `" &amp;&amp; echo ansible-tmp-1507074847.68-27868486600757="` echo /home/test/.ansible/tmp/ansible-tmp-1507074847.68-27868486600757 `" ) &amp;&amp; sleep 0'
<localhost> PUT /tmp/tmphUK67b TO /home/test/.ansible/tmp/ansible-tmp-1507074847.68-27868486600757/setup.py
<localhost> EXEC /bin/sh -c 'chmod u+x /home/test/.ansible/tmp/ansible-tmp-1507074847.68-27868486600757/ /home/test/.ansible/tmp/ansible-tmp-1507074847.68-27868486600757/setup.py &amp;&amp; sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python /home/test/.ansible/tmp/ansible-tmp-1507074847.68-27868486600757/setup.py; rm -rf "/home/test/.ansible/tmp/ansible-tmp-1507074847.68-27868486600757/" > /dev/null 2>&amp;1 &amp;&amp; sleep 0'
ok: [localhost]
META: ran handlers
Read vars_file 'awssecrets'

TASK [ec2_lc] *******************************************************************************************************************************************
task path: /home/test/git/ansible/poc.yml:11
Using module file /usr/lib/python2.7/site-packages/ansible/modules/cloud/amazon/ec2_lc.py
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: test
<localhost> EXEC /bin/sh -c 'echo ~ &amp;&amp; sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 &amp;&amp; mkdir -p "` echo /home/test/.ansible/tmp/ansible-tmp-1507074850.03-188251403327543 `" &amp;&amp; echo ansible-tmp-1507074850.03-188251403327543="` echo /home/test/.ansible/tmp/ansible-tmp-1507074850.03-188251403327543 `" ) &amp;&amp; sleep 0'
<localhost> PUT /tmp/tmph45VEJ TO /home/test/.ansible/tmp/ansible-tmp-1507074850.03-188251403327543/ec2_lc.py
<localhost> EXEC /bin/sh -c 'chmod u+x /home/test/.ansible/tmp/ansible-tmp-1507074850.03-188251403327543/ /home/test/.ansible/tmp/ansible-tmp-1507074850.03-188251403327543/ec2_lc.py &amp;&amp; sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python /home/test/.ansible/tmp/ansible-tmp-1507074850.03-188251403327543/ec2_lc.py; rm -rf "/home/test/.ansible/tmp/ansible-tmp-1507074850.03-188251403327543/" > /dev/null 2>&amp;1 &amp;&amp; sleep 0'
The full traceback is:
Traceback (most recent call last):
File "/tmp/ansible_m4w030dh/ansible_module_ec2_lc.py", line 317, in create_launch_config
connection.create_launch_configuration(**launch_config)
File "/usr/lib/python3.6/site-packages/botocore/client.py", line 312, in _api_call
return self._make_api_call(operation_name, kwargs)
File "/usr/lib/python3.6/site-packages/botocore/client.py", line 601, in _make_api_call
raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (ValidationError) when calling the CreateLaunchConfiguration operation: Placement tenancy is not supported for spot instances.

fatal: [localhost]: FAILED! => {
"changed": false,
"error": {
"code": "ValidationError",
"message": "Placement tenancy is not supported for spot instances.",
"type": "Sender"
},
"failed": true,
"invocation": {
"module_args": {
"assign_public_ip": null,
"associate_public_ip_address": null,
"aws_access_key": null,
"aws_secret_key": null,
"classic_link_vpc_id": null,
"classic_link_vpc_security_groups": null,
"ebs_optimized": false,
"ec2_url": null,
"image_id": "ami-ca18d8b3",
"instance_id": null,
"instance_monitoring": false,
"instance_profile_name": null,
"instance_type": "i3.large",
"kernel_id": null,
"key_name": null,
"name": "ec2 launch config with spot fail poc",
"placement_tenancy": "default",
"profile": null,
"ramdisk_id": null,
"region": "eu-west-1",
"security_groups": [],
"security_token": null,
"spot_price": 0.02,
"state": "present",
"user_data": null,
"user_data_path": null,
"validate_certs": true,
"volumes": null,
"vpc_id": null
}
},
"msg": "Failed to create launch configuration",
"response_metadata": {
"http_headers": {
"connection": "close",
"content-length": "308",
"content-type": "text/xml",
"date": "Tue, 03 Oct 2017 23:54:10 GMT",
"x-amzn-requestid": "26e2fc40-a896-11e7-be78-49d83c244553"
},
"http_status_code": 400,
"request_id": "26e2fc40-a896-11e7-be78-49d83c244553",
"retry_attempts": 0
}
}
to retry, use: --limit @/home/test/git/ansible/poc.retry

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

# Fixed code
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(
        dict(
            name=dict(required=True),
            image_id=dict(),
            instance_id=dict(),
            key_name=dict(),
            security_groups=dict(default=[], type="list"),
            user_data=dict(),
            user_data_path=dict(type="path"),
            kernel_id=dict(),
            volumes=dict(type="list"),
            instance_type=dict(),
            state=dict(default="present", choices=["present", "absent"]),
            spot_price=dict(type="float"),
            ramdisk_id=dict(),
            instance_profile_name=dict(),
            ebs_optimized=dict(default=False, type="bool"),
            associate_public_ip_address=dict(type="bool"),
            instance_monitoring=dict(default=False, type="bool"),
            assign_public_ip=dict(type="bool"),
            classic_link_vpc_security_groups=dict(type="list"),
            classic_link_vpc_id=dict(),
            vpc_id=dict(),
            placement_tenancy=dict(choices=["default", "dedicated"]),
        )
    )

    module = AnsibleModule(
        argument_spec=argument_spec,
        mutually_exclusive=[["user_data", "user_data_path"]],
    )

    if not HAS_BOTO3:
        module.fail_json(msg="boto3 required for this module")

    try:
        region, ec2_url, aws_connect_kwargs = get_aws_connection_info(
            module, boto3=True
        )
        connection = boto3_conn(
            module,
            conn_type="client",
            resource="autoscaling",
            region=region,
            endpoint=ec2_url,
            **aws_connect_kwargs,
        )
    except botocore.exceptions.NoRegionError:
        module.fail_json(
            msg=(
                "region must be specified as a parameter in AWS_DEFAULT_REGION environment variable or in boto configuration file"
            )
        )
    except botocore.exceptions.ClientError as e:
        module.fail_json(
            msg="unable to establish connection - " + str(e),
            exception=traceback.format_exc(),
            **camel_dict_to_snake_dict(e.response),
        )

    state = module.params.get("state")

    if state == "present":
        create_launch_config(connection, module)
    elif state == "absent":
        delete_launch_config(connection, module)