Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ add terraform.plan.variables #5113

Merged
merged 4 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion providers/terraform/connection/hcl_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestLoadHclBlocks(t *testing.T) {
require.NotNil(t, parser)
tfVars := tf.TfVars()
assert.Equal(t, 2, len(tfVars))
assert.Equal(t, 5, len(parser.Files()))
assert.Equal(t, 6, len(parser.Files()))
}

func TestLoadTfvars(t *testing.T) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions providers/terraform/connection/testdata/dynamic_block/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
variable "environment" {
type = string
}

locals {
set = {
set1 = {
name = "service.type"
value = "LoadBalancer"
}
set2 = {
name = "replicaCount"
value = "2"
}
set3 = {
name = "ingress.enabled"
value = "true"
}
set4 = {
name = "environment"
value = var.environment
}
}
}
resource "helm_release" "nginx" {
name = "my-nginx"
chart = "nginx"
repository = "https://charts.bitnami.com/bitnami"
version = "13.2.12"
dynamic "set" {
for_each = local.set
content {
name = set.value.name
value = set.value.value
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +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}
8 changes: 5 additions & 3 deletions providers/terraform/connection/tfplan.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ type Plan struct {
PriorState json.RawMessage `json:"prior_state,omitempty"`
Configuration json.RawMessage `json:"configuration,omitempty"`
PlannedValues plannedStateValues `json:"planned_values,omitempty"`
Variables variables `json:"variables,omitempty"`
Variables Variables `json:"variables,omitempty"`
ResourceChanges []ResourceChange `json:"resource_changes,omitempty"`
ResourceDrift []ResourceChange `json:"resource_drift,omitempty"`
RelevantAttributes []resourceAttr `json:"relevant_attributes,omitempty"`
OutputChanges map[string]change `json:"output_changes,omitempty"`
Applyable bool `json:"applyable,omitempty"`
Errored bool `json:"errored,omitempty"`
}

type plannedStateValues struct {
Expand Down Expand Up @@ -83,9 +85,9 @@ type resource struct {
SensitiveValues json.RawMessage `json:"sensitive_values,omitempty"`
}

type variables map[string]*variable
type Variables map[string]*Variable

type variable struct {
type Variable struct {
Value json.RawMessage `json:"value,omitempty"`
}

Expand Down
14 changes: 14 additions & 0 deletions providers/terraform/connection/tfplan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,17 @@ func TestTfplan(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, plan)
}

func TestTfWithDynamicBlocksAndVariables(t *testing.T) {
data, err := os.ReadFile("./testdata/dynamic_block/tfplan.json")
require.NoError(t, err)

var plan Plan
err = json.Unmarshal(data, &plan)
require.NoError(t, err)
assert.NotNil(t, plan)
_, ok := plan.Variables["environment"] // this exist in the testdata
assert.True(t, ok)
assert.True(t, plan.Applyable)
assert.False(t, plan.Errored)
}
14 changes: 14 additions & 0 deletions providers/terraform/resources/terraform.lr
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@
terraformVersion string
// Resource changes
resourceChanges() []terraform.plan.resourceChange
// Variables used to generate the Terraform plan
variables []terraform.plan.variable
// Whether `apply` is valid for the plan
applyable bool

Check warning on line 173 in providers/terraform/resources/terraform.lr

View workflow job for this annotation

GitHub Actions / Run spell check

`applyable` is not a recognized word. (unrecognized-spelling)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe canApply?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed and agreed that applyable is coming from Terraform plan output itself, so we will adopt the same name for consistency. cc @misterpantz

// Whether the plan errored
errored bool
}

// Terraform plan configuration
Expand All @@ -177,6 +183,14 @@
resources() []dict
}

// Terraform plan variable
terraform.plan.variable @defaults("name value") {
// Variable name
name string
// Variable value
value dict
}

// Terraform plan resource change
terraform.plan.resourceChange @defaults("type name") {
// Resource address
Expand Down
112 changes: 112 additions & 0 deletions providers/terraform/resources/terraform.lr.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions providers/terraform/resources/terraform.lr.manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,15 @@ resources:
title: Display all loaded Terraform modules
terraform.plan:
fields:
applyable:
min_mondoo_version: 9.0.0
errored:
min_mondoo_version: 9.0.0
formatVersion: {}
resourceChanges: {}
terraformVersion: {}
variables:
min_mondoo_version: 9.0.0
min_mondoo_version: latest
platform:
name:
Expand Down Expand Up @@ -132,6 +138,14 @@ resources:
platform:
name:
- terraform-plan
terraform.plan.variable:
fields:
name: {}
value: {}
min_mondoo_version: 9.0.0
platform:
name:
- terraform-plan
terraform.settings:
fields:
backend:
Expand Down
33 changes: 33 additions & 0 deletions providers/terraform/resources/tfplan.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ func initTerraformPlan(runtime *plugin.Runtime, args map[string]*llx.RawData) (m

args["formatVersion"] = llx.StringData(plan.FormatVersion)
args["terraformVersion"] = llx.StringData(plan.TerraformVersion)
args["applyable"] = llx.BoolData(plan.Applyable)
args["errored"] = llx.BoolData(plan.Errored)
args["variables"] = llx.ArrayData(
variablesToArrayInterface(runtime, plan.Variables),
types.Resource("terraform.plan.variables"),
)

return args, nil, nil
}
Expand Down Expand Up @@ -153,6 +159,11 @@ func (t *mqlTerraformPlanConfiguration) id() (string, error) {
return "terraform.plan.configuration", nil
}

func (t *mqlTerraformPlanVariable) id() (string, error) {
id := t.Name
return "terraform.plan.variable/name/" + id.Data, nil
}

type PlanConfiguration struct {
ProviderConfig map[string]json.RawMessage `json:"provider_config"`
RootModule struct {
Expand Down Expand Up @@ -223,3 +234,25 @@ func (t *mqlTerraformPlanConfiguration) resources() ([]interface{}, error) {
}
return res, nil
}

func variablesToArrayInterface(runtime *plugin.Runtime, variables connection.Variables) []interface{} {
var list []interface{}
for k, v := range variables {
var value interface{}
err := json.Unmarshal(v.Value, &value)
if err != nil {
continue
}
variable, err := CreateResource(runtime, "terraform.plan.variable", map[string]*llx.RawData{
"name": llx.StringData(k),
"value": llx.AnyData(value),
})
if err != nil {
continue
}

list = append(list, variable)
}

return list
}
Loading