Repository: ansible/ansible

Issue:
ec2_eni module does not allow to assign additional secondary private ip 
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
- ec2_eni

##### ANSIBLE VERSION
```
ansible 2.2.2.0
  config file = /home/pshewale/ansible.cfg
  configured module search path = ['library']
```
##### OS / ENVIRONMENT
NA

##### SUMMARY
When using `ec2_eni` module to modify existing network interface on AWS, it does not allow to assign additional secondary private IP. It removed all existing secondary private IP addresses, instead of just assigning additional one.

##### STEPS TO REPRODUCE
1. Get an ec2 instance where multiple private IPs are allowed ( for example c4.xlarge allows 15 IPs)
2. Make sure its network interface is already having one or more secondary private IPs
3. Try to assign new secondary private IP using ec2_eni module

```
- ec2_eni:
        region: us-east-1
        eni_id: eni-yyyyyyyy
        state: present
        secondary_private_ip_addresses: 172.16.1.3

```


##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
It should assign additional secondary private IP instead of overwriting it.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
It removed existing secondary private IP and then added new one.


ec2_eni module does not allow to assign additional secondary private ip 
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
- ec2_eni

##### ANSIBLE VERSION
```
ansible 2.2.2.0
  config file = /home/pshewale/ansible.cfg
  configured module search path = ['library']
```
##### OS / ENVIRONMENT
NA

##### SUMMARY
When using `ec2_eni` module to modify existing network interface on AWS, it does not allow to assign additional secondary private IP. It removed all existing secondary private IP addresses, instead of just assigning additional one.

##### STEPS TO REPRODUCE
1. Get an ec2 instance where multiple private IPs are allowed ( for example c4.xlarge allows 15 IPs)
2. Make sure its network interface is already having one or more secondary private IPs
3. Try to assign new secondary private IP using ec2_eni module

```
- ec2_eni:
        region: us-east-1
        eni_id: eni-yyyyyyyy
        state: present
        secondary_private_ip_addresses: 172.16.1.3

```


##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
It should assign additional secondary private IP instead of overwriting it.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
It removed existing secondary private IP and then added new one.




Discussion:
cc @wimnat
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @wimnat
[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/ec2_eni.py b/lib/ansible/modules/cloud/amazon/ec2_eni.py
--- a/lib/ansible/modules/cloud/amazon/ec2_eni.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_eni.py
@@ -100,6 +100,13 @@
         This option is mutually exclusive of secondary_private_ip_address_count
     required: false
     version_added: 2.2
+  purge_secondary_private_ip_addresses:
+    description:
+      - To be used with I(secondary_private_ip_addresses) to determine whether or not to remove any secondary IP addresses other than those specified.
+        Set secondary_private_ip_addresses to an empty list to purge all secondary addresses.
+    required: false
+    default: False
+    version_added: 2.5
   secondary_private_ip_address_count:
     description:
       - The number of secondary IP addresses to assign to the network interface. This option is mutually exclusive of secondary_private_ip_addresses
@@ -372,6 +379,7 @@ def modify_eni(connection, vpc_id, module, eni):
     source_dest_check = module.params.get("source_dest_check")
     delete_on_termination = module.params.get("delete_on_termination")
     secondary_private_ip_addresses = module.params.get("secondary_private_ip_addresses")
+    purge_secondary_private_ip_addresses = module.params.get("purge_secondary_private_ip_addresses")
     secondary_private_ip_address_count = module.params.get("secondary_private_ip_address_count")
     changed = False
 
@@ -397,15 +405,20 @@ def modify_eni(connection, vpc_id, module, eni):
         current_secondary_addresses = [i.private_ip_address for i in eni.private_ip_addresses if not i.primary]
         if secondary_private_ip_addresses is not None:
             secondary_addresses_to_remove = list(set(current_secondary_addresses) - set(secondary_private_ip_addresses))
-            if secondary_addresses_to_remove:
+            if secondary_addresses_to_remove and purge_secondary_private_ip_addresses:
                 connection.unassign_private_ip_addresses(network_interface_id=eni.id,
                                                          private_ip_addresses=list(set(current_secondary_addresses) -
                                                                                    set(secondary_private_ip_addresses)),
                                                          dry_run=False)
-            connection.assign_private_ip_addresses(network_interface_id=eni.id,
-                                                   private_ip_addresses=secondary_private_ip_addresses,
-                                                   secondary_private_ip_address_count=None,
-                                                   allow_reassignment=False, dry_run=False)
+                changed = True
+
+            secondary_addresses_to_add = list(set(secondary_private_ip_addresses) - set(current_secondary_addresses))
+            if secondary_addresses_to_add:
+                connection.assign_private_ip_addresses(network_interface_id=eni.id,
+                                                       private_ip_addresses=secondary_addresses_to_add,
+                                                       secondary_private_ip_address_count=None,
+                                                       allow_reassignment=False, dry_run=False)
+                changed = True
         if secondary_private_ip_address_count is not None:
             current_secondary_address_count = len(current_secondary_addresses)
 
@@ -562,6 +575,7 @@ def main():
             source_dest_check=dict(default=None, type='bool'),
             delete_on_termination=dict(default=None, type='bool'),
             secondary_private_ip_addresses=dict(default=None, type='list'),
+            purge_secondary_private_ip_addresses=dict(default=False, type='bool'),
             secondary_private_ip_address_count=dict(default=None, type='int'),
             attached=dict(default=None, type='bool')
         )
@@ -573,7 +587,8 @@ def main():
                            ],
                            required_if=([
                                ('state', 'absent', ['eni_id']),
-                               ('attached', True, ['instance_id'])
+                               ('attached', True, ['instance_id']),
+                               ('purge_secondary_private_ip_addresses', True, ['secondary_private_ip_addresses'])
                            ])
                            )
 


Test patch: