How to create Iterative Loop in Terraform - azure

I have around 50 resources that I create in a Terraform script.
I need now to add diagnostic logging for each resource.
The following code is what I use:
data "azurerm_monitor_diagnostic_categories" "vnet-spoke01" {
resource_id = module.MOD-VNET-SPOKE01.id
}
resource "azurerm_monitor_diagnostic_setting" "vnet-spoke01" {
name = "diag-${module.MOD-VNET-SPOKE01.vnetName}"
target_resource_id = module.MOD-VNET-SPOKE01.id
log_analytics_workspace_id = module.MOD-LOG-ANALYTICS-WORKSPACE.id
dynamic "log" {
for_each = data.azurerm_monitor_diagnostic_categories.vnet-spoke01.logs
content {
category = log.value
retention_policy {
days = 0
enabled = false
}
}
}
dynamic "metric" {
for_each = data.azurerm_monitor_diagnostic_categories.vnet-spoke01.metrics
content {
category = metric.value
retention_policy {
days = 0
enabled = false
}
}
}
}
As you can see, I'm adding VNET of spoke-1 diagnostic settings.
Can someone kindly guide me as to how I can add a for-loop so it goes through each resource (that I'd put in an array or list) and run through it?
e.g.
variable "myResources" {
type = list(string)
default = ["module.MOD-VNET-SPOKE01", "module.MOD-VNET-SPOKE02" etc...]
}
for a in myResources
{
.... execute diagnostic routine
}
How could I do this?
Many thanks

Sadly you can't do that. You can't dynamically resolve strings (e.g. "module.MOD-VNET-SPOKE01") into resource identifiers (e.g. module.MOD-VNET-SPOKE01.id.
Your variable would already have to contain all ids for your loop to work:
variable "myResources" {
type = list(string)
default = ["<id-SPOKE01>", "<id-SPOKE02>", etc...]
}
or through locals:
locals {
myResources = [module.MOD-VNET-SPOKE01.id, module.MOD-VNET-SPOKE02.id etc...]
}

Related

Azure Storage (Blob, Queue, Table) Logging in Terraform with for_each and locals

I am writing Terraform code to enable logging on Azure Storage Blob, Queue and Table types. With my current code, I need to fetch data for each Storage type,say for example Blob, and use it to get it's log and metrics details.
Is there any way I could use for_each and locals to avoid repeating the same block of code for each Storage type. Below is what the code looks like now for Blob type,
data "azurerm_monitor_diagnostic_categories" "storage_blob" {
resource_id = "${azurerm_storage_account.stamp.id}/blobServices/default/"
}
resource "azurerm_monitor_diagnostic_setting" "storage_blob" {
name = "storageblobladiagnostics"
target_resource_id = "${azurerm_storage_account.stamp.id}/blobServices/default/"
log_analytics_workspace_id = azurerm_log_analytics_workspace.stamp.id
dynamic "log" {
iterator = entry
for_each = data.azurerm_monitor_diagnostic_categories.storage_blob.logs
content {
category = entry.value
enabled = true
retention_policy {
enabled = true
days = 30
}
}
}
dynamic "metric" {
iterator = entry
for_each = data.azurerm_monitor_diagnostic_categories.storage_blob.metrics
content {
category = entry.value
enabled = true
retention_policy {
enabled = true
days = 30
}
}
}
}
The below implementation doesn't seem to work as the data block is not able handle the for_each expression in the dynamic block
locals {
storage = ["blobServices", "tableServices", "queueServices"]
}
data "azurerm_monitor_diagnostic_categories" "storage_blob" {
resource_id = "${azurerm_storage_account.stamp.id}/${each.key}/default/"
}
resource "azurerm_monitor_diagnostic_setting" "storage_blob" {
for_each = toset(local.storage)
name = "storageblobladiagnostics"
target_resource_id = "${azurerm_storage_account.stamp.id}/${each.key}/default/"
log_analytics_workspace_id = azurerm_log_analytics_workspace.stamp.id
dynamic "log" {
iterator = entry
for_each = data.azurerm_monitor_diagnostic_categories.storage_blob.logs
content {
category = entry.value
enabled = true
retention_policy {
enabled = true
days = 30
}
}
}
dynamic "metric" {
iterator = entry
for_each = data.azurerm_monitor_diagnostic_categories.storage_blob.metrics
content {
category = entry.value
enabled = true
retention_policy {
enabled = true
days = 30
}
}
}
}
In order for this to work, you would have to adjust the code slightly. In your example, the data source is not using for_each, so it cannot be used the way you want. The adjustment is as follows:
locals {
storage = ["blobServices", "tableServices", "queueServices"]
}
data "azurerm_monitor_diagnostic_categories" "storage_blob" {
for_each = toset(local.storage)
resource_id = "${azurerm_storage_account.stamp.id}/${each.key}/default/"
}
resource "azurerm_monitor_diagnostic_setting" "storage_blob" {
for_each = toset(local.storage)
name = "storageblobladiagnostics"
target_resource_id = "${azurerm_storage_account.stamp.id}/${each.key}/default/"
log_analytics_workspace_id = azurerm_log_analytics_workspace.stamp.id
dynamic "log" {
iterator = entry
for_each = "${data.azurerm_monitor_diagnostic_categories.storage_blob[each.key].logs}"
content {
category = entry.value
enabled = true
retention_policy {
enabled = true
days = 30
}
}
}
dynamic "metric" {
iterator = entry
for_each = "${data.azurerm_monitor_diagnostic_categories.storage_blob[each.key].metrics}"
content {
category = entry.value
enabled = true
retention_policy {
enabled = true
days = 30
}
}
}
}

Terraform - Output Index along with item

Is there a way to modify this output code to include the index number with the name? The current code below creates one output with Index '0', but each name should ideally be in its own Index (0, 1, 2, etc.)
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.1.0"
}
}
}
provider "azurerm" {
features {
}
}
variable "private_link_scopes" {
description = "A list of Azure private link scopes."
type = list(object({
name = string
resource_group_name = string
}))
}
locals {
rgs_map = {
for n in var.private_link_scopes : n.resource_group_name => {
name = n.resource_group_name
}
}
}
data "azurerm_resource_group" "rg" {
for_each = local.rgs_map
name = each.value.name
}
output "list" {
value = { for index, item in [var.private_link_scopes] : index => item[*].name }
}
It seems like there is a lot of structure change going on here. That may be due to other baggage or reasons that depend on it, but I think this could be simplified. I use locals in lieu of the variable, but I hope is help.
I'm not sure the splat operator there is what you want. That is the same as getting a list of all items' name attributes in that item value, but there would only be one per item so that seems strange.
locals {
scopes = [
{
name = "name_a"
rg_name = "rg_a"
},
{
name = "name_b"
rg_name = "rg_b"
}
]
}
data "azurerm_resource_group" "rg" {
# I don't see a reason to generate the intermediary map
# or to build another object in each map value. This is more
# simple. for_each with an object by name: rg_name
value = { for scope in local.scopes : scope.name => scope.rg_name }
name = each.value
}
output "list" {
# I don't see a need to use a splat expression here.
# You can just build a map with the index and the name
# here directly.
value = { for i, v in local.scopes : i => v.name }
}
In fact, if you don't need the resource group resources to be keyed by name, that can be simplified further to:
data "azurerm_resource_group" "rg" {
for_each = toset([for scope in local.scopes : scope.rg_name])
name = each.value
}

