Skip to content

Commit 54e3b3e

Browse files
authored
✨ add terraform.plan.variables (#5113)
This change adds `terraform.plan.variables` to our terraform provider for a plan connection. Using the test data added, when running cnquery shell, we now get: ``` ➜ cnquery git:(afiune/terraform/plan-variables) ✗ ./cnquery shell terraform plan providers/terraform/connection/testdata/dynamic_block/tfplan.json -c terraform.plan.variables ! detected Schrödinger's path, cannot detect if it is usable path=/Library/Mondoo/providers/.providers-unpack2980851121/.providers-unpack2980851121.json ! using builtin provider for terraform → user requested to ignore .terraform ! using builtin provider for terraform → loaded configuration from /Users/afiune/.config/mondoo/mondoo.yml using source default ! using builtin provider for terraform → connected to Terraform Plan ___ _ __ __ _ _ _ ___ _ __ _ _ / __| '_ \ / _` | | | |/ _ \ '__| | | | | (__| | | | (_| | |_| | __/ | | |_| | \___|_| |_|\__, |\__,_|\___|_| \__, | mondoo™ |_| |___/ interactive shell terraform.plan.variables: [ 0: terraform.plan.variable name="environment" value="dev" ] cnquery> ``` This allows us to write queries like: ``` cnquery> terraform.plan.variables.where(value == "dev") {*} terraform.plan.variables.where: [ 0: { value: "dev" name: "environment" } ] ``` --------- Signed-off-by: Salim Afiune Maya <[email protected]>
1 parent 6716881 commit 54e3b3e

File tree

11 files changed

+253
-4
lines changed

11 files changed

+253
-4
lines changed

.github/actions/spelling/expect.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
ACCOUNTADMIN
2+
applyable
23
atlassian
34
auditlog
45
Auths

providers/terraform/connection/hcl_parser_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func TestLoadHclBlocks(t *testing.T) {
3030
require.NotNil(t, parser)
3131
tfVars := tf.TfVars()
3232
assert.Equal(t, 2, len(tfVars))
33-
assert.Equal(t, 5, len(parser.Files()))
33+
assert.Equal(t, 6, len(parser.Files()))
3434
}
3535

3636
func TestLoadTfvars(t *testing.T) {

providers/terraform/connection/testdata/dynamic_block/.terraform.lock.hcl

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
variable "environment" {
2+
type = string
3+
}
4+
5+
locals {
6+
set = {
7+
set1 = {
8+
name = "service.type"
9+
value = "LoadBalancer"
10+
}
11+
set2 = {
12+
name = "replicaCount"
13+
value = "2"
14+
}
15+
set3 = {
16+
name = "ingress.enabled"
17+
value = "true"
18+
}
19+
set4 = {
20+
name = "environment"
21+
value = var.environment
22+
}
23+
}
24+
}
25+
resource "helm_release" "nginx" {
26+
name = "my-nginx"
27+
chart = "nginx"
28+
repository = "https://charts.bitnami.com/bitnami"
29+
version = "13.2.12"
30+
dynamic "set" {
31+
for_each = local.set
32+
content {
33+
name = set.value.name
34+
value = set.value.value
35+
}
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"format_version":"1.2","terraform_version":"1.8.5","variables":{"environment":{"value":"dev"}},"planned_values":{"root_module":{"resources":[{"address":"helm_release.nginx","mode":"managed","type":"helm_release","name":"nginx","provider_name":"registry.terraform.io/hashicorp/helm","schema_version":1,"values":{"atomic":false,"chart":"nginx","cleanup_on_fail":false,"create_namespace":false,"dependency_update":false,"description":null,"devel":null,"disable_crd_hooks":false,"disable_openapi_validation":false,"disable_webhooks":false,"force_update":false,"keyring":null,"lint":false,"max_history":0,"name":"my-nginx","namespace":"default","pass_credentials":false,"postrender":[],"recreate_pods":false,"render_subchart_notes":true,"replace":false,"repository":"https://charts.bitnami.com/bitnami","repository_ca_file":null,"repository_cert_file":null,"repository_key_file":null,"repository_password":null,"repository_username":null,"reset_values":false,"reuse_values":false,"set":[{"name":"environment","type":"","value":"dev"},{"name":"ingress.enabled","type":"","value":"true"},{"name":"replicaCount","type":"","value":"2"},{"name":"service.type","type":"","value":"LoadBalancer"}],"set_list":[],"set_sensitive":[],"skip_crds":false,"status":"deployed","timeout":300,"upgrade_install":null,"values":null,"verify":false,"version":"13.2.12","wait":true,"wait_for_jobs":false},"sensitive_values":{"metadata":[],"postrender":[],"repository_password":true,"set":[{},{},{},{}],"set_list":[],"set_sensitive":[]}}]}},"resource_changes":[{"address":"helm_release.nginx","mode":"managed","type":"helm_release","name":"nginx","provider_name":"registry.terraform.io/hashicorp/helm","change":{"actions":["create"],"before":null,"after":{"atomic":false,"chart":"nginx","cleanup_on_fail":false,"create_namespace":false,"dependency_update":false,"description":null,"devel":null,"disable_crd_hooks":false,"disable_openapi_validation":false,"disable_webhooks":false,"force_update":false,"keyring":null,"lint":false,"max_history":0,"name":"my-nginx","namespace":"default","pass_credentials":false,"postrender":[],"recreate_pods":false,"render_subchart_notes":true,"replace":false,"repository":"https://charts.bitnami.com/bitnami","repository_ca_file":null,"repository_cert_file":null,"repository_key_file":null,"repository_password":null,"repository_username":null,"reset_values":false,"reuse_values":false,"set":[{"name":"environment","type":"","value":"dev"},{"name":"ingress.enabled","type":"","value":"true"},{"name":"replicaCount","type":"","value":"2"},{"name":"service.type","type":"","value":"LoadBalancer"}],"set_list":[],"set_sensitive":[],"skip_crds":false,"status":"deployed","timeout":300,"upgrade_install":null,"values":null,"verify":false,"version":"13.2.12","wait":true,"wait_for_jobs":false},"after_unknown":{"id":true,"manifest":true,"metadata":true,"postrender":[],"set":[{},{},{},{}],"set_list":[],"set_sensitive":[]},"before_sensitive":false,"after_sensitive":{"metadata":[],"postrender":[],"repository_password":true,"set":[{},{},{},{}],"set_list":[],"set_sensitive":[]}}}],"configuration":{"provider_config":{"helm":{"name":"helm","full_name":"registry.terraform.io/hashicorp/helm"}},"root_module":{"resources":[{"address":"helm_release.nginx","mode":"managed","type":"helm_release","name":"nginx","provider_config_key":"helm","expressions":{"chart":{"constant_value":"nginx"},"name":{"constant_value":"my-nginx"},"repository":{"constant_value":"https://charts.bitnami.com/bitnami"},"version":{"constant_value":"13.2.12"}},"schema_version":1}],"variables":{"environment":{}}}},"timestamp":"2025-01-22T19:13:07Z","applyable":true,"complete":true,"errored":false}

providers/terraform/connection/tfplan.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ type Plan struct {
1919
PriorState json.RawMessage `json:"prior_state,omitempty"`
2020
Configuration json.RawMessage `json:"configuration,omitempty"`
2121
PlannedValues plannedStateValues `json:"planned_values,omitempty"`
22-
Variables variables `json:"variables,omitempty"`
22+
Variables Variables `json:"variables,omitempty"`
2323
ResourceChanges []ResourceChange `json:"resource_changes,omitempty"`
2424
ResourceDrift []ResourceChange `json:"resource_drift,omitempty"`
2525
RelevantAttributes []resourceAttr `json:"relevant_attributes,omitempty"`
2626
OutputChanges map[string]change `json:"output_changes,omitempty"`
27+
Applyable bool `json:"applyable,omitempty"`
28+
Errored bool `json:"errored,omitempty"`
2729
}
2830

2931
type plannedStateValues struct {
@@ -83,9 +85,9 @@ type resource struct {
8385
SensitiveValues json.RawMessage `json:"sensitive_values,omitempty"`
8486
}
8587

86-
type variables map[string]*variable
88+
type Variables map[string]*Variable
8789

88-
type variable struct {
90+
type Variable struct {
8991
Value json.RawMessage `json:"value,omitempty"`
9092
}
9193

providers/terraform/connection/tfplan_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,17 @@ func TestTfplan(t *testing.T) {
2121
require.NoError(t, err)
2222
assert.NotNil(t, plan)
2323
}
24+
25+
func TestTfWithDynamicBlocksAndVariables(t *testing.T) {
26+
data, err := os.ReadFile("./testdata/dynamic_block/tfplan.json")
27+
require.NoError(t, err)
28+
29+
var plan Plan
30+
err = json.Unmarshal(data, &plan)
31+
require.NoError(t, err)
32+
assert.NotNil(t, plan)
33+
_, ok := plan.Variables["environment"] // this exist in the testdata
34+
assert.True(t, ok)
35+
assert.True(t, plan.Applyable)
36+
assert.False(t, plan.Errored)
37+
}

providers/terraform/resources/terraform.lr

+14
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ terraform.plan {
167167
terraformVersion string
168168
// Resource changes
169169
resourceChanges() []terraform.plan.resourceChange
170+
// Variables used to generate the Terraform plan
171+
variables []terraform.plan.variable
172+
// Whether `apply` is valid for the plan
173+
applyable bool
174+
// Whether the plan errored
175+
errored bool
170176
}
171177

172178
// Terraform plan configuration
@@ -177,6 +183,14 @@ terraform.plan.configuration {
177183
resources() []dict
178184
}
179185

186+
// Terraform plan variable
187+
terraform.plan.variable @defaults("name value") {
188+
// Variable name
189+
name string
190+
// Variable value
191+
value dict
192+
}
193+
180194
// Terraform plan resource change
181195
terraform.plan.resourceChange @defaults("type name") {
182196
// Resource address

providers/terraform/resources/terraform.lr.go

+112
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

providers/terraform/resources/terraform.lr.manifest.yaml

+14
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,15 @@ resources:
8787
title: Display all loaded Terraform modules
8888
terraform.plan:
8989
fields:
90+
applyable:
91+
min_mondoo_version: 9.0.0
92+
errored:
93+
min_mondoo_version: 9.0.0
9094
formatVersion: {}
9195
resourceChanges: {}
9296
terraformVersion: {}
97+
variables:
98+
min_mondoo_version: 9.0.0
9399
min_mondoo_version: latest
94100
platform:
95101
name:
@@ -132,6 +138,14 @@ resources:
132138
platform:
133139
name:
134140
- terraform-plan
141+
terraform.plan.variable:
142+
fields:
143+
name: {}
144+
value: {}
145+
min_mondoo_version: 9.0.0
146+
platform:
147+
name:
148+
- terraform-plan
135149
terraform.settings:
136150
fields:
137151
backend:

providers/terraform/resources/tfplan.go

+33
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ func initTerraformPlan(runtime *plugin.Runtime, args map[string]*llx.RawData) (m
4040

4141
args["formatVersion"] = llx.StringData(plan.FormatVersion)
4242
args["terraformVersion"] = llx.StringData(plan.TerraformVersion)
43+
args["applyable"] = llx.BoolData(plan.Applyable)
44+
args["errored"] = llx.BoolData(plan.Errored)
45+
args["variables"] = llx.ArrayData(
46+
variablesToArrayInterface(runtime, plan.Variables),
47+
types.Resource("terraform.plan.variables"),
48+
)
4349

4450
return args, nil, nil
4551
}
@@ -153,6 +159,11 @@ func (t *mqlTerraformPlanConfiguration) id() (string, error) {
153159
return "terraform.plan.configuration", nil
154160
}
155161

162+
func (t *mqlTerraformPlanVariable) id() (string, error) {
163+
id := t.Name
164+
return "terraform.plan.variable/name/" + id.Data, nil
165+
}
166+
156167
type PlanConfiguration struct {
157168
ProviderConfig map[string]json.RawMessage `json:"provider_config"`
158169
RootModule struct {
@@ -223,3 +234,25 @@ func (t *mqlTerraformPlanConfiguration) resources() ([]interface{}, error) {
223234
}
224235
return res, nil
225236
}
237+
238+
func variablesToArrayInterface(runtime *plugin.Runtime, variables connection.Variables) []interface{} {
239+
var list []interface{}
240+
for k, v := range variables {
241+
var value interface{}
242+
err := json.Unmarshal(v.Value, &value)
243+
if err != nil {
244+
continue
245+
}
246+
variable, err := CreateResource(runtime, "terraform.plan.variable", map[string]*llx.RawData{
247+
"name": llx.StringData(k),
248+
"value": llx.AnyData(value),
249+
})
250+
if err != nil {
251+
continue
252+
}
253+
254+
list = append(list, variable)
255+
}
256+
257+
return list
258+
}

0 commit comments

Comments
 (0)