Terraform Module to create basic Azure Network Resources with optional Subnet/s, NSG/s, Service delegation, service endpoints and route table/s
Type of resources that are supported within the module:
- Virtual Network
- Subnets
- Subnet Service Delegation
- Virtual network service endpoints
- Private Link service / endpoint network policies for subnets
- Network Security Groups (NSG)
- Route Tables
- Test sample code Complete
- Tets sample code Simple
- Resource Group
- Azure DDoS Protection Plan
- One set of vNet
- Attaching DDos Protection (This module does not create DDos Protection Plan)
- Three sets of subnet
- Subnet 1
- With Service Endpoint to Microsoft Storage
- With RouteTable and two routes
- With NSG with one inbound and two outbound rules
- Subnet 2
- With Delegation
- No NSG
- No RouteTable
- Subnet 3
- With empty NSG (Default Rules only)
- With empty RouteTable
- Subnet 1
resource "azurerm_resource_group" "shared_network_hub" {
name = "rg-shared-hub-westeurope-001"
location = "westeurope"
}
# Creating ddos protection plan if var.ddos_plan is true, default is false
resource "azurerm_network_ddos_protection_plan" "westeu" {
name = "ddos-westeurope"
resource_group_name = azurerm_resource_group.shared_network_hub.name
location = "westeurope"
tags = {
ProjectName = "test-shared-hub"
Environment = "test"
Owner = "user@corp.com"
BusinessUnit = "IT-CORP"
CostCenter = "TestUnit"
}
}
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
# By default, this module does not create a resource group, provide the resource group name here
resource_group_name = azurerm_resource_group.shared_network_hub.name
location = "westeurope"
vnet_name = "vnet-shared-hub-westeurope-001"
vnet_address_space = ["10.0.0.0/23"]
dns_servers = [] # (Optional: Specify list of custom DNS servers) default is Azure provided DNS
# (Optional: Attaching DDoS Plan to virtual network) - by default DDoS Protection plan is not attached.
ddos_protection_plan = [{
ddos_protection_plan_id = azurerm_network_ddos_protection_plan.westeu.id
enable = true
}]
# Multiple Subnets, Service delegation, Service Endpoints, Network security groups and RouteTable.
# NSG association is added automatically for all subnets that specify NSG.
# RouteTable association is added automatically for all subnets that specify RouteTable.
subnets = {
# Subnet 1
subnet1 = {
subnet_name = "subnet1"
subnet_address_prefix = ["10.0.0.0/24"]
service_endpoints = ["Microsoft.Storage"]
enforce_private_link_endpoint_network_policies = true
# Creating NSG
# - To create empty NSG with default rules, only specify nsg = true
nsg = true
# Specifying NSG Inbound Rules
nsg_inbound_rules = [
{
name = "AllowHTTPSInbound"
description = "Allowing HTTPS Inbound"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "TCP"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "Internet"
destination_address_prefix = "VirtualNetwork"
}
]
# Specifying NSG Outbound Rules
nsg_outbound_rules = [
{
name = "AllowStorageOutbound"
description = "Allowing Outbound access to Azure Storage"
priority = 4020
direction = "Outbound"
access = "Allow"
protocol = "TCP"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "Storage"
},
{
name = "DenyInternetOutbound"
priority = 4096
direction = "Outbound"
access = "Deny"
protocol = "TCP"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "Internet"
}
]
# Creating RouteTable
# - To Create Empty RouteTable only specify route_table = true
route_table = true
# Specifying Routes
routes = [{
name = "test"
address_prefix = "10.0.0.0/24"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = "10.0.2.10"
},
{
name = "test2"
address_prefix = "52.32.152.22/32"
next_hop_type = "Internet"
}]
}
# Subnet 2
subnet2 = {
subnet_name = "subnet2"
subnet_address_prefix = ["10.0.1.0/25"]
delegation = [
{
name = "container_group_delegation"
service_name = "Microsoft.ContainerInstance/containerGroups"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
}
]
}
# Subnet 3
subnet3 = {
subnet_name = "subnet3"
subnet_address_prefix = ["10.0.1.128/25"]
nsg = true
route_table = true
}
}
}
# Adding Tag's to your Azure resources (Optional)
tags = {
ProjectName = "test-shared-hub"
Environment = "test"
Owner = "user@corp.com"
BusinessUnit = "IT-CORP"
CostCenter = "TestUnit"
}
}
By default, this module will not create or attach a DDoS Protection Plan to the Virtual Network. DDoS Protection Plan is limited to being created only once in every region (for now) so it didn't make sense to create it within the module. It is however possible to attach already created DDOs Plan outside of the module by defining the optional code below.
# (Optional: Attaching DDoS Plan to virtual network) - by default DDoS Protection plan is not attached.
ddos_protection_plan = [{
ddos_protection_plan_id = azurerm_network_ddos_protection_plan.ddos.id
enable_ddos_protection_plan = true
}]
Note: See Terraform documentation on how to create Azure DDos Protection Plan here: AzureNetwork DDoS Protection Plan
This is an optional feature and only applicable if you are using your own DNS servers superseding default Azure provided DNS Servers. Set the argument dns_servers = ["4.4.4.4"]
to enable this option. For multiple DNS servers, set the argument and provide comma seperated list of dns servers dns_servers = ["4.4.4.4", "8.8.8.8"]
This module handles the following types of subnet creation:
- Subnets - add, change, or delete of subnet supported. The subnet name and address range must be unique within the address space for the virtual network. A subnet may optionally have one or more service endpoints enabled for it. To enable a service endpoint for a service, select the service or services that you want to enable service endpoints for from the Services list. A subnet may optionally have one or more delegations enabled for it. Subnet delegation gives explicit permissions to the service to create service-specific resources in the subnet using a unique identifier during service deployment. To delegate for a service, select the service you want to delegate to from the Services list.
Service Endpoints allows connecting certain platform services into virtual networks. With this option, Azure virtual machines can interact with Azure SQL and Azure Storage accounts, as if they’re part of the same virtual network, rather than Azure virtual machines accessing them over the public endpoint.
This module supports enabling the service endpoint of your choosing under the virtual network and with the specified subnet. The list of Service endpoints to associate with the subnet values include: Microsoft.AzureActiveDirectory
, Microsoft.AzureCosmosDB
, Microsoft.ContainerRegistry
, Microsoft.EventHub
, Microsoft.KeyVault
, Microsoft.ServiceBus
, Microsoft.Sql
, Microsoft.Storage
and Microsoft.Web
Note: See complete list of supported service endpoints here: Azure virtual network service endpoints
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
#..omitted
# Multiple Subnets, Service delegation, Service Endpoints, Network security groups and RouteTable.
# NSG association is added automatically for all subnets that specify NSG.
# RouteTable association is added automatically for all subnets that specify RouteTable.
subnet1 = {
subnet_name = "subnet1"
subnet_address_prefix = ["10.0.0.0/24"]
service_endpoints = ["Microsoft.Storage"]
}
#..omitted
}
This module supports enabling the service delegation of your choosing under the specified subnet. For more information and see what Service Delegations are supported, check the terraform resource documentation.
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
#..omitted
# Multiple Subnets, Service delegation, Service Endpoints, Network security groups and RouteTable.
# NSG association is added automatically for all subnets that specify NSG.
# RouteTable association is added automatically for all subnets that specify RouteTable.
subnets = {
#..omitted
subnet2 = {
subnet_name = "subnet2"
subnet_address_prefix = ["10.0.1.0/25"]
delegation = [
{
name = "container_group_delegation"
service_name = "Microsoft.ContainerInstance/containerGroups"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
}
]
}
}
#..omitted
}
Network policies, like network security groups (NSG), are not yet fully supported (In Public Preview in limited regions) for Private Link Endpoints. In order to deploy a Private Link Endpoint on a given subnet, you must set the enforce_private_link_endpoint_network_policies
attribute to true
. This setting is only applicable for the Private Link Endpoint, for all other resources in the subnet access is controlled based on the Network Security Group which can be configured using the nsg = true
and specifying nsg_inbound_rules
& nsg_outbound_rules
resource in the module.
In this module you can Enable or Disable network policies for the private link endpoint on the subnet. The default value is false
.
Note: If you are enabling the Private Link Endpoints on the subnet you shouldn't use Private Link Services as it's conflicts.
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
#..omitted
# Multiple Subnets, Service delegation, Service Endpoints, Network security groups and RouteTable.
# NSG association is added automatically for all subnets that specify NSG.
# RouteTable association is added automatically for all subnets that specify RouteTable.
subnets = {
subnet1 = {
subnet_name = "subnet1"
subnet_address_prefix = ["10.0.0.0/24"]
enforce_private_link_endpoint_network_policies = true
}
}
#..omitted
}
In order to deploy a Private Link Service on a given subnet, you must set the enforce_private_link_service_network_policies
attribute to true
. This setting is only applicable for the Private Link Service, for all other resources in the subnet access is controlled based on the Network Security Group which can be configured using the nsg = true
and specifying nsg_inbound_rules
& nsg_outbound_rules
resource in the module.
In this module you can Enable or Disable network policies for the private link service on the subnet. The default value is false
.
Note: If you are enabling the Private Link service on the subnet then, you shouldn't use Private Link endpoints as it's conflicts.
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
#..omitted
# Multiple Subnets, Service delegation, Service Endpoints, Network security groups and RouteTable.
# NSG association is added automatically for all subnets that specify NSG.
# RouteTable association is added automatically for all subnets that specify RouteTable.
subnets = {
subnet1 = {
subnet_name = "subnet1"
subnet_address_prefix = ["10.0.0.0/24"]
enforce_private_link_service_network_policies = true
}
}
#..omitted
}
By default, a network security group is not created for the subnet, use nsg = true
to create NSG for the specified subnet
and use nsg_inbound_rules
and nsg_outbound_rules
in this module to create more specific rules for inbound and outbound flows.
In the Source and Destination columns, VirtualNetwork
, AzureLoadBalancer
, and Internet
are service tags, rather than IP addresses. In the protocol column, Any encompasses TCP
, UDP
, and ICMP
are allowed. When creating a rule, you can specify TCP
, UDP
, ICMP
or *
. 0.0.0.0/0
in the Source and Destination columns represents all addresses.
Note: If a Network security group (NSG) is created it will automatically be attached to the specific subnet.
Note: You cannot remove the default rules, but you can override them by creating rules with higher priorities.
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
#..omitted
# Multiple Subnets, Service delegation, Service Endpoints, Network security groups and RouteTable.
# NSG association is added automatically for all subnets that specify NSG.
# RouteTable association is added automatically for all subnets that specify RouteTable.
subnets = {
subnet1 = {
subnet_name = "subnet1"
subnet_address_prefix = ["10.0.0.0/24"]
# Creating NSG
# To create empty NSG with default rules, only specify nsg = true
nsg = true
# Specifying NSG Inbound Rules
nsg_inbound_rules = [
{
name = "AllowHTTPSInbound"
description = "Allowing HTTPS Inbound"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "TCP"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "Internet"
destination_address_prefix = "VirtualNetwork"
}
]
# Specifying NSG Outbound Rules
nsg_outbound_rules = [
{
name = "AllowStorageOutbound"
description = "Allowing Outbound access to Azure Storage"
priority = 4020
direction = "Outbound"
access = "Allow"
protocol = "TCP"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "Storage"
},
{
name = "DenyInternetOutbound"
priority = 4096
direction = "Outbound"
access = "Deny"
protocol = "TCP"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "Internet"
}
]
#..omitted
}
}
#..omitted
}
Name | Description | Type | Sample Input |
---|---|---|---|
name |
(Required) The name of the security rule. This needs to be unique across all Rules in the Network Security Group. Changing this forces a new resource to be created. | string | "AllowStorageOutbound" |
description |
(Optional) A description for this rule. Restricted to 140 characters. | string | "Allowing Outbound access to Azure Storage" |
priority |
(Required) Specifies the priority of the rule. The value can be between 100 and 4096 . The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule. |
number | 4020 |
direction |
(Required) The direction specifies if rule will be evaluated on incoming or outgoing traffic. Possible values are Inbound and Outbound . |
string | "Outbound" |
access |
(Required) Specifies whether network traffic is allowed or denied. Possible values are Allow and Deny . |
string | "Allow" |
protocol |
(Required) Network protocol this rule applies to. Possible values include Tcp , Udp , Icmp , Esp , Ah or * (which matches all). |
string | "TCP" |
source_port_range |
(Optional) Source Port or Range. Integer or range between 0 and 65535 or * to match any. This is required if source_port_ranges is not specified. |
string | "*" |
source_port_ranges |
(Optional) List of source ports or port ranges. This is required if source_port_range is not specified. |
list | ["443", "3389"] |
destination_port_range |
(Optional) Destination Port or Range. Integer or range between 0 and 65535 or * to match any. This is required if destination_port_ranges is not specified. |
string | "443" |
destination_port_ranges |
(Optional) List of destination ports or port ranges. This is required if destination_port_range is not specified. |
list | ["443", "445"] |
source_address_prefix |
(Optional) CIDR or source IP range or * to match any IP. Tags such as ‘VirtualNetwork’ , ‘AzureLoadBalancer’ and ‘Internet’ can also be used. This is required if source_address_prefixes is not specified. To use subnets prefix as source address there is a support to mention the subnet key in the defined subnet like subnet1 to get the Address Prefix of subnet 1 |
string | "VirtualNetwork" |
source_address_prefixes |
(Optional) List of source address prefixes. Tags may not be used. This is required if source_address_prefix is not specified. |
list | ["10.52.55.25", 10.52.56.25"] |
destination_address_prefix |
(Optional) CIDR or destination IP range or * to match any IP. Tags such as ‘VirtualNetwork’ , ‘AzureLoadBalancer’ and ‘Internet’ can also be used. Besides, it also supports all available Service Tags like ‘Sql.WestEurope‘ , ‘Storage.EastUS‘ , etc. You can list the available service tags with the cli: shell az network list-service-tags --location westcentralus . For further information please see Azure CLI - az network list-service-tags. This is required if destination_address_prefixes is not specified. to use subnets prefix as destination address there is a support to only mention the subnet key in the defined subnet like subnet1 to get the Address Prefix of subnet 1 |
string | "Storage" |
destination_address_prefixes |
(Optional) List of destination address prefixes. Tags may not be used. This is required if destination_address_prefix is not specified. |
list | ["10.52.55.25", 10.52.56.25"] |
source_application_security_group_ids |
(Optional) A List of source Application Security Group ID's | list | [] |
destination_application_security_group_ids |
(Optional) A List of destination Application Security Group ID's | list | [] |
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
#..omitted
# Multiple Subnets, Service delegation, Service Endpoints, Network security groups and RouteTable.
# NSG association is added automatically for all subnets that specify NSG.
# RouteTable association is added automatically for all subnets that specify RouteTable.
subnets = {
subnet1 = {
subnet_name = "subnet1"
subnet_address_prefix = ["10.0.0.0/24"]
# Creating RouteTable
# To Create Empty RouteTable only specify route_table = true
route_table = true
# Specifying Routes
routes = [{
name = "test"
address_prefix = "10.0.0.0/24"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = "10.0.2.10"
},
{
name = "test2"
address_prefix = "52.32.152.22/32"
next_hop_type = "Internet"
}]
}
}
#..omitted
}
Well-defined naming and metadata tagging conventions help to quickly locate and manage resources. These conventions also help associate cloud usage costs with business teams via chargeback and show back accounting mechanisms.
An effective naming convention assembles resource names by using important resource information as parts of a resource's name. For example, using these recommended naming conventions, a public IP resource for a production azure firewall workload is named like this: pip-azfirewall-prod-westeurope-001
.
Note: Please keep in mind that there are restrictions and limitations on how Azure resources are named please reference to the: Naming rules and restrictions for Azure resources
When applying metadata tags to the cloud resources, you can include information about those assets that couldn't be included in the resource name. You can use that information to perform more sophisticated filtering and reporting on resources. This information can be used by IT or business teams to find resources or generate reports about resource usage and billing.
The following list provides good recommendation on common tags that capture important context and information about resources. Use this list as a starting point to establish your tagging conventions, to better understand tagging best practices checkout Cloud Adoption Framework's Resource naming and tagging decision guide
Tag Name | Description | Key | Example Value | Required? |
---|---|---|---|---|
Project Name | Name of the Project for the infra is created. This is mandatory to create a resource names. | ProjectName | {Project name} | Yes |
Application Name | Name of the application, service, or workload the resource is associated with. | ApplicationName | {app name} | Yes |
Approver | Name Person responsible for approving costs related to this resource. | Approver | {email} | Yes |
Business Unit | Top-level division of your company that owns the subscription or workload the resource belongs to. In smaller organizations, this may represent a single corporate or shared top-level organizational element. | BusinessUnit | FINANCE, MARKETING,{Product Name},CORP,SHARED | Yes |
Cost Center | Accounting cost center associated with this resource. | CostCenter | {number} | Yes |
Disaster Recovery | Business criticality of this application, workload, or service. | DR | Mission Critical, Critical, Essential | Yes |
Environment | Deployment environment of this application, workload, or service. | Environment | Prod, Dev, QA, Stage, Test | Yes |
Owner Name | Owner of the application, workload, or service. | Owner | {email} | Yes |
Requester Name | User that requested the creation of this application. | Requestor | {email} | Yes |
Service Class | Service Level Agreement level of this application, workload, or service. | ServiceClass | Dev, Bronze, Silver, Gold | Yes |
Start Date of the project | Date when this application, workload, or service was first deployed. | StartDate | {date} | No |
End Date of the Project | Date when this application, workload, or service is planned to be retired. | EndDate | {date} | No |
Note: This module allows you to manage the above metadata tags directly, as a variable using
variables.tf
and.tfvars
files as well as merge it with tags fromlocal
All Azure resources which support tagging can be tagged by specifying key-values in argumenttags
.
main.tf
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
#..omitted
tags = {
ProjectName = "Platform-Demo"
Environment = "dev"
Owner = "user@corp.com"
BusinessUnit = "CORP"
CostCenter = "IT"
ServiceClass = "Dev"
}
#..omitted
}
main.tf
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
#..omitted
tags = var.tags
#..omitted
}
variables.tf
variable "tags" {
type = map(string)
description = "(Optional) Resource tagging"
default = {}
}
dev.tfvars
tags = {
ProjectName = "Platform-Demo"
Environment = "dev"
Owner = "user@corp.com"
ServiceClass = "Dev"
}
Note: Local values can be helpful to avoid repeating the same values or expressions multiple times in a configuration, but if overused they can also make a configuration hard to read by future maintainers by hiding the actual values used, see Terraform documentation for more information about local blocks
main.tf
local {
common_tags = {
BusinessUnit = "CORP"
CostCenter = "IT"
}
}
module "shared_network" {
source = "haflidif/terraform-azurerm-network"
version = "1.0.0"
#..omitted
tags = merge(local.common_tags, var.tags)
#..omitted
}
variables.tf
variable "tags" {
type = map(string)
description = "(Optional) Resource tagging"
default = {}
}
dev.tfvars
tags = {
ProjectName = "Platform-Demo"
Environment = "dev"
Owner = "user@corp.com"
ServiceClass = "Dev"
}
Name | Version |
---|---|
terraform | >= 0.13 |
azurerm | >= 2.90.0 |
Name | Version |
---|---|
azurerm | >= 2.90.0 |
Name | Description | Type | Default |
---|---|---|---|
resource_group_name |
The name of the resource group in which resources are created | string | "" |
location |
The location of the resource group in which resources are created | string | "" |
vnet_name |
The name of the virtual network | string | "" |
vnet_address_space |
Virtual Network address space to be used | list | [] |
dns_servers |
(Optional) List of DNS servers to use for virtual network as described here | list | [] |
ddos_protection_plan |
(Optional) If DDoS protection plan should be attatched to the virtual network as described here | object | [] |
ddos_protection_plan_id |
(Optional) Provide DDoS Protection Plan Id within the ddos_protection_plan object as described here |
string | "" |
enable_ddos_protection_plan |
(Optional) Controls wether DDoS Protection Plan is enabled or disabled as described here | bool | N/A |
subnets |
For each subnet, create an object that contain required keys and attributes as described here | object | {} |
subnet_name |
A name of subnets inside virtual network | string | "snet-(Uses each key in subnets object as deafult)" |
subnet_address_prefix |
A list of subnets address prefixes inside virtual network | {} |
|
delegation |
(Optional) Defines a subnet delegation feature. takes an object as described here | object | {} |
service_endpoints |
(Optional) Service endpoints for the virtual subnet | object | {} |
nsg |
(Optional) Controls if an NSG should be created and attached to the subnet - NSG is not created by default for each subnet | bool | false |
nsg_name |
(Optional) Overwrites the use of subnet name minus snet- |
string | "nsg-(Uses the subnet name minus snet- if present)" |
nsg_inbound_rule |
(Optional) Define custom NSG inbound rules settings as described here | object | {} |
nsg_outbound_rule |
(Optional) Define custom NSG outbound rules settings as described here | object | {} |
route_table |
(Optional) Controls if an Route Table should be created and attached to the subnet - RouteTable is not created by default for each subnet | bool | false |
route_table_name |
(Optional) Overwrites the use of subnet name minus snet- |
string | "rt-(Uses the subnet name minus snet- if present)" |
routes |
(Optional) Define User Defined routes (UDR) for the route table as described here | object | {} |
Tags |
A map of tags to add to all resources as described here | map | {} |
Name | Type | Description |
---|---|---|
virtual_network_name |
string | The name of the virtual network. |
virtual_network_id |
string | The virtual network id. |
virtual_network_address_space |
list | List of address spaces that are used in the virtual network. |
virtual_network_subnets |
map | Map of subnets created within the module, outputs id , name and address_prefixes in a map based on the subnet key. |
network_security_groups |
map | Map of network security groups (NSG) created by the module, outputs id and name in a map based on the subnet key |
route_tables |
map | Map of route tables created by the module, outputs id and name in a map based on the subnet key |
Originally created by Haflidi Fridthjofsson