Repository: cloudtools/troposphere

Issue:
EC2 NoDevice error with boolean values.
It appears that the `NoDevice` field in the `BlockDeviceMapping` was updated in #858 to be a boolean.  Unfortunately now, the generated templates are failing to deploy, regardless of what value you use for the field.  When set to either True or False, I receive the following error when trying to deploy a generated template via CloudFormation: `Value of property NoDevice must be an object`

Looking at the [docs](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-blockdev-mapping.html#cfn-ec2-blockdev-mapping-nodevice)  the type is listed as boolean, however the last example on the page `Unmapping an AMI-defined Device` shows the use of an empty map, `{}`, and not a string or boolean value.

I saw that @tweakster noted he received a error stating this should be a string, but I have tried multiple regions, and always receive the same error if an empty map is not used.

We have been using this value successfully in previous versions, and I propose that the change be reverted.  I will work on submitting the required code change, later today, and can provide a sample of working code using this empty mapping as well.


Discussion:

Resolving patch:
diff --git a/examples/EC2_Remove_Ephemeral_Drive.py b/examples/EC2_Remove_Ephemeral_Drive.py
new file mode 100644
--- /dev/null
+++ b/examples/EC2_Remove_Ephemeral_Drive.py
@@ -0,0 +1,82 @@
+from troposphere import FindInMap, GetAtt, Output, Parameter, Ref, Template
+from troposphere.ec2 import NO_DEVICE
+
+import troposphere.ec2 as ec2
+
+
+template = Template()
+
+keyname_param = template.add_parameter(Parameter(
+    "KeyName",
+    Description="Name of an existing EC2 KeyPair to enable SSH "
+                "access to the instance",
+    Type="String",
+))
+
+template.add_mapping('RegionMap', {
+      "ap-northeast-1": {"AMI": "ami-6959870f"},
+      "ap-northeast-2": {"AMI": "ami-08d77266"},
+      "ap-south-1": {"AMI": "ami-50591a3f"},
+      "ap-southeast-1": {"AMI": "ami-d9dca7ba"},
+      "ap-southeast-2": {"AMI": "ami-02ad4060"},
+      "ca-central-1": {"AMI": "ami-13e45c77"},
+      "eu-central-1": {"AMI": "ami-e613ac89"},
+      "eu-west-1": {"AMI": "ami-eed00d97"},
+      "eu-west-2": {"AMI": "ami-ba5f42de"},
+      "sa-east-1": {"AMI": "ami-1ca7d970"},
+      "us-east-1": {"AMI": "ami-bcdc16c6"},
+      "us-east-2": {"AMI": "ami-49426e2c"},
+      "us-west-1": {"AMI": "ami-1b17257b"},
+      "us-west-2": {"AMI": "ami-19e92861"}
+    })
+
+ec2_instance = template.add_resource(ec2.Instance(
+    "Ec2Instance",
+    ImageId=FindInMap("RegionMap", Ref("AWS::Region"), "AMI"),
+    InstanceType="t2.micro",
+    KeyName=Ref(keyname_param),
+    SecurityGroups=["default"],
+    BlockDeviceMappings=[
+        ec2.BlockDeviceMapping(
+            DeviceName='/dev/sdb',
+            NoDevice=NO_DEVICE),
+        ec2.BlockDeviceMapping(
+            DeviceName='/dev/sdc',
+            NoDevice=NO_DEVICE),
+    ]
+))
+
+template.add_output([
+    Output(
+        "InstanceId",
+        Description="InstanceId of the newly created EC2 instance",
+        Value=Ref(ec2_instance),
+    ),
+    Output(
+        "AZ",
+        Description="Availability Zone of the newly created EC2 instance",
+        Value=GetAtt(ec2_instance, "AvailabilityZone"),
+    ),
+    Output(
+        "PublicIP",
+        Description="Public IP address of the newly created EC2 instance",
+        Value=GetAtt(ec2_instance, "PublicIp"),
+    ),
+    Output(
+        "PrivateIP",
+        Description="Private IP address of the newly created EC2 instance",
+        Value=GetAtt(ec2_instance, "PrivateIp"),
+    ),
+    Output(
+        "PublicDNS",
+        Description="Public DNSName of the newly created EC2 instance",
+        Value=GetAtt(ec2_instance, "PublicDnsName"),
+    ),
+    Output(
+        "PrivateDNS",
+        Description="Private DNSName of the newly created EC2 instance",
+        Value=GetAtt(ec2_instance, "PrivateDnsName"),
+    ),
+])
+
+print(template.to_json())
diff --git a/troposphere/ec2.py b/troposphere/ec2.py
--- a/troposphere/ec2.py
+++ b/troposphere/ec2.py
@@ -117,11 +117,14 @@ class EBSBlockDevice(AWSProperty):
     }
 
 
