Terraform: Object List Value Has No Attributes - terraform

Getting an error on Terraform Plan saying my object has no attributes for the name value. We are deploying about 7 private dns zones and many of them live in the same resource group. some may live in others, but most live in the same one.
Error: Unsupported attribute
on Modules/privatednszone/main.tf line 4, in data "azurerm_resource_group" "this":
name = each.value.name
This value does not have any attributes.
MAIN
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "2.83.0"
}
}
}
provider "azurerm" {
features {}
}
variable "private_dns_zones" {
type = map(object({
dns_zone_name = string
resource_group_name = string
tags = map(string)
vnet_links = list(object({
zone_to_vnet_link_name = string
vnet_name = string
networking_resource_group = string
zone_to_vnet_link_exists = bool
vnet_link_rg_name = string
}))
zone_exists = bool
registration_enabled = bool
}))
description = "Map containing Private DNS Zone Objects"
default = {}
}
data "azurerm_resource_group" "this" {
# read from local variable, index is resource_group_name
for_each = local.rgs_map
name = each.value.name
}
locals {
rgs_map = {
for n in var.private_dns_zones :
n.resource_group_name => {
name = n.resource_group_name
}
}
}
output "rgs_map" {
value = local.rgs_map
}
output "rg_data" {
value = data.azurerm_resource_group.this
}
TFVARS
Code below is a sample of two dns zones, but there are additional ones.
private_dns_zones = {
zone1 = {
dns_zone_name = "privatelink.vaultcore.azure.net"
resource_group_name = "Terraform1"
tags = {
iac = "Terraform"
syntax = "zone1"
}
zone_exists = false
vnet_links = [
{
zone_to_vnet_link_name = "vaultcore-vnet-eastus2-01"
vnet_name = "vnet-eastus2-01"
networking_resource_group = "Terraform1"
zone_to_vnet_link_exists = false
vnet_link_rg_name = "Terraform1"
}
]
registration_enabled = false
},
zone2 = {
dns_zone_name = "privatelink.monitor.azure.com"
resource_group_name = "Terraform1"
tags = {
iac = "Terraform"
syntax = "zone2"
}
zone_exists = false
vnet_links = [
{
zone_to_vnet_link_name = "monitor-vnet-eastus2-01"
vnet_name = "vnet-eastus2-01"
networking_resource_group = "Terraform1"
zone_to_vnet_link_exists = false
vnet_link_rg_name = "Terraform1"
}
]
registration_enabled = false
}
}

You code seems to work fine only if I use different resource group names. As you are using duplicate values of resource group names which is your requirement creating a map "rgs_map" with your code is not possible as it will error out with below :
So , in order to resolve the above error , I used something like below :
locals {
rgs_map = {
for i,n in var.private_dns_zones : "${i}" =>{
name = n.resource_group_name
}
}
}
Complete code:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "2.83.0"
}
}
}
provider "azurerm" {
features {}
}
variable "private_dns_zones" {
type = map(object({
dns_zone_name = string
resource_group_name = string
tags = map(string)
vnet_links = list(object({
zone_to_vnet_link_name = string
vnet_name = string
networking_resource_group = string
zone_to_vnet_link_exists = bool
vnet_link_rg_name = string
}))
zone_exists = bool
registration_enabled = bool
}))
description = "Map containing Private DNS Zone Objects"
default = {}
}
data "azurerm_resource_group" "this" {
# read from local variable, index is resource_group_name
for_each = local.rgs_map
name = each.value.name
}
locals {
rgs_map = {
for i,n in var.private_dns_zones : "${i}" =>{
name = n.resource_group_name
}
}
}
output "rgs_map" {
value = local.rgs_map
}
output "rg_data" {
value = data.azurerm_resource_group.this
}
Output:

Related

Terraform resource with for_each depending on a boolean variable

