terraform count index and length - azure

Trying to create multiple private DNS zone
non prod --> Dev,QA,UAT.
prod --> Prd,DR
Resource should be created only if is_nonprod is set to 1. emphasized text(boolean value).
Idea is to use count twice in resource block : once for boolean and once for length function.
resource "azurerm_private_dns_zone" "example" {
count = var.is_nonprod ? 1 : 0 && length(var.env)
name = var.env[count.index].npr
resource_group_name = "examplerg"
}
variable file:
variable "env" {
description = "List of routes to be added to the route table"
default = []
type = list(map(any))
}
variable "is_nonprod " {
default = true
}
tfvars
env = [
{ npr = "qa" },
{ npr = "uat" },
{ npr = "dev" }
]
Error :
The true and false result expressions must have consistent types. The given
expressions are number and bool, respectively.
workaround :
resource "azurerm_private_dns_zone" "example" {
count = var.is_nonprod ? 1 : 0
count = length(var.env)
name = var.env[count.index].npr
resource_group_name = "examplerg"
}
Error :
The argument "count" was already set at main.tf:96,3-8. Each argument may be
set only once.

var.is_nonprod ? 1 : 0 && length(var.env) does not look like the logic you want here based on what you described in the question. It seems like you really want var.is_nonprod ? length(var.env) : 0, which is also syntactically valid. The && operator inputs and returns booleans, which are not valid as an input type to the count metaparameter. count takes a number as input (typically the number of resources you want to manage), and not true or false.

Seems like datatype conflicts. You can directly use count = var.is_nonprod ? length(var.env) : 0 and it should give you expected results.

Related

Using terraform's for_each with data that doesn't have a unique key

