Skip to content

Commit 87bea4d

Browse files
tas50claude
andcommitted
✨ cloudformation: add dependsOn, rules, and improve README
Add `dependsOn` field to `cloudformation.resource` exposing DependsOn dependencies (both single-string and list forms). Add `rules()` to `cloudformation.template` for parameter validation constraints. Expand provider README with usage instructions and practical query examples. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f2644a0 commit 87bea4d

File tree

10 files changed

+207
-5
lines changed

10 files changed

+207
-5
lines changed

providers/cloudformation/README.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,70 @@
1+
# CloudFormation Provider
12

3+
Static analysis provider for AWS CloudFormation and SAM templates. Parses YAML/JSON templates locally without requiring AWS credentials or API access.
24

5+
## Usage
36

4-
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-reference.html
7+
```shell
8+
mql shell cloudformation <path-to-template>
9+
```
10+
11+
Supports both YAML and JSON template formats.
12+
13+
```shell
14+
# Single template file
15+
mql shell cloudformation template.yaml
16+
17+
# JSON format
18+
mql shell cloudformation stack.json
19+
```
20+
21+
## Examples
22+
23+
**List all resources in a template**
24+
25+
```shell
26+
mql> cloudformation.template.resources { name type }
27+
cloudformation.template.resources: [
28+
0: {
29+
name: "MyVPC"
30+
type: "AWS::EC2::VPC"
31+
}
32+
1: {
33+
name: "MySubnet"
34+
type: "AWS::EC2::Subnet"
35+
}
36+
]
37+
```
38+
39+
**Check that no resources use a specific type**
40+
41+
```shell
42+
mql> cloudformation.template.resources.none(type == "AWS::IAM::User")
43+
```
44+
45+
**Ensure all S3 buckets have versioning enabled**
46+
47+
```shell
48+
mql> cloudformation.template.resources.where(type == "AWS::S3::Bucket") {
49+
properties["VersioningConfiguration"]["Status"] == "Enabled"
50+
}
51+
```
52+
53+
**Check that all Lambda functions use a supported runtime**
54+
55+
```shell
56+
mql> cloudformation.template.resources.where(type == "AWS::Lambda::Function") {
57+
properties["Runtime"] == /^python3\.(11|12|13)$/
58+
}
59+
```
60+
61+
**One-shot query from the command line**
62+
63+
```shell
64+
mql run cloudformation template.yaml -c "cloudformation.template.resources.where(type == /SecurityGroup/) { name properties }"
65+
```
66+
67+
## References
68+
69+
- [AWS CloudFormation Template Reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/introduction.html)
70+
- [AWS SAM Template Specification](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification.html)

providers/cloudformation/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
module go.mondoo.com/mql/v13/providers/cloudformation
22

3-
go 1.25.6
3+
go 1.26.0
44

55
replace go.mondoo.com/mql/v13 => ../..
66

