""" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0 """ from cfnlint.helpers import RESOURCE_SPECS from cfnlint.rules import CloudFormationLintRule, RuleMatch class Value(CloudFormationLintRule): """Check if Outputs have string values""" id = "E6003" shortdesc = "Outputs have values of strings" description = "Making sure the outputs have strings as values" source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html" tags = ["outputs"] def __init__(self): """Init""" super().__init__() self.resourcetypes = [] def initialize(self, cfn): resourcespecs = RESOURCE_SPECS[cfn.regions[0]] self.resourcetypes = resourcespecs["ResourceTypes"] def match(self, cfn): matches = [] template = cfn.template getatts = cfn.search_deep_keys("Fn::GetAtt") refs = cfn.search_deep_keys("Ref") # If using a getatt make sure the attribute of the resource # is not of Type List for getatt in getatts: if getatt[0] == "Outputs": if getatt[2] == "Value": obj = getatt[-1] if isinstance(obj, list): if not isinstance(obj[0], str): continue objtype = ( template.get("Resources", {}).get(obj[0], {}).get("Type") ) if objtype and isinstance(obj[1], str): attribute = ( self.resourcetypes.get(objtype, {}) .get("Attributes", {}) .get(obj[1], {}) .get("Type") ) if attribute == "List": if getatt[-4] != "Fn::Join" and getatt[-3] != 1: message = "Output {0} value {1} is of type list" matches.append( RuleMatch( getatt, message.format(getatt[1], "/".join(obj)), ) ) # If using a ref for an output make sure it isn't a # Parameter of Type List for ref in refs: if ref[0] == "Outputs": if ref[2] == "Value": obj = ref[-1] if isinstance(obj, str): param = template.get("Parameters", {}).get(obj) if param: paramtype = param.get("Type") if paramtype: if paramtype.startswith("List<"): if ref[-4] != "Fn::Join" and ref[-3] != 1: message = "Output {0} value {1} is of type list" matches.append( RuleMatch(ref, message.format(ref[1], obj)) ) return matches