+NO_DEVICE = {}
+
+
 class BlockDeviceMapping(AWSProperty):
     props = {
         'DeviceName': (basestring, True),
         'Ebs': (EBSBlockDevice, False),      # Conditional
-        'NoDevice': (boolean, False),
+        'NoDevice': (dict, False),
         'VirtualName': (basestring, False),  # Conditional
     }
 


Test patch:
diff --git a/tests/examples_output/EC2_Remove_Ephemeral_Drive.template b/tests/examples_output/EC2_Remove_Ephemeral_Drive.template
new file mode 100644
--- /dev/null
+++ b/tests/examples_output/EC2_Remove_Ephemeral_Drive.template
@@ -0,0 +1,140 @@
+{
+    "Mappings": {
+        "RegionMap": {
+            "ap-northeast-1": {
+                "AMI": "ami-6959870f"
+            },
+            "ap-northeast-2": {
+                "AMI": "ami-08d77266"
+            },
+            "ap-south-1": {
+                "AMI": "ami-50591a3f"
+            },
+            "ap-southeast-1": {
+                "AMI": "ami-d9dca7ba"
+            },
+            "ap-southeast-2": {
+                "AMI": "ami-02ad4060"
+            },
+            "ca-central-1": {
+                "AMI": "ami-13e45c77"
+            },
+            "eu-central-1": {
+                "AMI": "ami-e613ac89"
+            },
+            "eu-west-1": {
+                "AMI": "ami-eed00d97"
+            },
+            "eu-west-2": {
+                "AMI": "ami-ba5f42de"
+            },
+            "sa-east-1": {
+                "AMI": "ami-1ca7d970"
+            },
+            "us-east-1": {
+                "AMI": "ami-bcdc16c6"
+            },
+            "us-east-2": {
+                "AMI": "ami-49426e2c"
+            },
+            "us-west-1": {
+                "AMI": "ami-1b17257b"
+            },
+            "us-west-2": {
+                "AMI": "ami-19e92861"
+            }
+        }
+    },
+    "Outputs": {
+        "AZ": {
+            "Description": "Availability Zone of the newly created EC2 instance",
+            "Value": {
+                "Fn::GetAtt": [
+                    "Ec2Instance",
+                    "AvailabilityZone"
+                ]
+            }
+        },
+        "InstanceId": {
+            "Description": "InstanceId of the newly created EC2 instance",
+            "Value": {
+                "Ref": "Ec2Instance"
+            }
+        },
+        "PrivateDNS": {
+            "Description": "Private DNSName of the newly created EC2 instance",
+            "Value": {
+                "Fn::GetAtt": [
+                    "Ec2Instance",
+                    "PrivateDnsName"
+                ]
+            }
+        },
+        "PrivateIP": {
+            "Description": "Private IP address of the newly created EC2 instance",
+            "Value": {
+                "Fn::GetAtt": [
+                    "Ec2Instance",
+                    "PrivateIp"
+                ]
+            }
+        },
+        "PublicDNS": {
+            "Description": "Public DNSName of the newly created EC2 instance",
+            "Value": {
+                "Fn::GetAtt": [
+                    "Ec2Instance",
+                    "PublicDnsName"
+                ]
+            }
+        },
+        "PublicIP": {
+            "Description": "Public IP address of the newly created EC2 instance",
+            "Value": {
+                "Fn::GetAtt": [
+                    "Ec2Instance",
+                    "PublicIp"
+                ]
+            }
+        }
+    },
+    "Parameters": {
+        "KeyName": {
+            "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instance",
+            "Type": "String"
+        }
+    },
+    "Resources": {
+        "Ec2Instance": {
+            "Properties": {
+                "BlockDeviceMappings": [
+                    {
+                        "DeviceName": "/dev/sdb",
+                        "NoDevice": {}
+                    },
+                    {
+                        "DeviceName": "/dev/sdc",
+                        "NoDevice": {}
+                    }
+                ],
+                "ImageId": {
+                    "Fn::FindInMap": [
+                        "RegionMap",
+                        {
+                            "Ref": "AWS::Region"
+                        },
+                        "AMI"
+                    ]
+                },
+                "InstanceType": "t2.micro",
+                "KeyName": {
+                    "Ref": "KeyName"
+                },
+                "SecurityGroups": [
+                    "default"
+                ]
+            },
+            "Type": "AWS::EC2::Instance"
+        }
+    }
+}