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!