cfn-check 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cfn-check might be problematic. Click here for more details.
- cfn_check-0.1.1/LICENSE +21 -0
- cfn_check-0.1.1/PKG-INFO +247 -0
- cfn_check-0.1.1/README.md +206 -0
- cfn_check-0.1.1/cfn_check/__init__.py +2 -0
- cfn_check-0.1.1/cfn_check/cli/__init__.py +0 -0
- cfn_check-0.1.1/cfn_check/cli/root.py +72 -0
- cfn_check-0.1.1/cfn_check/cli/utils/__init__.py +0 -0
- cfn_check-0.1.1/cfn_check/cli/utils/attributes.py +12 -0
- cfn_check-0.1.1/cfn_check/cli/utils/files.py +68 -0
- cfn_check-0.1.1/cfn_check/cli/validate.py +81 -0
- cfn_check-0.1.1/cfn_check/evaluation/__init__.py +0 -0
- cfn_check-0.1.1/cfn_check/evaluation/check.py +20 -0
- cfn_check-0.1.1/cfn_check/evaluation/errors.py +33 -0
- cfn_check-0.1.1/cfn_check/evaluation/search.py +137 -0
- cfn_check-0.1.1/cfn_check/evaluation/validate.py +51 -0
- cfn_check-0.1.1/cfn_check/loader/__init__.py +0 -0
- cfn_check-0.1.1/cfn_check/loader/loader.py +21 -0
- cfn_check-0.1.1/cfn_check/logging/__init__.py +0 -0
- cfn_check-0.1.1/cfn_check/logging/models.py +20 -0
- cfn_check-0.1.1/cfn_check/rules/__init__.py +0 -0
- cfn_check-0.1.1/cfn_check/rules/rule.py +25 -0
- cfn_check-0.1.1/cfn_check/rules/rules.py +2 -0
- cfn_check-0.1.1/cfn_check/shared/__init__.py +0 -0
- cfn_check-0.1.1/cfn_check/shared/types.py +17 -0
- cfn_check-0.1.1/cfn_check/validation/__init__.py +0 -0
- cfn_check-0.1.1/cfn_check/validation/validator.py +47 -0
- cfn_check-0.1.1/cfn_check.egg-info/PKG-INFO +247 -0
- cfn_check-0.1.1/cfn_check.egg-info/SOURCES.txt +33 -0
- cfn_check-0.1.1/cfn_check.egg-info/dependency_links.txt +1 -0
- cfn_check-0.1.1/cfn_check.egg-info/entry_points.txt +2 -0
- cfn_check-0.1.1/cfn_check.egg-info/requires.txt +4 -0
- cfn_check-0.1.1/cfn_check.egg-info/top_level.txt +3 -0
- cfn_check-0.1.1/example/rules.py +12 -0
- cfn_check-0.1.1/pyproject.toml +47 -0
- cfn_check-0.1.1/setup.cfg +4 -0
cfn_check-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ada Lündhé
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cfn_check-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cfn-check
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Validate Cloud Formation
|
|
5
|
+
Author-email: Ada Lundhe <adalundhe@lundhe.audio>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Ada Lündhé
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/adalundhe/cfn-check
|
|
29
|
+
Keywords: cloud-formation,testing,aws,cli
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
32
|
+
Classifier: Operating System :: OS Independent
|
|
33
|
+
Requires-Python: >=3.12
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
License-File: LICENSE
|
|
36
|
+
Requires-Dist: pydantic
|
|
37
|
+
Requires-Dist: pyyaml
|
|
38
|
+
Requires-Dist: hyperlight-cocoa
|
|
39
|
+
Requires-Dist: async-logging
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# <b>CFN Check</b>
|
|
43
|
+
<b>A tool for checking CloudFormation</b>
|
|
44
|
+
|
|
45
|
+
[](https://pypi.org/project/cfn-check/)
|
|
46
|
+
[](https://github.com/adalundhe/cfn-check/blob/main/LICENSE)
|
|
47
|
+
[](https://github.com/adalundhe/cfn-check/blob/main/CODE_OF_CONDUCT.md)
|
|
48
|
+
[](https://pypi.org/project/cfn-check/)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
| Package | Cocoa |
|
|
52
|
+
| ----------- | ----------- |
|
|
53
|
+
| Version | 0.1.0 |
|
|
54
|
+
| Download | https://pypi.org/project/cfn-check/ |
|
|
55
|
+
| Source | https://github.com/adalundhe/cfn-check |
|
|
56
|
+
| Keywords | cloud-formation, testing, aws, cli |
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
CFN-Check is a small, fast, friendly tool for validating AWS CloudFormation YAML templates. It is code-driven, with
|
|
60
|
+
rules written as simple, `Rule` decorator wrapped python class methods for `Rules`-inheriting classes.
|
|
61
|
+
|
|
62
|
+
<br/>
|
|
63
|
+
|
|
64
|
+
# Why CFN-Check?
|
|
65
|
+
|
|
66
|
+
AWS has its own tools for validating Cloud Formation - `cfn-lint` and `cfn-guard`. `cfn-check` aims to solve
|
|
67
|
+
problems inherint to `cfn-lint` more than `cfn-guard`, primarily:
|
|
68
|
+
|
|
69
|
+
- Confusing, unclear syntax around rules configuration
|
|
70
|
+
- Inability to parse non-resource wildcards
|
|
71
|
+
- Inability to validate non-resource template data
|
|
72
|
+
- Inabillity to use structured models to validate input
|
|
73
|
+
|
|
74
|
+
In comparison to `cfn-guard`, `cfn-check` is pure Python, thus
|
|
75
|
+
avoiding YADSL (Yet Another DSL) headaches. It also proves
|
|
76
|
+
significantly more configurable/modular/hackable as a result.
|
|
77
|
+
|
|
78
|
+
CFN-Check uses a combination of simple depth-first-search tree
|
|
79
|
+
parsing, friendly `cfn-lint` like query syntax, `Pydantic` models,
|
|
80
|
+
and `pytest`-like assert-driven checks to make validating your
|
|
81
|
+
Cloud Formation easy while offering both CLI and Python API interfaces.
|
|
82
|
+
|
|
83
|
+
<br/>
|
|
84
|
+
|
|
85
|
+
# Getting Started
|
|
86
|
+
|
|
87
|
+
`cfn-check` requires:
|
|
88
|
+
|
|
89
|
+
- `Python 3.12`
|
|
90
|
+
- Any number of valid CloudFormation templates or a path to said templates.
|
|
91
|
+
- A `.py` file containing at least one `Rules` class with at least one valid `@Rule()` decorated method
|
|
92
|
+
|
|
93
|
+
To get started (we recommend using `uv`), run:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
uv venv
|
|
97
|
+
source .venv/bin/activate
|
|
98
|
+
|
|
99
|
+
uv pip install cfn-check
|
|
100
|
+
|
|
101
|
+
touch rules.py
|
|
102
|
+
touch template.yaml
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Next open the `rules.py` file and create a basic Python class
|
|
106
|
+
as below.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from cfn_check import Rules, Rule
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class ValidateResourceType(Rules):
|
|
113
|
+
|
|
114
|
+
@Rule(
|
|
115
|
+
"Resources::*::Type",
|
|
116
|
+
"It checks Resource::Type is correctly definined",
|
|
117
|
+
)
|
|
118
|
+
def validate_test(self, value: str):
|
|
119
|
+
assert value is not None, '❌ Resource Type not defined'
|
|
120
|
+
assert isinstance(value, str), '❌ Resource Type not a string'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This provides us a basic rule set that validates that the `Type` field of our CloudFormation template(s) exists and is the correct data type.
|
|
124
|
+
|
|
125
|
+
> [!NOTE]
|
|
126
|
+
> Don't worry about adding an `__init__()` method to this class!
|
|
127
|
+
|
|
128
|
+
Next open the `template.yaml` file and paste the following CloudFormation:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
AWSTemplateFormatVersion: '2010-09-09'
|
|
132
|
+
Parameters:
|
|
133
|
+
ExistingSecurityGroups:
|
|
134
|
+
Type: List<AWS::EC2::SecurityGroup::Id>
|
|
135
|
+
ExistingVPC:
|
|
136
|
+
Type: AWS::EC2::VPC::Id
|
|
137
|
+
Description: The VPC ID that includes the security groups in the ExistingSecurityGroups parameter.
|
|
138
|
+
InstanceType:
|
|
139
|
+
Type: String
|
|
140
|
+
Default: t2.micro
|
|
141
|
+
AllowedValues:
|
|
142
|
+
- t2.micro
|
|
143
|
+
- m1.small
|
|
144
|
+
Mappings:
|
|
145
|
+
AWSInstanceType2Arch:
|
|
146
|
+
t2.micro:
|
|
147
|
+
Arch: HVM64
|
|
148
|
+
m1.small:
|
|
149
|
+
Arch: HVM64
|
|
150
|
+
AWSRegionArch2AMI:
|
|
151
|
+
us-east-1:
|
|
152
|
+
HVM64: ami-0ff8a91507f77f867
|
|
153
|
+
HVMG2: ami-0a584ac55a7631c0c
|
|
154
|
+
Resources:
|
|
155
|
+
SecurityGroup:
|
|
156
|
+
Type: AWS::EC2::SecurityGroup
|
|
157
|
+
Properties:
|
|
158
|
+
GroupDescription: Allow HTTP traffic to the host
|
|
159
|
+
VpcId: !Ref ExistingVPC
|
|
160
|
+
SecurityGroupIngress:
|
|
161
|
+
- IpProtocol: tcp
|
|
162
|
+
FromPort: 80
|
|
163
|
+
ToPort: 80
|
|
164
|
+
CidrIp: 0.0.0.0/0
|
|
165
|
+
SecurityGroupEgress:
|
|
166
|
+
- IpProtocol: tcp
|
|
167
|
+
FromPort: 80
|
|
168
|
+
ToPort: 80
|
|
169
|
+
CidrIp: 0.0.0.0/0
|
|
170
|
+
AllSecurityGroups:
|
|
171
|
+
Type: Custom::Split
|
|
172
|
+
Properties:
|
|
173
|
+
ServiceToken: !GetAtt AppendItemToListFunction.Arn
|
|
174
|
+
List: !Ref ExistingSecurityGroups
|
|
175
|
+
AppendedItem: !Ref SecurityGroup
|
|
176
|
+
AppendItemToListFunction:
|
|
177
|
+
Type: AWS::Lambda::Function
|
|
178
|
+
Properties:
|
|
179
|
+
Handler: index.handler
|
|
180
|
+
Role: !GetAtt LambdaExecutionRole.Arn
|
|
181
|
+
Code:
|
|
182
|
+
ZipFile: !Join
|
|
183
|
+
- ''
|
|
184
|
+
- - var response = require('cfn-response');
|
|
185
|
+
- exports.handler = function(event, context) {
|
|
186
|
+
- ' var responseData = {Value: event.ResourceProperties.List};'
|
|
187
|
+
- ' responseData.Value.push(event.ResourceProperties.AppendedItem);'
|
|
188
|
+
- ' response.send(event, context, response.SUCCESS, responseData);'
|
|
189
|
+
- '};'
|
|
190
|
+
Runtime: nodejs20.x
|
|
191
|
+
MyEC2Instance:
|
|
192
|
+
Type: AWS::EC2::Instance
|
|
193
|
+
Properties:
|
|
194
|
+
ImageId: !FindInMap
|
|
195
|
+
- AWSRegionArch2AMI
|
|
196
|
+
- !Ref AWS::Region
|
|
197
|
+
- !FindInMap
|
|
198
|
+
- AWSInstanceType2Arch
|
|
199
|
+
- !Ref InstanceType
|
|
200
|
+
- Arch
|
|
201
|
+
SecurityGroupIds: !GetAtt AllSecurityGroups.Value
|
|
202
|
+
InstanceType: !Ref InstanceType
|
|
203
|
+
LambdaExecutionRole:
|
|
204
|
+
Type: AWS::IAM::Role
|
|
205
|
+
Properties:
|
|
206
|
+
AssumeRolePolicyDocument:
|
|
207
|
+
Version: '2012-10-17'
|
|
208
|
+
Statement:
|
|
209
|
+
- Effect: Allow
|
|
210
|
+
Principal:
|
|
211
|
+
Service:
|
|
212
|
+
- lambda.amazonaws.com
|
|
213
|
+
Action:
|
|
214
|
+
- sts:AssumeRole
|
|
215
|
+
Path: /
|
|
216
|
+
Policies:
|
|
217
|
+
- PolicyName: root
|
|
218
|
+
PolicyDocument:
|
|
219
|
+
Version: '2012-10-17'
|
|
220
|
+
Statement:
|
|
221
|
+
- Effect: Allow
|
|
222
|
+
Action:
|
|
223
|
+
- logs:*
|
|
224
|
+
Resource: arn:aws:logs:*:*:*
|
|
225
|
+
Outputs:
|
|
226
|
+
AllSecurityGroups:
|
|
227
|
+
Description: Security Groups that are associated with the EC2 instance
|
|
228
|
+
Value: !Join
|
|
229
|
+
- ', '
|
|
230
|
+
- !GetAtt AllSecurityGroups.Value
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
This represents a basic configuration for an AWS Lambda function.
|
|
234
|
+
|
|
235
|
+
Finally, run:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
cfn-lint validate -r rules.py template.yaml
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
which outputs:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
2025-09-17T01:46:41.542078+00:00 - INFO - 19783474 - /Users/adalundhe/Documents/Duckbill/cfn-check/cfn_check/cli/validate.py:validate.80 - ✅ 1 validations met for 1 templates
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Congrats! You've just made the cloud a bit better place!
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# <b>CFN Check</b>
|
|
2
|
+
<b>A tool for checking CloudFormation</b>
|
|
3
|
+
|
|
4
|
+
[](https://pypi.org/project/cfn-check/)
|
|
5
|
+
[](https://github.com/adalundhe/cfn-check/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/adalundhe/cfn-check/blob/main/CODE_OF_CONDUCT.md)
|
|
7
|
+
[](https://pypi.org/project/cfn-check/)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
| Package | Cocoa |
|
|
11
|
+
| ----------- | ----------- |
|
|
12
|
+
| Version | 0.1.0 |
|
|
13
|
+
| Download | https://pypi.org/project/cfn-check/ |
|
|
14
|
+
| Source | https://github.com/adalundhe/cfn-check |
|
|
15
|
+
| Keywords | cloud-formation, testing, aws, cli |
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
CFN-Check is a small, fast, friendly tool for validating AWS CloudFormation YAML templates. It is code-driven, with
|
|
19
|
+
rules written as simple, `Rule` decorator wrapped python class methods for `Rules`-inheriting classes.
|
|
20
|
+
|
|
21
|
+
<br/>
|
|
22
|
+
|
|
23
|
+
# Why CFN-Check?
|
|
24
|
+
|
|
25
|
+
AWS has its own tools for validating Cloud Formation - `cfn-lint` and `cfn-guard`. `cfn-check` aims to solve
|
|
26
|
+
problems inherint to `cfn-lint` more than `cfn-guard`, primarily:
|
|
27
|
+
|
|
28
|
+
- Confusing, unclear syntax around rules configuration
|
|
29
|
+
- Inability to parse non-resource wildcards
|
|
30
|
+
- Inability to validate non-resource template data
|
|
31
|
+
- Inabillity to use structured models to validate input
|
|
32
|
+
|
|
33
|
+
In comparison to `cfn-guard`, `cfn-check` is pure Python, thus
|
|
34
|
+
avoiding YADSL (Yet Another DSL) headaches. It also proves
|
|
35
|
+
significantly more configurable/modular/hackable as a result.
|
|
36
|
+
|
|
37
|
+
CFN-Check uses a combination of simple depth-first-search tree
|
|
38
|
+
parsing, friendly `cfn-lint` like query syntax, `Pydantic` models,
|
|
39
|
+
and `pytest`-like assert-driven checks to make validating your
|
|
40
|
+
Cloud Formation easy while offering both CLI and Python API interfaces.
|
|
41
|
+
|
|
42
|
+
<br/>
|
|
43
|
+
|
|
44
|
+
# Getting Started
|
|
45
|
+
|
|
46
|
+
`cfn-check` requires:
|
|
47
|
+
|
|
48
|
+
- `Python 3.12`
|
|
49
|
+
- Any number of valid CloudFormation templates or a path to said templates.
|
|
50
|
+
- A `.py` file containing at least one `Rules` class with at least one valid `@Rule()` decorated method
|
|
51
|
+
|
|
52
|
+
To get started (we recommend using `uv`), run:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv venv
|
|
56
|
+
source .venv/bin/activate
|
|
57
|
+
|
|
58
|
+
uv pip install cfn-check
|
|
59
|
+
|
|
60
|
+
touch rules.py
|
|
61
|
+
touch template.yaml
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Next open the `rules.py` file and create a basic Python class
|
|
65
|
+
as below.
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from cfn_check import Rules, Rule
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ValidateResourceType(Rules):
|
|
72
|
+
|
|
73
|
+
@Rule(
|
|
74
|
+
"Resources::*::Type",
|
|
75
|
+
"It checks Resource::Type is correctly definined",
|
|
76
|
+
)
|
|
77
|
+
def validate_test(self, value: str):
|
|
78
|
+
assert value is not None, '❌ Resource Type not defined'
|
|
79
|
+
assert isinstance(value, str), '❌ Resource Type not a string'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This provides us a basic rule set that validates that the `Type` field of our CloudFormation template(s) exists and is the correct data type.
|
|
83
|
+
|
|
84
|
+
> [!NOTE]
|
|
85
|
+
> Don't worry about adding an `__init__()` method to this class!
|
|
86
|
+
|
|
87
|
+
Next open the `template.yaml` file and paste the following CloudFormation:
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
AWSTemplateFormatVersion: '2010-09-09'
|
|
91
|
+
Parameters:
|
|
92
|
+
ExistingSecurityGroups:
|
|
93
|
+
Type: List<AWS::EC2::SecurityGroup::Id>
|
|
94
|
+
ExistingVPC:
|
|
95
|
+
Type: AWS::EC2::VPC::Id
|
|
96
|
+
Description: The VPC ID that includes the security groups in the ExistingSecurityGroups parameter.
|
|
97
|
+
InstanceType:
|
|
98
|
+
Type: String
|
|
99
|
+
Default: t2.micro
|
|
100
|
+
AllowedValues:
|
|
101
|
+
- t2.micro
|
|
102
|
+
- m1.small
|
|
103
|
+
Mappings:
|
|
104
|
+
AWSInstanceType2Arch:
|
|
105
|
+
t2.micro:
|
|
106
|
+
Arch: HVM64
|
|
107
|
+
m1.small:
|
|
108
|
+
Arch: HVM64
|
|
109
|
+
AWSRegionArch2AMI:
|
|
110
|
+
us-east-1:
|
|
111
|
+
HVM64: ami-0ff8a91507f77f867
|
|
112
|
+
HVMG2: ami-0a584ac55a7631c0c
|
|
113
|
+
Resources:
|
|
114
|
+
SecurityGroup:
|
|
115
|
+
Type: AWS::EC2::SecurityGroup
|
|
116
|
+
Properties:
|
|
117
|
+
GroupDescription: Allow HTTP traffic to the host
|
|
118
|
+
VpcId: !Ref ExistingVPC
|
|
119
|
+
SecurityGroupIngress:
|
|
120
|
+
- IpProtocol: tcp
|
|
121
|
+
FromPort: 80
|
|
122
|
+
ToPort: 80
|
|
123
|
+
CidrIp: 0.0.0.0/0
|
|
124
|
+
SecurityGroupEgress:
|
|
125
|
+
- IpProtocol: tcp
|
|
126
|
+
FromPort: 80
|
|
127
|
+
ToPort: 80
|
|
128
|
+
CidrIp: 0.0.0.0/0
|
|
129
|
+
AllSecurityGroups:
|
|
130
|
+
Type: Custom::Split
|
|
131
|
+
Properties:
|
|
132
|
+
ServiceToken: !GetAtt AppendItemToListFunction.Arn
|
|
133
|
+
List: !Ref ExistingSecurityGroups
|
|
134
|
+
AppendedItem: !Ref SecurityGroup
|
|
135
|
+
AppendItemToListFunction:
|
|
136
|
+
Type: AWS::Lambda::Function
|
|
137
|
+
Properties:
|
|
138
|
+
Handler: index.handler
|
|
139
|
+
Role: !GetAtt LambdaExecutionRole.Arn
|
|
140
|
+
Code:
|
|
141
|
+
ZipFile: !Join
|
|
142
|
+
- ''
|
|
143
|
+
- - var response = require('cfn-response');
|
|
144
|
+
- exports.handler = function(event, context) {
|
|
145
|
+
- ' var responseData = {Value: event.ResourceProperties.List};'
|
|
146
|
+
- ' responseData.Value.push(event.ResourceProperties.AppendedItem);'
|
|
147
|
+
- ' response.send(event, context, response.SUCCESS, responseData);'
|
|
148
|
+
- '};'
|
|
149
|
+
Runtime: nodejs20.x
|
|
150
|
+
MyEC2Instance:
|
|
151
|
+
Type: AWS::EC2::Instance
|
|
152
|
+
Properties:
|
|
153
|
+
ImageId: !FindInMap
|
|
154
|
+
- AWSRegionArch2AMI
|
|
155
|
+
- !Ref AWS::Region
|
|
156
|
+
- !FindInMap
|
|
157
|
+
- AWSInstanceType2Arch
|
|
158
|
+
- !Ref InstanceType
|
|
159
|
+
- Arch
|
|
160
|
+
SecurityGroupIds: !GetAtt AllSecurityGroups.Value
|
|
161
|
+
InstanceType: !Ref InstanceType
|
|
162
|
+
LambdaExecutionRole:
|
|
163
|
+
Type: AWS::IAM::Role
|
|
164
|
+
Properties:
|
|
165
|
+
AssumeRolePolicyDocument:
|
|
166
|
+
Version: '2012-10-17'
|
|
167
|
+
Statement:
|
|
168
|
+
- Effect: Allow
|
|
169
|
+
Principal:
|
|
170
|
+
Service:
|
|
171
|
+
- lambda.amazonaws.com
|
|
172
|
+
Action:
|
|
173
|
+
- sts:AssumeRole
|
|
174
|
+
Path: /
|
|
175
|
+
Policies:
|
|
176
|
+
- PolicyName: root
|
|
177
|
+
PolicyDocument:
|
|
178
|
+
Version: '2012-10-17'
|
|
179
|
+
Statement:
|
|
180
|
+
- Effect: Allow
|
|
181
|
+
Action:
|
|
182
|
+
- logs:*
|
|
183
|
+
Resource: arn:aws:logs:*:*:*
|
|
184
|
+
Outputs:
|
|
185
|
+
AllSecurityGroups:
|
|
186
|
+
Description: Security Groups that are associated with the EC2 instance
|
|
187
|
+
Value: !Join
|
|
188
|
+
- ', '
|
|
189
|
+
- !GetAtt AllSecurityGroups.Value
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
This represents a basic configuration for an AWS Lambda function.
|
|
193
|
+
|
|
194
|
+
Finally, run:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
cfn-lint validate -r rules.py template.yaml
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
which outputs:
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
2025-09-17T01:46:41.542078+00:00 - INFO - 19783474 - /Users/adalundhe/Documents/Duckbill/cfn-check/cfn_check/cli/validate.py:validate.80 - ✅ 1 validations met for 1 templates
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Congrats! You've just made the cloud a bit better place!
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from cocoa.cli import CLI, CLIStyle
|
|
5
|
+
from cocoa.ui.config.mode import TerminalMode
|
|
6
|
+
from cocoa.ui.components.terminal import Section, SectionConfig
|
|
7
|
+
from cocoa.ui.components.header import Header, HeaderConfig
|
|
8
|
+
from cocoa.ui.components.terminal import Terminal, EngineConfig
|
|
9
|
+
|
|
10
|
+
from .validate import validate
|
|
11
|
+
|
|
12
|
+
async def create_header(
|
|
13
|
+
terminal_mode: TerminalMode = "full",
|
|
14
|
+
):
|
|
15
|
+
|
|
16
|
+
cfn_check_terminal_mode = "extended"
|
|
17
|
+
if terminal_mode == "ci":
|
|
18
|
+
cfn_check_terminal_mode = "compatability"
|
|
19
|
+
|
|
20
|
+
header = Section(
|
|
21
|
+
SectionConfig(height="xx-small", width="large"),
|
|
22
|
+
components=[
|
|
23
|
+
Header(
|
|
24
|
+
"header",
|
|
25
|
+
HeaderConfig(
|
|
26
|
+
header_text="cfn-check",
|
|
27
|
+
color="indian_red_3",
|
|
28
|
+
attributes=["bold"],
|
|
29
|
+
terminal_mode=cfn_check_terminal_mode,
|
|
30
|
+
font='cyberpunk'
|
|
31
|
+
),
|
|
32
|
+
),
|
|
33
|
+
],
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
terminal = Terminal(
|
|
37
|
+
[
|
|
38
|
+
header,
|
|
39
|
+
],
|
|
40
|
+
config=EngineConfig(
|
|
41
|
+
max_height=40,
|
|
42
|
+
max_width=120
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return await terminal.render_once()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@CLI.root(
|
|
51
|
+
validate,
|
|
52
|
+
global_styles=CLIStyle(
|
|
53
|
+
header=create_header,
|
|
54
|
+
flag_description_color="white",
|
|
55
|
+
error_color="indian_red_3",
|
|
56
|
+
error_attributes=["italic"],
|
|
57
|
+
flag_color="indian_red_3",
|
|
58
|
+
text_color="slate_blue_2",
|
|
59
|
+
subcommand_color="slate_blue_2",
|
|
60
|
+
indentation=5,
|
|
61
|
+
terminal_mode="extended",
|
|
62
|
+
),
|
|
63
|
+
)
|
|
64
|
+
async def cfn_check():
|
|
65
|
+
'''
|
|
66
|
+
Check and test CloudFormation
|
|
67
|
+
'''
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def run():
|
|
72
|
+
asyncio.run(CLI.run(sys.argv[1:]))
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from cfn_check.rules.rules import Rules
|
|
2
|
+
from cfn_check.validation.validator import Validator
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def bind(
|
|
6
|
+
rule_set: Rules,
|
|
7
|
+
validation: Validator
|
|
8
|
+
):
|
|
9
|
+
validation.func = validation.func.__get__(rule_set, rule_set.__class__)
|
|
10
|
+
setattr(rule_set, validation.func.__name__, validation.func)
|
|
11
|
+
|
|
12
|
+
return validation
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
import asyncio
|
|
3
|
+
import os
|
|
4
|
+
import yaml
|
|
5
|
+
from cfn_check.loader.loader import (
|
|
6
|
+
Loader,
|
|
7
|
+
create_tag,
|
|
8
|
+
find_templates,
|
|
9
|
+
)
|
|
10
|
+
from cfn_check.shared.types import YamlObject, Data
|
|
11
|
+
|
|
12
|
+
def open_template(path: str) -> YamlObject | Exception:
|
|
13
|
+
try:
|
|
14
|
+
with open(path, 'r') as f:
|
|
15
|
+
return yaml.load(f, Loader=Loader)
|
|
16
|
+
except (Exception, ) as e:
|
|
17
|
+
raise e
|
|
18
|
+
|
|
19
|
+
def is_file(path: str) -> bool:
|
|
20
|
+
return os.path.isdir(path) is False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def load_templates(
|
|
24
|
+
path: str,
|
|
25
|
+
tags: list[str],
|
|
26
|
+
file_pattern: str | None = None,
|
|
27
|
+
):
|
|
28
|
+
loop = asyncio.get_event_loop()
|
|
29
|
+
|
|
30
|
+
if await loop.run_in_executor(
|
|
31
|
+
None,
|
|
32
|
+
is_file,
|
|
33
|
+
path,
|
|
34
|
+
) or file_pattern is None:
|
|
35
|
+
template_filepaths = [
|
|
36
|
+
path,
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
elif file_pattern:
|
|
40
|
+
|
|
41
|
+
template_filepaths = await loop.run_in_executor(
|
|
42
|
+
None,
|
|
43
|
+
find_templates,
|
|
44
|
+
path,
|
|
45
|
+
file_pattern,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
assert len(template_filepaths) > 0 , '❌ No matching files found'
|
|
49
|
+
|
|
50
|
+
for tag in tags:
|
|
51
|
+
new_tag = await loop.run_in_executor(
|
|
52
|
+
None,
|
|
53
|
+
create_tag,
|
|
54
|
+
tag,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
Loader.add_constructor(f'!{tag}', new_tag)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
templates: list[Data] = await asyncio.gather(*[
|
|
61
|
+
loop.run_in_executor(
|
|
62
|
+
None,
|
|
63
|
+
open_template,
|
|
64
|
+
template_path,
|
|
65
|
+
) for template_path in template_filepaths
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
return templates
|