cfn-check 0.3.2__tar.gz → 0.9.0__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.
- {cfn_check-0.3.2 → cfn_check-0.9.0}/PKG-INFO +201 -25
- {cfn_check-0.3.2 → cfn_check-0.9.0}/README.md +198 -23
- cfn_check-0.9.0/cfn_check/cli/config.py +10 -0
- cfn_check-0.9.0/cfn_check/cli/render.py +141 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/cli/root.py +3 -1
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/cli/utils/attributes.py +1 -1
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/cli/utils/files.py +46 -21
- cfn_check-0.9.0/cfn_check/cli/utils/stdout.py +18 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/cli/validate.py +36 -26
- cfn_check-0.9.0/cfn_check/collection/collection.py +59 -0
- cfn_check-0.9.0/cfn_check/evaluation/evaluator.py +128 -0
- cfn_check-0.9.0/cfn_check/evaluation/parsing/operators/__init__.py +1 -0
- cfn_check-0.9.0/cfn_check/evaluation/parsing/operators/value_operator.py +118 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/evaluation/parsing/query_parser.py +26 -2
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/evaluation/parsing/token.py +124 -67
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/evaluation/parsing/token_type.py +2 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/evaluation/validate.py +34 -3
- cfn_check-0.9.0/cfn_check/rendering/__init__.py +1 -0
- cfn_check-0.9.0/cfn_check/rendering/cidr_solver.py +66 -0
- cfn_check-0.9.0/cfn_check/rendering/renderer.py +1316 -0
- cfn_check-0.9.0/cfn_check/rendering/utils.py +13 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/rules/rule.py +3 -0
- cfn_check-0.9.0/cfn_check/validation/__init__.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/validation/validator.py +11 -1
- cfn_check-0.9.0/cfn_check/yaml/__init__.py +5 -0
- cfn_check-0.9.0/cfn_check/yaml/anchor.py +18 -0
- cfn_check-0.9.0/cfn_check/yaml/comments.py +1201 -0
- cfn_check-0.9.0/cfn_check/yaml/compat.py +235 -0
- cfn_check-0.9.0/cfn_check/yaml/composer.py +229 -0
- cfn_check-0.9.0/cfn_check/yaml/configobjwalker.py +15 -0
- cfn_check-0.9.0/cfn_check/yaml/constructor.py +1722 -0
- cfn_check-0.9.0/cfn_check/yaml/cyaml.py +196 -0
- cfn_check-0.9.0/cfn_check/yaml/docinfo.py +115 -0
- cfn_check-0.9.0/cfn_check/yaml/dumper.py +216 -0
- cfn_check-0.9.0/cfn_check/yaml/emitter.py +1813 -0
- cfn_check-0.9.0/cfn_check/yaml/error.py +314 -0
- cfn_check-0.9.0/cfn_check/yaml/events.py +265 -0
- cfn_check-0.9.0/cfn_check/yaml/loader.py +91 -0
- cfn_check-0.9.0/cfn_check/yaml/main.py +1521 -0
- cfn_check-0.9.0/cfn_check/yaml/mergevalue.py +37 -0
- cfn_check-0.9.0/cfn_check/yaml/nodes.py +148 -0
- cfn_check-0.9.0/cfn_check/yaml/parser.py +909 -0
- cfn_check-0.9.0/cfn_check/yaml/reader.py +274 -0
- cfn_check-0.9.0/cfn_check/yaml/representer.py +1146 -0
- cfn_check-0.9.0/cfn_check/yaml/resolver.py +390 -0
- cfn_check-0.9.0/cfn_check/yaml/scalarbool.py +41 -0
- cfn_check-0.9.0/cfn_check/yaml/scalarfloat.py +103 -0
- cfn_check-0.9.0/cfn_check/yaml/scalarint.py +122 -0
- cfn_check-0.9.0/cfn_check/yaml/scalarstring.py +140 -0
- cfn_check-0.9.0/cfn_check/yaml/scanner.py +2390 -0
- cfn_check-0.9.0/cfn_check/yaml/serializer.py +231 -0
- cfn_check-0.9.0/cfn_check/yaml/tag.py +124 -0
- cfn_check-0.9.0/cfn_check/yaml/timestamp.py +61 -0
- cfn_check-0.9.0/cfn_check/yaml/tokens.py +382 -0
- cfn_check-0.9.0/cfn_check/yaml/util.py +262 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check.egg-info/PKG-INFO +201 -25
- cfn_check-0.9.0/cfn_check.egg-info/SOURCES.txt +81 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check.egg-info/requires.txt +1 -1
- cfn_check-0.9.0/example/pydantic_rules.py +35 -0
- cfn_check-0.9.0/example/renderer_test.py +42 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/example/rules.py +3 -3
- cfn_check-0.9.0/example/test_models/__init__.py +0 -0
- cfn_check-0.9.0/example/test_models/models.py +54 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/pyproject.toml +5 -4
- cfn_check-0.3.2/cfn_check/collection/collection.py +0 -2
- cfn_check-0.3.2/cfn_check/evaluation/evaluator.py +0 -85
- cfn_check-0.3.2/cfn_check/loader/loader.py +0 -21
- cfn_check-0.3.2/cfn_check.egg-info/SOURCES.txt +0 -37
- {cfn_check-0.3.2 → cfn_check-0.9.0}/LICENSE +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/__init__.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/cli/__init__.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/cli/utils/__init__.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/collection/__init__.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/evaluation/__init__.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/evaluation/errors.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/evaluation/parsing/__init__.py +0 -0
- {cfn_check-0.3.2/cfn_check/loader → cfn_check-0.9.0/cfn_check/logging}/__init__.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/logging/models.py +0 -0
- {cfn_check-0.3.2/cfn_check/logging → cfn_check-0.9.0/cfn_check/rendering/parsing}/__init__.py +0 -0
- /cfn_check-0.3.2/cfn_check/rules/__init__.py → /cfn_check-0.9.0/cfn_check/rendering/parsing/cloudformation_loader.py +0 -0
- {cfn_check-0.3.2/cfn_check/shared → cfn_check-0.9.0/cfn_check/rules}/__init__.py +0 -0
- {cfn_check-0.3.2/cfn_check/validation → cfn_check-0.9.0/cfn_check/shared}/__init__.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check/shared/types.py +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check.egg-info/dependency_links.txt +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check.egg-info/entry_points.txt +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/cfn_check.egg-info/top_level.txt +0 -0
- {cfn_check-0.3.2 → cfn_check-0.9.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cfn-check
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Validate Cloud Formation
|
|
5
5
|
Author-email: Ada Lundhe <adalundhe@lundhe.audio>
|
|
6
6
|
License: MIT License
|
|
@@ -27,14 +27,15 @@ License: MIT License
|
|
|
27
27
|
|
|
28
28
|
Project-URL: Homepage, https://github.com/adalundhe/cfn-check
|
|
29
29
|
Keywords: cloud-formation,testing,aws,cli
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
30
31
|
Classifier: Programming Language :: Python :: 3.12
|
|
31
32
|
Classifier: Programming Language :: Python :: 3.13
|
|
32
33
|
Classifier: Operating System :: OS Independent
|
|
33
|
-
Requires-Python: >=3.
|
|
34
|
+
Requires-Python: >=3.11
|
|
34
35
|
Description-Content-Type: text/markdown
|
|
35
36
|
License-File: LICENSE
|
|
36
37
|
Requires-Dist: pydantic
|
|
37
|
-
Requires-Dist:
|
|
38
|
+
Requires-Dist: ruamel.yaml
|
|
38
39
|
Requires-Dist: hyperlight-cocoa
|
|
39
40
|
Requires-Dist: async-logging
|
|
40
41
|
Dynamic: license-file
|
|
@@ -50,7 +51,7 @@ Dynamic: license-file
|
|
|
50
51
|
|
|
51
52
|
| Package | cfn-check |
|
|
52
53
|
| ----------- | ----------- |
|
|
53
|
-
| Version | 0.
|
|
54
|
+
| Version | 0.8.1 |
|
|
54
55
|
| Download | https://pypi.org/project/cfn-check/ |
|
|
55
56
|
| Source | https://github.com/adalundhe/cfn-check |
|
|
56
57
|
| Keywords | cloud-formation, testing, aws, cli |
|
|
@@ -70,15 +71,20 @@ problems inherint to `cfn-lint` more than `cfn-guard`, primarily:
|
|
|
70
71
|
- Inability to parse non-resource wildcards
|
|
71
72
|
- Inability to validate non-resource template data
|
|
72
73
|
- Inabillity to use structured models to validate input
|
|
74
|
+
- Poor ability to parse and render CloudFormation Refs/Functions
|
|
73
75
|
|
|
74
76
|
In comparison to `cfn-guard`, `cfn-check` is pure Python, thus
|
|
75
77
|
avoiding YADSL (Yet Another DSL) headaches. It also proves
|
|
76
78
|
significantly more configurable/modular/hackable as a result.
|
|
79
|
+
`cfn-check` can resolve _some_ (not all) CloudFormation Intrinsic
|
|
80
|
+
Functions and Refs.
|
|
77
81
|
|
|
78
82
|
CFN-Check uses a combination of simple depth-first-search tree
|
|
79
83
|
parsing, friendly `cfn-lint` like query syntax, `Pydantic` models,
|
|
80
84
|
and `pytest`-like assert-driven checks to make validating your
|
|
81
85
|
Cloud Formation easy while offering both CLI and Python API interfaces.
|
|
86
|
+
CFN-Check also uses a lightning-fast AST-parser to render your templates,
|
|
87
|
+
allowing you to validate policy, not just a YAML document.
|
|
82
88
|
|
|
83
89
|
<br/>
|
|
84
90
|
|
|
@@ -250,19 +256,20 @@ Congrats! You've just made the cloud a bit better place!
|
|
|
250
256
|
|
|
251
257
|
# Queries, Tokens, and Syntax
|
|
252
258
|
|
|
253
|
-
A `cfn-check` Query is a string made up of
|
|
259
|
+
A `cfn-check` Query is a string made up of period (`.`) delimited "Tokens" centered around four primary types:
|
|
254
260
|
|
|
255
261
|
- <b>`Keys`</b> - `<KEY>`: String name key Tokens that perform exact matching on keys of key/value pairs in a CloudFormation document.
|
|
256
|
-
- <b>`
|
|
262
|
+
- <b>`Values`</b> - `(<KEY> = <VALUE>)`: Parenthesis-enclosed `K==V` pairs that perform matching on values of key/value pairs in a CloudFormation document.
|
|
263
|
+
- <b>`Patterns`</b> - `<\d+>`: Arrow-enclosed regex pattern Tokens that perform pattern-based matching on keys of key/value pairs in a CloudFormation document.
|
|
257
264
|
- <b>`Ranges`</b> - `[]`: Brackets enclosed Tokens that perform array selection and filtering in a CloudFormation document.
|
|
258
265
|
|
|
259
266
|
|
|
260
|
-
In addition to `Key`, `Pattern`, and `Range` selection, you can also incorporate:
|
|
267
|
+
In addition to `Key`, `Value`, `Pattern`, and `Range` selection, you can also incorporate:
|
|
261
268
|
|
|
262
269
|
- <b>`Bounded Ranges`</b> - `[<A>-<B>]`: Exact matches from the starting position (if specified) to the end position (if specified) of an array
|
|
263
270
|
- <b>`Indicies`</b> - `[<A>]`: Exact matches the specified indicies of an array
|
|
264
271
|
- <b>`Key Ranges`</b> - `[<KEY>]`: Exact matches keys of objects within an array
|
|
265
|
-
- <b>`Pattern Ranges`</b> (`[
|
|
272
|
+
- <b>`Pattern Ranges`</b> (`[<\d+>]`): Matches they keys of objects within an array based on the specified pattern
|
|
266
273
|
- <b>`Wildcards`</b> (`*`): Selects all values for a given object or array or returns the non-object/array value at the specified path
|
|
267
274
|
- <b>`Wildcard Ranges`</b> (`[*]`): Selects all values for a given array and ensures that *only* the values of a valid array type are returned (any other type will be treated as a mismatch).
|
|
268
275
|
|
|
@@ -276,6 +283,27 @@ Resources
|
|
|
276
283
|
|
|
277
284
|
as your query, you'll select all items within the CloudFormation document under the `Resources` key.
|
|
278
285
|
|
|
286
|
+
|
|
287
|
+
### Working with Values
|
|
288
|
+
|
|
289
|
+
In addition to searching by keys, filtering by the values associated with those keys is the most common way you'll traverse and validate your CloudFormation template. To filter `Resource` key matches by the value of their `Type` value to only return EC2 Instances, we would specify a query like:
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
Resources.*.(Type == AWS::EC2::Instance)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
You can also match multiple values by utilizing the `in`/`IN` operator:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
Resources.*.(Type in AWS::Lambda::Function,AWS::Serverless::Function)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
and providing a comma-delimited list of values.
|
|
302
|
+
|
|
303
|
+
> [!NOTE]
|
|
304
|
+
> Value queries do not support Nested Ranges or nested queries, but *do* support
|
|
305
|
+
> Wildcards and Patterns. See below for more info!
|
|
306
|
+
|
|
279
307
|
### Working with Patterns
|
|
280
308
|
|
|
281
309
|
If an object within a CloudFormation document contains multiple similar keys you want to select, `Pattern` Tokens are your go-to solution. Consider this segment of CloudFormation:
|
|
@@ -302,7 +330,7 @@ Resources:
|
|
|
302
330
|
We want to select <i>both</i> `SecurityGroupIngress` and `SecurityGroupEgress` to perform the same rule evaluations. Since the keys for both blocks start with `SecurityGroup`, we could write a Query using a Pattern Token like:
|
|
303
331
|
|
|
304
332
|
```
|
|
305
|
-
Resources
|
|
333
|
+
Resources.SecurityGroup.Properties.<SecurityGroup>
|
|
306
334
|
```
|
|
307
335
|
|
|
308
336
|
which would allow us to use a single rule to evaluate both:
|
|
@@ -311,7 +339,7 @@ which would allow us to use a single rule to evaluate both:
|
|
|
311
339
|
class ValidateSecurityGroups(Collection):
|
|
312
340
|
|
|
313
341
|
@Rule(
|
|
314
|
-
"Resources
|
|
342
|
+
"Resources.SecurityGroup.Properties.<SecurityGroup>",
|
|
315
343
|
"It checks Security Groups are correctly definined",
|
|
316
344
|
)
|
|
317
345
|
def validate_security_groups(self, value: list[dict]):
|
|
@@ -342,7 +370,7 @@ Wildcard Tokens allow you to select all matching objects, array entries, or valu
|
|
|
342
370
|
In fact, you've already used one! In the first example, we use a Wildcard Token in the below query:
|
|
343
371
|
|
|
344
372
|
```
|
|
345
|
-
Resources
|
|
373
|
+
Resources.*.Type
|
|
346
374
|
```
|
|
347
375
|
|
|
348
376
|
To select all `Resource` objects, then further extract the `Type` field from each object. This helps us avoid copy-paste rules at the potential cost of deferring more work to individual `Rule` methods if we aren't careful and select too much!
|
|
@@ -361,7 +389,7 @@ Ranges allow you to perform sophisticated selection of objects or data within a
|
|
|
361
389
|
Unbounded ranges allow you to select and return an array in its entirety. For example:
|
|
362
390
|
|
|
363
391
|
```
|
|
364
|
-
Resources
|
|
392
|
+
Resources.SecurityGroup.Properties.SecurityGroupIngress.[]
|
|
365
393
|
```
|
|
366
394
|
|
|
367
395
|
Would return all SecurityGroupIngress objects in the CloudFormation document as a list, allowing you to check that the array of ingresses has been both defined *and* populated.
|
|
@@ -372,7 +400,7 @@ Would return all SecurityGroupIngress objects in the CloudFormation document as
|
|
|
372
400
|
Indexes allow you to select specific positions within an array. For example:
|
|
373
401
|
|
|
374
402
|
```
|
|
375
|
-
Resources
|
|
403
|
+
Resources.SecurityGroup.Properties.SecurityGroupIngress.[0]
|
|
376
404
|
```
|
|
377
405
|
|
|
378
406
|
Would return the first SecurityGroupIngress objects in the document.
|
|
@@ -386,7 +414,7 @@ Bounded Ranges allow you to select subsets of indicies within an array (much lik
|
|
|
386
414
|
As an example:
|
|
387
415
|
|
|
388
416
|
```
|
|
389
|
-
Resources
|
|
417
|
+
Resources.SecurityGroup.Properties.SecurityGroupIngress.[1-3]
|
|
390
418
|
```
|
|
391
419
|
|
|
392
420
|
Would select the second and third SecurityGroupIngress objects in the document.
|
|
@@ -394,13 +422,13 @@ Would select the second and third SecurityGroupIngress objects in the document.
|
|
|
394
422
|
Start or end positions are optional for Bounded Ranges. If a starting position is not defined, `cfn-check` will default to `0`. Likewise, if an end position is not defined, `cfn-check` will default to the end of given list. For example:
|
|
395
423
|
|
|
396
424
|
```
|
|
397
|
-
Resources
|
|
425
|
+
Resources.SecurityGroup.Properties.SecurityGroupIngress.[-3]
|
|
398
426
|
```
|
|
399
427
|
|
|
400
428
|
selects the first through third SecurityGroupIngress objects in the document while:
|
|
401
429
|
|
|
402
430
|
```
|
|
403
|
-
Resources
|
|
431
|
+
Resources.SecurityGroup.Properties.SecurityGroupIngress.[3-]
|
|
404
432
|
```
|
|
405
433
|
|
|
406
434
|
selects the remaining SecurityGroupIngress objects starting from the third.
|
|
@@ -417,7 +445,7 @@ Often times it's easier to match based upon an array's contents than by exact in
|
|
|
417
445
|
For example:
|
|
418
446
|
|
|
419
447
|
```
|
|
420
|
-
Resources
|
|
448
|
+
Resources.MyEC2Instance.Properties.ImageId.[AWSRegionArch2AMI]
|
|
421
449
|
```
|
|
422
450
|
|
|
423
451
|
returns only the EC2 ImageIds where the ImageId exactly matches `AWSRegionArch2AMI`.
|
|
@@ -428,7 +456,7 @@ returns only the EC2 ImageIds where the ImageId exactly matches `AWSRegionArch2A
|
|
|
428
456
|
Pattern Ranges function much like Key Ranges, but utilize regex-based pattern matching for comparison. Adapting the above example:
|
|
429
457
|
|
|
430
458
|
```
|
|
431
|
-
Resources
|
|
459
|
+
Resources.MyEC2Instance.Properties.ImageId.[<^AWSRegion>]
|
|
432
460
|
```
|
|
433
461
|
|
|
434
462
|
returns only the EC2 ImageIds where the ImageId begins with `AWSRegion`. This can be helpful in checking for and enforcing naming standards, etc.
|
|
@@ -441,13 +469,13 @@ Wildcard Ranges extend the powerful functionality of Wildcard Tokens with the ad
|
|
|
441
469
|
For example we know:
|
|
442
470
|
|
|
443
471
|
```
|
|
444
|
-
Resources
|
|
472
|
+
Resources.*.Type
|
|
445
473
|
```
|
|
446
474
|
|
|
447
475
|
Selects all `Resource` objects. If we convert the Wildcard Token in the query to a Wildcard Range Token:
|
|
448
476
|
|
|
449
477
|
```
|
|
450
|
-
Resources
|
|
478
|
+
Resources.[*].Type
|
|
451
479
|
```
|
|
452
480
|
|
|
453
481
|
The Rule will fail as below:
|
|
@@ -481,7 +509,7 @@ Note that the array we want is nested within another array, and we need to make
|
|
|
481
509
|
We can accomplish this by using a Wildcard Range Token in our Query as below:
|
|
482
510
|
|
|
483
511
|
```
|
|
484
|
-
Resources
|
|
512
|
+
Resources.AppendItemToListFunction.Properties.Code.ZipFile.[*].[]
|
|
485
513
|
```
|
|
486
514
|
|
|
487
515
|
Which allows us to then evaluate the Unbounded Range token against each array item, returning only the array we want.
|
|
@@ -498,7 +526,7 @@ You can use multiple Tokens within a Range Token by seperating each token with a
|
|
|
498
526
|
For example:
|
|
499
527
|
|
|
500
528
|
```
|
|
501
|
-
Resources
|
|
529
|
+
Resources.SecurityGroup.Properties.SecurityGroupIngress.[0, -2]
|
|
502
530
|
```
|
|
503
531
|
|
|
504
532
|
Would select all except the last element of an array.
|
|
@@ -506,7 +534,7 @@ Would select all except the last element of an array.
|
|
|
506
534
|
This also applies to Bounded Ranges, Key Ranges, Pattern Ranges, and Wildcard Ranges! For example:
|
|
507
535
|
|
|
508
536
|
```
|
|
509
|
-
Resources
|
|
537
|
+
Resources.MyEC2Instance.Properties.ImageId.[(^AWSRegion),(^),(^Custom)]
|
|
510
538
|
```
|
|
511
539
|
|
|
512
540
|
will select any EC2 ImageIds that start with either `AWSRegion` or `Custom`.
|
|
@@ -530,12 +558,160 @@ ZipFile: !Join
|
|
|
530
558
|
from our previous examples, we used the below query to select the nested array:
|
|
531
559
|
|
|
532
560
|
```
|
|
533
|
-
Resources
|
|
561
|
+
Resources.AppendItemToListFunction.Properties.Code.ZipFile.[*].[]
|
|
534
562
|
```
|
|
535
563
|
|
|
536
564
|
With Nested Ranges, this can be shortened to:
|
|
537
565
|
|
|
538
566
|
```
|
|
539
|
-
Resources
|
|
567
|
+
Resources.AppendItemToListFunction.Properties.Code.ZipFile.[[]]
|
|
568
|
+
```
|
|
540
569
|
|
|
541
570
|
Which is both more concise *and* more representitave of our intention to select only the array.
|
|
571
|
+
|
|
572
|
+
# Grouping Queries
|
|
573
|
+
|
|
574
|
+
CFN-Check grouping allows you significant freedom of expression in how you write queries while *also* allowing you to more easily restrict and filter results by multiple criterion. Queries support both logical "or" and "and" statements via the `|` and `&` operators respectively. For example, consider the previous values query where we used an `in` operator:
|
|
575
|
+
|
|
576
|
+
```
|
|
577
|
+
Resources.*.(Type in AWS::Lambda::Function,AWS::Serverless::Function)
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
This could be rewritten as:
|
|
581
|
+
```
|
|
582
|
+
Resources.*(Type == AWS::Lambda::Function | Type == AWS::Serverless::Function)
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
A more likely scenario might be finding specifically NodeJS Lambda functions. For example:
|
|
586
|
+
|
|
587
|
+
```
|
|
588
|
+
Resources.*.(Type == AWS::Lambda::Function,AWS::Serverless::Function & Properties.Runtime == <nodejs20>)
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Queries are "split" by `|` operator and then "grouped" by `&` operator. That means if we want a query to match one set of criterion <i>or</i> another we could write:
|
|
592
|
+
|
|
593
|
+
```
|
|
594
|
+
Resources.*.(Type == AWS::Lambda::Function & Properties.Runtime == <nodejs20> | Type == AWS::Serverless::Function & Properties.Runtime == <nodejs20>)
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
<br/>
|
|
598
|
+
|
|
599
|
+
# Using Pydantic Models
|
|
600
|
+
|
|
601
|
+
In addition to traditional `pytest`-like assert statements, `cfn-lint` can validate results returned by queries via `Pydantic` models.
|
|
602
|
+
|
|
603
|
+
For example, consider again the initial example where we validate the `Type` field of `Resource` objects.
|
|
604
|
+
|
|
605
|
+
```python
|
|
606
|
+
from cfn_check import Collection, Rule
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
class ValidateResourceType(Collection):
|
|
610
|
+
|
|
611
|
+
@Rule(
|
|
612
|
+
"Resources.*.Type",
|
|
613
|
+
"It checks Resource.Type is correctly definined",
|
|
614
|
+
)
|
|
615
|
+
def validate_test(self, value: str):
|
|
616
|
+
assert value is not None, '❌ Resource Type not defined'
|
|
617
|
+
assert isinstance(value, str), '❌ Resource Type not a string'
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
Rather than explicitly querying for the type field and writing assertions, we can instead define a `Pydantic` schema, then pass all `Resource` objects to that schema by specifying it as a Python type hint in our `Rule` method's signature.
|
|
621
|
+
|
|
622
|
+
```python
|
|
623
|
+
from cfn_check import Collection, Rule
|
|
624
|
+
from pydantic import BaseModel, StrictStr
|
|
625
|
+
|
|
626
|
+
class Resource(BaseModel):
|
|
627
|
+
Type: StrictStr
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
class ValidateResourceType(Collection):
|
|
631
|
+
|
|
632
|
+
@Rule(
|
|
633
|
+
"Resources.*",
|
|
634
|
+
"It checks Resource.Type is correctly definined",
|
|
635
|
+
)
|
|
636
|
+
def validate_test(self, value: Resource):
|
|
637
|
+
assert value is not None
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
By deferring type and existence assertions to `Pydantic` models, you can focus your actual assertion logic on business/security policy checks.
|
|
641
|
+
|
|
642
|
+
<br/>
|
|
643
|
+
|
|
644
|
+
# Using .query()
|
|
645
|
+
|
|
646
|
+
Some of the most challenging validations to write in CFN-Guard or CFN-Lint are those requring validation of other template information against an existing selection. For example, validating that a Lambda has a LoggingGroup attached and specified within the same template.
|
|
647
|
+
|
|
648
|
+
CFN-Check makes performing these complex assertions intuitive and painless by allowing you to execute additional querieis within a rule via the `.query()` method. For example, to perform the LoggingGroup validation above you might write:
|
|
649
|
+
|
|
650
|
+
```python
|
|
651
|
+
@Rule("Resources.*.(Type in AWS::Lambda::Function,AWS::Serverless::Function)", "It validates a lambda is configured correctly")
|
|
652
|
+
def validate_lambda(self, lambda_resource: Lambda):
|
|
653
|
+
assert isinstance(lambda_resource, Lambda), "Not a valid Lambda"
|
|
654
|
+
|
|
655
|
+
log_groups = self.query(
|
|
656
|
+
f"Resources.{lambda_resource.Properties.LoggingConfig.LogGroup}",
|
|
657
|
+
transforms=[
|
|
658
|
+
lambda data: LoggingGroup(**data)
|
|
659
|
+
]
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
assert log_groups is not None, f"No resources found for LoggingGroup {lambda_resource.Properties.LoggingConfig.LogGroup}"
|
|
663
|
+
assert len(log_groups) > 0, "No matching logging group found in Resources for Lambda"
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
The `query()` method accepts the following parameters:
|
|
667
|
+
|
|
668
|
+
- `query - (str, required)`: A string CFN-Check query as used when defining Rules.
|
|
669
|
+
- `document - (dict/list/any, optional)`: The filepath to a CloudFormation template document. This document must have been loaded by and specified to CFN-Check either via the default CLI path param, the `-i/--import-values` optional CLI arg, or under the `input_values` config key. This will cause the query specified in the call to execute against the template specified.
|
|
670
|
+
- `transforms - (list[Function], optional)`: A list of functions that can be used to modify and filter returned results. In the example above, we use a single transform function to convert matches returned to a `LoggingGroup` Pydantic model instance.
|
|
671
|
+
|
|
672
|
+
# The Rendering Engine
|
|
673
|
+
|
|
674
|
+
### Overview
|
|
675
|
+
|
|
676
|
+
In Version 0.6.X, CFN-Check introduced a rendering engine, which allows it
|
|
677
|
+
to parse and execute Refs and all CloudFormation intrinsic functions via
|
|
678
|
+
either the CloudFormation document or user-supplied values. This additional
|
|
679
|
+
also resulted in the:
|
|
680
|
+
|
|
681
|
+
```bash
|
|
682
|
+
cfn-check render <TEMPLATE_PATH >
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
command being added, allowing you to effectively "dry run" render your
|
|
686
|
+
CloudFormation templates akin to the `helm template` command for Helm.
|
|
687
|
+
|
|
688
|
+
By default, `cfn-check render` outputs to stdout, however you can easily
|
|
689
|
+
save rendered output to a file via the `-o/--output-file` flag. For example:
|
|
690
|
+
|
|
691
|
+
```bash
|
|
692
|
+
cfn-check render template.yml -o rendered.yml
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
The `cfn-check render` command also offers the following options:
|
|
696
|
+
|
|
697
|
+
- `-a/--attributes`: A list of <key>=<value> input `!GetAtt` attributes to use
|
|
698
|
+
- `-m/--mappings`: A list of <key>=<value> input `Mappings` to use
|
|
699
|
+
- `-p/--parameters`: A list of <key>=<value> input `Parameters` to use
|
|
700
|
+
- `-l/--log-level`: The log level to use
|
|
701
|
+
|
|
702
|
+
### The Rendering Engine during Checks
|
|
703
|
+
|
|
704
|
+
By default rendering is enabled when running `cfn-check` validation. You can
|
|
705
|
+
disable it by supplying `no-render` to the `-F/--flags` option as below:
|
|
706
|
+
|
|
707
|
+
```bash
|
|
708
|
+
cfn-check validate -F no-render -r rules.py template.yaml
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
Disabling rendering means CFN-Check will validate your template as-is, with
|
|
712
|
+
no additional pre-processing and no application of user input values.
|
|
713
|
+
|
|
714
|
+
> [!WARNING]
|
|
715
|
+
> CloudFormation documents are <b>not</b> "plain yaml" and disabling
|
|
716
|
+
> rendering means any dynamically determined values will likely fail
|
|
717
|
+
> to pass validation, resulting in false positives for failures!
|