""" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0 """ from cfnlint.rules import CloudFormationLintRule, RuleMatch class DependsOnObsolete(CloudFormationLintRule): """Check unneeded DepensOn Resource Configuration""" id = "W3005" shortdesc = "Check obsolete DependsOn configuration for Resources" description = ( "Check if DependsOn is specified if not needed. " "A Ref or a Fn::GetAtt already is an implicit dependency." ) source_url = ( "https://aws.amazon.com/blogs/devops/optimize-aws-cloudformation-templates/" ) tags = ["resources", "dependson", "ref", "getatt"] def get_resource_references(self, cfn, ref_function, resource): """Get tree of all resource references of a resource""" trees = cfn.search_deep_keys(ref_function) # Filter only resoureces trees = filter(lambda x: x[0] == "Resources", trees) # Filter on the given resource only trees = filter(lambda x: x[1] == resource, trees) return trees def check_depends_on(self, cfn, resource, key, path): """Check if the DependsOn is already specified""" matches = [] # Get references trees = self.get_resource_references(cfn, "Ref", resource) for tree in trees: if tree[-1] == key: message = 'Obsolete DependsOn on resource ({0}), dependency already enforced by a "Ref" at {1}' matches.append( RuleMatch(path, message.format(key, "/".join(map(str, tree[:-1])))) ) # Get the GetAtt trees = self.get_resource_references(cfn, "Fn::GetAtt", resource) for tree in trees: # GettAtt formation is "resource : Attribute", just check the resource if tree[-1][0] == key: message = 'Obsolete DependsOn on resource ({0}), dependency already enforced by a "Fn:GetAtt" at {1}' matches.append( RuleMatch(path, message.format(key, "/".join(map(str, tree[:-1])))) ) return matches def match(self, cfn): matches = [] resources = cfn.get_resources() for resource_name, resource_values in resources.items(): depends_ons = resource_values.get("DependsOn") if depends_ons: path = ["Resources", resource_name, "DependsOn"] self.logger.debug("Validating unneeded DependsOn for %s", resource_name) if isinstance(depends_ons, list): for index, depends_on in enumerate(depends_ons): matches.extend( self.check_depends_on( cfn, resource_name, depends_on, path[:] + [index] ) ) else: matches.extend( self.check_depends_on(cfn, resource_name, depends_ons, path) ) return matches