Repository: aws-cloudformation/cfn-lint

Issue:
Nested stack reference to InstanceProfile triggers E2502 Property IamInstanceProfile should relate to AWS::IAM::InstanceProfile
*cfn-lint version: `0.8.1`*

# Description of issue

When using nested stacks and passing IamInstanceProfile ARNs between stacks, E2502 is triggered though it shouldn't be.

# Steps to reproduce

Create a parent template like this
```yaml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  IAMInstanceProfile:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3-us-west-2.amazonaws.com/example-bucket/example-instance-profile.yml
  Instance:
    Type: AWS::CloudFormation::Stack
    Properties:
      Parameters:
        IamInstanceProfile: !GetAtt IAMInstanceProfile.Outputs.InstanceProfileArn
      TemplateURL: https://s3-us-west-2.amazonaws.com/example-bucket/example-instance.yml
```
and a child template like this

```yaml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - ExampleRole
Outputs:
  InstanceProfileArn:
    Value: !GetAtt InstanceProfile.Arn
```

# Expected results

The `IamInstanceProfile` parameter in the parent template's `Instance` sub-stack resource definition does indeed contain a valid IAM Instance Profile ARN (passed in from the `IAMInstanceProfile` sub-stack resource and as a result, there should be no error.

Ideally cfn-lint would recognize that `GetAtt` is referencing an output from another stack which very well could be an InstanceProfile ARN and as a result, optimistically not report this error.

Alternatively, if cfn-lint could introspect the sub-stack and determine the object type of the output, it would know whether or not it was the correct object type.

# Actual results

cfn-lint reports the error

> E2502 Property IamInstanceProfile should relate to AWS::IAM::InstanceProfile for Resources/Instance/Properties/Parameters/IamInstanceProfile/Fn::GetAtt
> example-parent.yml:11:9


Discussion:
Got it.  I'll double check that.  If it relates to a nested stack we will just bail cause we can't verify anything at that point.

Resolving patch:
diff --git a/src/cfnlint/rules/resources/iam/InstanceProfile.py b/src/cfnlint/rules/resources/iam/InstanceProfile.py
--- a/src/cfnlint/rules/resources/iam/InstanceProfile.py
+++ b/src/cfnlint/rules/resources/iam/InstanceProfile.py
@@ -43,12 +43,17 @@ def match(self, cfn):
                 obj = tree[-1]
                 objtype = cfn.template.get('Resources', {}).get(obj[0], {}).get('Type')
                 if objtype:
-                    if objtype != 'AWS::IAM::InstanceProfile':
+                    if objtype not in ['AWS::IAM::InstanceProfile', 'AWS::CloudFormation::Stack', 'AWS::CloudFormation::CustomResource']:
                         message = 'Property IamInstanceProfile should relate to AWS::IAM::InstanceProfile for %s' % (
                             '/'.join(map(str, tree[:-1])))
                         matches.append(RuleMatch(tree[:-1], message))
                     else:
-                        if cfn.template.get('Resources', {}).get(tree[1], {}).get('Type') in ['AWS::EC2::SpotFleet']:
+                        if objtype in ['AWS::CloudFormation::Stack']:
+                            if obj[1] != 'Outputs':
+                                message = 'Property IamInstanceProfile should relate to AWS::IAM::InstanceProfile for %s' % (
+                                    '/'.join(map(str, tree[:-1])))
+                                matches.append(RuleMatch(tree[:-1], message))
+                        elif cfn.template.get('Resources', {}).get(tree[1], {}).get('Type') in ['AWS::EC2::SpotFleet']:
                             if obj[1] != 'Arn':
                                 message = 'Property IamInstanceProfile should be an ARN for %s' % (
                                     '/'.join(map(str, tree[:-1])))


Test patch:
diff --git a/test/fixtures/templates/good/resources/iam/instance_profile.yaml b/test/fixtures/templates/good/resources/iam/instance_profile.yaml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/templates/good/resources/iam/instance_profile.yaml
@@ -0,0 +1,12 @@
+AWSTemplateFormatVersion: 2010-09-09
+Resources:
+  IAMInstanceProfile:
+    Type: AWS::CloudFormation::Stack
+    Properties:
+      TemplateURL: https://s3-us-west-2.amazonaws.com/example-bucket/example-instance-profile.yml
+  Instance:
+    Type: AWS::CloudFormation::Stack
+    Properties:
+      Parameters:
+        IamInstanceProfile: !GetAtt IAMInstanceProfile.Outputs.InstanceProfileArn
+      TemplateURL: https://s3-us-west-2.amazonaws.com/example-bucket/example-instance.yml
diff --git a/test/rules/resources/iam/test_iam_instance_profile.py b/test/rules/resources/iam/test_iam_instance_profile.py
--- a/test/rules/resources/iam/test_iam_instance_profile.py
+++ b/test/rules/resources/iam/test_iam_instance_profile.py
@@ -24,6 +24,9 @@ def setUp(self):
         """Setup"""
         super(TestPropertyVpcId, self).setUp()
         self.collection.register(InstanceProfile())
+        self.success_templates = [
+            'fixtures/templates/good/resources/iam/instance_profile.yaml'
+        ]
 
     def test_file_positive(self):
         """Test Positive"""