When using terraform's for_each you have to specify a unique id to be used as a way to link the generated recource with its source definition.
I'd like to use a natural index for this, rather than an arbitrary unique value. In this case I'm working with DNS, so the natural index would be the DNS record name (FQDN)... Only that isn't always unique; i.e. you can have multipe A records for example.com to allow load balancing, or you may have multiple TXT records for providing verification to multiple vendors.
Is there a way to combine the natural index with a calculated value to provide a unique value; e.g. so we have the natural index followed by a 1 if it's the first time this value's seen, a 2 for the first duplicate, etc?
Specific Requirement / Context
I'm working on migrating our DNS records to be managed via IaC using Terraform/Terragrunt (this is for scenarios where the entries are manually managed, rather than those where the related service is also under IaC).
I'm hoping to hold the record data in CSVs (or similar) to avoid those managing the records day to day from requiring familiarity with TF/TG; instead allowing them to just update the data, and have the pipeline take care of the rest.
The CSV format would be something like this:
myId
RecordName
Type
Value
1
A
1.2.3.4
2
A
2.3.4.5
3
test
A
3.4.5.6
4
test
A
4.5.6.7
5
www
cname
example.com
Note: I'm considering each DNS Zone would have a folder with its name, and a CSV formatted as above which gives the records for that zone; so the above would be in the /example.com/ folder, and thus we'd have 2 A records for example.com, 2 for test.example.com and one CName for www.example.com which pointed to example.com.
locals {
instances = csvdecode(file("myDnsRecords.csv"))
}
resource aws_route53_zone zone {
name = var.domainname
provider = aws
}
resource aws_route53_record route53_entry {
for_each = {for inst in local.instances : inst.myId => inst}
name = "${each.value.RecordName}${each.value.RecordName == "" ? "" : "."}${var.domainname}"
type = each.value.Type
zone_id = aws_route53_zone.zone.zone_id
ttl = 3600
records = [each.value.Value]
}
I don't want the myId column though; as that doesn't add value / has no real relationship to the records; so if we were to remove/insert a record early in the CSV and renumber the following records it would result in a number of changes being required to records which hadn't really changed, just because their related "index" had changed.
I also don't want those working with these CSVs to have to manually manage such fields; i.e. I could provide another column and ask that they populate this as below... but that's asking for human error and adding complexity:
myId
RecordName
Type
Value
1
A
1.2.3.4
2
A
2.3.4.5
test1
test
A
3.4.5.6
test2
test
A
4.5.6.7
www1
www
cname
example.com
Question
Is there a way I can use a for_each loop with CSV data such as below, whilst working around the unique constraint?
RecordName
Type
Value
A
1.2.3.4
A
2.3.4.5
test
A
3.4.5.6
test
A
4.5.6.7
www
cname
example.com
You can add unique keys to the data structure:
locals {
instances = csvdecode(file("myDnsRecords.csv"))
instance_map = zipmap(range(0,length(local.instances)), local.instances)
}
resource "..." "..." {
for_each = local.instance_map
...
}
Terraform's for expressions when constructing a mapping have a "grouping mode" where it allows duplicate keys in return for the values of the map all being lists of potentially-multiple values that all had the same key.
I would therefore start by using that to project the CSV data into a map(list(map(string))) value where the keys are build from the record name and type, like this:
locals {
records_raw = csvdecode(file("${path.module}/myDnsRecords.csv"))
records_grouped = tomap({
for row in local.records_raw :
"${row.RecordName} ${row.RecordType}" => row...
})
}
The resulting data structure would be shaped like this:
records_grouped = tomap({
" A" = tolist([
{ RecordName = "", Type = "A", Value = "1.2.3.4" },
{ RecordName = "", Type = "A", Value = "2.3.4.5" },
])
"test A" = tolist([
{ RecordName = "test", Type = "A", Value = "3.4.5.6" },
{ RecordName = "test", Type = "A", Value = "4.5.6.7" },
])
"www CNAME" = tolist([
{ RecordName = "www", Type = "CNAME", Value = "example.com" },
])
})
Collecting the records with common keys into lists means that we now have a list index for each one that's unique only within the records with the common key.
So now we can project this one more time into a flat map of maps (map(map(string))) by incorporating those list indices into the map keys:
locals {
records = tomap(merge([
for group_key, group in local.records_grouped : {
for idx, record in group :
"${group_key} ${idx}" => group
}
]...))
}
This should produce a data structure like the following:
records = tomap({
" A 0" = { RecordName = "", Type = "A", Value = "1.2.3.4" }
" A 1" = { RecordName = "", Type = "A", Value = "2.3.4.5" }
"test A 0" = { RecordName = "test", Type = "A", Value = "3.4.5.6" }
"test A 1" = { RecordName = "test", Type = "A", Value = "4.5.6.7" }
"www CNAME 0" = { RecordName = "www", Type = "CNAME", Value = "example.com" }
})
That data structure is suitably-shaped for a for_each expression, so finally:
resource "aws_route53_record" "example" {
for_each = local.records
name = "${each.value.RecordName}${each.value.RecordName == "" ? "" : "."}${var.domainname}"
type = each.value.Type
zone_id = aws_route53_zone.zone.zone_id
ttl = 3600
records = [each.value.Value]
}
This will produce instance unique instance keys for each entry in the source CSV file while keeping all of the distinct (name, type) pairs separated so that you can add new ones without disturbing any existing records:
aws_route53_record.example[" A 0"]
aws_route53_record.example[" A 1"]
aws_route53_record.example["test A 0"]
...etc
You mentioned wanting a separate instance for each row in your CSV file but I also wanted to note that the aws_route53_record resource type is already designed to manage multiple records with the same name and type together, and so I think it would actually be fine to leave the records grouped together. (The name `aws_route53_record is a bit of a misnomer because each instance of this resource type manages a record set, not just a single record.)
Here's a variation that works that way:
locals {
records_raw = csvdecode(file("${path.module}/myDnsRecords.csv"))
record_groups = tomap({
for row in local.records_raw :
"${row.RecordName} ${row.RecordType}" => row...
})
recordsets = tomap({
for group_key, group in local.record_groups : group_key => {
name = group[0].Name
type = group[0].Type
values = group[*].Value
}
})
}
resource "aws_route53_record" "example" {
for_each = local.recordsets
name = "${each.value.name}${each.value.name == "" ? "" : "."}${var.domainname}"
type = each.value.type
zone_id = aws_route53_zone.zone.zone_id
ttl = 3600
records = each.value.values
}
This time the final map has one element per recordset instead of one element per record, after grouping all of the individual records together using their names and types. Now you don't need any synthetic indices at all because the name and type pair is the natural unique identifier for a Route53 recordset.
Does this get what you're looking for?
Dataset
RecordName,Type,Value
,A,1.2.3.4
,A,2.3.4.5
test,A,3.4.5.6
test,A,4.5.6.7
www,cname,example.com
main.tf
locals {
records = [for pref in {for _, key in distinct([for i, v in csvdecode(file("myDnsRecords.csv")): v.RecordName]): key => [for r in csvdecode(file("myDnsRecords.csv")): r if key == r.RecordName]}: {for i, r in pref: ("${r.RecordName}_${i}") => r}]
}
output "test" {
value = local.records
}
Output
Changes to Outputs:
+ test = [
+ {
+ _0 = {
+ RecordName = ""
+ Type = "A"
+ Value = "1.2.3.4"
}
+ _1 = {
+ RecordName = ""
+ Type = "A"
+ Value = "2.3.4.5"
}
},
+ {
+ test_0 = {
+ RecordName = "test"
+ Type = "A"
+ Value = "3.4.5.6"
}
+ test_1 = {
+ RecordName = "test"
+ Type = "A"
+ Value = "4.5.6.7"
}
},
+ {
+ www_0 = {
+ RecordName = "www"
+ Type = "cname"
+ Value = "example.com"
}
},
]
You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

how to dynamically pass parameter to terraform module and run a module based on a condition

I have a requirement to run modules on conditional basis and also to build the parameter list for the called module dynamically from map variable.
My main.tf file looks like below
provider "aws" {
region = var.region
}
module "CreateResource1" {
source = "./modules/CreateResource1"
ProductName = "Test1"
ProductColour = "Red"
ProductShape = "Hexagone"
}
module "CreateResource2" {
source = "./modules/CreateResource2"
ProductName = "Test2"
ProductType = "xyz"
ProductModel = "abc"
ProductPrice = ""
}
the requirement is a conditional variable module_name which user will pass and based on that i need to execute any one of the modules as per condition.
Also instead of passing the parameter like ProductName, ProductColour, ProductShape as a separate variables the user will be passing them as a dict variable and i would like to build the variable dynamically with both key and value for the module.
Input from user will be like below.
module "Resource" {
module_name = "CreateResource1"
parameters = {
ProductName = "Test1"
ProductColour = "Red"
ProductShape = "Hexagone"
}
}
based on above inputs i need to select the module to run and build parameters for the module.
as i am new to terraform any leads will be appreciated.
Terraform Version used is 1.0.0
Regards
If possible, I would just change the modules to accept a map like the calling module:
(Disclaimer: partial, untested example)
modules/resource/main.tf
variable "module_name" {
type = string
}
variable "parameters" {
type = map
}
module "CreateResource1" {
source = "../CreateResource1"
count = var.module_name == "CreateResource1" ? 1 : 0
parameters = var.parameters
}
module "CreateResource2" {
source = "../CreateResource2"
count = var.module_name == "CreateResource2" ? 1 : 0
parameters = var.parameters
}
main.tf
module "Resource" {
source = "./modules/resource"
module_name = "CreateResource1"
parameters = {
ProductName = "Test1"
ProductColour = "Red"
ProductShape = "Hexagone"
}
}
The modules that are called would need a small modification to define a parameter input variable of type map and then the values could be accessed in the code using a lookup function (e.g. lookup(var.parameters, "ProductName", "") - note the 3rd param allows you to specify a default if the element does not exist in the map. There is no reason why that value cannot be taken from an input variable, so if you don't want to hard code it here, you could for example, pass it in from a default set in the calling module, and passed into all/some of the children). So for example:
modules/CreateResource1/main.tf (Partial example)
variable "parameters" {
type = map
}
resource "some_resource_type" "some_resource_name" {
ProductName = lookup(var.parameters, "ProductName", "Product1")
ProductColour = lookup(var.parameters, "ProductColour", "Red")
ProductShape = lookup(var.parameters, "ProductShape", "Circle")
}
So in this example, the resource created by CreateResource1 requires 3 params. If they exist in the map that is passed by module Resource then they will be used, and for any that are not present in the map that is passed in, the defaults will be used (in this case "Product1", "Red" & "Circle").

Terraform deploy module with a condition

what am I trying to accomplish?
I am trying to deploy a terraform module only if a input variable is greater than 0
what have I tried?
var.blocks > 0 ? module.diagnostic_mssql_db.target_resource_id : 1,
what was the result?
Error: Unsupported attribute
on ..\mssql-tf-docs\modules\db\main.tf line 27, in module "diagnostic_mssql_db":
27: "Blocks" = var.blocks > 0 ? module.diagnostic_mssql_db.target_resource_id : 1,
|----------------
| module.diagnostic_mssql_db is object with no attributes
This object does not have an attribute named "target_resource_id".
where is your example code?
notice in the part below "main.tf"
section "ds_log_api_endpoints"
main.tf
# SQL Server Database within a SQL Server Server
resource "azurerm_mssql_database" "db" {
name = var.name
server_id = var.server_id
collation = var.collation
license_type = var.license_type
sku_name = var.sku_name
max_size_gb = var.max_size_gb
zone_redundant = var.zone_redundant
read_scale = var.read_scale
read_replica_count = var.read_replica_count
auto_pause_delay_in_minutes = var.sku_name == "GP_S_Gen5_1" ? var.auto_pause_delay_in_minutes : 0
min_capacity = var.sku_name == "GP_S_Gen5_1" ? var.min_capacity : 0
}
# Diagnostic setting
module "diagnostic_mssql_db" {
source = "github.com/faraday23/terraform-azurerm-monitor-diagnostic-setting.git"
storage_account = var.sa_storage_account
sa_resource_group = var.storage_account_resource_group
target_resource_id = azurerm_mssql_database.db.id
target_resource_name = azurerm_mssql_database.db.name
ds_allmetrics_rentention_days = var.metric
ds_log_api_endpoints = { "AutomaticTuning" = var.automatic_tuning > 0 ? module.diagnostic_mssql_db.target_resource_id : 1
"Blocks" = var.blocks > 0 ? module.diagnostic_mssql_db.target_resource_id : 1,
"DatabaseWaitStatistics" = var.database_wait_statistics > 0 ? module.diagnostic_mssql_db.target_resource_id : 1,
"Deadlocks" = var.deadlocks > 0 ? module.diagnostic_mssql_db.target_resource_id : 1,
"Errors" = var.error_log > 0 ? module.diagnostic_mssql_db.target_resource_id : 1,
"Timeouts" = var.timeouts > 0 ? module.diagnostic_mssql_db.target_resource_id : 1,
"QueryStoreRuntimeStatistics" = var.query_store_runtime_statistics > 0 ? module.diagnostic_mssql_db.target_resource_id : 1,
"QueryStoreWaitStatistics" = var.query_store_wait_statistics > 0? module.diagnostic_mssql_db.target_resource_id : 1,
"SQLinsights" = var.sql_insights > 0 ? module.diagnostic_mssql_db.target_resource_id : 1
}
}
variables.tf
#######
# Name
#######
variable "name" {
description = "The name of the Ms SQL Database. Changing this forces a new resource to be created."
type = string
}
############
# Server Id
############
variable "server_id" {
description = "The id of the Ms SQL Server on which to create the database. Changing this forces a new resource to be created."
type = string
default = ""
}
#######
# Flags
#######
# Audit Log Enabled
variable "audit_log_enabled" {
description = "The audit log enabled of the resource."
type = bool
default = false
}
variable "log_retention_days" {
description = "Specifies the number of days to keep in the Threat Detection audit logs"
type = number
default = 7
}
variable "collation" {
description = "Specifies the collation of the database. Changing this forces a new resource to be created."
type = string
default = "SQL_Latin1_General_CP1_CI_AS"
}
variable "license_type" {
description = "Specifies the license type applied to this database. Possible values are LicenseIncluded and BasePrice."
type = string
default = "LicenseIncluded"
}
variable "sku_name" {
description = "Specifies the name of the sku used by the database. Changing this forces a new resource to be created. For example, GP_S_Gen5_2,HS_Gen4_1,BC_Gen5_2, ElasticPool, Basic,S0, P2 ,DW100c, DS100."
type = string
default = "GP_S_Gen5_1"
validation {
condition = can(regex("GP_S_Gen5_1|GP_S_Gen5_2|HS_Gen4_1|BC_Gen5_2|ElasticPool|Basic|S0|P2|DW100c|DS100", var.sku_name))
error_message = "Sku name invalid. Must be GP_S_Gen5_1,GP_S_Gen5_2,HS_Gen4_1,BC_Gen5_2, ElasticPool, Basic,S0, P2 ,DW100c, DS100."
}
}
variable "min_capacity" {
type = number
default = 0.5
}
variable "auto_pause_delay_in_minutes" {
type = number
default = -1
}
variable "max_size_gb" {
type = number
default = 4
}
variable "zone_redundant" {
type = bool
default = false
}
variable "read_scale" {
type = bool
default = false
}
variable "read_replica_count" {
type = number
default = 0
}
##
# Diagnostic setting variable
##
variable "sa_storage_account" {
description = "This blob storage will hold all Threat Detection audit logs. Required if state is Enabled."
type = string
default = ""
}
variable "storage_account_resource_group" {
description = "Azure resource group where the storage account resides."
type = string
}
variable "automatic_tuning" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
variable "database_wait_statistics" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
variable "query_store_runtime_statistics" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
variable "query_store_wait_statistics" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
variable "error_log" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
variable "sql_insights" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
variable "deadlocks" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
variable "timeouts" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
variable "metric" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
variable "blocks" {
description = "Retention only applies to storage account. Retention policy ranges from 1 to 365 days. If you do not want to apply any retention policy and retain data forever, set retention (days) to 0."
type = string
}
Terraform 0.13 introduced support for for_each and count to modules.
See https://www.terraform.io/docs/configuration/modules.html#multiple-instances-of-a-module for more details and examples.
For example:
module "diagnostic_mssql_db" {
count = var.blocks > 0 ? 1 : 0
. . .
}
This will give you 0 or 1 module.diagnostic_mssql_db resource depending on the value of var.blocks.
This issue looks a bit confused, but if you think from a different perspective, then it's easy to solve.
It seems you want to create a MySQL database and other resources in the module. But the other resources depend on the condition. Think, all the resources in the Terraform have a count property, and if the count equal to 0. It means you only need to set the count variable for all the condition resources. Inside the module, use the variable target_resource_id directly.

Terraform: Conditional creation of a resource based on a variable in .tfvars

I have resources defined in .tf files that are generic to several applications. I populate many of the fields via a .tfvars file. I need to omit some of the resources entirely based on variables in the .tfvars.
For example if I have a resource like:
resource "cloudflare_record" "record" {
zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
name = "${var.subdomain}"
value = "${var.origin_server}"
type = "CNAME"
ttl = 1
proxied = true
}
But then I declare something like cloudflare = false in my .tfvars file I'd like to be able to do something like this:
if var.cloudflare {
resource "cloudflare_record" "record" {
zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
name = "${var.subdomain}"
value = "${var.origin_server}"
type = "CNAME"
ttl = 1
proxied = true
}
}
I've looked at dynamic blocks but that looks like you can only use those to edit fields and blocks within a resource. I need to be able to ignore an entire resource.
Add a count parameter with a ternary conditional using the variable declared in .tfvars like this:
resource "cloudflare_record" "record" {
count = var.cloudflare ? 1 : 0
zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
name = "${var.subdomain}"
value = "${var.origin_server}"
type = "CNAME"
ttl = 1
proxied = true
}
In this example var.cloudflare is a boolean declared in the .tfvars file. If it is true a count of 1 record will be created. If it is false a count of 0 record will be created.
After the count apply the resource becomes a group, so later in the reference use 0-index of the group:
cloudflare_record.record[0].some_field
Expanding on #Joel Guerra's answer, after you use count to determine whether to deploy the resource or not, you can use the one() function to refer to the resource without an index (i.e. without having to use [0]).
For example, after defining the resource like below
resource "cloudflare_record" "record" {
count = var.cloudflare ? 1 : 0
}
Define a local variable like below
locals {
cloudflare_record_somefield = one(cloudflare_record.record[*].some_field)
}
Now instead of cloudflare_record.record[0].some_field, you can use
local.cloudflare_record_somefield
If the count is 0 (e.g. var.cloudflare is false and the resource wasn't created) then local.cloudflare_record_somefield would return null (instead of returning an error when indexing using [0]).
Reference: https://developer.hashicorp.com/terraform/language/functions/one
An issue i'm seeing this with is if the resource your trying to create is already using a for_each then you can't use both count and for_each in the resource. I'm still trying to find an answer on this will update if I find something better.

cloudflare terraform provider firewall creation with loop

I am trying to work around a constraint where firewall creation is split into 2 sections, creating a filter and creating the rule based on the filter. Filter creation exposes a filter id that should be used in the fw rule creation. I cant wrap my head on how to properly iterate through the map that has values for filter and rule and include newly created filter. if i just use a simple map with name and expression, things work, but if i add rule priority things break
here is my map
variable "fw_allowfilters1" {
description = "list of expressions for firewall to be included in the allow rules"
type = map(object({
fvalue = string
priority = number
}))
default = {
"office_filter1" = [
{
fvalue = "ip.geoip.asnum eq 111111"
priority = 1
}
]
"office_filter2" = [
{
fvalue = "ip.src eq 8.8.8.8"
priority = 3
}
]
}
}
now here is my code for both filter and FW
resource "cloudflare_filter" "allow-filters1" {
for_each = var.fw_allowfilters1
zone_id = var.zoneid
expression = each.value.fvalue
description = each.key
//description = [for o in var.fw_allowfilters1: "Filter_${var.fw_allowfilters1.name}"]
//expression = [for o in var.fw_allowfilters1: var.fw_allowfilters1.value]
}
resource "cloudflare_firewall_rule" "whitelist-rule" {
for_each = cloudflare_filter.allow-filters1
action = "allow"
filter_id = tostring(each.value.id)
zone_id = var.zoneid
description = [for p in var.fw_allowfilters1.name: p.name ]
priority = [for p in var.fw_allowfilters1.priority: p.priority ]
}
now if i dont include priority, i can do the for_each on the filter output in firewall creation, using id output from the resource and key for descirption ( cf tf provider uses description as a name) however, if i need to add the key, i need to iterate through the map with values plus the id that is output of the filter creation and I am not sure how to properly map it. code currently does not work.
so i figured it out and it was not easy:) using locals helped me create proper iterators:
resource "cloudflare_filter" "filters1" {
for_each = var.fw_rules
zone_id = var.zoneid
description = "Filter_${tostring(each.key)}"
expression = tostring(each.value[0])
}
locals {
filterids = [for f in cloudflare_filter.filters1 : f.id] //putting filter
IDs into a separate list for concat later
fwvalues = (values(var.fw_rules)) // putting values from the map of fwvalues into
a separate list to use the index position of a particular value as an interator when
creating commong object that has both filterid and fwvalues
fwkeys = (keys(var.fw_rules)) //putting keys into a separate list
//iterating over all elements in the allowfilters1, combining existing lists in the
variable with the ID value and readding the key as an element in the list
withid3 = {for p in var.fw_rules : local.fwkeys[index(local.fwvalues, p.*)] =>
concat(p, list(local.filterids[index(local.fwvalues,
p.*)]),list(local.fwkeys[index(local.fwvalues, p.*)]))} //working version
}
resource "cloudflare_firewall_rule" "fw-rules" {
for_each = local.withid3
action = each.value[2]
filter_id = each.value[4]
paused = each.value[3]
zone_id = var.zoneid
description = "FW-${tostring(each.value[2])}-${tostring(each.key)}"
priority = each.value[1]
}
where varilable is this:
// the syntax is the following: name of the rule(try to be precise = [ expression, priority,action, disabled - boolean] - all values should be strings, make sure to terminate the quotes correctly
allowed values for the action are: block, challenge, js_challenge, allow, log, bypass
list has to be maintained according to the rule priority
variable "fw_rules" {
description = "list of expressions for firewall to be included in therules"
type = map
default = {
office_allow = ["putexpressionhere","1","allow","false"],
office_allow1 = ["putexpressionhere1","2","deny","false"]
}

Resources