Repository: cloudtools/troposphere

Issue:
CreationPolicy is a permitted parameter of WaitCondition
This appears to be valid CloudFormation:
https://github.com/awslabs/aws-batch-helpers/blob/master/gpu-example/create-ami.yaml#L23

Doing this in troposphere, it complains about a missing resource handle on the WaitCondition, and indeed the CloudFormation documentation says it is [required](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-waitcondition.html). Is the [dependsOn](https://github.com/awslabs/aws-batch-helpers/blob/master/gpu-example/create-ami.yaml#L67) clause somehow implicitly creating a WaitConditionHandle? How can I do this in troposphere?


Discussion:
I can't say why the create-ami.yaml would work in CloudFormation. Something AWS support might be able to discuss.

As you noted, the CF documentation states a WaitConditionHandle is a required property for WaitCondition and troposphere enforces it. The easy fix is to add a handle and just ref it into your WaitCondition. There is an example [here](https://github.com/cloudtools/troposphere/blob/master/examples/WaitObject.py).
@markpeek Thank you for your help.

Enterprise support confirmed the following for me, and will update their documentation (at some point..).

> Basically, we now recommend the use of CreationPolicy over WaitHandles for most of the cases, that's why you don't see a WaitHandle in that template
> Maybe we need to update our docs.
> We mention it there on the third block of text: "If you use a CreationPolicy with a wait condition, do not specify any of the wait condition's properties." But I agree this could potentially be shown clearer on each of the properties. Maybe as a conditional.


Resolving patch:
diff --git a/troposphere/cloudformation.py b/troposphere/cloudformation.py
--- a/troposphere/cloudformation.py
+++ b/troposphere/cloudformation.py
@@ -5,7 +5,7 @@
 
 from . import AWSHelperFn, AWSObject, AWSProperty, BaseAWSObject, Tags
 from . import encode_to_dict
-from .validators import integer, boolean, encoding
+from .validators import boolean, check_required, encoding, integer
 
 
 class Stack(AWSObject):
@@ -37,10 +37,22 @@ class WaitCondition(AWSObject):
 
     props = {
         'Count': (integer, False),
-        'Handle': (basestring, True),
-        'Timeout': (integer, True),
+        'Handle': (basestring, False),
+        'Timeout': (integer, False),
     }
 
+    def validate(self):
+        if 'CreationPolicy' in self.resource:
+            for k in self.props.keys():
+                if k in self.properties:
+                    raise ValueError(
+                        "Property %s cannot be specified with CreationPolicy" %
+                        k
+                    )
+        else:
+            required = ['Handle', 'Timeout']
+            check_required(self.__class__.__name__, self.properties, required)
+
 
 class WaitConditionHandle(AWSObject):
     resource_type = "AWS::CloudFormation::WaitConditionHandle"
diff --git a/troposphere/validators.py b/troposphere/validators.py
--- a/troposphere/validators.py
+++ b/troposphere/validators.py
@@ -217,6 +217,12 @@ def exactly_one(class_name, properties, conditionals):
     return specified_count
 
 
+def check_required(class_name, properties, conditionals):
+    for c in conditionals:
+        if c not in properties:
+            raise ValueError("Resource %s required in %s" % c, class_name)
+
+
 def json_checker(name, prop):
     from . import AWSHelperFn
 


Test patch:
diff --git a/tests/test_cloudformation.py b/tests/test_cloudformation.py
new file mode 100644
--- /dev/null
+++ b/tests/test_cloudformation.py
@@ -0,0 +1,39 @@
+import unittest
+from troposphere import Ref
+from troposphere.cloudformation import WaitCondition, WaitConditionHandle
+from troposphere.policies import CreationPolicy, ResourceSignal
+
+
+class TestWaitCondition(unittest.TestCase):
+    def test_CreationPolicy(self):
+        w = WaitCondition(
+            "mycondition",
+            CreationPolicy=CreationPolicy(
+                ResourceSignal=ResourceSignal(
+                    Timeout='PT15M')),
+        )
+        w.validate()
+
+    def test_CreationPolicyWithProps(self):
+        w = WaitCondition(
+            "mycondition",
+            Count=10,
+            CreationPolicy=CreationPolicy(
+                ResourceSignal=ResourceSignal(
+                    Timeout='PT15M')),
+        )
+        with self.assertRaises(ValueError):
+            w.validate()
+
+    def test_RequiredProps(self):
+        handle = WaitConditionHandle("myWaitHandle")
+        w = WaitCondition(
+            "mycondition",
+            Handle=Ref(handle),
+            Timeout="300",
+        )
+        w.validate()
+
+
+if __name__ == '__main__':
+    unittest.main()