def check_value_getatt()

in src/cfnlint/rules/resources/properties/ValueRefGetAtt.py [0:0]


    def check_value_getatt(self, value, path, **kwargs):
        """Check GetAtt"""
        matches = []
        cfn = kwargs.get('cfn')
        value_specs = kwargs.get('value_specs', {}).get('GetAtt')
        list_value_specs = kwargs.get('list_value_specs', {}).get('GetAtt')
        property_type = kwargs.get('property_type')
        property_name = kwargs.get('property_name')
        # You can sometimes get a list or a string with . in it
        if isinstance(value, list):
            resource_name = value[0]
            if len(value[1:]) == 1:
                resource_attribute = value[1].split('.')
            else:
                resource_attribute = value[1:]
        elif isinstance(value, six.string_types):
            resource_name = value.split('.')[0]
            resource_attribute = value.split('.')[1:]
        is_value_a_list = self.is_value_a_list(path[:-1], property_name)
        if path[-1] == 'Fn::GetAtt' and property_type == 'List' and is_value_a_list:
            specs = list_value_specs
        else:
            specs = value_specs

        resource_type = cfn.template.get('Resources', {}).get(resource_name, {}).get('Type')

        if cfnlint.helpers.is_custom_resource(resource_type):
            #  A custom resource voids the spec.  Move on
            return matches

        if resource_type in ['AWS::CloudFormation::Stack', 'AWS::ServiceCatalog::CloudFormationProvisionedProduct'] and resource_attribute[0] == 'Outputs':
            # Nested Stack Outputs
            # if its a string type we are good and return matches
            # if its a list its a failure as Outputs can only be strings
            if is_value_a_list and property_type == 'List':
                message = 'CloudFormation stack outputs need to be strings not lists at {0}'
                matches.append(RuleMatch(path, message.format('/'.join(map(str, path)))))

            return matches

        if specs is None:
            # GetAtt specs aren't specified skip
            return matches
        if not specs:
            # GetAtt is specified but empty so there are no valid options
            message = 'Property "{0}" has no valid Fn::GetAtt options at {1}'
            matches.append(RuleMatch(path, message.format(property_name, '/'.join(map(str, path)))))
            return matches

        if resource_type not in specs:
            message = 'Property "{0}" can Fn::GetAtt to a resource of types [{1}] at {2}'
            matches.append(
                RuleMatch(
                    path,
                    message.format(
                        property_name,
                        ', '.join(map(str, specs)),
                        '/'.join(map(str, path)))))
        elif isinstance(specs[resource_type], list):
            found = False
            for allowed_att in specs[resource_type]:
                if '.'.join(map(str, resource_attribute)) == allowed_att:
                    found = True
            if not found:
                message = 'Property "{0}" can Fn::GetAtt to a resource attribute "{1}" at {2}'
                matches.append(
                    RuleMatch(
                        path,
                        message.format(
                            property_name,
                            specs[resource_type],
                            '/'.join(map(str, path)))))
        elif '.'.join(map(str, resource_attribute)) != specs[resource_type]:
            print(resource_type)
            message = 'Property "{0}" can Fn::GetAtt to a resource attribute "{1}" at {2}'
            matches.append(
                RuleMatch(
                    path,
                    message.format(
                        property_name,
                        specs[resource_type],
                        '/'.join(map(str, path)))))

        return matches