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
}
}
Related
In the following code (not the cleanest ) I need to access each value on the init-script list and pass it to the dynamic "init-scripts" block.
when I try to use each.value, terraform returns the var.cluster attributes but not the values of the list. How do I access the value of the list. Appreciate your help with this.
variable "clusters" {
type = map(object({
cluster-name = string
init-scripts = list(string)
}))
}
resource "databricks_cluster" "cluster" {
for_each = var.clusters
cluster_name = each.key
dynamic "init_scripts" {
for_each = { for script in var.clusters[each.key].init-scripts : script => script if var.clusters[each.key].init-scripts != null }
content {
file {
destination = each.value #returns the value of var.clusters
}
}
}
}
Looks like it was a simple fix. Iterator seem to solve the issue.
dynamic "init_scripts" {
for_each = { for script in var.clusters[each.key].init-scripts : script => script if var.clusters[each.key].init-scripts != null }
iterator = script
content {
file {
destination = script.value
}
}
}
I have a simple use case where I am provisioning a key vault but want to define a variable as its only really needed here (local) but I get the error variable not allowed.
variable "secrets" {
type = map(string)
default = {
"price-cosmos-db-primary-key" = azurerm_cosmosdb_account.acc.primary_key
"price-cosmos-db-endpoint" = azurerm_cosmosdb_account.acc.endpoint
}
}
resource "azurerm_key_vault_secret" "keyvaultsecrets" {
count = length(local.secrets)
name = keys(local.secrets)[count.index]
value = values(local.secrets)[count.index]
key_vault_id = azurerm_key_vault.price_keyvault.id
depends_on = [
azurerm_cosmosdb_account.acc
]
}
Is it possible to do the equivalent but using locals?
Use locals, it will work.
locals {
secrets = {
"price-cosmos-db-primary-key" = azurerm_cosmosdb_account.acc.primary_key
"price-cosmos-db-endpoint" = azurerm_cosmosdb_account.acc.endpoint
}
}
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
}
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...]
}
Given the following resource block:
resource "azuredevops_variable_group" "secrets" {
project_id = ...
name = "my-secrets"
description = "..."
allow_access = true
key_vault {
name = "my-akv"
service_endpoint_id = azuredevops_serviceendpoint_azurerm.dev.id
}
dynamic "variable" {
for_each = [
"secret-1",
"secret-2",
"secret-3",
])
content {
name = variable.value
}
}
}
I need to set one more variable to this resource that is static. How should I merge a dynamic block? something like:
merge (
dynamic "variable" {
for_each = [
"secret-1",
"secret-2",
"secret-3",
])
content {
name = variable.value
}
},
variable {
name = "secret-4"
value = "foo"
}
)
Adding another variable block after the dynamic block will raise Error: "variable.0.value": conflicts with key_vault.