How to select all values?

hi,

i have the issue that i am not sure how to choose all values possibly for the tags keys.
here is my sentinel policy that doesn’t work for my expectation:

import "tfplan-functions" as plan
import "azure-functions" as azure

param mandatory_tag default [
	[],
]

param resource_types default [
  "azurerm_windows_virtual_machine",
]

allAzureResourcesWithStandardTags =
                        azure.find_resources_with_standard_tags(resource_types)

violatingAzureResources = plan.filter_attribute_does_not_match_regex(
  allAzureResourcesWithStandardTags,
  "tags.[all]",
  "^[a-zA-Z0-9]+$",
  true,
)

main = rule {
  length(violatingAzureResources["messages"]) is 0
}

the purpose is choose all values from possible tags and check them with the regex [a-zA-Z0-9]
but, i can’t find any document for wildcard mark.

@jaesukdo1986

i am not sure how to choose all values possibly for the tags keys

You can use the values function to return all values within the tags map. Here is an example of how you can use this function:

data = { "a": 2, "b": 3 }
values(data)       // [3, 2]

You may find it difficult to use this in the policy example you have shared as the filter_attribute_does_not_match_regex function expects the attr value to be a string. I recommend that you write an idiomatic policy and refrain from using the tfplan-functions library.

if don’t use the tfplan-functions library, so what function i can use to check the tags values with the regex ?

here is my new policy

import "strings"

tag = "tag_key"
data = {
  "tag_key": "tag_value",
}

tagValue = data[tag]
result = strings.regexMatch(tagValue, "^[a-zA-Z0-9]+$")

main = rule {
  length(result["messages"]) is 0
}

but got error because there is no function name “regexMatch”

Hi @jaesukdo1986,

Regex matching is a core part of the Sentinel specification and is done through the matches operator.

In your example, you would use tagValue matches "^[a-zA-Z0-9]+$"

if we don’t use tfplan so how can it check from terraform plan for all the tags values in there ?

how can i turn a map into list ?

import "strings"
import "tfplan-functions" as plan
import "azure-functions" as azure

param resource_types default [
  "azurerm_windows_virtual_machine",
]

allAzureResourcesWithStandardTags =
  azure.find_resources_with_standard_tags(resource_types)

tags_values = values(allAzureResourcesWithStandardTags)

tags_values_string = strings.join(tags_values_list, ", ")

result = plan.filter_attribute_does_not_match_regex(allAzureResourcesWithStandardTags, tags_values_string, "^[a-zA-Z0-9]+$", true)

main = rule {
  length(result["messages"]) is 0
}

because the tags_values is a map, i want to turn it into a list to be used with strings function later on

Loop through the map variable and write values to new list variable.

tags =
for tags_values as _, a {
tag = a.(whatever the path is for tags)
append(tags, tag)
}

@kardell2006g
i got error : error calling function “join”: invalid type for joining: undefined
my entire code:

import "strings"
import "tfplan-functions" as plan
import "azure-functions" as azure

param resource_types default [
  "azurerm_windows_virtual_machine",
]

allAzureResourcesWithStandardTags =
  azure.find_resources_with_standard_tags(resource_types)

tags_values = values(allAzureResourcesWithStandardTags)

tags = []
for tags_values as _, a {
  tag = a.tags
  append(tags, tag)
}

tags_values_string = strings.join(tags, ", ")

result = plan.filter_attribute_does_not_match_regex(allAzureResourcesWithStandardTags, tags_values_string, "^[a-zA-Z0-9]+$", true)

main = rule {
  length(result["messages"]) is 0
}

@jaesukdo1986 is there a reason why you don’t want to fallback to an all or any expression as shown in the following?

import "azure-functions" as azure

param mandatory_tag default [
	[],
]

param resource_types default [
  "azurerm_virtual_machine",
]

regex = "^[a-zA-Z0-9]+$"

resources = azure.find_resources_with_standard_tags(resource_types)

verify_tag_values = rule {
	all resources as _, resource {
  	all values(resource.change.after.tags) as tag {
    	tag matches regex
    }
  }
}

main = rule {
  verify_tag_values
}

As for the error you are getting with strings.join, it looks like you may have a typo:

// Currently
tags_values_string = strings.join(tags, ", ")

// Change to
tags_values_string = strings.join(tags, ",")

You may want to revisit this policy logic as tag_values contains a list of resources and therefore a.tags will be undefined. I think that a.change.after.tags is the correct reference.

tags_values = values(allAzureResourcesWithStandardTags)

tags = []
for tags_values as _, a {
  tag = a.tags
  append(tags, tag)
}

As always, post a working policy example in the playground with mock data and the function libraries and folks will be in a better position to assist.

@hcrhall thanks for your suggestion for “all” expression. it works like a charm, but can’t print out what tags values have an issue with. Any ideas is appreciated!

@jaesukdo1986 you could try the following. It’s not a very elegant solution as you ideally want to use a logging function but I think you get the idea:

import "azure-functions" as azure

param mandatory_tag default [
	[],
]

param resource_types default [
  "azurerm_virtual_machine",
]

regex = "^[a-zA-Z0-9]+$"

resources = azure.find_resources_with_standard_tags(resource_types)

violations = filter resources as _, resource {
	any resource.change.after.tags as tag, value {
  	value not matches regex
	}
}

if violations is not empty {
	for violations as _, v {
    print("Resource", v.address, "has tags", keys(v.change.after.tags), "which violates the requirements of this policy.")
  }
}

main = rule {
  violations
}
1 Like

that’s work, not 100% for the print output, but at least it can pop out the tags from where it placed! Thank you so much!