# Copyright (c) 2012-2013, Mark Peek <mark@peek.org>
# All rights reserved.
#
# See LICENSE file for full license.

from . import AWSObject, AWSProperty
from .validators import integer, boolean, status
from .validators import iam_path, iam_role_name, iam_group_name, iam_user_name

try:
    from awacs.aws import Policy
    policytypes = (dict, Policy)
except ImportError:
    policytypes = dict,


Active = "Active"
Inactive = "Inactive"


class AccessKey(AWSObject):
    resource_type = "AWS::IAM::AccessKey"

    props = {
        'Serial': (integer, False),
        'Status': (status, False),
        'UserName': (basestring, True),
    }


class PolicyType(AWSObject):
    resource_type = "AWS::IAM::Policy"

    props = {
        'Groups': ([basestring], False),
        'PolicyDocument': (policytypes, True),
        'PolicyName': (basestring, True),
        'Roles': ([basestring], False),
        'Users': ([basestring], False),
    }


class Policy(AWSProperty):
    props = {
        'PolicyDocument': (policytypes, True),
        'PolicyName': (basestring, True),
    }

PolicyProperty = Policy


class Group(AWSObject):
    def validate_title(self):
        iam_group_name(self.title)

    resource_type = "AWS::IAM::Group"

    props = {
        'GroupName': (iam_group_name, False),
        'ManagedPolicyArns': ([basestring], False),
        'Path': (iam_path, False),
        'Policies': ([Policy], False),
    }


class InstanceProfile(AWSObject):
    resource_type = "AWS::IAM::InstanceProfile"

    props = {
        'Path': (iam_path, False),
        'Roles': (list, True),
    }


class Role(AWSObject):
    def validate_title(self):
        iam_role_name(self.title)

    resource_type = "AWS::IAM::Role"

    props = {
        'AssumeRolePolicyDocument': (policytypes, True),
        'ManagedPolicyArns': ([basestring], False),
        'Path': (iam_path, False),
        'Policies': ([Policy], False),
        'RoleName': (iam_role_name, False),
    }


class LoginProfile(AWSProperty):
    props = {
        'Password': (basestring, True),
        'PasswordResetRequired': (boolean, False),
    }


class User(AWSObject):
    resource_type = "AWS::IAM::User"

    props = {
        'Path': (iam_path, False),
        'Groups': ([basestring], False),
        'ManagedPolicyArns': ([basestring], False),
        'LoginProfile': (LoginProfile, False),
        'Policies': ([Policy], False),
        'UserName': (iam_user_name, False),
    }


class UserToGroupAddition(AWSObject):
    resource_type = "AWS::IAM::UserToGroupAddition"

    props = {
        'GroupName': (basestring, True),
        'Users': (list, True),
    }


class ManagedPolicy(AWSObject):
    resource_type = "AWS::IAM::ManagedPolicy"

    props = {
        'Description': (basestring, False),
        'Groups': ([basestring], False),
        'Path': (iam_path, False),
        'PolicyDocument': (policytypes, True),
        'Roles': ([basestring], False),
        'Users': ([basestring], False),
    }

troposphere/iam.py
# Copyright (c) 2012-2013, Mark Peek <mark@peek.org>
# All rights reserved.
#
# See LICENSE file for full license.

from . import AWSObject, AWSProperty
from .validators import integer, boolean, status
from .validators import iam_path, iam_role_name, iam_group_name, iam_user_name

try:
    from awacs.aws import Policy
    policytypes = (dict, Policy)
except ImportError:
    policytypes = dict,


Active = "Active"
Inactive = "Inactive"


class AccessKey(AWSObject):
    resource_type = "AWS::IAM::AccessKey"

    props = {
        'Serial': (integer, False),
        'Status': (status, False),
        'UserName': (basestring, True),
    }


class PolicyType(AWSObject):
    resource_type = "AWS::IAM::Policy"

    props = {
        'Groups': ([basestring], False),
        'PolicyDocument': (policytypes, True),
        'PolicyName': (basestring, True),
        'Roles': ([basestring], False),
        'Users': ([basestring], False),
    }


class Policy(AWSProperty):
    props = {
        'PolicyDocument': (policytypes, True),
        'PolicyName': (basestring, True),
    }

PolicyProperty = Policy


class Group(AWSObject):
    resource_type = "AWS::IAM::Group"

    props = {
        'GroupName': (iam_group_name, False),
        'ManagedPolicyArns': ([basestring], False),
        'Path': (iam_path, False),
        'Policies': ([Policy], False),
    }


class InstanceProfile(AWSObject):
    resource_type = "AWS::IAM::InstanceProfile"

    props = {
        'Path': (iam_path, False),
        'Roles': (list, True),
    }


class Role(AWSObject):
    resource_type = "AWS::IAM::Role"

    props = {
        'AssumeRolePolicyDocument': (policytypes, True),
        'ManagedPolicyArns': ([basestring], False),
        'Path': (iam_path, False),
        'Policies': ([Policy], False),
        'RoleName': (iam_role_name, False),
    }


class LoginProfile(AWSProperty):
    props = {
        'Password': (basestring, True),
        'PasswordResetRequired': (boolean, False),
    }


class User(AWSObject):
    resource_type = "AWS::IAM::User"

    props = {
        'Path': (iam_path, False),
        'Groups': ([basestring], False),
        'ManagedPolicyArns': ([basestring], False),
        'LoginProfile': (LoginProfile, False),
        'Policies': ([Policy], False),
        'UserName': (iam_user_name, False),
    }


class UserToGroupAddition(AWSObject):
    resource_type = "AWS::IAM::UserToGroupAddition"

    props = {
        'GroupName': (basestring, True),
        'Users': (list, True),
    }


class ManagedPolicy(AWSObject):
    resource_type = "AWS::IAM::ManagedPolicy"

    props = {
        'Description': (basestring, False),
        'Groups': ([basestring], False),
        'Path': (iam_path, False),
        'PolicyDocument': (policytypes, True),
        'Roles': ([basestring], False),
        'Users': ([basestring], False),
    }

troposphere/iam.py
diff --git a/troposphere/iam.py b/troposphere/iam.py
index 472fe2a3d..980d6a4b8 100644
--- 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 = {

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).

