Table of Contents

Terraform Module for a Private Endpoint

Summary: This is a terraform module that I use to deploy a private endpoint for resources in Azure.
Date: 8 February 2025

Read the post to learn more about private endpoints and:

About Private Endpoints

Private endpoints are a way to connect to a resource in Azure privately. This means that the resource is not exposed to the public internet. Instead, the resource is connected to a virtual network and can only be accessed from within that network. To accomplish this, a private endpoint is assigned a network interface card (NIC) that is connected to the virtual network. The network card gets assigned an IP address and a DNS name that can be used to connect to the resource. The private endpoint is connected to the resource.

Note the following tips should you start working with private endpoints:

Terraform Module for a Private Endpoint

The module for the Private Endpoint is defined over three files:

main.tf

Notice the following:

main.tf
resource "azurerm_private_endpoint" "private_endpoint" {
  name                          = var.name
  location                      = var.location
  resource_group_name           = var.resource_group_name
  subnet_id                     = var.subnet_id
  tags                          = var.tags
  custom_network_interface_name = "nic-${var.name}"
 
  private_service_connection {
    name                           = "${var.name}-serviceconnection"
    private_connection_resource_id = var.private_connection_resource_id
    is_manual_connection           = var.is_manual_connection
    subresource_names              = try([var.subresource_name], null)
    request_message                = try(var.request_message, null)
  }
 
  private_dns_zone_group {
    name                 = var.private_dns_zone_group_name
    private_dns_zone_ids = var.private_dns_zone_group_ids
  }
 
  dynamic "ip_configuration" {
    for_each = { for i, ip_config in var.ip_configurations : ip_config.private_ip_address => { index = i, member_name = ip_config.member_name } }
 
    content {
      name               = "${var.name}-ipconfig-${ip_configuration.value.index}"
      private_ip_address = ip_configuration.key
      subresource_name   = var.subresource_name
      member_name        = ip_configuration.value.member_name
    }
  }
 
  lifecycle {
    ignore_changes = [
    ]
  }
}

outputs.tf

outputs.tf
output "id" {
  description = "Specifies the resource id of the private endpoint."
  value       = azurerm_private_endpoint.private_endpoint.id
}
 
output "nic_name" {
  description = "Specifies the name of the private endpoint nic."
  value       = azurerm_private_endpoint.private_endpoint.custom_network_interface_name
}
 
output "private_dns_zone_group" {
  description = "Specifies the private dns zone group of the private endpoint."
  value       = azurerm_private_endpoint.private_endpoint.private_dns_zone_group
}
 
output "private_dns_zone_configs" {
  description = "Specifies the private dns zone(s) configuration"
  value       = azurerm_private_endpoint.private_endpoint.private_dns_zone_configs
}

variables.tf

Note that most of the descriptions are copied from the terraform registry.
variables.tf
variable "name" {
  description = "(Required) Specifies the name of the private endpoint. Changing this forces a new resource to be created."
  type        = string
}
 
variable "resource_group_name" {
  description = "(Required) The name of the resource group. Changing this forces a new resource to be created."
  type        = string
}
 
variable "private_connection_resource_id" {
  description = "(Required) Specifies the resource id of the private link service"
  type        = string
}
 
variable "location" {
  description = "(Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created."
  type        = string
}
 
variable "subnet_id" {
  description = "(Required) Specifies the resource id of the subnet"
  type        = string
}
 
variable "private_dns_zone_group_name" {
  description = "(Required) Specifies the Name of the Private DNS Zone Group. Changing this forces a new private_dns_zone_group resource to be created."
  type        = string
}
 
variable "private_dns_zone_group_ids" {
  description = "(Required) Specifies the list of Private DNS Zones to include within the private_dns_zone_group."
  type        = list(string)
}
 
variable "ip_configurations" {
  description = "(Optional) Specifies the static IP addresses within the private endpoint's subnet to be used. Changing this forces a new resource to be created."
  default     = []
  type        = list(object({ private_ip_address = string, member_name = string }))
}
 
variable "is_manual_connection" {
  description = "(Optional) Specifies whether the private endpoint connection requires manual approval from the remote resource owner."
  type        = string
  default     = false
}
 
variable "subresource_name" {
  description = "(Optional) Specifies a subresource name which the Private Endpoint is able to connect to."
  type        = string
  default     = null
}
 
variable "request_message" {
  description = "(Optional) Specifies a message passed to the owner of the remote resource when the private endpoint attempts to establish the connection to the remote resource."
  type        = string
  default     = null
}
 
variable "tags" {
  description = "(Optional) Specifies the tags of the network security group"
  default     = {}
}