Repository: cloudtools/troposphere

Issue:
AutoScalingGroup LaunchConfigurationName requirement
AWS::AutoScaling::AutoScalingGroup<sup>1</sup>  specifies that the LaunchConfigurationName and InstanceId fields are conditionally required (one must be present, and they are mutually exclusive) but the Troposphere class 'AutoScalingGroup' requires the LaunchConfigurationName parameter unconditionally. The result of this is that an autoscaling group requiring an InstanceId can not be provisioned through the 'AutoScalingGroup' class.

<sup>1</sup> (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-group.html#cfn-as-group-instanceid)



Discussion:
Thanks for the issue, @dmccaffrey - I've gone ahead and posted the issue with easily fixing something like this to the mailing list at: https://groups.google.com/forum/#!topic/cloudtools-dev/vHpr9Em2_0Q .  The simplest way wouldn't be the most satisfying I don't think, but may be worth it for now just to get this working for people.

Anyway, before I jump in and fix this issue I'd like to get some discussion going around this.  If you'd like to be in on that discussion, please join the mailing list.

Thanks!

Just to put this out, but is there a workaround meanwhile until the proper fix gets merged in? It does stop us from using InstanceId property in the 'AutoScalingGroup' class. Thanks


Resolving patch:
diff --git a/troposphere/autoscaling.py b/troposphere/autoscaling.py
--- a/troposphere/autoscaling.py
+++ b/troposphere/autoscaling.py
@@ -107,7 +107,7 @@ class AutoScalingGroup(AWSObject):
         'HealthCheckGracePeriod': (int, False),
         'HealthCheckType': (basestring, False),
         'InstanceId': (basestring, False),
-        'LaunchConfigurationName': (basestring, True),
+        'LaunchConfigurationName': (basestring, False),
         'LoadBalancerNames': (list, False),
         'MaxSize': (integer, True),
         'MetricsCollection': ([MetricsCollection], False),
@@ -135,6 +135,16 @@ def validate(self):
                         "The UpdatePolicy attribute "
                         "MinInstancesInService must be less than the "
                         "autoscaling group's MaxSize")
+        launch_config = self.properties.get('LaunchConfigurationName')
+        instance_id = self.properties.get('InstanceId')
+        if launch_config and instance_id:
+            raise ValueError("LaunchConfigurationName and InstanceId "
+                             "are mutually exclusive.")
+        if not launch_config and not instance_id:
+            raise ValueError("Must specify either LaunchConfigurationName or "
+                             "InstanceId: http://docs.aws.amazon.com/AWSCloud"
+                             "Formation/latest/UserGuide/aws-properties-as-gr"
+                             "oup.html#cfn-as-group-instanceid")
         return True
 
 


Test patch:
diff --git a/tests/test_asg.py b/tests/test_asg.py
new file mode 100644
--- /dev/null
+++ b/tests/test_asg.py
@@ -0,0 +1,46 @@
+import unittest
+from troposphere.autoscaling import AutoScalingGroup
+
+
+class TestAutoScalingGroup(unittest.TestCase):
+    def test_exclusive(self):
+        group = AutoScalingGroup(
+            'mygroup',
+            InstanceId="i-1234",
+            LaunchConfigurationName="I'm a test",
+            MaxSize="1",
+            MinSize="1",
+        )
+        with self.assertRaises(ValueError):
+            self.assertTrue(group.validate())
+
+    def test_none(self):
+        group = AutoScalingGroup(
+            'mygroup',
+            MaxSize="1",
+            MinSize="1",
+        )
+        with self.assertRaises(ValueError):
+            self.assertTrue(group.validate())
+
+    def test_instanceid(self):
+        group = AutoScalingGroup(
+            'mygroup',
+            InstanceId="i-1234",
+            MaxSize="1",
+            MinSize="1",
+        )
+        self.assertTrue(group.validate())
+
+    def test_launchconfigurationname(self):
+        group = AutoScalingGroup(
+            'mygroup',
+            LaunchConfigurationName="I'm a test",
+            MaxSize="1",
+            MinSize="1",
+        )
+        self.assertTrue(group.validate())
+
+
+if __name__ == '__main__':
+    unittest.main()