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
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}"
}
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
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" {}
}
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]
}
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
}