# Buggy code
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(
        dict(
            state=dict(required=True, choices=["present", "absent"], type="str"),
            policy_name=dict(required=True, type="str"),
            service_namespace=dict(
                required=True,
                choices=["ecs", "elasticmapreduce", "ec2", "appstream", "dynamodb"],
                type="str",
            ),
            resource_id=dict(required=True, type="str"),
            scalable_dimension=dict(
                required=True,
                choices=[
                    "ecs:service:DesiredCount",
                    "ec2:spot-fleet-request:TargetCapacity",
                    "elasticmapreduce:instancegroup:InstanceCount",
                    "appstream:fleet:DesiredCapacity",
                    "dynamodb:table:ReadCapacityUnits",
                    "dynamodb:table:WriteCapacityUnits",
                    "dynamodb:index:ReadCapacityUnits",
                    "dynamodb:index:WriteCapacityUnits",
                ],
                type="str",
            ),
            policy_type=dict(
                required=True,
                choices=["StepScaling", "TargetTrackingScaling"],
                type="str",
            ),
            step_scaling_policy_configuration=dict(required=False, type="dict"),
            target_tracking_scaling_policy_configuration=dict(
                required=False, type="dict"
            ),
            minimum_tasks=dict(required=False, type="int"),
            maximum_tasks=dict(required=False, type="int"),
            override_task_capacity=dict(required=False, type=bool),
        )
    )

    module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True)

    connection = module.client("application-autoscaling")

    if module.params.get("state") == "present":
        # A scalable target must be registered prior to creating a scaling policy
        scalable_target_result = create_scalable_target(connection, module)
        policy_result = create_scaling_policy(connection, module)
        # Merge the results of the scalable target creation and policy deletion/creation
        # There's no risk in overriding values since mutual keys have the same values in our case
        merged_result = merge_results(scalable_target_result, policy_result)
        module.exit_json(**merged_result)
    else:
        policy_result = delete_scaling_policy(connection, module)
        module.exit_json(**policy_result)


# Traceback (botocore.exceptions.ParamValidationError)
ansible-playbook -vvvvv test.yaml -e type=webserver
ansible-playbook 2.7.0.dev0
config file = None
configured module search path = ['/Users/nhudson/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.7/site-packages/ansible
executable location = /usr/local/bin/ansible-playbook
python version = 3.7.0 (default, Jun 29 2018, 20:13:13) [Clang 9.1.0 (clang-902.0.39.2)]
No config file found; using defaults
setting up inventory plugins
[WARNING]: Unable to parse /etc/ansible/hosts as an inventory source

[WARNING]: No inventory was parsed, only implicit localhost is available

[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

Loading callback plugin default of type stdout, v2.0 from /usr/local/lib/python3.7/site-packages/ansible/plugins/callback/default.py

PLAYBOOK: test.yaml ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************
1 plays in test.yaml

PLAY [Test playbook"] ******************************************************************************************************************************************************************************************************************************************************************************************************************************************************
META: ran handlers

TASK [include vars] ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************
task path: /Users/nhudson/ansible-test/test.yaml:8
looking for "include.yaml" at "/Users/nhudson/ansible-test/vars/include.yaml"
looking for "include.yaml" at "/Users/nhudson/ansible-test/include.yaml"
ok: [localhost] => {
"ansible_facts": {
"asg_desired_capacity": 15,
"asg_max_size": 15,
"asg_min_size": 2,
"container_memory": 256,
"ec2_asg_down_threshold": 35,
"ec2_asg_up_threshold": 49,
"ecsasg_max_cap": 320,
"ecsasg_min_cap": 32,
"metrictype": {
"webserver": "ALBRequestCountPerTarget"
},
"targetvalue": {
"webserver": 12000
},
"task_cpu_units": 128,
"task_mem_units": 256
},
"ansible_included_var_files": [
"/Users/nhudson/ansible-test/include.yaml"
],
"changed": false
}

TASK [Create ECS ASG Policy] ***********************************************************************************************************************************************************************************************************************************************************************************************************************************************
task path: /Users/nhudson/ansible-test/test.yaml:12
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: nhudson
<127.0.0.1> EXEC /bin/sh -c 'echo ~nhudson &amp;&amp; sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 &amp;&amp; mkdir -p "` echo /Users/nhudson/.ansible/tmp/ansible-tmp-1531864512.677988-193768434592477 `" &amp;&amp; echo ansible-tmp-1531864512.677988-193768434592477="` echo /Users/nhudson/.ansible/tmp/ansible-tmp-1531864512.677988-193768434592477 `" ) &amp;&amp; sleep 0'
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/aws/core.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/ec2.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/aws/__init__.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/basic.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/_text.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/six/__init__.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/common/__init__.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/common/_collections_compat.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/parsing/__init__.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/parsing/convert_bool.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/pycompat24.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/common/dict_transformations.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/cloud.py
Using module_utils file /usr/local/lib/python3.7/site-packages/ansible/module_utils/ansible_release.py
Using module file /usr/local/lib/python3.7/site-packages/ansible/modules/cloud/amazon/aws_application_scaling_policy.py
<127.0.0.1> PUT /Users/nhudson/.ansible/tmp/ansible-local-51600kd2fx8n5/tmpcpjt8b7j TO /Users/nhudson/.ansible/tmp/ansible-tmp-1531864512.677988-193768434592477/aws_application_scaling_policy.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /Users/nhudson/.ansible/tmp/ansible-tmp-1531864512.677988-193768434592477/ /Users/nhudson/.ansible/tmp/ansible-tmp-1531864512.677988-193768434592477/aws_application_scaling_policy.py &amp;&amp; sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/local/opt/python/bin/python3.7 /Users/nhudson/.ansible/tmp/ansible-tmp-1531864512.677988-193768434592477/aws_application_scaling_policy.py &amp;&amp; sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /Users/nhudson/.ansible/tmp/ansible-tmp-1531864512.677988-193768434592477/ > /dev/null 2>&amp;1 &amp;&amp; sleep 0'
The full traceback is:
Traceback (most recent call last):
File "/var/folders/2q/908100k9689g1__yx2g40xch0000gn/T/ansible_hwix06ul/ansible_module_aws_application_scaling_policy.py", line 423, in create_scaling_policy
TargetTrackingScalingPolicyConfiguration=scaling_policy['TargetTrackingScalingPolicyConfiguration']
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/botocore/client.py", line 314, in _api_call
return self._make_api_call(operation_name, kwargs)
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/botocore/client.py", line 586, in _make_api_call
api_params, operation_model, context=request_context)
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/botocore/client.py", line 621, in _convert_to_request_dict
api_params, operation_model)
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/botocore/validate.py", line 291, in serialize_to_request
raise ParamValidationError(report=report.generate_report())
botocore.exceptions.ParamValidationError: Parameter validation failed:
Invalid type for parameter TargetTrackingScalingPolicyConfiguration.TargetValue, value: 12000, type: <class 'str'>, valid types: <class 'float'>, <class 'decimal.Decimal'>, <class 'int'>

fatal: [localhost]: FAILED! => {
"boto3_version": "1.7.58",
"botocore_version": "1.10.58",
"changed": false,
"invocation": {
"module_args": {
"aws_access_key": null,
"aws_secret_key": null,
"ec2_url": null,
"maximum_tasks": 320,
"minimum_tasks": 32,
"override_task_capacity": null,
"policy_name": "ECS-Autoscale",
"policy_type": "TargetTrackingScaling",
"profile": null,
"region": null,
"resource_id": "service/test-webserver/webserver",
"scalable_dimension": "ecs:service:DesiredCount",
"security_token": null,
"service_namespace": "ecs",
"state": "present",
"step_scaling_policy_configuration": null,
"target_tracking_scaling_policy_configuration": {
"DisableScaleIn": false,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ALBRequestCountPerTarget",
"ResourceLabel": "app/test-webserver/f433c2530b1d465e/targetgroup/test-webserver/1518cd89679a6f0e"
},
"ScaleInCooldown": 60,
"ScaleOutCooldown": 30,
"TargetValue": "12000"
},
"validate_certs": true
}
},
"msg": "Failed to create scaling policy: Parameter validation failed:\nInvalid type for parameter TargetTrackingScalingPolicyConfiguration.TargetValue, value: 12000, type: <class 'str'>, valid types: <class 'float'>, <class 'decimal.Decimal'>, <class 'int'>"
}
to retry, use: --limit @/Users/nhudson/ansible-test/test.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(
            state=dict(required=True, choices=["present", "absent"], type="str"),
            policy_name=dict(required=True, type="str"),
            service_namespace=dict(
                required=True,
                choices=["ecs", "elasticmapreduce", "ec2", "appstream", "dynamodb"],
                type="str",
            ),
            resource_id=dict(required=True, type="str"),
            scalable_dimension=dict(
                required=True,
                choices=[
                    "ecs:service:DesiredCount",
                    "ec2:spot-fleet-request:TargetCapacity",
                    "elasticmapreduce:instancegroup:InstanceCount",
                    "appstream:fleet:DesiredCapacity",
                    "dynamodb:table:ReadCapacityUnits",
                    "dynamodb:table:WriteCapacityUnits",
                    "dynamodb:index:ReadCapacityUnits",
                    "dynamodb:index:WriteCapacityUnits",
                ],
                type="str",
            ),
            policy_type=dict(
                required=True,
                choices=["StepScaling", "TargetTrackingScaling"],
                type="str",
            ),
            step_scaling_policy_configuration=dict(required=False, type="dict"),
            target_tracking_scaling_policy_configuration=dict(
                required=False,
                type="dict",
                options=dict(
                    CustomizedMetricSpecification=dict(type="dict"),
                    DisableScaleIn=dict(type="bool"),
                    PredefinedMetricSpecification=dict(type="dict"),
                    ScaleInCooldown=dict(type="int"),
                    ScaleOutCooldown=dict(type="int"),
                    TargetValue=dict(type="float"),
                ),
            ),
            minimum_tasks=dict(required=False, type="int"),
            maximum_tasks=dict(required=False, type="int"),
            override_task_capacity=dict(required=False, type=bool),
        )
    )

    module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True)

    connection = module.client("application-autoscaling")

    # Remove any target_tracking_scaling_policy_configuration suboptions that are None
    policy_config_options = [
        "CustomizedMetricSpecification",
        "DisableScaleIn",
        "PredefinedMetricSpecification",
        "ScaleInCooldown",
        "ScaleOutCooldown",
        "TargetValue",
    ]
    if isinstance(module.params["target_tracking_scaling_policy_configuration"], dict):
        for option in policy_config_options:
            if (
                module.params["target_tracking_scaling_policy_configuration"][option]
                is None
            ):
                module.params["target_tracking_scaling_policy_configuration"].pop(
                    option
                )

    if module.params.get("state") == "present":
        # A scalable target must be registered prior to creating a scaling policy
        scalable_target_result = create_scalable_target(connection, module)
        policy_result = create_scaling_policy(connection, module)
        # Merge the results of the scalable target creation and policy deletion/creation
        # There's no risk in overriding values since mutual keys have the same values in our case
        merged_result = merge_results(scalable_target_result, policy_result)
        module.exit_json(**merged_result)
    else:
        policy_result = delete_scaling_policy(connection, module)
        module.exit_json(**policy_result)