Reputation: 49
I'm working on an infrastructure setup using Terraform where I've encountered an issue related to using the for_each meta-argument within a resource block. I have a proxy module structured as follows:
locals {
artifacts_downloads_path = "${path.cwd}/artifacts"
proxy_zip_file_name = "${var.proxy_name}.${var.proxy_version}.zip"
local_proxy_artifact_directory = "${local.artifacts_downloads_path}/${var.proxy_name}"
script_file_directory = "${path.cwd}/scripts"
terraform_state_output = data.terraform_remote_state.terraform_state_values.outputs
}
module "target_server" {
for_each = {
for targetServer in var.list_of_target_servers_entries : targetServer => targetServer
}
source = "../target_server"
apigee_env = var.apigee_env
target_server_json = jsondecode(element(values(local.terraform_state_output), index(keys(local.terraform_state_output), each.value)))
depends_on = [data.terraform_remote_state.terraform_state_values]
}
module "kvm" {
for_each = {
for kvm in var.list_of_kvm_entries : kvm => kvm
}
source = "../kvm"
apigee_env = var.apigee_env
proxy_name = var.proxy_name
kvm_json = jsondecode(element(values(local.terraform_state_output), index(keys(local.terraform_state_output), each.value)))["entries"]
}
resource "apigee_proxy" "proxy" {
count = fileexists("${local.artifacts_downloads_path}/${local.proxy_zip_file_name}") ? 1 : 0
name = var.proxy_name
bundle = "${local.artifacts_downloads_path}/${local.proxy_zip_file_name}"
bundle_hash = filebase64sha256("${local.artifacts_downloads_path}/${local.proxy_zip_file_name}")
depends_on = [module.target_server, module.kvm]
}
resource "apigee_proxy_deployment" "proxy_deployment" {
count = fileexists("${local.artifacts_downloads_path}/${local.proxy_zip_file_name}") ? 1 : 0
proxy_name = apigee_proxy.proxy[0].name
environment_name = var.apigee_env
revision = apigee_proxy.proxy[0].revision
service_account = var.service_account
depends_on = [apigee_proxy.proxy[0]]
}
resource "null_resource" "clean_up" {
count = fileexists("${local.artifacts_downloads_path}/${local.proxy_zip_file_name}") ? 1 : 0
triggers = {
value = filebase64sha256("${local.artifacts_downloads_path}/${local.proxy_zip_file_name}")
}
provisioner "local-exec" {
command = <<EOF
cd ${local.script_file_directory}
bash ./delete_old_APIGEE_resource_revisions.sh ${var.apigee_gcp_project_id} ${var.proxy_name} ${var.access_token} ${var.num_of_proxy_revisions_to_be_retained} "apis"
rm -rf ${local.artifacts_downloads_path}/${local.proxy_zip_file_name}
EOF
}
depends_on = [apigee_proxy_deployment.proxy_deployment[0]]
}
In this module, I'm using a data block to fetch remote state data:
data "terraform_remote_state" "terraform_state_values" {
backend = "gcs"
config = {
bucket = var.terraform_state_bucket_name #local.terraform_state_bucket
prefix = var.terraform_state_bucket_path_prefix #local.terraform_state_bucket_prefix
}
}
I'm trying to consume the output of this terraform_remote_state in the target_server module:
resource "apigee_target_server" "target_server" {
for_each = {
for index, entry in jsondecode(var.target_server_json) : index => entry
}
environment_name = var.apigee_env
name = each.value.name
host = strcontains(each.value.host, "https") ? split("https://", each.value.host)[1] : each.value.host
port = each.value.port
is_enabled = each.value.enabled ? true : false
protocols = each.value.protocol != [] ? [each.value.protocol] : []
}
However, upon running terraform plan, I encounter the following error: var.target_server_json will be known only after apply The "for_each" map includes keys derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.
I tried refactoring it to use count instead. Here's the attempt I made:
locals {
items_count = length(jsondecode(var.target_server_json))
}
resource "apigee_target_server" "target_server" {
count = local.items_count
# Access elements dynamically using the index
environment_name = var.apigee_env
name = var.target_server_json[count.index].name
host = strcontains(var.target_server_json[count.index].host, "https") ? split("https://", var.target_server_json[count.index].host)[1] : var.target_server_json[count.index].host
port = var.target_server_json[count.index].port
is_enabled = var.target_server_json[count.index].enabled ? true : false
protocols = var.target_server_json[count.index].protocol != [] ? [var.target_server_json[count.index].protocol] : []
}
Upvotes: 4
Views: 3468
Reputation: 238081
target_server_json
must be known at plan time. Its not possibile to iterate in TF, regardless of for_each
or count
over uknown number of items.
So to fix your error make sure that target_server_json
is known at plan time, which means you have to pass it as an input argument to your configuration files. This way, TF will know exactly how many elements is in target_server_json
Upvotes: 4