From 6b69bbf7df25aba248ef6cb5eac1fb970f41781b Mon Sep 17 00:00:00 2001 From: worm Date: Sat, 25 Nov 2023 20:28:18 -0800 Subject: [PATCH] initial commit --- README.md | 11 +++++++++++ example-plan.json | 1 + main.raku | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 README.md create mode 100644 example-plan.json create mode 100644 main.raku diff --git a/README.md b/README.md new file mode 100644 index 0000000..9f878c3 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +The goal of this product is to evaluate the contents of a Terraform (or OpenTofu) run and permit/deny it taking place. + +It will use the JSON `plan` representation as outlined here: +https://developer.hashicorp.com/terraform/internals/json-format#plan-representation + +Chiefly the `resource_changes` collection. + +The simple goal of at this stage is to allow someone to write very simple rules for Terraform compliance. + +For instance, "aws_instance must be of type t3.micro" +Initially, examples will be simple and static. diff --git a/example-plan.json b/example-plan.json new file mode 100644 index 0000000..834652f --- /dev/null +++ b/example-plan.json @@ -0,0 +1 @@ +{"format_version":"1.1","terraform_version":"1.3.7","variables":{"foo":{"value":"bar"}},"planned_values":{"root_module":{"resources":[{"address":"aws_ebs_volume.example","mode":"managed","type":"aws_ebs_volume","name":"example","provider_name":"registry.terraform.io/hashicorp/aws","schema_version":0,"values":{"availability_zone":"us-west-2a","final_snapshot":false,"multi_attach_enabled":null,"outpost_arn":null,"size":4,"tags":null,"tags_all":{"bazz":"Qux","foo":"bar","region":"us-west-2"},"timeouts":null},"sensitive_values":{"tags_all":{}}}]}},"resource_changes":[{"address":"aws_ebs_volume.example","mode":"managed","type":"aws_ebs_volume","name":"example","provider_name":"registry.terraform.io/hashicorp/aws","change":{"actions":["create"],"before":null,"after":{"availability_zone":"us-west-2a","final_snapshot":false,"multi_attach_enabled":null,"outpost_arn":null,"size":4,"tags":null,"tags_all":{"bazz":"Qux","foo":"bar","region":"us-west-2"},"timeouts":null},"after_unknown":{"arn":true,"encrypted":true,"id":true,"iops":true,"kms_key_id":true,"snapshot_id":true,"tags_all":{},"throughput":true,"type":true},"before_sensitive":false,"after_sensitive":{"tags_all":{}}}}],"configuration":{"provider_config":{"aws":{"name":"aws","full_name":"registry.terraform.io/hashicorp/aws","version_constraint":"4.53.0","expressions":{"default_tags":[{"tags":{"references":["var.foo"]}}]}}},"root_module":{"resources":[{"address":"aws_ebs_volume.example","mode":"managed","type":"aws_ebs_volume","name":"example","provider_config_key":"aws","expressions":{"availability_zone":{"constant_value":"us-west-2a"},"size":{"constant_value":4}},"schema_version":0}],"variables":{"foo":{"default":"bar"}}}}} diff --git a/main.raku b/main.raku new file mode 100644 index 0000000..6c49019 --- /dev/null +++ b/main.raku @@ -0,0 +1,21 @@ +use JSON::Fast; + +my $plan = from-json "example-plan.json".IO.slurp; +my $resource-changes = $plan; +my %required-tags = "foo" => ["bar", "buzz"]; + +# There's no reason to expect that non-managed resources will be needed. +# However, Sentinel policies often have this as a parameter +# .grep is Raku's 'filter' method: https://docs.raku.org/routine/grep +sub get-resource-type (@resource-list, Str $resource-type, Str $mode="managed") { + @resource-list.grep: { $_ eq $mode && $_ eq $resource-type } +} + + +sub check-tags ($resource, %required_tags) { + return True if %required_tags.grep($resource); # or $resource.change.after.tags eq "bar"; +} + +my @aws-ebs-volumes = get-resource-type($resource-changes, "aws_ebs_volume"); + +say @aws-ebs-volumes.grep: { check-tags($_, %required-tags) };