Terraform - cloud run multiple environment variables from list

I'm trying to set multiple environment variables on a cloud run module I've created. The example I'm following from Terraform is static. Is it possible to dynamically create these?
template {
spec {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
env {
name = "SOURCE"
value = "remote"
}
env {
name = "TARGET"
value = "home"
}
}
}
}
https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_service#example-usage---cloud-run-service-multiple-environment-variables
I've tried:
dynamic "env" {
for_each = var.envs
content {
name = each.key
value = each.value
}
}
But I get the following error:
A reference to "each.value" has been used in a context in which it unavailable, such as when the configuration no longer contains the value in
│ its "for_each" expression. Remove this reference to each.value in your configuration to work around this error.
Edit: Full code example
resource "google_cloud_run_service" "default" {
name = "cloudrun-srv"
location = "us-central1"
template {
spec {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
env {
name = "SOURCE"
value = "remote"
}
env {
name = "TARGET"
value = "home"
}
}
}
}
traffic {
percent = 100
latest_revision = true
}
autogenerate_revision_name = true
}
When you use dynamic blocks, you can't use each. It should be:
dynamic "env" {
for_each = var.envs
content {
name = env.key
value = env.value
}
}

Terraform: create namespaces and role binding using lists

The namespace is created in next way, so "role bindings" are applied depending of the "app_env" code
**variables.tf**
variable app_name {}
variable app_env {}
locals {
custom_role_dev = "Enterprise Development Project"
custom_role_prd = "Enterprise Production Project"
}
**main.tf**
resource "kubernetes_namespace" "kube_ns" {
metadata {
name = var.app_name
}
}
resource "kubernetes_role" "custom_role_dev" {
count var.app_env == "d" ? 1 : 0
metadata {
name = local.custom_role_dev
namespace = var.app_name
}
rule {
api_groups = [""]
resources = ["<options>"]
verbs = ["*"]
}
depends_on = [kubernetes_namespace.kube_ns]
}
resource "kubernetes_role" "custom_role_prd" {
count var.app_env == "p" ? 1 : 0
metadata {
name = local.custom_role_prd
namespace = var.app_name
}
rule {
api_groups = [""]
resources = ["<options>"]
verbs = ["*"]
}
depends_on = [kubernetes_namespace.kube_ns]
}
In order to create several namespace and applying their respective roles, I want to use "lists" to replace "app_name" variable but I don't know how to iterate the "kubernetes_role" block.
I think this 2 links are very close what I want to do
Convert list to map with index in Terraform
Terraform - conditionally creating a resource within a loop
Can this be done with "for_each" or "count"?

Need help to call data source in for_each

I am replacing count with for_each for following code. Need help in referring index of resource when calling data source of azurerm_monitor_diagnostic_categories under dynamic "log".
resource "azurerm_monitor_diagnostic_setting" "diag" {
count = length(var.resource_id)
name = "diag"
target_resource_id = var.resource_id[count.index]
log_analytics_workspace_id = var.log_analytics_workspace_id
dynamic "log" {
for_each = data.azurerm_monitor_diagnostic_categories.resource[count.index].logs
content {
category = log.value
enabled = true
Was looking for the same information when finding your question.
The below works for me:
dynamic "log" {
for_each = [for lg in data.azurerm_monitor_diagnostic_categories.webui.logs : {
lg_name = lg
}
]
content {
category = log.value.lg_name
enabled = true
retention_policy {
enabled = false
}
}
}

Resources