Given the following map and its implementation:
variable "tunnel_service_maps" {
default = {}
type = map(object({
target_service = string
create_service_token = bool
}))
tunnel_service_maps = {
tunnel1 = {
target_service = "http://tunnel1"
create_service_token = true
}
tunnel2 = {
target_service = "http://tunnel2"
create_service_token = false
}
tunnel3 = {
target_service = "http://tunnel3"
create_service_token = true
}
}
I want to create the following resource exclusively if create_service_token == true:
resource "example_resource" "example" {
for_each = var.tunnel_service_maps # <- row to change
name = "allow-service-token-${each.value.target_service}"
}
The expected results will be 2 example resources created
That should be easy to achieve:
resource "example_resource" "example" {
for_each = { for k, v in var.tunnel_service_maps : k => v if v.create_service_token }
name = "allow-service-token-${each.value.target_service}"
}

for each module reference between modules with a map object

im trying to create an instance group with reference to an GCE, but im unable to refer to the id from instance group to link to the GCE
variable "compute_engine_instances" {
type = map(object({
instance_name = string
machine_type = string
zone = string
tags = list(string)
image_name = string
image_project = string
labels =object({
app_id = number
cost_center = string
owner = string
email = string
})
}))
}
module "qat_hosted_pacs_compute_engines" {
source = "../modules/compute_engine"
for_each = var.compute_engine_instances
project_id = var.project_id
instance_name = each.value.instance_name
machine_type = each.value.machine_type
tags = each.value.tags
labels = each.value.labels
subnetwork = var.subnetwork
zone = each.value.zone
image_name = each.value.image_name
image_project = each.value.image_project
}
variable "instance_group" {
type = map(object({
group_manager_name = string
zone = string
}))
}
module "qat_hosted_pacs_app_grp" {
source = "../modules/instance_groups"
for_each = var.instance_group
group_manager_name = each.value.group_manager_name
zone = each.value.zone
project_id = var.project_id
instances = module.qat_hosted_pacs_compute_engines.vm_name #unable to figure out how to reference the GCE
}
#output.tf looks like this for compute engine module
output "compute_engine_instances" {
value = {
for k, v in module.qat_hosted_pacs_compute_engines : k => v.vm_name
}
}
The root module looks like this for compute engine
data "google_compute_image" "compute_image" {
name = var.image_name
project = var.image_project
}
resource "google_compute_instance" "generic_instance" {
project = var.project_id
name = var.instance_name
machine_type = var.machine_type
zone = var.zone
tags = var.tags
labels = var.labels
boot_disk {
initialize_params {
image = data.google_compute_image.compute_image.self_link
}
auto_delete = true
}
network_interface {
subnetwork = var.subnetwork
}
}
#outputs.tf here looks like this for gce resource
output "vm_name" {
value = google_compute_instance.generic_instance.id
description = "The name of the VM"
}
And the instance group manager looks like this
resource "google_compute_instance_group" "igm" {
name = var.group_manager_name
zone = var.zone
project = var.project_id
instances = var.instances
named_port {
name = "http"
port = "8080"
}
named_port {
name = "https"
port = "8443"
}
lifecycle {
create_before_destroy = true
}
}
i get the foll. error
Error: Unsupported attribute
on main.tf line 45, in module "qat_hosted_pacs_app_grp":
45: instances = module.qat_hosted_pacs_compute_engines.vm_name
|----------------
| module.qat_hosted_pacs_compute_engines is object with 2 attributes
This object does not have an attribute named "vm_name".
terraform tf vars file
compute_engine_instances ={
"test-adi"={
instance_name = "test-vm"
machine_type = "n1-standard-1"
zone = "us-east4-b"
tags = ["foo","bar"]
image_name = "gold-centos-7"
image_project = "dev-cietools"
labels = {
app_id = "56"
cost_center = "156"
owner = "ops"
email = "ops"
}}
"test-adi-2"={
instance_name = "test-vm-2"
machine_type = "n1-standard-1"
zone = "us-east4-b"
tags = ["foo","bar"]
image_name = "centos-7"
image_project = "dev-cietools"
labels = {
app_id = "56"
cost_center = "856"
owner = "ops"
email = "ops"
}
}
}
subnetwork = "sandbox-us-east4"
project_id = "cloudops-dev01-sb"
instance_group = {
"igm" = {
group_manager_name = "test"
zone = "us-east4-b"
}
}
Since you used for_each in your google_compute_instance_group module, you have to use key to refer to individual instances of the module, e.g.
instances = module.qat_hosted_pacs_compute_engines["test-adi"].vm_name
or if you want to pass all vm_name created for all values of for_each, you can do:
instances = values(module.qat_hosted_pacs_compute_engines)[*].vm_name

unknown block type dns_config

i am getting error "Blocks of type "dns_config" are not expected here" in my terraform
main.tf file. Here is my code.
I am trying this in GCP
This code is breaking at dns_config section.
provider google {
project = var.project
region = var.region
version = "4.22.0"
credentials = var.credentials
}
resource "google_container_cluster" "primary" {
name = "${var.service-name}-${lower(var.site-id)}"
location = var.region
node_locations = [var.zone]
network = var.vpc-id
subnetwork = var.subnet-id
enable_autopilot = true
initial_node_count = var.initial-node-count
dns_config {
cluster_dns = "CLOUD_DNS"
cluster_dns_domain = "cluster.qg${var.site-id}stream"
cluster_dns_scope = "CLUSTER_SCOPE"
}
ip_allocation_policy {
cluster_secondary_range_name = var.subnet-pod-ip-range-name
services_secondary_range_name = var.subnet-service-ip-range-name
}
dynamic "release_channel" {
for_each = var.release-channel != null ? [{ channel : var.release-channel }] : []
content {
channel = var.release-channel
}
}
logging_service = var.logging_service
monitoring_service = var.monitoring_service
}
terraform {
backend "pg" {}
}

depends_on "A static list expression is required."

I'm using the depends_on block with a condition check while creating object storage. Surprisingly, I saw the following error. Any pointers on how to resolve it?
code:
locals {
is_gov = local.realm == "oc2" || local.realm == "oc3" ? true : false
}
resource "oci_identity_compartment" "gov_comp" {
compartment_id = var.comp1
description = "GOV COMP"
name = "gov_comp"
defined_tags = { "Operations.CostCenter" = "001" }
freeform_tags = { "Department" = "Executives" }
}
resource "oci_identity_compartment" "non_gov_comp" {
compartment_id = var.comp3
description = "commerical comp"
name = "non_gov_cmop"
defined_tags = { "Operations.CostCenter" = "000" }
freeform_tags = { "Department" = "Non-Executives" }
}
resource "oci_objectstorage_bucket" "test_bucket" {
compartment_id = var.compartment_id
name = var.bucket_name
namespace = var.bucket_namespace
depends_on = is_gov ? [oci_identity_compartment.gov_comp] : [oci_identity_compartment.non_gov_comp]
}
Error:
depends_on = local.is_gov ? [oci_identity_compartment.gov_comp] : [
A static list expression is required.
Your gov_comp and non_gov_comp are always created toghether. They are not exclusive. Thus your test_bucket should be create when both these resources get created:
resource "oci_objectstorage_bucket" "test_bucket" {
compartment_id = var.compartment_id
name = var.bucket_name
namespace = var.bucket_namespace
depends_on = [oci_identity_compartment.gov_com, oci_identity_compartment.non_gov_comp]
}

Terraform for_each if value exists in object

I would like to dynamically create some subnets and route tables from a .tfvars file, and then link each subnet to the associated route table if specified.
Here is my .tfvars file:
vnet_spoke_object = {
specialsubnets = {
Subnet_1 = {
name = "test1"
cidr = ["10.0.0.0/28"]
route = "route1"
}
Subnet_2 = {
name = "test2"
cidr = ["10.0.0.16/28"]
route = "route2"
}
Subnet_3 = {
name = "test3"
cidr = ["10.0.0.32/28"]
}
}
}
route_table = {
route1 = {
name = "route1"
disable_bgp_route_propagation = true
route_entries = {
re1 = {
name = "rt-rfc-10-28"
prefix = "10.0.0.0/28"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = "10.0.0.10"
}
}
}
route2 = {
name = "route2"
disable_bgp_route_propagation = true
route_entries = {
re1 = {
name = "rt-rfc-10-28"
prefix = "10.0.0.16/28"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = "10.0.0.10"
}
}
}
}
...and here is my build script:
provider "azurerm" {
version = "2.18.0"
features{}
}
variable "ARM_LOCATION" {
default = "uksouth"
}
variable "ARM_SUBSCRIPTION_ID" {
default = "asdf-b31e023c78b8"
}
variable "vnet_spoke_object" {}
variable "route_table" {}
module "names" {
source = "./nbs-azure-naming-standard"
env = "dev"
location = var.ARM_LOCATION
subId = var.ARM_SUBSCRIPTION_ID
}
resource "azurerm_resource_group" "test" {
name = "${module.names.standard["resource-group"]}-vnet"
location = var.ARM_LOCATION
}
resource "azurerm_virtual_network" "test" {
name = "${module.names.standard["virtual-network"]}-test"
location = var.ARM_LOCATION
resource_group_name = azurerm_resource_group.test.name
address_space = ["10.0.0.0/16"]
}
resource "azurerm_subnet" "test" {
for_each = var.vnet_spoke_object.specialsubnets
name = "${module.names.standard["subnet"]}-${each.value.name}"
resource_group_name = azurerm_resource_group.test.name
virtual_network_name = azurerm_virtual_network.test.name
address_prefixes = each.value.cidr
}
resource "azurerm_route_table" "test" {
for_each = var.route_table
name = "${module.names.standard["route-table"]}-${each.value.name}"
location = var.ARM_LOCATION
resource_group_name = azurerm_resource_group.test.name
disable_bgp_route_propagation = each.value.disable_bgp_route_propagation
dynamic "route" {
for_each = each.value.route_entries
content {
name = route.value.name
address_prefix = route.value.prefix
next_hop_type = route.value.next_hop_type
next_hop_in_ip_address = contains(keys(route.value), "next_hop_in_ip_address") ? route.value.next_hop_in_ip_address: null
}
}
}
That part works fine in creating the vnet/subnet/route resources, but the problem I face is to dynamically link each subnet to the route table listed in the .tfvars. Not all the subnets will have a route table associated with it, thus it will need to only run IF the key/value route is listed.
resource "azurerm_subnet_route_table_association" "test" {
for_each = {
for key, value in var.vnet_spoke_object.specialsubnets:
key => value
if value.route != null
}
lifecycle {
ignore_changes = [
subnet_id
]
}
subnet_id = azurerm_subnet.test[each.key].id
route_table_id = azurerm_route_table.test[each.key].id
}
The error I face with the above code is:
Error: Unsupported attribute
on main.tf line 65, in resource "azurerm_subnet_route_table_association" "test":
65: if value.route != null
This object does not have an attribute named "route".
I have tried various ways with no success, and I'm at a loss here and would appreciate any guidance posisble.
Based on your scenario, I'm guessing vnet_spoke_object in input looks like this:
vnet_spoke_object = {
specialsubnets = {
subnetA = {
cidr = "..."
}
subnetB = {
cidr = "..."
route = "..."
}
}
}
The problem with that is that a missing route entry doesn't resolve to null, it causes a panic or crash. You'd need to write your input like this (with explicit nulls):
vnet_spoke_object = {
specialsubnets = {
subnetA = {
cidr = "..."
route = null
}
subnetB = {
cidr = "..."
route = "..."
}
}
}
Or lookup route by name and provide a null default in your for map generator expression like this:
for_each = {
for key, value in var.vnet_spoke_object.specialsubnets:
key => value
if lookup(value, "route", null) != null
}

Resources