Terraform conditional creation of Service endpoint for a subnet - azure

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

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

Terraform subnet call into virtual network gateway

How we can call subnet into Virtual network gateway?
Subnet
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-vpg-dev"
address_prefix = "10.2.1.0/24"
}
tags = {
environment = var.tag_dev
}
}
Virtual network gateway
resource "azurerm_virtual_network_gateway" "virtual_network_gateway" {
name = "vgw-vgp-dev"
location = var.resource_group_location_north_europe
resource_group_name = var.resource_group_name
type = "Vpn"
vpn_type = "RouteBased"
active_active = false
enable_bgp = false
sku = "Basic"
ip_configuration {
name = azurerm_public_ip.public_ip_address.name
public_ip_address_id = azurerm_public_ip.public_ip_address.id
private_ip_address_allocation = "Static"
subnet_id = **here I wan to call my subnet which is defined in the code above**
}
}
so as you can see that there are 2 code blocks, 1 is subnet and the other is virtual network gateway.
I want to refer subnet (snet-vpg-dev) into virtual network gateway as a value of parameter called subnet_id
To get the Id of the subnet, you can take the subnet exported attribute of the vnet, convert it to a list and take the first element, like this
ip_configuration {
name = azurerm_public_ip.public_ip_address.name
public_ip_address_id = azurerm_public_ip.public_ip_address.id
private_ip_address_allocation = "Static"
subnet_id = tolist(azurerm_virtual_network.virtual_network.subnet)[0].id
}
Another solution is to use the azurerm_subnet resource rather than the inline subnet blocks.
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet
You can directly retrieve the id of the subnet since it has a dedicated resource
The template would be something like
Subnet
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"]
tags = {
environment = var.tag_dev
}
}
resource "azurerm_subnet" "subnet" {
name = "vNetVPN-Dev"
resource_group_name = var.resource_group_name
virtual_network_name = azurerm_virtual_network.virtual_network.name
address_prefixes = ["10.2.1.0/24"]
}
Virtual network gateway
resource "azurerm_virtual_network_gateway" "virtual_network_gateway" {
name = "vgw-vgp-dev"
location = var.resource_group_location_north_europe
resource_group_name = var.resource_group_name
type = "Vpn"
vpn_type = "RouteBased"
active_active = false
enable_bgp = false
sku = "Basic"
ip_configuration {
name = azurerm_public_ip.public_ip_address.name
public_ip_address_id = azurerm_public_ip.public_ip_address.id
private_ip_address_allocation = "Static"
subnet_id = azurerm_subnet.subnet.id
}
}

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 azure virtual network creation

I have written the below code for creating a virtual network in terraform azure:
I have files in the root folder for main.tf,variables.tf and terraform.tfvars. I have got two modules for resource group and virtual network.
In the root folder I have the below code:
main.tf
#Resource Group Creation
module "resource_group" {
source = "./modules/resource_group"
rgname_var = var.resource_group
loc_var = var.location
}
#Virtual Network Creation
module "vnet" {
source = "./modules/vnet"
loc = module.resource_group.loc_o
rgname = module.resource_group.rgname_o
address_space = var.vnet_address_space
terraform.tfvars:
vnet_name = "VirtualNetwork"
vnet_address_space = "<ip>"
subnet1_address_prefix="<ip1>"
subnet2_address_prefix="<ip2>"
subnet3_address_prefix="<ip3">
In the vnet module, I have the below code:
resource "azurerm_virtual_network" "vnet" {
name = var.vnet_name
location = var.loc
resource_group_name = var.rgname
address_space = var.vnet_address_space
}
#Subnet Creation
resource "azurerm_subnet" "subnet-1" {
name= var.subnet_name1
resource_group_name = var.rgname
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefix = var.subnet1_address_prefix
}
#Subnet Creation
resource "azurerm_subnet" "subnet-2" {
name= var.subnet_name2
resource_group_name = var.rgname
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefix = var.subnet2_address_prefix
}
#Subnet Creation
resource "azurerm_subnet" "subnet-3" {
name= var.subnet_name3
name= var.subnet_name1
resource_group_name = var.rgname
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefix = var.subnet3_address_prefix
}
I am getting the below error :
address_space = var.vnet_address_space |---------------- | var.vnet_address_space is ""
Inappropriate value for attribute "address_space": list of string required.*
I have tried giving one ip for virtual network address space at the first place,because it was working for single subnets. then the system was expecting list instead of string. Any idea how to set the values for virtual network?
Your current var.vnet_address_space is just a string:
vnet_address_space = "<ip>"
However address_space should be a list of strings, not a single string only.
The easiest way to change it to list of strings would be:
vnet_address_space = ["<ip>"]
Alternatively, you can do it as follows:
resource "azurerm_virtual_network" "vnet" {
name = var.vnet_name
location = var.loc
resource_group_name = var.rgname
address_space = [var.vnet_address_space]
}

Resources