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