Get ip adress and hostname output in terraform

Viewed 2824

So I start to explore Terraform, and so far Im able to get my network and VMs up in Azure. Since I have seven VM`s that are created, I would like to get the IP-adress and the Hostname of this.

In my main.tf i have this:

provider "azurerm" {
  features {}
  # Configuration options
}



module "splunk_architect" {  
  source = "./modules/architect"
  
}

And just an example from my main.tf in /modules/architect

resource "azurerm_network_interface" "main" {
    for_each = toset(var.vm_names)
  name                = "${each.value}-nic"
  location            = azurerm_resource_group.rsg.location
  resource_group_name = azurerm_resource_group.rsg.name


  ip_configuration {
    name                          = "testconfiguration1"
    subnet_id                     = azurerm_subnet.subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.pubip[each.key].id
  }

And my outputs.tf in /modules/architect

output "ip" {
  value = azurerm_network_interface.main[each.key].private_ip_address
}

So when i run this, i get this error message:

Error: Reference to "each" in context without for_each

  on modules\architect\outputs.tf line 6, in output "ip":    6:   value = azurerm_network_interface.main[each.key].private_ip_address

The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set.

So I have tried to set the for_each in the module, but did not get that to work. I have also been through the documentations without any success. Any tips to what I can try to get the IP of each VM printed out?

1 Answers

Since you have multiple virtual machines (declared with for_each), you will also have multiple private IP addresses to return. You'll need to decide for your module what is the best way to return those IP addresses to the caller.

One common answer is to return a map whose keys are the elements from var.vm_names, so the caller can easily correlate the VM names it passed in to the IP addresses you're returning, like this:

output "ip" {
  value = tomap({
    for name, vm in azurerm_network_interface.main : name => vm.private_ip_address
  })
}

This is a for expression, which constructs a new data structure from an existing data structure by evaluating expressions against each element. In this case, it's taking the keys from azurerm_network_interface.main -- which will match the values given in for_each -- and mapping them to the private_ip_address attribute for each object.

The result will therefore appear as a map from VM names to IP addresses, perhaps like this:

{
  "example1" = "10.1.2.1"
  "example2" = "10.1.2.45"
}

Not directly related to your question, but note also that if your module only ever uses var.vm_names as a set then it can be better to declare it as a set in the first place, rather than converting it at each use, because then it'll be clearer to users of your module that the order of the strings inside doesn't matter and that there can't be two elements with the same string:

variable "vm_names" {
  type = set(string)
}

With that declaration, var.vm_names will already be a set of strings and so you don't need to explicitly convert it in for_each:

  for_each = var.vm_names
Related