I want to set up a Terraform module that assigns a policy to an Azure resource according to Terraforms policy assignment example.
In order to assign the allowed locations policy, I want to pass the list of allowed locations as a list of strings from the variables.tf file to the main.tf, where the assignment is executed.
#Allowed Locations Policy Assignment
resource "azurerm_policy_assignment" "allowedlocations" {
name = "allowed-locations"
scope = var.scope_allowedlocations
policy_definition_id = var.policy_allowedlocations.id
description = "This policy enables you to restrict the locations."
display_name = "Allowed Locations"
parameters = <<PARAMETERS
{
"allowedLocations": {
"value": ${var.listofallowedlocations}
}
}
PARAMETERS
}
# Scope of the Allowed Locations policy
variable "scope_allowedlocations" {
description = "The scope of the allowed locations assignment."
default = "Subscription"
}
# Scope of the Allowed Locations policy
variable "policy_allowedlocations" {
description = "The allowed locations policy (created by the policy-define module)."
default = "default"
}
# List of the Allowed Locations
variable "listofallowedlocations" {
type = list(string)
description = "The allowed locations list."
default = [ "West Europe", "North Europe", "East US" ]
}
Executing with terraform plan
leads to the following error:
Error: Invalid template interpolation value
on modules/policy-assign/main.tf line 16, in resource "azurerm_policy_assignment" "allowedlocations":
12:
13:
14:
15:
16: "value": ${var.listofallowedlocations}
17:
18:
19:
|----------------
| var.listofallowedlocations is list of string with 3 elements
Cannot include the given value in a string template: string required.
Thus, I don't know how to exactly pass the list from the variables file to the PARAMETERS section of the policy assignment resource. In Terraforms policy assignment example the list is directly inline coded in the PARAMETERS section and it works. But there is no passing of variables...:
parameters = <<PARAMETERS
{
"allowedLocations": {
"value": [ "West Europe" ]
}
}
PARAMETERS
tf file format will be automatically loaded during operations. Create a variables file, for example, variables.tf and open the file for edit. Add the below variable declarations to the variables file. Replace the SSH key private file path and the public key with our own.
Using the -var Command-line Flag The -var flag is used to pass values for Input Variables to Terraform commands. This flag can be used with both of the Terraform plan and apply commands. The argument passed to the -var flag is a string surrounded by either single or double quotes.
To do so, simply set the environment variable in the format TF_VAR_<variable name> . The variable name part of the format is the same as the variables declared in the variables.tf file. For example, to set the ami variable run the below command to set its corresponding value.
» jsonencode Function jsonencode encodes a given value to a string using JSON syntax. The JSON encoding is defined in RFC 7159. This function maps Terraform language values to JSON values in the following way: Terraform type. JSON type.
jsonencode encodes a given value to a string using JSON syntax. The JSON encoding is defined in RFC 7159. This function maps Terraform language values to JSON values in the following way: list (...) set (...) tuple (...) map (...) object (...)
This function maps Terraform language values to JSON values in the following way: list (...) set (...) tuple (...) map (...) object (...)
In Terraforms policy assignment example the list is directly inline coded in the PARAMETERS section and it works. But there is no passing of variables...: When you are interpolating a value into a string that value must itself be convertible to string, or else Terraform cannot join the parts together to produce a single string result.
Terraform provides a ready-to-use function for JSON because its a common need. If you need more control over the above decisions then you'd need to use more complex techniques to describe to Terraform what you need.
When you are interpolating a value into a string that value must itself be convertible to string, or else Terraform cannot join the parts together to produce a single string result.
There are a few different alternatives here, with different tradeoffs.
The option I personally would choose here is to not use the <<PARAMETERS
syntax at all and to just build that entire value using jsonencode
:
parameters = jsonencode({
allowedLocations = {
value = var.listofallowedlocations
}
})
This avoids the need for your configuration to deal with any JSON syntax issues at all, and (subjectively) therefore makes the intent clearer and future maintenence easier.
In any situation where the result is a single valid JSON value, I would always choose to use jsonencode
rather than the template language. I'm including the other options below for completeness in case a future reader is trying to include a collection value into a string template that isn't producing JSON.
A second option is to write an expression to tell Terraform a way to convert your list value into a string value in a suitable format. In your case you wanted JSON and so jsonencode
again would probably be the most suitable choice:
parameters = <<PARAMETERS
{
"allowedLocations": {
"value": ${jsonencode(var.listofallowedlocations)}
}
}
PARAMETERS
In other non-JSON situations, when the result is a simple list of strings, the join
function can be useful to just concatenate all of the strings together with a fixed delimiter.
Any of Terraform's functions that produce a single string as a result is a candidate here. The ones under "String Functions" and "Encoding Functions" are the most likely choices.
Finally, for situations where the mapping from the collection value to the resulting string is something custom that no standard function can handle, you can use the template repetition syntax:
parameters = <<CONFIG
%{ for port in var.ports ~}
listen 127.0.0.1:${port}
%{ endfor ~}
CONFIG
In this case, Terraform will evaluate the body of the repetition construct once for each element in var.ports
and concatenate all of the results together in order to produce the result. You can generate all sorts of textual output formats using this approach, though if the template gets particularly complicated it could be better to factor it out into a separate file and use the templatefile
function to evaluate it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With