Skip to content

feat(misconf): export raw Terraform data to Rego #8741

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

Merged
merged 6 commits into from
May 27, 2025

Conversation

nikpivkin
Copy link
Contributor

@nikpivkin nikpivkin commented Apr 16, 2025

Description

Related issues

Usage examples

Example config:

resource "aws_s3_bucket" "test" {
  bucket = "test"
  tags = {
    Environment = "Production"
    Project     = "ProjectX"
  }
}

variable "password" {}

resource "aws_db_instance" "test" {
  instance_class       = "db.t3.micro"
  password             = var.password
}

data "aws_secretsmanager_random_password" "test" {}

Output:

 (MEDIUM): The resource "aws_s3_bucket" is missing required tags: {"Owner"}
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════


See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 main.tf:3-6
   via main.tf:1-7 (aws_s3_bucket.test)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1   resource "aws_s3_bucket" "test" {
   2     bucket = "test"
   3 ┌   tags = {
   4 │     Environment = "Production"
   5 │     Project     = "ProjectX"
   6 └   }
   7   }
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


 (MEDIUM): The data source "aws_secretsmanager_random_password" has an ephemeral equivalent and should be replaced with it
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════


See https://developer.hashicorp.com/terraform/language/resources/ephemeral#ephemeral-resources
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 main.tf:16
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  16 [ data "aws_secretsmanager_random_password" "test" {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


 (HIGH): "password" is not a write-only attribute. Use the write-only alternative "password_wo"
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════


See https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 main.tf:13
   via main.tf:11-14 (aws_db_instance.test)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  11   resource "aws_db_instance" "test" {
  12     instance_class       = "db.t3.micro"
  13 [   password             = var.password
  14   }
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Checks:

required_tags.rego

# METADATA
# title: Resources should have required tags
# related_resources:
# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging
# custom:
#   id: TF_001
#   short_code: required-tags
#   severity: MEDIUM
#   input:
#     selector:
#     - type: terraform-raw
package user. tf_required_tags

import rego.v1

required_tags := {"Environment", "Owner", "Project"}

resources_to_check := {"aws_s3_bucket"}

deny contains res if {
	some block in input.modules[_].blocks
	block.kind == "resource"
	block.type in resources_to_check
	tags := block.attributes.tags

	used_tags := {k | some k, _ in tags.value}
	missed_tags := required_tags - used_tags
	count(missed_tags) > 0
	res := result.new(
		sprintf("The resource %q is missing required tags: %v", [block.type, missed_tags]),
		tags,
	)
}

ephemeral_data_sources.rego

# METADATA
# title: Use ephemeral resources when data source has an equivalent
# related_resources:
# - https://developer.hashicorp.com/terraform/language/resources/ephemeral#ephemeral-resources
# custom:
#   id: TF_002
#   short_code: ephemeral_data_sources
#   severity: MEDIUM
#   input:
#     selector:
#     - type: terraform-raw
package user.tf_eph_data_sources

import rego.v1

data_sources_to_check := {
	"aws_secretsmanager_random_password",
	"aws_secretsmanager_secret_version",
}

deny contains res if {
	some block in input.modules[_].blocks
	block.kind == "data"
	block.type in data_sources_to_check
	res := result.new(
		sprintf("The data source %q has an ephemeral equivalent and should be replaced with it", [block.type]),
		block,
	)
}

write_only_arguments.rego

# METADATA
# title: Use write-only arguments
# related_resources:
# - https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments
# custom:
#   id: TF_003
#   short_code: wo-arguments
#   severity: HIGH
#   input:
#     selector:
#     - type: terraform-raw
package user.tf_wo_args

import rego.v1

resources_to_check := {
	"aws_db_instance": ["password", "password_wo"],
	"aws_docdb_cluster": ["master_password", "master_password_wo"],
}

deny contains res if {
	some block in input.modules[_].blocks
	block.kind == "resource"
	[original, alternative] := resources_to_check[block.type]
	attr := block.attributes[original]
	res := result.new(
		sprintf("%q is not a write-only attribute. Use the write-only alternative %q", [original, alternative]),
		attr,
	)
}

Checklist

  • I've read the guidelines for contributing to this repository.
  • I've followed the conventions in the PR title.
  • I've added tests that prove my fix is effective or that my feature works.
  • I've updated the documentation with the relevant information (if needed).
  • I've added usage information (if the PR introduces new options)
  • I've included a "before" and "after" example to the description (if the PR is a user interface change).

@nikpivkin
Copy link
Contributor Author

nikpivkin commented Apr 16, 2025

@simar7 This is a POC of an idea that has been discussed here.

What can be improved:

  • Export data directly as OPA values, which will skip the step of marshaling to json and unmarshaling to OPA values and improve performance
  • Export references from expressions to detect relationships between blocks. Example uses:
    • Find related blocks for complex checking.
    • Find non-ephemeral input variables that are used in write-only attributes.

Related:

Comment on lines 56 to 68
e.logger.Debug("Scan raw Terraform data")
results2, err := e.regoScanner.ScanInput(ctx, types.SourceTerraform, rego.Input{
Contents: terraform.ExportModules(modules),
Path: basePath,
})
if err != nil {
e.logger.Error("Failed to scan raw Terraform data",
log.FilePath(basePath), log.Err(err))
} else {
results = append(results, results2...)
}

e.logger.Debug("Finished applying checks")
Copy link
Member

Choose a reason for hiding this comment

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

Can we somehow put this behind a flag? Doing this each time even when raw terraform is not needed might be a bit wasteful and may impact performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done abe2422

@nikpivkin nikpivkin requested a review from DmitriyLewen as a code owner May 15, 2025 10:04
Signed-off-by: nikpivkin <[email protected]>
@nikpivkin nikpivkin removed the request for review from DmitriyLewen May 15, 2025 11:49
Copy link
Member

@simar7 simar7 left a comment

Choose a reason for hiding this comment

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

lgtm, left a couple of comments.

@nikpivkin nikpivkin requested a review from simar7 May 24, 2025 05:49
@simar7 simar7 added this pull request to the merge queue May 27, 2025
Merged via the queue into aquasecurity:main with commit aaecc29 May 27, 2025
17 checks passed
@nikpivkin nikpivkin deleted the tf-raw branch May 30, 2025 04:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat(misconf): export raw Terraform data to Rego feat(misconf): Support additional terraform attributes
2 participants