77
require (
8-
github.com/aws-cloudformation/rain v1.24.2
8+
github.com/aws-cloudformation/rain v1.24.3
99
github.com/rs/zerolog v1.34.0
1010
github.com/stretchr/testify v1.11.1
1111
go.mondoo.com/mql/v13 v13.0.0-rc7

providers/cloudformation/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
7777
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
7878
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
7979
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
80-
github.com/aws-cloudformation/rain v1.24.2 h1:mBlBwkZTavTdP3fr81gGR1NrQEAhcncs9hcKc1qn+jo=
81-
github.com/aws-cloudformation/rain v1.24.2/go.mod h1:mg8IIq7r1ss1wDClMqgtovZO2CDNokE+w9i9XRvvBEE=
80+
github.com/aws-cloudformation/rain v1.24.3 h1:n/ZylHMgGHTDcZLo49TjE9z0kWrsdPWLrmVoLuLh4ig=
81+
github.com/aws-cloudformation/rain v1.24.3/go.mod h1:/MdRW1x1OFuoTJJU52g6m6hMNNh4HLvVttrdlCtL8Bk=
8282
github.com/aws/aws-sdk-go-v2 v1.41.3 h1:4kQ/fa22KjDt13QCy1+bYADvdgcxpfH18f0zP542kZA=
8383
github.com/aws/aws-sdk-go-v2 v1.41.3/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
8484
github.com/aws/aws-sdk-go-v2/config v1.32.11 h1:ftxI5sgz8jZkckuUHXfC/wMUc8u3fG1vQS0plr2F2Zs=

providers/cloudformation/resources/cloudformation.lr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ cloudformation.template @defaults("description") {
2222
metadata() map[string]dict
2323
// Template conditions
2424
conditions() map[string]dict
25+
// Template rules for parameter validation
26+
rules() map[string]dict
2527
// Template resources
2628
resources() []cloudformation.resource
2729
// Template outputs
@@ -44,6 +46,8 @@ cloudformation.resource @defaults("name") {
4446
attributes map[string]dict
4547
// Resource properties
4648
properties map[string]dict
49+
// Resource dependencies (DependsOn)
50+
dependsOn []string
4751
}
4852

4953
// AWS CloudFormation Output

providers/cloudformation/resources/cloudformation.lr.go

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

providers/cloudformation/resources/cloudformation.lr.versions

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ cloudformation.output.properties 11.0.0
77
cloudformation.resource 11.0.0
88
cloudformation.resource.attributes 11.0.0
99
cloudformation.resource.condition 11.0.0
10+
cloudformation.resource.dependsOn 13.0.1
1011
cloudformation.resource.documentation 11.0.0
1112
cloudformation.resource.name 11.0.0
1213
cloudformation.resource.properties 11.0.0
@@ -20,6 +21,7 @@ cloudformation.template.metadata 11.0.0
2021
cloudformation.template.outputs 11.0.0
2122
cloudformation.template.parameters 11.0.0
2223
cloudformation.template.resources 11.0.0
24+
cloudformation.template.rules 13.0.1
2325
cloudformation.template.transform 11.0.0
2426
cloudformation.template.types 11.0.0
2527
cloudformation.template.version 11.0.0

providers/cloudformation/resources/cloudformation_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,43 @@ func TestCloudformationResources(t *testing.T) {
198198
assert.Equal(t, 1, len(res.Data))
199199
})
200200

201+
t.Run("cloudformation dependsOn", func(t *testing.T) {
202+
path := "../testdata/dependson.yaml"
203+
tpl, err := loadTemplate(path)
204+
require.NoError(t, err)
205+
206+
res := tpl.GetResources()
207+
require.NoError(t, res.Error)
208+
assert.Equal(t, 4, len(res.Data))
209+
210+
for i := range res.Data {
211+
resource := res.Data[i].(*mqlCloudformationResource)
212+
switch resource.Name.Data {
213+
case "MySubnet":
214+
// single string DependsOn
215+
assert.Equal(t, []any{"MyVPC"}, resource.DependsOn.Data)
216+
case "MyInstance":
217+
// list DependsOn
218+
assert.Equal(t, []any{"MyVPC", "MySubnet"}, resource.DependsOn.Data)
219+
case "MyVPC", "MyQueue":
220+
// no DependsOn
221+
assert.Empty(t, resource.DependsOn.Data)
222+
}
223+
}
224+
})
225+
226+
t.Run("cloudformation rules", func(t *testing.T) {
227+
path := "../testdata/rules.yaml"
228+
tpl, err := loadTemplate(path)
229+
require.NoError(t, err)
230+
231+
res := tpl.GetRules()
232+
require.NoError(t, res.Error)
233+
assert.Equal(t, 1, len(res.Data))
234+
val := res.Data["ProdInstanceType"]
235+
assert.NotNil(t, val)
236+
})
237+
201238
t.Run("cloudformation transform", func(t *testing.T) {
202239
path := "../testdata/transform.yaml"
203240
tpl, err := loadTemplate(path)

providers/cloudformation/resources/template.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"go.mondoo.com/mql/v13/types"
1313
"go.mondoo.com/ranger-rpc/codes"
1414
"go.mondoo.com/ranger-rpc/status"
15+
"gopkg.in/yaml.v3"
1516
)
1617

1718
func initCloudformationTemplate(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) {
@@ -108,6 +109,10 @@ func (r *mqlCloudformationTemplate) conditions() (map[string]any, error) {
108109
return r.extractDict(cft.Conditions)
109110
}
110111

112+
func (r *mqlCloudformationTemplate) rules() (map[string]any, error) {
113+
return r.extractDict(cft.Rules)
114+
}
115+
111116
func (x *mqlCloudformationResource) id() (string, error) {
112117
return x.Name.Data, nil
113118
}
@@ -162,13 +167,27 @@ func (r *mqlCloudformationTemplate) resources() ([]any, error) {
162167
}
163168
}
164169

170+
var dependsOn []any
171+
_, val, err = gatherMapValue(valueNode, "DependsOn")
172+
if err == nil {
173+
switch val.Kind {
174+
case yaml.ScalarNode:
175+
dependsOn = []any{val.Value}
176+
case yaml.SequenceNode:
177+
for _, item := range val.Content {
178+
dependsOn = append(dependsOn, item.Value)
179+
}
180+
}
181+
}
182+
165183
pkg, err := CreateResource(r.MqlRuntime, "cloudformation.resource", map[string]*llx.RawData{
166184
"name": llx.StringData(keyNode.Value),
167185
"type": llx.StringData(resourceType),
168186
"condition": llx.StringData(resourceCondition),
169187
"documentation": llx.StringData(resourceDocumentation),
170188
"attributes": llx.MapData(attrs, types.Dict),
171189
"properties": llx.MapData(props, types.Dict),
190+
"dependsOn": llx.ArrayData(dependsOn, types.String),
172191
})
173192
if err != nil {
174193
return nil, err
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Resources:
2+
MyVPC:
3+
Type: "AWS::EC2::VPC"
4+
Properties:
5+
CidrBlock: "10.0.0.0/16"
6+
MySubnet:
7+
Type: "AWS::EC2::Subnet"
8+
DependsOn: MyVPC
9+
Properties:
10+
VpcId: !Ref MyVPC
11+
CidrBlock: "10.0.1.0/24"
12+
MyInstance:
13+
Type: "AWS::EC2::Instance"
14+
DependsOn:
15+
- MyVPC
16+
- MySubnet
17+
Properties:
18+
ImageId: "ami-0ff8a91507f77f867"
19+
MyQueue:
20+
Type: "AWS::SQS::Queue"
21+
Properties: {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Parameters:
2+
Environment:
3+
Type: String
4+
AllowedValues:
5+
- dev
6+
- staging
7+
- prod
8+
InstanceType:
9+
Type: String
10+
Rules:
11+
ProdInstanceType:
12+
RuleCondition:
13+
Fn::Equals:
14+
- !Ref Environment
15+
- prod
16+
Assertions:
17+
- Assert:
18+
Fn::Contains:
19+
- - m5.large
20+
- m5.xlarge
21+
- !Ref InstanceType
22+
AssertDescription: "Production instances must be m5.large or m5.xlarge"
23+
Resources:
24+
MyInstance:
25+
Type: "AWS::EC2::Instance"
26+
Properties:
27+
InstanceType: !Ref InstanceType

0 commit comments

Comments
 (0)