forked from aws-cloudformation/cfn-lint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGetAtt.py
107 lines (94 loc) · 4.86 KB
/
GetAtt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""
import logging
import six
from cfnlint.rules import CloudFormationLintRule
from cfnlint.rules import RuleMatch
import cfnlint.helpers
LOGGER = logging.getLogger('cfnlint')
class GetAtt(CloudFormationLintRule):
"""Check if GetAtt values are correct"""
id = 'E1010'
shortdesc = 'GetAtt validation of parameters'
description = 'Validates that GetAtt parameters are to valid resources and properties of those resources'
source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html'
tags = ['functions', 'getatt']
def __init__(self):
"""Init """
super(GetAtt, self).__init__()
self.propertytypes = []
self.resourcetypes = []
def initialize(self, cfn):
resourcespecs = cfnlint.helpers.RESOURCE_SPECS[cfn.regions[0]]
self.resourcetypes = resourcespecs['ResourceTypes']
self.propertytypes = resourcespecs['PropertyTypes']
def match(self, cfn):
matches = []
getatts = cfn.search_deep_keys('Fn::GetAtt')
valid_getatts = cfn.get_valid_getatts()
valid_attribute_functions = ['Ref']
for getatt in getatts:
if len(getatt[-1]) < 2:
message = 'Invalid GetAtt for {0}'
matches.append(RuleMatch(getatt, message.format('/'.join(map(str, getatt[:-1])))))
continue
if isinstance(getatt[-1], six.string_types):
resname, restype = getatt[-1].split('.', 1)
else:
resname = None
restype = None
if isinstance(getatt[-1][1], six.string_types):
resname = getatt[-1][0]
restype = '.'.join(getatt[-1][1:])
elif isinstance(getatt[-1][1], dict):
# You can ref the secondary part of a getatt
resname = getatt[-1][0]
restype = getatt[-1][1]
if len(restype) == 1:
for k in restype.keys():
if k not in valid_attribute_functions:
message = 'GetAtt only supports functions "{0}" for attributes at {1}'
matches.append(
RuleMatch(
getatt,
message.format(
', '.join(map(str, valid_attribute_functions)),
'/'.join(map(str, getatt[:-1])))))
else:
message = 'Invalid GetAtt structure {0} at {1}'
matches.append(RuleMatch(getatt, message.format(
getatt[-1], '/'.join(map(str, getatt[:-1])))))
# setting restype to None as we can't validate that anymore
restype = None
else:
message = 'Invalid GetAtt structure {0} at {1}'
matches.append(RuleMatch(getatt, message.format(
getatt[-1], '/'.join(map(str, getatt[:-1])))))
# only check resname if its set. if it isn't it is because of bad structure
# and an error is already provided
if resname:
if resname in valid_getatts:
if restype is not None:
# Check for maps
restypeparts = restype.split('.')
if restypeparts[0] in valid_getatts[resname] and len(restypeparts) >= 2:
if valid_getatts[resname][restypeparts[0]].get('Type') != 'Map':
message = 'Invalid GetAtt {0}.{1} for resource {2}'
matches.append(RuleMatch(
getatt[:-1], message.format(resname, restype, getatt[1])))
else:
continue
if restype not in valid_getatts[resname] and '*' not in valid_getatts[resname]:
message = 'Invalid GetAtt {0}.{1} for resource {2}'
matches.append(RuleMatch(
getatt[:-1], message.format(resname, restype, getatt[1])))
else:
modules = cfn.get_modules()
if any(resname.startswith(s) for s in modules.keys()):
LOGGER.debug('Will consider valid getatt %s because it references a resource within a module', resname)
else:
message = 'Invalid GetAtt {0}.{1} for resource {2}'
matches.append(RuleMatch(getatt, message.format(resname, restype, getatt[1])))
return matches