Repository: ansible/ansible-modules-core

Issue:
ec2 module broken with multiple ENIs attached
##### Issue Type:
- Bug Report
##### Plugin Name:

ec2.py
##### Ansible Version:

ansible 2.1.0 (devel b01caa371f) last updated 2016/03/08 20:18:29 (GMT -700)
  lib/ansible/modules/core: (detached HEAD 45745424f7) last updated 2016/03/08 20:18:32 (GMT -700)
  lib/ansible/modules/extras: (detached HEAD b51efc51bc) last updated 2016/03/08 20:18:36 (GMT -700)
  config file = 
  configured module search path = Default w/o overrides
##### Ansible Configuration:

N/A
##### Environment:

N/A
##### Summary:

An AWS instance with multiple ENIs attached causes a failure.  This looks to have been introduced in change 2f83000  (Added "EC2 instance" termination_protection and source_dest_check cha…) in June 2015.

The breakage is that the call to inst.get_attribute('sourceDestCheck')['sourceDestCheck'] fails because when multiple ENIs are attached, the sourceDestCheck applies to individual ENIs, not to the instance as a whole.
##### Steps To Reproduce:
1. use ec2.py module to create instance
2. stop instance using ec2.py
3. create additional using ec2_eni.py
4. start instance using ec2.py
##### Expected Results:

Instance starts.
##### Actual Results:

`An exception occurred during task execution. The full traceback is:
Traceback (most recent call last):
  File "/Users/kenneth/.ansible/tmp/ansible-tmp-1457908444.48-23265952758740/ec2", line 3731, in <module>
    main()
  File "/Users/kenneth/.ansible/tmp/ansible-tmp-1457908444.48-23265952758740/ec2", line 1417, in main
    (changed, instance_dict_array, new_instance_ids) = startstop_instances(module, ec2, instance_ids, state, instance_tags)
  File "/Users/kenneth/.ansible/tmp/ansible-tmp-1457908444.48-23265952758740/ec2", line 1288, in startstop_instances
    if inst.get_attribute('sourceDestCheck')['sourceDestCheck'] != source_dest_check:
  File "/Library/Python/2.7/site-packages/boto/ec2/instance.py", line 526, in get_attribute
    dry_run=dry_run
  File "/Library/Python/2.7/site-packages/boto/ec2/connection.py", line 1141, in get_instance_attribute
    InstanceAttribute, verb='POST')
  File "/Library/Python/2.7/site-packages/boto/connection.py", line 1208, in get_object
    raise self.ResponseError(response.status, response.reason, body)
boto.exception.EC2ResponseError: EC2ResponseError: 400 Bad Request
<?xml version="1.0" encoding="UTF-8"?>
<Response><Errors><Error><Code>InvalidInstanceID</Code><Message>There are multiple interfaces attached to instance 'i-4f4c26cb'. Please specify an interface ID for the operation instead.</Message></Error></Errors><RequestID>3425ca5a-7f01-40ce-9ffd-4635aed6f509</RequestID></Response>
`

Note the affected instances start cleanly from the AWS console



Discussion:
I'm experiencing this problem even when trying to simply shut down an instance with multiple ENIs:

Sample YML:

```
      ec2:
        region: "{{ aws_region }}"
        instance_ids: "{{ ctrl_instance_ids }}"
        state: stopped
        wait: yes
```

Hrm. Unfortunately, the `source_dest_check` parameter on the module is a `bool`, when in reality it's a per-interface decision. The options we have are:
- When there are multiple ENI's, apply the user's choice (yes/no) to all of the interfaces
- When there are multiple ENI's, skip src/dest checks entirely and expect the user to use the ec2_eni module to handle interface settings

It looks like that option is fairly old (added in 1.6), so we can't remove it, so what behavior do folks think makes the most sense? If I missed an option, chime on in. 

@ansible, ping. This issue is still waiting on your response.
[click here for bot help](https://github.com/jctanner/ansibullbot/blob/ISSUE_TRIAGE_CLASS/ISSUE_HELP.md)

<!--- boilerplate: issue_renotify_maintainer --->

@ansible, ping. This issue is still waiting on your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)

<!--- boilerplate: issue_renotify_maintainer --->


Resolving patch:
diff --git a/cloud/amazon/ec2.py b/cloud/amazon/ec2.py
--- a/cloud/amazon/ec2.py
+++ b/cloud/amazon/ec2.py
@@ -1310,9 +1310,22 @@ def startstop_instances(module, ec2, instance_ids, state, instance_tags):
         for inst in res.instances:
 
             # Check "source_dest_check" attribute
-            if inst.vpc_id is not None and inst.get_attribute('sourceDestCheck')['sourceDestCheck'] != source_dest_check:
-                inst.modify_attribute('sourceDestCheck', source_dest_check)
-                changed = True
+            try:
+                if inst.vpc_id is not None and inst.get_attribute('sourceDestCheck')['sourceDestCheck'] != source_dest_check:
+                    inst.modify_attribute('sourceDestCheck', source_dest_check)
+                    changed = True
+            except boto.exception.EC2ResponseError as exc:
+                # instances with more than one Elastic Network Interface will
+                # fail, because they have the sourceDestCheck attribute defined
+                # per-interface
+                if exc.code == 'InvalidInstanceID':
+                    for interface in inst.interfaces:
+                        if interface.source_dest_check != source_dest_check:
+                            ec2.modify_network_interface_attribute(interface.id, "sourceDestCheck", source_dest_check)
+                            changed = True
+                else:
+                    module.fail_json(msg='Failed to handle source_dest_check state for instance {0}, error: {1}'.format(inst.id, exc),
+                                     exception=traceback.format_exc(exc))
 
             # Check "termination_protection" attribute
             if inst.get_attribute('disableApiTermination')['disableApiTermination'] != termination_protection:


Test patch: