Repository: ansible/ansible

Issue:
Ansible won't convert string to int
##### SUMMARY
When looking up variable that is an int, ansible converts to string and wont convert back to int.

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
aws_application_scaling_policy

##### ANSIBLE VERSION
```
ansible 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
  python version = 3.7.0 (default, Jun 29 2018, 20:13:13) [Clang 9.1.0 (clang-902.0.39.2)]
```

##### CONFIGURATION
```
```

##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-spggecific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.-->
```
OSX 10.13.5
```

##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->


<!--- Paste example playbooks or commands between quotes below -->
`include.yaml`
```
task_cpu_units: 128
task_mem_units: 256
container_memory: 256
ecsasg_min_cap: 32
ecsasg_max_cap: 320
asg_min_size: 2
asg_max_size: 15
asg_desired_capacity: 15
metrictype:
   webserver: ALBRequestCountPerTarget
targetvalue:
   webserver: 12000
ec2_asg_down_threshold: 35
ec2_asg_up_threshold: 49
```

`test.yaml`
```
- name: Test playbook
  hosts: localhost
  connection: local
  gather_facts: False

  tasks:
    - name: include vars
      include_vars:
        file: "include.yaml"

    - name: Create ECS ASG Policy
      aws_application_scaling_policy:
        policy_name: "ECS-Autoscale"
        state: present
        service_namespace: ecs
        resource_id: service/test-webserver/webserver
        scalable_dimension: ecs:service:DesiredCount
        minimum_tasks: "{{ ecsasg_min_cap }}"
        maximum_tasks: "{{ ecsasg_max_cap }}"
        policy_type: TargetTrackingScaling
        target_tracking_scaling_policy_configuration:
          TargetValue: "{{ targetvalue[type] | int }}"
          PredefinedMetricSpecification:
            PredefinedMetricType: ALBRequestCountPerTarget
            ResourceLabel: app/test-webserver/f433c2530b1d465e/targetgroup/test-webserver/1518cd89679a6f0e
          ScaleOutCooldown: 30
          ScaleInCooldown: 60
          DisableScaleIn: false
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Create ASG policy for ECS Service

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
The failure is coming from the dict object `targetvalue` in the `include.yaml` file.  When calling `{{ targetvalue[type] | int }}` it always returns the var value as a string, and cannot be converted to an int, even when casted.

<!--- Paste verbatim command output between quotes below -->
```
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 && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /Users/nhudson/.ansible/tmp/ansible-tmp-1531864512.677988-193768434592477 `" && echo ansible-tmp-1531864512.677988-193768434592477="` echo /Users/nhudson/.ansible/tmp/ansible-tmp-1531864512.677988-193768434592477 `" ) && 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 && 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 && 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>&1 && 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
```


Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/amazon/aws_application_scaling_policy.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/aws_application_scaling_policy.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @chenl87 @gurumaia
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/aws_application_scaling_policy.py b/lib/ansible/modules/cloud/amazon/aws_application_scaling_policy.py
--- a/lib/ansible/modules/cloud/amazon/aws_application_scaling_policy.py
+++ b/lib/ansible/modules/cloud/amazon/aws_application_scaling_policy.py
@@ -170,7 +170,7 @@
 policy_type:
     description: The policy type.
     returned: when state present
-    type: string    
+    type: string
 min_capacity:
     description: The minimum value to scale to in response to a scale in event. Required if I(state) is C(present).
     returned: when state present
@@ -197,7 +197,7 @@
             type: string
             sample: "ChangeInCapacity, PercentChangeInCapacity, ExactCapacity"
         cooldown:
-            description: The amount of time, in seconds, after a scaling activity completes 
+            description: The amount of time, in seconds, after a scaling activity completes
                 where previous trigger-related scaling activities can influence future scaling events
             returned: when state present and the policy type is StepScaling
             type: int
@@ -462,7 +462,18 @@ def main():
                                                         ], 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'),
+        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)
@@ -472,6 +483,15 @@ def main():
 
     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)


Test patch: