Selecting specific subnet id from virtual network with multiple subnets - terraform

I want to reference the id of subnet 2 in another resource block but the subnets don't have an index value. i.e "${azurerm_virtual_network.test.subnet.2.id}" will not work.
resource "azurerm_virtual_network" "test" {
name = "virtualNetwork1"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
address_space = ["10.0.0.0/16"]
dns_servers = ["10.0.0.4", "10.0.0.5"]
subnet {
name = "subnet1"
address_prefix = "10.0.1.0/24"
}
subnet {
name = "subnet2"
address_prefix = "10.0.2.0/24"
}
subnet {
name = "subnet3"
address_prefix = "10.0.3.0/24"
security_group = "${azurerm_network_security_group.test.id}"
}
}

Solved with the help of Expose ID of an embedded subnet defined in azurerm_virtual_network #1913
...
subnet_id = "${azurerm_virtual_network.test.subnet.*.id[1]}"
...

azurerm_virtual_network.test.subnet.*.id[1]
What has happened here is that * gets all subnets and [1] is pointing to index 2.

Related

get the subnetid from subnets of type map(object)

I need to fetch the subnetid from azurerm_subnet data resource as subnet is used in dynamic block of azurerm_virtual_network as map(object) type
resource "azurerm_virtual_network" "example" {
name = "example-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
dynamic "subnet" {
for_each = var.subnets
content {
name = subnet.value.name
address_prefix = subnet.value.address_prefix
security_group = azurerm_network_security_group.example[subnet.key].id
}
}
}
Fetch the second subnetid to attach it to storage account
resource "azurerm_storage_account" "example" {
count = length(var.subnets)
name = "storageaccountname"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "GRS"
network_rules {
default_action = "Deny"
virtual_network_subnet_ids = ["${data.azurerm_subnet.subnetid.id}"]
}
}
Please can any one help to solve as i want to declare subnet in azurerm_virtual_network as dynamic block and get the subnet if from the data resource and please find my terraform.tfvars as below
subnets = {
subnet1 = {
name = "subnet1"
address_prefix = "10.0.0.0/24"
}
subnet2 = {
name = "subnet2"
address_prefix = "10.0.1.0/24"
}
subnet3 = {
name = "subnet3"
address_prefix = "10.0.2.0/24"
}
}
IMPORTANT
count = length(var.subnets) in resource "azurerm_storage_account" "example" {} is still there in your question which is logically incorrect as I have stated in the comments.
Answer
With your comments, I am assuming that you want to use id of subnet2 in network_rules of resource "azurerm_storage_account" "example" {}. With your current approach where creating subnets within the virtual network resource you have to use splat expressions and locals to make a map out of the set object and then can directly refer wherever is required.
While doing referencing even with locals and splat expressions it is still required to use the name of the subnet as it is not possible for terraform to know what you want without any data.
resource "azurerm_virtual_network" "example" {
name = "example-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
dynamic "subnet" {
for_each = var.subnets
content {
name = subnet.value.name
address_prefix = subnet.value.address_prefix
# security_group = azurerm_network_security_group.example[subnet.key].id ## I have ignored it as no relevant code is shared###
}
}
}
locals {
subnets = { for subnet in azurerm_virtual_network.example.subnet : subnet.name => subnet }
}
resource "azurerm_storage_account" "example" {
#count = length(var.subnets) ## Removed it too as logically incorrect with the current code ##
name = "storageaccountname"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "GRS"
network_rules {
default_action = "Deny"
virtual_network_subnet_ids = [local.subnets.subnet2.id]
}
You do not need a data source when referencing the attributes from one resource/module to another resource/module in the root module.
However, I suggest using azurerm_subnet resource for easier reference in spite of creating subnets in the virtual network resource itself because your might need Microsoft.Storage service_endpoints in your subnets for working with storage account with network_rules.

How can I reference azurerm_subnet which in not declared in the root module?

I have a virtual network with 2 subnets
Virtual network: vNetVPN-Dev
Subnet: snet-vgp-dev
Subnet: snet-internal-vm
resource "azurerm_virtual_network" "virtual_network" {
name = "vNetVPN-Dev"
location = var.resource_group_location_north_europe
resource_group_name = var.resource_group_name
address_space = ["10.1.16.0/23", "10.2.0.0/16", "172.16.100.0/24"]
subnet {
name = "snet-vgp-dev"
address_prefix = "10.2.1.0/24"
}
# =================== Virtual network for vm
subnet {
name = "snet-internal-vm"
address_prefix = "10.2.10.0/24"
}
tags = {
environment = var.tag_dev
}
}
and now I want to reference snet-internal-vm in this block of code (below)
resource "azurerm_network_interface" "nic" {
name = "internal-nic-vm"
location = var.resource_group_location_north_europe
resource_group_name = var.resource_group_name
ip_configuration {
name = "internal-vm"
subnet_id = **here_I_want_to_reference**
private_ip_address_allocation = "Dynamic"
}
}
I tried to reproduce the same in my environment to create NIC creation with Subnet reference:
Terraform Code
provider "azurerm" {
features {}
}
resource "azurerm_virtual_network" "virtual_network" {
name = "vNetVPN-Dev"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
address_space = ["10.1.16.0/23", "10.2.0.0/16", "172.16.100.0/24"]
subnet {
name = "snet-vgp-dev"
address_prefix = "10.2.1.0/24"
}
subnet {
name = "snet-internal-vm"
address_prefix = "10.2.10.0/24"
}
}
#-----NIC Creation-----------
resource "azurerm_network_interface" "nic" {
name = "internal-nic-vm"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
ip_configuration {
name = "internal-vm"
subnet_id = azurerm_virtual_network.virtual_network.subnet.*.id[1]
private_ip_address_allocation = "Dynamic"
}
}
Terraform Apply.
As mentioned by #sylvainmtz , I could get subnet referred successfully while creating NIC.
Based on your question, I can only assume you should be using data blocks to do this. https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subnet

how to create two vnets and some subnets in each one of those VNETs using terraform

how to create two VNETs and some subnets in each one of those VNETs dynamically using terraform?
Currently, I'm creating four separate resources 2 each for VNETs and subnets.
I wanna use just one resource block for VNET and one resource block for the subnet in the tf code and have terraform pick for say VNET "test" two subnets (test1-subnet, test2-subnet) and second VNET say "dev" two separate subnets (dev1-subnet, dev2-subnet).
How to achieve this scenario using terraform for azure provider?
resource "azurerm_virtual_network" "example" {
name = "rest-network"
address_space = ["10.0.0.0/16"]
location = "eastus"
resource_group_name = "test-rg"
}
resource "azurerm_virtual_network" "example1" {
name = "test-network"
address_space = ["10.1.0.0/16"]
location = "eastus"
resource_group_name = "test-rg"
}
resource "azurerm_subnet" "example" {
name = "rest-subnet"
resource_group_name = "test-rg"
virtual_network_name = azurerm_virtual_network.example.name
address_prefix = "10.0.1.0/24"
enforce_private_link_endpoint_network_policies = true
}
resource "azurerm_subnet" "example1" {
name = "test-subnet"
resource_group_name = "test-rg"
virtual_network_name = azurerm_virtual_network.example1.name
address_prefix = "10.1.1.0/24"
enforce_private_link_endpoint_network_policies = true
}
You have to parametrize your setup. One way, could be as follows (example which you may need to modify to suit your specific need):
variable "vnets" {
default = {
test = {
address_space = ["10.0.0.0/16"]
resource_group_name = "test-rg"
subnets = [{
address_prefix = "10.0.1.0/24"
},
{
address_prefix = "10.0.2.0/24"
}
]
},
dev = {
address_space = ["10.1.0.0/16"]
resource_group_name = "dev-rg"
subnets = [{
address_prefix = "10.1.1.0/24"
},
{
address_prefix = "10.1.2.0/24"
}
]
}
}
}
locals {
# flatten the var.vnets variable
vnet_subnet_map = merge([
for vnet_name, vnet_details in var.vnets:
{
for idx, subnet in vnet_details.subnets:
"${vnet_name}-${idx}" => {
vnet_name = vnet_name
subnet_name = "${vnet_name}${idx}-subnet"
address_space = vnet_details.address_space
resource_group_name = vnet_details.resource_group_name
subnet_address_prefix = subnet.address_prefix
}
}
]...) # do NOT remove the dots
}
Then you create the two vnets and their subnets:
resource "azurerm_virtual_network" "vnet" {
for_each = var.vnets
name = "${each.key}-network"
address_space = each.value.address_space
location = "eastus"
resource_group_name = each.value.resource_group_name
}
resource "azurerm_subnet" "subnet" {
for_each = local.vnet_subnet_map
name = each.value.subnet_name
resource_group_name = each.value.resource_group_name
virtual_network_name = azurerm_virtual_network.vnet[each.value.vnet_name].name
address_prefix = each.value.subnet_address_prefix
enforce_private_link_endpoint_network_policies = true
}

Terraform conditional creation of Service endpoint for a subnet

I have a terraform script for creating a virtual network and subnets in azure. The code inherits a module which has the both creation of vnet and subnet. I am trying to create a VNET and two subnet, but want to enable service end point for a specific subnet only. Need help on how to do it
module "vnet" {
source = "./modules/VirtualNetwork"
VirtualNetwork_Name = "${var.prefix}-${var.resource_group_name}-VNET1"
Resource_Group_Name = azurerm_resource_group.resource_group.name
Location = azurerm_resource_group.resource_group.location
VirtualNetwork_AddressSpace = ["10.4.0.0/23"]
Subnet_Name = ["snet-1","snet-2"]
Subnet_Addresses = ["10.4.0.0/24","10.4.1.0/24"]
Service_Endpoints = vnet.Subnet_Name == "snet-1" ? ["Microsoft.AzureCosmosDB"] : [""]
if subnet=="snet-1" then ["Microsoft.AzureCosmosDB"] else ["nothing"]
Tags = {
environment = "prod"
resource = "VNET"
cost_center = "Test Cost Ceneter"
}
}
The below code is for network module
# Creates the virtual network for the resources
resource "azurerm_virtual_network" "vnet" {
name = var.VirtualNetwork_Name
location = var.Location
resource_group_name = var.Resource_Group_Name
address_space = var.VirtualNetwork_AddressSpace
tags = var.Tags
}
# Create two subnet for the vnet
resource "azurerm_subnet" "subnet" {
name = var.Subnet_Name[count.index]
address_prefix = var.Subnet_Addresses[count.index]
resource_group_name = var.Resource_Group_Name
virtual_network_name = azurerm_virtual_network.vnet.name
count = length(var.Subnet_Name)
# service_endpoints = ["Microsoft.AzureCosmosDB"]
service_endpoints = var.Service_Endpoints
}
According to your requirement, you just want to enable a service endpoint for a specific subnet only. You can set the conditional-expressions in the azurerm_subnet block.
You could change the code like this and I have validated it on my side.
main.if in the root directory.
variable "subnet_name" {
default = ["subnet1","subnet2"]
}
# retrieve a specific subnet via the index of subnet list.
locals {
subnet_name_enable_service_endpoint = element(var.subnet_name,0)
}
...
module "vnet" {
source = "./modules/VirtualNetwork"
VirtualNetwork_Name = "${var.prefix}-${var.resource_group_name}-VNET1"
Resource_Group_Name = azurerm_resource_group.main.name
Location = azurerm_resource_group.main.location
VirtualNetwork_AddressSpace = ["10.4.0.0/23"]
Subnet_Addresses = ["10.4.0.0/24","10.4.1.0/24"]
Subnet_Name = var.subnet_name
specfic_subnet_name = local.subnet_name_enable_service_endpoint
Service_Endpoints = ["Microsoft.AzureCosmosDB"]
Tags = {
environment = "prod"
resource = "VNET"
cost_center = "Test Cost Ceneter"
}
}
Network module configuration is in the path ./modules/VirtualNetwork.
# declare a variable for accepting the specific subnet.
variable "specfic_subnet_name" {
}
...
#Create Virtual Network in Primary Resource Group
resource "azurerm_virtual_network" "primary" {
name = var.VirtualNetwork_Name
resource_group_name = var.Resource_Group_Name
address_space = var.VirtualNetwork_AddressSpace
location = var.Location
tags = var.Tags
}
#Create Subnet in Virtual Network
resource "azurerm_subnet" "primary" {
count = length(var.Subnet_Name)
name = var.Subnet_Name[count.index]
resource_group_name = var.Resource_Group_Name
virtual_network_name = azurerm_virtual_network.primary.name
address_prefixes = [element(var.Subnet_Addresses,count.index)]
service_endpoints = element(var.Subnet_Name,count.index) == var.specfic_subnet_name ? var.Service_Endpoints : [""]
}
./modules/VirtualNetwork only the subnet creation part
# Create two subnet for the vnet
resource "azurerm_subnet" "subnet" {
name = var.Subnet_Name[count.index]
address_prefix = var.Subnet_Addresses[count.index]
resource_group_name = var.Resource_Group_Name
virtual_network_name = azurerm_virtual_network.vnet.name
count = length(var.Subnet_Name)
service_endpoints = element(var.Service_Endpoints,count.index)
}
main.tf
module "vnet" {
source = "./modules/VirtualNetwork"
VirtualNetwork_Name = "${var.prefix}-${var.resource_group_name}-VNET1"
Resource_Group_Name = azurerm_resource_group.resource_group.name
Location = azurerm_resource_group.resource_group.location
VirtualNetwork_AddressSpace = ["10.4.0.0/23"]
Subnet_Name = ["snet-1","snet-2"]
Subnet_Addresses = ["10.4.0.0/24","10.4.1.0/24"]
Service_Endpoints = [["Microsoft.AzureCosmosDB",""], [""]]
}
The key is to pass the service end point as a list Service_Endpoints = [["Microsoft.AzureCosmosDB",""], [""]]. Based on the index of the subnet it will assign the service end point

how to reference objects in terraform

I am creating an Azure VNet using terraform, and creating a couple subnets in it. Later on , I want to create a network interface, and want to put it in one of the subnets already created for VNet. I do not know how to reference that subnet.
I tried below but it is now working:
subnet_id = "${azurerm_virtual_network.virtual-network.subnet.ServersSubnet.id}"
resource "azurerm_virtual_network" "virtual-network" {
name = "${var.ClientShortName}-az-network"
address_space = ["${local.AzureInfraNetwork}"]
location = "${var.resource-location}"
resource_group_name = "${azurerm_resource_group.test-resource-group.name}"
subnet {
name = "ServersSubnet"
address_prefix = "${local.ServersSubnet}"
}
subnet {
name = "GatewaySubnet"
address_prefix = "${local.GatewaySubnet}"
}
}
Error: Cannot index a set value
on main.tf line 120, in resource "azurerm_network_interface" "DCNIC":
120: subnet_id = "${azurerm_virtual_network.virtual-network.subnet.ServersSubnet.id}"
Block type "subnet" is represented by a set of objects, and set elements do
not have addressable keys. To find elements matching specific criteria, use a
"for" expression with an "if" clause.
Below is the complete solution.
If the subnets are created as blocks, you can reference a given subnet's resource ID as follows:
resource "azurerm_resource_group" "main" {
name = "vnet-rg"
location = "eastus"
}
resource "azurerm_virtual_network" "main" {
name = "my-vnet"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
address_space = ["10.0.0.0/16"]
subnet {
name = "subnet1"
address_prefix = "10.0.1.0/24"
}
subnet {
name = "subnet2"
address_prefix = "10.0.2.0/24"
}
subnet {
name = "subnet3"
address_prefix = "10.0.3.0/24"
}
}
output "subnet1_id" {
value = azurerm_virtual_network.main.subnet.*.id[0]
}
output "subnet2_id" {
value = azurerm_virtual_network.main.subnet.*.id[1]
}
output "subnet3_id" {
value = azurerm_virtual_network.main.subnet.*.id[2]
}
When creating subnets as blocks you must reference them using the list syntax, e.g.:
foo = azurerm_virtual_network.virtual-network.subnet[0].id
bar = azurerm_virtual_network.virtual-network.subnet[1].id
This is useful if the subnets form a pool of redundant resources and you don't care about referencing any subnet in particular.
I don't believe that's your case, so you might consider creating your subnets as separated resources, e.g:
resource "azurerm_virtual_network" "main" {
name = "${var.ClientShortName}-az-network"
address_space = [local.AzureInfraNetwork]
location = var.resource-location
resource_group_name = azurerm_resource_group.test-resource-group.name
}
resource "azurerm_subnet" "server" {
virtual_network_name = azurerm_virtual_network.main.name
name = "ServersSubnet"
address_prefix = local.ServersSubnet
}
resource "azurerm_subnet" "gateway" {
virtual_network_name = azurerm_virtual_network.main.name
name = "GatewaySubnet"
address_prefix = local.ServersSubnet
}
Then you could reference one of your subnets using the regular object attribute syntax:
foo = azurerm_subnet.server.id
Also note that I'm using terraform => 0.12 syntax, so I can write foo.bar instead of "${foo.bar}" when I don't need string interpolation.
If anyone else needs the answer to this use this:
"${element(azuread_application.events_backend.app_role[*].id,0)}"
You can break this out into creating the virtual network and the subnet as separate resources. Advantage of this is that you can return your subnets as a map rather than a list, making it easier to retrieve by name later on, it also makes it stable if you need to add/remove subnets at a later stage.
locals {
subnets = {
Servers = "10.0.1.0/24",
Gateway = "10.0.2.0/24"
}
}
resource "azurerm_resource_group" "main" {
name = "vnet-rg"
location = "eastus"
}
resource "azurerm_virtual_network" "main" {
name = "my-vnet"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
address_space = [ local.AzureInfraNetwork ]
}
resource "azurerm_subnet" "main" {
for_each = var.subnets
name = "${each.key}-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = [ each.value ]
}
output "subnets" {
value = azurerm_subnet.main
}

Resources