Repository: ansible/ansible

Issue:
having issues with ec2_elb_lb and idempotence
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME

`ec2_elb_lb` module.

##### ANSIBLE VERSION

2.2, installed via pip.

##### CONFIGURATION

```
AWS_REGION=eu-west-1
AWS_ACCESS_KEY=xxx
AWS_SECRET_KEY=yyy
```

##### OS / ENVIRONMENT

N/A

##### SUMMARY

One (or more) parameter(s) are breaking idempotence. I have some examples that work, but this one in particular, doesn't.
I am aware of #21323 and I tried w/o that parameter, but it didn't change anything. I backported the patch in the PR and it also didn't change anything. 

##### STEPS TO REPRODUCE

```yaml
- name: ELB(s) should be created
  ec2_elb_lb:
    region: "{{ item.region | default(aws_region) }}"
    aws_access_key: "{{ item.access_key | default(omit) }}"
    aws_secret_key: "{{ item.secret_key | default(omit) }}"
    profile: "{{ item.boto_profile | default(omit) }}"
    name: "{{ item.name }}"
    access_logs: "{{ item.access_logs | default(omit) }}"
    connection_draining_timeout: "{{ item.connection_draining_timeout | default(omit) }}"
    cross_az_load_balancing: "{{ item.cross_az_load_balancing | default(omit) }}"
    health_check: "{{ item.health_check | default(omit) }}"
    idle_timeout: "{{ item.idle_timeout | default(omit) }}"
    instance_ids: "{{ item.instance_ids | default(omit) }}"
    listeners: "{{ item.listeners }}"
    purge_instance_ids: "{{ item.purge_instance_ids | default('True') }}"
    purge_listeners: "{{ item.purge_listeners | default('True') }}"
    purge_subnets: "{{ item.purge_subnets | default('True') }}"
    purge_zones: "{{ item.purge_zones | default('True') }}"
    scheme: "{{ item.scheme | default('internal') }}"
    security_group_ids: "{{ item.security_group_ids | default(omit) }}"
    state: present
    stickiness: "{{ item.stickiness | default(omit) }}"
    subnets: "{{ item.subnets | default(omit) }}"
    zones: "{{ item.zones | default(omit) }}"
    tags: "{{ item.tags | default(omit) }}"
    validate_certs: True
    wait: True
    wait_timeout: 300
  with_items: "{{ elb_to_create }}"
  register: _elbs_created
  tags: elb
```

```yaml
elb_to_create:
  - name: 'be-gone-lemon'
    cross_az_load_balancing: True
    health_check:
      ping_protocol: http
      ping_port: 80
      ping_path: '/'
      response_timeout: 3
      interval: 30
      unhealthy_threshold: 2
      healthy_threshold: 10
    idle_timeout: 5
    instance_ids: "{{ instance_ids }}"
    security_group_ids: [ "{{ security_group_created.results[0].group_id }}" ]
    listeners:
      - protocol: http
        load_balancer_port: 80
        instance_port: 80
        proxy_protocol: 80
    purge_instance_ids: False
    purge_listeners: False
    purge_subnets: False
    scheme: internet-facing
    stickiness:
      type: loadbalancer
      enabled: True
      expiration: 300
    subnets:
      - "{{ vpcs_created[0].subnets[0].id }}"
      - "{{ vpcs_created[0].subnets[1].id }}"
    tags:
      Name: 'im-a-cool-lb'
      env: 'testing'
      purpose: 'delete-me-dummy'
```

##### EXPECTED RESULTS

Task is idempotent.

##### ACTUAL RESULTS

```
TASK [elb : ELB(s) should be created] ******************************************
changed: [localhost] => (item={u'subnets': [u'subnet-083a336c', u'subnet-94fcf3e2'], u'health_check': {u'response_timeout': 3, u'ping_port': 80, u'ping_protocol': u'http', u'interval': 30, u'unhealthy_threshold': 2, u'ping_path': u'/', u'healthy_threshold': 10}, u'name': u'be-gone-lemon', u'tags': {u'Name': u'im-a-cool-lb', u'env': u'testing', u'purpose': u'delete-me-dummy'}, u'scheme': u'internet-facing', u'purge_subnets': False, u'listeners': [{u'instance_port': 80, u'proxy_protocol': 80, u'load_balancer_port': 80, u'protocol': u'http'}], u'instance_ids': [u'i-0fef3127d41c3ed37', u'i-0e40a3dd74b74b828'], u'purge_instance_ids': False, u'idle_timeout': 5, u'purge_listeners': False, u'cross_az_load_balancing': True, u'security_group_ids': [u'sg-de5046b8']})
```



Discussion:
cc @jsdalton
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Upon further inspection, it turns out that `cross_az_load_balancing` really needs to be set to **yes** or **no**. Something should be done about it so that it is consistent with the way you can specify boolean values.

Also, it seems that the `idle_timeout` parameter is breaking idempotence as well. Removing this parameter and setting the `cross_az_load_balancing ` to either _yes_ or _no_ made the task idempotent.

@willthames given you've touched this module fairly recent and as I don't have the time to set up a proper testing environment for making a PR at the moment, would you be willing to take a look at the boolean switch issue? 😄 
According to the [ELB boto docs](http://boto.cloudhackers.com/en/latest/ref/elb.html#boto.ec2.elb.ELBConnection.modify_lb_attribute), `crossZoneLoadBalancing` takes a boolean.

What I believe is the problem is 
https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/ec2_elb_lb.py#L1246

It should have a `type='bool'` in the argument_spec (one could argue that using `type='int'` might be a better fix for `connection_draining` too, thinking about it, and would likely fix `idle_timeout` too) - are you able to give that a try and create a PR if it does work (if not I can probably sort one next week)

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/ec2_elb_lb.py b/lib/ansible/modules/cloud/amazon/ec2_elb_lb.py
--- a/lib/ansible/modules/cloud/amazon/ec2_elb_lb.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_elb_lb.py
@@ -1242,8 +1242,8 @@ def main():
         purge_subnets={'default': False, 'required': False, 'type': 'bool'},
         scheme={'default': 'internet-facing', 'required': False},
         connection_draining_timeout={'default': None, 'required': False},
-        idle_timeout={'default': None, 'required': False},
-        cross_az_load_balancing={'default': None, 'required': False},
+        idle_timeout={'default': None, 'type': 'int', 'required': False},
+        cross_az_load_balancing={'default': None, 'type': 'bool', 'required': False},
         stickiness={'default': None, 'required': False, 'type': 'dict'},
         access_logs={'default': None, 'required': False, 'type': 'dict'},
         wait={'default': False, 'type': 'bool', 'required': False},


Test patch: