Repository: ansible/ansible

Issue:
AWS elasticache tries to do destructive change when zone is not present
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
elasticache

##### ANSIBLE VERSION
```
ansible 2.4.0.0
  config file = /Users/lkostka/devbox/prov/ansible.cfg
  configured module search path = [u'/Users/lkostka/devbox/prov/modules']
  ansible python module location = /Users/lkostka/.virtualenvs/prov/lib/python2.7/site-packages/ansible
  executable location = /Users/lkostka/.virtualenvs/prov/bin/ansible
  python version = 2.7.13 (default, Dec 18 2016, 07:03:39) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]
```

##### CONFIGURATION
```
ANSIBLE_PIPELINING(/Users/lkostka/devbox/prov/ansible.cfg) = True
ANSIBLE_SSH_ARGS(env: ANSIBLE_SSH_ARGS) =
DEFAULT_CALLBACK_PLUGIN_PATH(/Users/lkostka/devbox/prov/ansible.cfg) = [u'/Users/lkostka/devbox/prov/callbacks']
DEFAULT_FILTER_PLUGIN_PATH(/Users/lkostka/devbox/prov/ansible.cfg) = [u'/Users/lkostka/devbox/prov/filters']
DEFAULT_FORKS(/Users/lkostka/devbox/prov/ansible.cfg) = 40
DEFAULT_LOOKUP_PLUGIN_PATH(/Users/lkostka/devbox/prov/ansible.cfg) = [u'/Users/lkostka/devbox/prov/lookups']
DEFAULT_MODULE_PATH(/Users/lkostka/devbox/prov/ansible.cfg) = [u'/Users/lkostka/devbox/prov/modules']
DEFAULT_SCP_IF_SSH(/Users/lkostka/devbox/prov/ansible.cfg) = true
DEFAULT_TRANSPORT(/Users/lkostka/devbox/prov/ansible.cfg) = ssh
DEPRECATION_WARNINGS(/Users/lkostka/devbox/prov/ansible.cfg) = True
RETRY_FILES_ENABLED(/Users/lkostka/devbox/prov/ansible.cfg) = False
```

##### OS / ENVIRONMENT
mac os

##### SUMMARY
Module must receive null (None in python) to be able to work. If `zone` is not specified then module receives `''`. We do not care about in which az redis is. Without explicit zone, module fails because it receives empty string as a value of zone and [tries to do destructive change](https://github.com/ansible/ansible/blob/v2.4.0.0-1/lib/ansible/modules/cloud/amazon/elasticache.py#L420).

##### STEPS TO REPRODUCE
Just create a cluster without specifying `zone`.

##### EXPECTED RESULTS
Module should try to do destructive change when no zone key is missing from yaml file.

##### ACTUAL RESULTS
https://github.com/ansible/ansible/blob/v2.4.0.0-1/lib/ansible/modules/cloud/amazon/elasticache.py#L420

No zone key in module params causes module to see `zone` key value as `''`
AWS elasticache tries to do destructive change when zone is not present
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
elasticache

##### ANSIBLE VERSION
```
ansible 2.4.0.0
  config file = /Users/lkostka/devbox/prov/ansible.cfg
  configured module search path = [u'/Users/lkostka/devbox/prov/modules']
  ansible python module location = /Users/lkostka/.virtualenvs/prov/lib/python2.7/site-packages/ansible
  executable location = /Users/lkostka/.virtualenvs/prov/bin/ansible
  python version = 2.7.13 (default, Dec 18 2016, 07:03:39) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]
```

##### CONFIGURATION
```
ANSIBLE_PIPELINING(/Users/lkostka/devbox/prov/ansible.cfg) = True
ANSIBLE_SSH_ARGS(env: ANSIBLE_SSH_ARGS) =
DEFAULT_CALLBACK_PLUGIN_PATH(/Users/lkostka/devbox/prov/ansible.cfg) = [u'/Users/lkostka/devbox/prov/callbacks']
DEFAULT_FILTER_PLUGIN_PATH(/Users/lkostka/devbox/prov/ansible.cfg) = [u'/Users/lkostka/devbox/prov/filters']
DEFAULT_FORKS(/Users/lkostka/devbox/prov/ansible.cfg) = 40
DEFAULT_LOOKUP_PLUGIN_PATH(/Users/lkostka/devbox/prov/ansible.cfg) = [u'/Users/lkostka/devbox/prov/lookups']
DEFAULT_MODULE_PATH(/Users/lkostka/devbox/prov/ansible.cfg) = [u'/Users/lkostka/devbox/prov/modules']
DEFAULT_SCP_IF_SSH(/Users/lkostka/devbox/prov/ansible.cfg) = true
DEFAULT_TRANSPORT(/Users/lkostka/devbox/prov/ansible.cfg) = ssh
DEPRECATION_WARNINGS(/Users/lkostka/devbox/prov/ansible.cfg) = True
RETRY_FILES_ENABLED(/Users/lkostka/devbox/prov/ansible.cfg) = False
```

##### OS / ENVIRONMENT
mac os

##### SUMMARY
Module must receive null (None in python) to be able to work. If `zone` is not specified then module receives `''`. We do not care about in which az redis is. Without explicit zone, module fails because it receives empty string as a value of zone and [tries to do destructive change](https://github.com/ansible/ansible/blob/v2.4.0.0-1/lib/ansible/modules/cloud/amazon/elasticache.py#L420).

##### STEPS TO REPRODUCE
Just create a cluster without specifying `zone`.

##### EXPECTED RESULTS
Module should try to do destructive change when no zone key is missing from yaml file.

##### ACTUAL RESULTS
https://github.com/ansible/ansible/blob/v2.4.0.0-1/lib/ansible/modules/cloud/amazon/elasticache.py#L420

No zone key in module params causes module to see `zone` key value as `''`


Discussion:
cc @alachaum @jsdalton
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @alachaum @jsdalton
[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/elasticache.py b/lib/ansible/modules/cloud/amazon/elasticache.py
--- a/lib/ansible/modules/cloud/amazon/elasticache.py
+++ b/lib/ansible/modules/cloud/amazon/elasticache.py
@@ -217,10 +217,11 @@ def create(self):
                       CacheSecurityGroupNames=self.cache_security_groups,
                       SecurityGroupIds=self.security_group_ids,
                       CacheParameterGroupName=self.cache_parameter_group,
-                      CacheSubnetGroupName=self.cache_subnet_group,
-                      PreferredAvailabilityZone=self.zone)
+                      CacheSubnetGroupName=self.cache_subnet_group)
         if self.cache_port is not None:
             kwargs['Port'] = self.cache_port
+        if self.zone is not None:
+            kwargs['PreferredAvailabilityZone'] = self.zone
 
         try:
             self.conn.create_cache_cluster(**kwargs)
@@ -496,7 +497,7 @@ def main():
         cache_subnet_group=dict(default=""),
         cache_security_groups=dict(default=[], type='list'),
         security_group_ids=dict(default=[], type='list'),
-        zone=dict(default=""),
+        zone=dict(),
         wait=dict(default=True, type='bool'),
         hard_modify=dict(type='bool')
     ))


Test patch: