forked from aws-cloudformation/cfn-lint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Configuration.py
146 lines (128 loc) · 5.92 KB
/
Configuration.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""
import six
from cfnlint.helpers import REGISTRY_SCHEMAS
from cfnlint.rules import CloudFormationLintRule
from cfnlint.rules import RuleMatch
import cfnlint.helpers
class Configuration(CloudFormationLintRule):
"""Check Base Resource Configuration"""
id = 'E3001'
shortdesc = 'Basic CloudFormation Resource Check'
description = 'Making sure the basic CloudFormation resources are properly configured'
source_url = 'https://github.com/aws-cloudformation/cfn-python-lint'
tags = ['resources']
def _check_resource(self, cfn, resource_name, resource_values):
""" Check Resource """
valid_attributes = [
'Condition',
'CreationPolicy',
'DeletionPolicy',
'DependsOn',
'Metadata',
'Properties',
'Type',
'UpdatePolicy',
'UpdateReplacePolicy',
]
valid_custom_attributes = [
'Condition',
'DeletionPolicy',
'DependsOn',
'Metadata',
'Properties',
'Type',
'UpdateReplacePolicy',
'Version',
]
matches = []
if not isinstance(resource_values, dict):
message = 'Resource not properly configured at {0}'
matches.append(RuleMatch(
['Resources', resource_name],
message.format(resource_name)
))
return matches
# validate condition is a string
condition = resource_values.get('Condition', '')
if not isinstance(condition, six.string_types):
message = 'Condition for resource {0} should be a string'
matches.append(RuleMatch(
['Resources', resource_name, 'Condition'],
message.format(resource_name)
))
resource_type = resource_values.get('Type', '')
if not isinstance(resource_type, six.string_types):
message = 'Type has to be a string at {0}'
matches.append(RuleMatch(
['Resources', resource_name],
message.format('/'.join(['Resources', resource_name]))
))
return matches
# Type is valid continue analysis
if resource_type.startswith('Custom::') or resource_type == 'AWS::CloudFormation::CustomResource':
check_attributes = valid_custom_attributes
else:
check_attributes = valid_attributes
for property_key, _ in resource_values.items():
if property_key not in check_attributes:
message = 'Invalid resource attribute {0} for resource {1}'
matches.append(RuleMatch(
['Resources', resource_name, property_key],
message.format(property_key, resource_name)))
if not resource_type:
message = 'Type not defined for resource {0}'
matches.append(RuleMatch(
['Resources', resource_name],
message.format(resource_name)
))
elif not isinstance(resource_type, six.string_types):
message = 'Type has to be a string at {0}'
matches.append(RuleMatch(
['Resources', resource_name],
message.format('/'.join(['Resources', resource_name]))
))
else:
self.logger.debug('Check resource types by region...')
for region, specs in cfnlint.helpers.RESOURCE_SPECS.items():
if region in cfn.regions:
if resource_type not in specs['ResourceTypes'] and resource_type not in [s['typeName'] for s in REGISTRY_SCHEMAS]:
if not resource_type.startswith(('Custom::', 'AWS::Serverless::')) and not resource_type.endswith('::MODULE'):
message = 'Invalid or unsupported Type {0} for resource {1} in {2}'
matches.append(RuleMatch(
['Resources', resource_name, 'Type'],
message.format(resource_type, resource_name, region)
))
if 'Properties' not in resource_values:
resource_spec = cfnlint.helpers.RESOURCE_SPECS[cfn.regions[0]]
if resource_type in resource_spec['ResourceTypes']:
properties_spec = resource_spec['ResourceTypes'][resource_type]['Properties']
# pylint: disable=len-as-condition
if len(properties_spec) > 0:
required = 0
for _, property_spec in properties_spec.items():
if property_spec.get('Required', False):
required += 1
if required > 0:
if resource_type == 'AWS::CloudFormation::WaitCondition' and 'CreationPolicy' in resource_values.keys():
self.logger.debug('Exception to required properties section as CreationPolicy is defined.')
else:
message = 'Properties not defined for resource {0}'
matches.append(RuleMatch(
['Resources', resource_name],
message.format(resource_name)
))
return matches
def match(self, cfn):
matches = []
resources = cfn.template.get('Resources', {})
if not isinstance(resources, dict):
message = 'Resource not properly configured'
matches.append(RuleMatch(['Resources'], message))
else:
for resource_name, resource_values in cfn.template.get('Resources', {}).items():
self.logger.debug('Validating resource %s base configuration', resource_name)
matches.extend(self._check_resource(cfn, resource_name, resource_values))
return matches