Approach: create a small reusable Terraform module that creates a resource group (optional), an Azure VNet with two subnets (app/db), an NSG bound to the app subnet allowing HTTP/HTTPS and an explicit deny for other inbound traffic, and tag resources. Export useful IDs for consumption. Use AzureRM backend (or Terraform Cloud) for remote state with locking and RBAC.main.tfhcl
terraform {
required_providers {
azurerm = { source = "hashicorp/azurerm" version = "~>3.0" }
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "rg" {
name = var.resource_group_name
location = var.location
tags = var.tags
}
resource "azurerm_virtual_network" "vnet" {
name = "${var.name_prefix}-vnet"
address_space = var.vnet_address_space
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
tags = var.tags
}
resource "azurerm_subnet" "app" {
name = "${var.name_prefix}-app-subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [var.app_subnet_prefix]
}
resource "azurerm_subnet" "db" {
name = "${var.name_prefix}-db-subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [var.db_subnet_prefix]
}
resource "azurerm_network_security_group" "app_nsg" {
name = "${var.name_prefix}-app-nsg"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
tags = var.tags
security_rule {
name = "Allow-HTTP"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "0.0.0.0/0"
destination_address_prefix = "*"
}
security_rule {
name = "Allow-HTTPS"
priority = 110
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "0.0.0.0/0"
destination_address_prefix = "*"
}
security_rule {
name = "Deny-All-Inbound"
priority = 200
direction = "Inbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "0.0.0.0/0"
destination_address_prefix = "*"
}
}
resource "azurerm_subnet_network_security_group_association" "app_assoc" {
subnet_id = azurerm_subnet.app.id
network_security_group_id = azurerm_network_security_group.app_nsg.id
}
variables.tfhcl
variable "name_prefix" {
type = string
description = "Prefix for resource names"
}
variable "resource_group_name" {
type = string
description = "Resource group name to create/use"
}
variable "location" {
type = string
default = "eastus"
}
variable "vnet_address_space" {
type = list(string)
default = ["10.0.0.0/16"]
}
variable "app_subnet_prefix" {
type = string
default = "10.0.1.0/24"
}
variable "db_subnet_prefix" {
type = string
default = "10.0.2.0/24"
}
variable "tags" {
type = map(string)
description = "Tags to apply (e.g., environment, owner)"
default = { environment = "dev", owner = "team@example.com" }
}
Outputs (module usage): export vnet_id, app_subnet_id, db_subnet_id, app_nsg_id so calling code can attach route tables, NVA, or VMs:- vnet_id = azurerm_virtual_network.vnet.id- app_subnet_id = azurerm_subnet.app.id- db_subnet_id = azurerm_subnet.db.id- app_nsg_id = azurerm_network_security_group.app_nsg.idRemote state & secrets:- Use the azurerm backend (Azure Storage Account + container + blob) with state locking (blob lease) and RBAC so only pipeline/service principals can modify state.- Store backend credentials via managed identity or service principal in CI (avoid hardcoding).- Protect secrets with Azure Key Vault; reference secret-backed variables in pipelines.- Use workspaces or separate backend keys per environment (dev/stage/prod) to isolate state.- Consider Terraform Cloud/Enterprise for state, policy enforcement (Sentinel), run history and sensitive data handling.Notes/Trade-offs:- Explicit deny rule provides defense-in-depth though Azure NSG default rules already block many flows; be careful with rule priorities.- Keep module inputs minimal; allow overrides for subnets/tags to support different environments.