Is it possible to use the value of a resource in a different resource when those resources are in different modules

main.tf
variables.tf
baseline_modules/vnet
…main.tf
…variables.tf
…outputs.tf
baseline_modules/app_service
…main.tf
…variables.tf
…outputs.tf

I have a resource in baseline_modules/vnet/main.tf

resource "azurerm_private_dns_zone" "app_service_dns_zone" {
  name                = "privatelink.azurewebsites.net"
  resource_group_name = var.resource_group_name
}

In baseline_modules/app_service/main.tf I need the ID of azurerm_private_dns_zone.app_service_dns_zone but since its in another module, its proving tricky.

  private_dns_zone_group {
    name                 = "private-dns-zone-group"
    private_dns_zone_ids = [**azurerm_private_dns_zone.app_service_dns_zone.id**]
  }

I have tried using an output from the vnets module and changing the bold section above to

module.vnet.app_service_dns_zone.id

But that doesnt work. It says there is no module vnet declared in module.services

The root module main.tf calls the app service module and the vnets modules

module "services" {
  for_each = local.services
  source              = "./baseline_modules/app_service"
  common_tags         = local.global_tags
  resource_group_name = azurerm_resource_group.main.name
  environment_name    = var.environment_name
  product_name        = var.product_name
  blah blah
}

module "vnets" {
  source              = "./baseline_modules/vnet"
  common_tags         = local.global_tags
  resource_group_name = azurerm_resource_group.main.name
  environment_name    = var.environment_name
  primary_location    = var.primary_location
  secondary_location  = var.secondary_location
  naming_conventions  = module.naming_conventions
  replicate_data      = var.replicate_data
}

I think I am close, but cant quite put it together. Initially I had the code for the private dns zone in the baseline_modules/app_service/main.tf but that had its own problems as it was trying to create the zone 8 time(1 for each web app in the for each loop). This of course causes Terraform to freak out because it see it already exists and says it needs to be imported in order to be used.

I need to DNS zone to be created once and each of the private endpoints/private dns zone groups to use the same value.

Thoughts?

Hi @washburnjoe,

From what you’ve described it sounds like the missing piece here is that the output value app_service_dns_zone is exported from module "vnets", and so visible to the root module, but you haven’t passed it in to module "services" and so it isn’t visible there.

To complete what you started I would first add a new input variable declaration into your ./baseline_modules/app_service module, like this:

variable "dns_zone_id" {
  type = string
}

Then in your private_dns_zone_group block you can refer to this as var.dns_zone_id:

  private_dns_zone_group {
    name                 = "private-dns-zone-group"
    private_dns_zone_ids = [var.dns_zone_id]
  }

If you try just the above you’ll see Terraform report an error that dns_zone_id is required when calling the services module but not yet specified. To address that error you can then finally change the module "services" block to pass the output value from module "vnets" into the value of that input variable:

module "services" {
  source   = "./baseline_modules/app_service"
  for_each = local.services

  # ...
  dns_zone_id = module.vnet.app_service_dns_zone.id
}

The reason this works but what you tried did not is that module.vnet exists in the root module scope, not in the services module scope. Each module has its own separate namespaces, so in order to make the same value visible inside the services module you need to explicitly pass it in as an input variable.

What you’re doing here is the beginnings of module composition. The documentation page I linked there contains some more information about different design patterns for passing data between modules, in case it’s useful to see some different examples similar to what you are trying for here.

1 Like

Thank you so much. That gets me much closer. Its possible my resource does not have an id, because the error is now

│   on main.tf line 47, in module "services":
│   47:   dns_zone_id         = module.vnets.app_service_dns_zone.id
│     ├────────────────
│     │ module.vnets.app_service_dns_zone is "privatelink.azurewebsites.net"
│ 
│ Can't access attributes on a primitive-typed value (string).

So it would appear that the dns zone does not have an ID. But you solved what I asked. I am truly thankful. I am going to read deeper into the documentation you linked.

From this error it sounds like your output "app_service_dns_zone" is configured to return just the DNS zone name as a string rather than an object. I’m not familiar with with Azure to know if the DNS zone name is a suitable ID for a zone but if it is then you could drop the .id from the end of that expression and just access the string directly.

If that zone name string isn’t a suitable value for private_dns_zone_ids then you’ll need to modify the output "app_service_dns_zone" to return whatever data you need to pass from one module to the other.