Repository: cloudtools/troposphere

Issue:
BUG: IAM Role title should not be validated
The title specified in troposphere is not the actual physical resource name and should be validated as such (https://github.com/cloudtools/troposphere/blob/fe72f7d3f7b0711a22173c1240134173aafef574/troposphere/iam.py#L75-L77).

The next snippet was created today using `troposphere==1.5.0`and `boto3==1.2.6`:

``` python
>>> import boto3
>>> import troposphere
>>> import troposphere.iam
>>> import time
>>>
>>> long_title = 'VeryLongName{}'.format('Z' * 100)
>>> print len(long_title)
112
>>>
>>> # create a role
...
>>> role = troposphere.iam.Role(
...     long_title,
...     AssumeRolePolicyDocument={
...         "Statement": [{
...             "Action": ["sts:AssumeRole"],
...             "Effect": "Allow",
...             "Principal": {"Service": ["ec2.amazonaws.com"]}
...         }]
...     })
Traceback (most recent call last):
  File "<stdin>", line 8, in <module>
  File "/Users/hugo/.virtualenvs/tmp-5ce4367de56b6bde/lib/python2.7/site-packages/troposphere/__init__.py", line 44, in __init__
    self.validate_title()
  File "/Users/hugo/.virtualenvs/tmp-5ce4367de56b6bde/lib/python2.7/site-packages/troposphere/iam.py", line 77, in validate_title
    iam_role_name(self.title)
  File "/Users/hugo/.virtualenvs/tmp-5ce4367de56b6bde/lib/python2.7/site-packages/troposphere/validators.py", line 98, in iam_role_name
    raise ValueError('IAM Role Name may not exceed 64 characters')
ValueError: IAM Role Name may not exceed 64 characters
>>>
>>>
>>> # delete validator
...
>>> del troposphere.iam.Role.validate_title
>>> # try again
...
>>> role = troposphere.iam.Role(
...     long_title,
...     AssumeRolePolicyDocument={
...         "Statement": [{
...             "Action": ["sts:AssumeRole"],
...             "Effect": "Allow",
...             "Principal": {"Service": ["ec2.amazonaws.com"]}
...         }]
...     })
>>> template = troposphere.Template()
>>> template.add_resource(role)
<troposphere.iam.Role object at 0x10ee02990>
>>> print template.to_json()
{
    "Resources": {
        "VeryLongNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ": {
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Statement": [
                        {
                            "Action": [
                                "sts:AssumeRole"
                            ],
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [
                                    "ec2.amazonaws.com"
                                ]
                            }
                        }
                    ]
                }
            },
            "Type": "AWS::IAM::Role"
        }
    }
}
>>> client = boto3.client('cloudformation', 'us-east-1')
>>> stack = client.create_stack(
...     StackName='testTroposphere',
...     TemplateBody=template.to_json(),
...     Capabilities=['CAPABILITY_IAM'])
>>>
>>> while client.describe_stacks(StackName=stack['StackId'])['Stacks'][0]['StackStatus'] != 'CREATE_COMPLETE':
...     import time
...     time.sleep(1)
...
>>> resources = client.describe_stack_resources(StackName=stack['StackId'])
>>> for r in resources['StackResources']:
...     physical_id = r['PhysicalResourceId']
...     print("{} ({} chars)".format(physical_id, len(physical_id)))
...
testTroposphere-VeryLongNameZZZZZZZZZZZZZZZZZZZZZZ-PTHEM9FPNX28 (63 chars)
```

The snippet above shows that the physical id was chosen by CloudFormation and isn't just a trimmed version of the title (it includes a random part too).



Discussion:
@hitbra - I see your point that it is possible to create a role even if the the name supplied is greater than 51 characters (51 + 13 = 64). However, this does create a situation where the `long_title` is truncated possibly in a way that is not desirable. There seems to me to be some advantage in being told this will happen before I stand up my stack. Removing the validation all together works, but it also does not help with a truncation scenario. Is there a way to have the best of both worlds? Is there an acceptable interface for overriding the check in certain scenarios? If the validation is kept, the language they error out with should be updated, since anything above 51 characters is probably truncated.

Pull request #429 is related to this ticket.

@rcuza: CloudFormation will pick a name for you regardless or the title length, why bother with truncation?

In my understanding, troposphere is just a way to build CloudFormation templates using Python objects. It's agnostic and all of its features should behave the same way CloudFormation does. If CloudFormation does not have validation on that, why would troposphere add such a thing?

This was something that broke my code when I upgraded troposphere and I couldn't find anything in the CloudFormation documentation at the time, then I looked at the code and saw what was going on and it seemed to be something unexpected for the general public.

For reference: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html

@hitbra: The validations have been helpful to me because they let me build templates that have fewer problems when I use them to stand up infrastructure. I agree with you that there should only be validations that provide value.

I am confused about the difference between **RoleName** and **PhysicalResourceId**. The documentation clearly says that the limit of the **RoleName** is 64 [see http://docs.aws.amazon.com/IAM/latest/APIReference/API_Role.html ]. When creating a role through the web GUI, one is presented with the option to set the **RoleName**. When one does it via CloudFormation, then the resource name becomes the **RoleName** and **PhysicalResourceId** minus a dash and the unique ID (14 characters). I can't find in the CloudFormation documents if there is a way to set the **RoleName** without the unique ID.

Let's assume that this is the intended behavior: all IAM roles will have a name that is up to 64 characters long where 14 characters are chosen by the standing up process. This leaves 50 characters that someone writing a CloudFormation template can use. Any more than this and some of it will be lost.

I think the validation can be helpful at this point. If I have roles that only differ after 50 characters, then they will look the same after they get truncated. For instance:
- `'VeryLongNameAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJoseSmith'`
- `'VeryLongNameAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKosSmith'`

would become
- `'VeryLongNameAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-1P3793NZ4KTJO`
- `'VeryLongNameAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-3WS793NZ4KTJO`

These two names are hard to distinguish. A validation in troposphere can help prevent this. 

As to your point, that you don't want validation forced upon you, I agree that if a reasonable usage can be devised, it would sometimes be desirable to turn off these checks. 

@rcuza Troposphere will prepend the stack name to the generated name, so it would actually become. `StackName-VeryLongNameAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-1P3793NZ4KTJO`. 

Since 
- there is no way to know the stack name when generating the template
- troposphere should (imho) prevent the generation of templates that will fail, not restrict the generation of valid templates.

I'm adding a +1 to this proposal


Resolving patch:
diff --git a/troposphere/iam.py b/troposphere/iam.py
--- a/troposphere/iam.py
+++ b/troposphere/iam.py
@@ -50,9 +50,6 @@ class Policy(AWSProperty):
 
 
 class Group(AWSObject):
-    def validate_title(self):
-        iam_group_name(self.title)
-
     resource_type = "AWS::IAM::Group"
 
     props = {
@@ -73,9 +70,6 @@ class InstanceProfile(AWSObject):
 
 
 class Role(AWSObject):
-    def validate_title(self):
-        iam_role_name(self.title)
-
     resource_type = "AWS::IAM::Role"
 
     props = {


Test patch: