Hello,
Not sure if this was reported already, but I am having trouble following page at: https://www.hashicorp.com/blog/hashicorp-terraform-0-12-preview-for-and-for-each
when it comes to iterating lists that are declared in a locals block or those output by a module.
Suppose we have:
locals {
#ip_whitelist = "${module.ip_whitelist.list}" -- also not working
ip_whitelist = ["127.0.0.1/32", "192.168.0.0/16"]
}
resource "azurerm_container_registry" "redacted" {
name = "redacted"
resource_group_name = "${module.redacted.resource_group_name}"
location = "${module.redacted.location}"
admin_enabled = false
sku = "Premium"
tags = "${var.tags}"
georeplication_locations = ["East US"]
network_rule_set {
default_action = "Deny"
# dynamic ip_rule block
dynamic "ip_rule" {
for_each = local.ip_whitelist # <--- error here?
content {
action = "Allow"
ip_range = local.ip_whitelist # <--- what do you put here when iterating?
}
}
# static ip_rule block
ip_rule {
action = "Allow"
ip_range = "192.168.0.0/16"
}
}
}
This produces the following when running terraform plan/apply:
Error: Unknown variable
on poc_acr.tf line 31, in resource "azurerm_container_registry" "redacted":
31: for_each = local.ip_whitelist
There is no variable named "local".
Same happens if I were to use module.ip_whitelist.list directly
Terraform version output:
Terraform v0.12.5
+ provider.azurerm v1.32.0
+ provider.external v1.2.0
+ provider.local v1.3.0
+ provider.random v2.1.2
Here's another try based on official documentation.
module "ip_whitelist" {
source = "../../modules/ip_whitelist"
}
resource "azurerm_container_registry" "redacted" {
name = "redacted"
resource_group_name = "${module.core_westus.resource_group_name}"
location = "${module.core_westus.location}"
admin_enabled = false
sku = "Premium"
tags = "${var.tags}"
georeplication_locations = ["East US"]
network_rule_set {
default_action = "Deny"
dynamic "ip_rule" {
for_each = module.ip_whitelist.ip_whitelist # the output var here is a *list*
content {
action = "Allow"
ip_range = ip_rule.value
}
}
}
}
位 terraform apply -target azurerm_container_registry.redacted
Acquiring state lock. This may take a few moments...
Error: Unknown variable
on poc_acr.tf line 24, in resource "azurerm_container_registry" "redacted":
24: for_each = module.ip_whitelist.ip_whitelist
There is no variable named "module".
Tried upgrading to v0.12.6 just now and still seeing the same error. FYI, running on Windows.
At this point I feel this is more of a bug, rather than a question as I am seeing the same error message if it's a module output, a local variable or a "variable" (as in var)
After some guesswork and fiddling, I found this oddly works:
locals {
acr_combined_wl = concat(module.ip_whitelist.ip_whitelist,module.core_westus.ase_outbound_addresses,module.core_eastus.ase_outbound_addresses) # combined list of all IPs in CIDR format
}
resource "azurerm_container_registry" "redacted" {
name = "redacted"
resource_group_name = "${module.core_westus.resource_group_name}"
location = "${module.core_westus.location}"
admin_enabled = false
sku = "Premium"
tags = "${var.tags}"
georeplication_locations = ["East US"]
network_rule_set {
default_action = "Deny"
ip_rule = [
for ip in local.acr_combined_wl: {
action = "Allow"
ip_range = ip
}
]
}
}
This seems to iterate and provide correct ip_rule entries.
Hi @CryptonZylog,
Thanks for reporting the issue. That particular version of the azurerm provider is using a special feature in network_rule_set and ip_rule to allow setting the blocks as individual attributes. It appears as though this is interfering with the for_each evaluation and error reporting.
I don't see those fields in the master branch of the provider any longer, so they may be phasing them out entirely. We'll see if we can get terraform to output a better error here, or or at least hint that attribute syntax may be required.
I don't see those fields in the master branch of the provider any longer, so they may be phasing them out entirely.
Which fields are you thinking will be phased out? The workaround I have discovered or the layout of network_rule_set->ip_rule as documented here? Just want to know whether the "workaround" I have is what is supposed to work and not something that will go away shortly.
We'll see if we can get terraform to output a better error here, or or at least hint that attribute syntax may be required.
Thank you!
Those fields don't appear in the master branch of the provider. I'm not sure what the plan is for that resource in general. Your "workaround" is correct for this version of the provider, because those fields are used as attributes, therefore can use the expression syntax.
I am also facing a similar issue with aws resource. Here is the sample code:
locals {
lifecycle_glacier_rule_enabled = var.glacier_transition_days == -1 ? [] : [""]
lifecycle_expiry_rule_enabled = var.expiration_days == -1 ? [] : [""]
}
locals {
transition_map = {
GLACIER = {
enabled = local.lifecycle_glacier_rule_enabled
transition_days = var.glacier_transition_days
}
}
}
resource "aws_s3_bucket" "bucket" {
bucket = var.bucket_name
acl = "log-delivery-write" #required to enable logging
versioning {
enabled = true
}
dynamic "lifecycle_rule" {
# for_each = local.lifecycle_glacier_rule_enabled
for_each = [for storageclass in local.transition_map : { # error line, no variable named 'local'
class = lifecycle_rule.key
days = storageclass.value
}
if storageclass.enabled.value == true]
content {
id = "auto_glacier"
enabled = true
transition {
storage_class = lifecycle_rule.value.class
days = lifecycle_rule.value.day
}
}
}
Same issue is faced if you use replace local with var
Terraform v0.12.6
@test-in-prod not knowing your particular use case -- a possible workaround to use of the ACR network filtering is to provision a standalone vnet with a single subnet that is attached to your ACR with the service endpoint as defined here
That means that ACR accepts connections only from that subnet. You can then quite easily configure NSGs on the subnet to provide access via network peering.
I believe what @teamterraform was pointing towards is that the ACR network filtering is currently in Preview and may indeed change before it goes GA.
Hope that helps.
Same issue with the VMware vSphere provider
data "vsphere_datacenter" "datacenter" {
name = "dc"
}
data "vsphere_virtual_machine" "template" {
name = "centos7"
datacenter_id = data.vsphere_datacenter.datacenter.id
}
locals {
disks = {
for disk in data.vsphere_virtual_machine.template.disks : index(data.vsphere_virtual_machine.template.disks, disk) => disk
}
} # data.vsphere_virtual_machine.template.disks is a list of maps
resource "vsphere_virtual_machine" "vsphere_vm" {
...
dynamic "disk" {
for_each = local.disks
iterator = "template_disk"
content {
label = "disk${template_disk.key}"
unit_number = template_disk.key
size = template_disk.value["size"]
eagerly_scrub = template_disk.value["eagerly_scrub"]
thin_provisioned = template_disk.value["thin_provisioned"]
}
}
...
}
Error returned from terraform plan:
Error: Unknown variable
There is no variable named "local".
If I try feeding the for disk in data.vsphere_virtual_machine.template.disks .. iteration directly to for_each instead of using a separate local.disks value, the error returned is There is no variable named "data".
Issue only appears when iterating over a list inside inside a child module. When modules are not used, everything works without a problem.
Terraform v0.12.12
+ provider.vsphere 1.13
Hi,
Also I am running into the same problem, but using the resource aws_alb_listener as it can be in the code:
resource "aws_alb_listener" "web_app_elb_listener_http" {
load_balancer_arn = local.web_elb_arn
port = 80
protocol = "HTTP"
dynamic default_action {
for_each = lookup( [ var.web_elb_listener_http_actions ], "def_action", [])
content {
type = def_action.value.type
dynamic fixed_response {
for_each = lookup(def_action.value, "fixed_response", [])
content {
content_type = fixed_response.value.content_type
status_code = fixed_response.value.status_code
message_body = lookup(fixed_response.value, "message_body", "" )
}
}
}
}
}
when doing terraform plan I get :
terraform plan
Error: Unknown variable
on ../../../modules/cert_domain/elb_listener.tf line 23, in resource "aws_alb_listener" "web_app_elb_listener_http":
23: for_each = lookup( [ var.web_elb_listener_http_actions ], "def_actiona", [])
There is no variable named "var".
Error: Reference to undeclared resource
on ../../../modules/cert_domain/elb_listener.tf line 27, in resource "aws_alb_listener" "web_app_elb_listener_http":
27: for_each = lookup(def_action.value, "fixed_response", [])
A managed resource "def_action" "value" has not been declared in
cert_domain_creation.
The variable declaration:
variable "web_elb_listener_http_actions" {
description = "Here we configure the default action for the http listener"
default = {
def_action =[
{
type = "fixed-response"
fixed_response = [
{
content_type = "text/plain"
message_body = "Fixed response content"
status_code = "202"
}
]
}
]
}
}
version is
Terraform v0.12.17
+ provider.aws v2.41.0
Ohh I realized of my error it is working as it should... sorry for the mistake... it is working like this:
resource "aws_alb_listener" "web_app_elb_listener_http" {
load_balancer_arn = local.web_elb_arn
port = 80
protocol = "HTTP"
default_action {
type = var.web_elb_listener_http_actions.def_action.type
dynamic fixed_response {
for_each = [ lookup(var.web_elb_listener_http_actions.def_action, "fixed_response", {}) ]
content {
content_type = fixed_response.value.content_type
status_code = fixed_response.value.status_code
message_body = lookup(fixed_response.value, "message_body", "" )
}
}
}
}
And the variable:
variable "web_elb_listener_http_actions" {
description = "Here we configure the default action for the http listener"
default ={
def_action = {
type = "fixed-response"
fixed_response = {
content_type = "text/plain"
message_body = ""
status_code = "202"
}
}
}
}
I realized of my error ... so I am not in this bug...
I am getting the same error , is there a fix in any version ?
There is no variable named "var".
resource "azurerm_container_registry" "container_registry" {
for_each = var.acr
name = lookup(each.value, "acr_name", null) == null ? lower(format("%s-acr", each.key)) : lower(format("%s", each.value["acr_name"]))
resource_group_name = each.value["resource_group_name"]
location = each.value["location"]
sku = lookup(each.value, "sku", "Standard")
admin_enabled = lookup(each.value, "admin_enabled", true)
georeplication_locations = each.value["georeplication_locations"]
network_rule_set {
default_action = "Deny"
dynamic "ip_rule" {
for_each = var.acr
content {
action = "Allow"
ip_range = each.value["ip_rule"]
}
}
}
}
acr = {
acr_2 = {
acr_name = "testingACR2"
resource_group_name = "ctc-sandbox-testing-rg-cc"
admin_enabled = "true"
georeplication_locations = ["East US",]
location = "canada central"
sku = "Premium"
subnet_id = ["some_id_1234",]
ip_rule = ["192.168.99.0/24",]
}
}
Same issue on helm provider using set
dynamic "set" {
for_each = [local.tag_keys]
iterator = "tag"
content {
name = "podLabels[${index(local.tag_keys, tag.key)}]"
value = "${tag.key}=${lookup(var.tags, tag.key)}"
}
}
Error
Error: Unknown variable
on ../modules/external_dns/main.tf line 54, in resource "helm_release" "external_dns":
54: for_each = [local.tag_keys]
There is no variable named "local".
Error: Invalid expression
on ../modules/external_dns/main.tf line 55, in resource "helm_release" "external_dns":
55: iterator = "tag"
A single static variable reference is required: only attribute access and
indexing with constant keys. No calculations, function calls, template
expressions, etc are allowed here.
We were getting this as well on the Azure Container Registry resource, and this is because the ip_rule is an attribute and not a block, which is why "dynamic" doesn't work. The documentation doesn't match the code, but I think the code is actually wrong in this case. It's a provider bug I believe, but there is also a bug in Terraform that makes the error very confusing.
@test-in-prod has the correct solution. Using an attribute declaration instead of a block declaration.
resource "azurerm_container_registry" "redacted" {
name = "redacted"
resource_group_name = "${module.core_westus.resource_group_name}"
location = "${module.core_westus.location}"
admin_enabled = false
sku = "Premium"
tags = "${var.tags}"
georeplication_locations = ["East US"]
network_rule_set {
default_action = "Deny"
+ ip_rule = [
+ for ip in local.acr_combined_wl: {
+ action = "Allow"
+ ip_range = ip
+ }
+ ]
}
}
The problem seems to be related to iterator. I found that when I avoided using iterator (and used the default iterator name instead, i.e. the name of the dynamic block), the error went away.
So the error message is definitely wrong.
on ../dlm.tf line 11, in resource "aws_dlm_lifecycle_policy" "this":
11: for_each = var.schedules
There is no variable named "var".
I renamed the issue to more precisely match the original problem statement.
I ran through a number of the additional configurations presented here, and most seem to be user error, or unrelated issues which have been fixed since 0.12. The initial report contains sufficient information to reproduce the issue; if you have questions concerning different configurations, it's better to use the community forum where there are more people ready to help.
There's also the issue of the error message itself being incorrect. Unless this has all been fixed in v0.13 (I haven't tried it yet).
Most helpful comment
After some guesswork and fiddling, I found this oddly works:
This seems to iterate and provide correct
ip_ruleentries.