Todo serviço PaaS do Azure que oferece Private Endpoint — Storage, SQL, Key Vault, Service Bus — usa por baixo dos panos o mesmo mecanismo: o Private Link Service. A diferença é que, com esse recurso, você aplica o mesmo padrão à sua própria aplicação. Um Load Balancer interno passa a ser consumido por outra VNet, até de outra subscription ou tenant, sem peering, sem rota manual e sem nada exposto publicamente.
Sétimo artigo da parte Network da série. Aqui eu modelo as duas pontas do cenário com Terraform, o lado provedor (quem expõe a aplicação) e o lado consumidor (quem acessa via Private Endpoint), e confirmo de verdade que a conexão foi aprovada.
Segurança: os comandos deste guia de Private Link Service com Terraform utilizam variáveis de ambiente para credenciais. Nunca insira IDs de subscription, senhas ou chaves diretamente nos comandos — use TF_VAR_*, um arquivo .tfvars fora do Git, ou o Azure Key Vault.
Sumário
- Pré-requisitos
- Lado provedor: VNet, Load Balancer e Private Link Service
- Lado consumidor: VNet separada e Private Endpoint
- terraform plan e apply
- Validando de verdade: status da conexão
- Quando usar Private Link Service (e quando não usar)
- Conclusão
- Referências oficiais
Pré-requisitos
Antes de configurar o Private Link Service com Terraform, você vai precisar do Azure CLI autenticado (az login), do Terraform >= 1.5 e de permissão de Network Contributor na subscription. O provider usado é o azurerm ~> 4.0.
az account show --query "{name:name, id:id}" -o table
O cenário tem duas VNets sem peering nenhum entre elas — toda a conectividade acontece através do Private Link, que é justamente o ponto central deste artigo.

Lado provedor: VNet, Load Balancer e Private Link Service
O provedor precisa de um Load Balancer interno Standard na frente da aplicação (mesmo sem VMs reais no backend pool, pro propósito deste artigo) e de uma subnet dedicada só pro Private Link Service — ele usa essa subnet pra alocar internamente os IPs de NAT que traduzem o tráfego do consumidor pro backend. Não confunda com o Azure NAT Gateway, que cuida da saída para a internet:
module "rg" {
source = "../../terraform-resource-group-modules"
resource_type = "rg"
project_name = "blog-castilho-pls"
environment = "prod"
location = var.location
tags = var.tags
}
module "vnet_provider" {
source = "../../terraform-virtual-network-modules"
name = "vnet-provider-blog-castilho"
resource_group_name = module.rg.name
location = var.location
address_space = [var.provider_vnet_address_space]
subnets = [
{ key = "provider", name = "snet-provider", address_prefixes = [var.provider_subnet_prefix] },
{
key = "pls_nat"
name = "snet-pls-nat"
address_prefixes = [var.pls_nat_subnet_prefix]
private_link_service_network_policies_enabled = false
},
]
tags = var.tags
}
module "lb_internal" {
source = "../../terraform-load-balancer-modules"
name = "lb-internal-blog-castilho"
resource_group_name = module.rg.name
location = var.location
frontend_subnet_id = module.vnet_provider.subnets["provider"].id
protocol = "Tcp"
port = 80
tags = var.tags
}
module "pls" {
source = "../../terraform-private-link-service-modules"
name = "pls-blog-castilho"
resource_group_name = module.rg.name
location = var.location
nat_subnet_id = module.vnet_provider.subnets["pls_nat"].id
load_balancer_frontend_ip_configuration_id = module.lb_internal.frontend_ip_configuration_id
tags = var.tags
}
Cada bloco usa um módulo reutilizável da série (resource group, VNet, Load Balancer e Private Link Service). Os endereços vêm do variables.tf: o provedor usa 10.90.0.0/16, com a subnet da aplicação em 10.90.1.0/24 e a de NAT do Private Link Service em 10.90.2.0/24; o consumidor usa 10.91.0.0/16, com a subnet em 10.91.1.0/24. O código completo, com os módulos, está no GitHub, no link do fim do artigo.
Lado consumidor: VNet separada e Private Endpoint
Do outro lado, uma VNet completamente separada — sem peering — cria um Private Endpoint apontando pro Private Link Service pelo ID dele:
module "vnet_consumer" {
source = "../../terraform-virtual-network-modules"
name = "vnet-consumer-blog-castilho"
resource_group_name = module.rg.name
location = var.location
address_space = [var.consumer_vnet_address_space]
subnets = [
{
key = "consumer"
name = "snet-consumer"
address_prefixes = [var.consumer_subnet_prefix]
private_endpoint_network_policies = "Disabled"
},
]
tags = var.tags
}
module "private_endpoint" {
source = "../../terraform-private-endpoint-modules"
name = "pe-blog-castilho"
resource_group_name = module.rg.name
location = var.location
subnet_id = module.vnet_consumer.subnets["consumer"].id
private_connection_resource_id = module.pls.id
tags = var.tags
}
No módulo, is_manual_connection vem como false por padrão, o que pede aprovação automática — funciona quando o dono do Private Link Service já configurou auto-approval pra determinadas subscriptions (não é o caso aqui, mas mesmo sem essa configuração explícita o Azure aprovou automaticamente por ser o mesmo dono dos dois lados, como visto na validação abaixo). Em cenários entre organizações diferentes, o normal é usar is_manual_connection = true e o dono do serviço aprovar manualmente cada conexão pelo portal ou CLI.
terraform plan e apply
terraform init -backend-config=backend.hcl
terraform plan -out=tfplan.out
terraform apply tfplan.out
O ambiente completo cria 12 recursos: 2 VNets, 3 subnets, Load Balancer interno (com backend pool, probe e regra), Private Link Service e Private Endpoint. Diferente de VPN Gateway ou ExpressRoute, esses recursos de rede provisionam rápido — o apply completo levou menos de dois minutos.
Validando de verdade: status da conexão
A prova de que o Private Link está funcionando não é o apply ter completado sem erro — é o status da conexão no Private Endpoint. Consultando via CLI:
az network private-endpoint show \
--name pe-blog-castilho \
--resource-group rg-blog-castilho-pls-prod \
--query "{status: privateLinkServiceConnections[0].privateLinkServiceConnectionState.status, description: privateLinkServiceConnections[0].privateLinkServiceConnectionState.description}"
# {
# "description": "Auto Approved",
# "status": "Approved"
# }

status: Approved com description: Auto Approved confirma que o Private Endpoint está de fato conectado ao Private Link Service — não só criado, mas na relação correta e aprovada. É esse campo que você monitoraria em produção pra detectar conexões pendentes de aprovação manual.
Quando usar Private Link Service (e quando não usar)
Private Link Service faz sentido quando você precisa expor um serviço interno pra consumidores em VNets ou tenants diferentes sem depender de peering (que exige coordenação de address space e abre rota bidirecional) nem de VPN/ExpressRoute (que conecta redes inteiras, não só um serviço específico). Ele não é a escolha certa quando os dois lados já estão na mesma VNet ou já têm peering configurado. Nesse caso, expor via IP privado direto com um NSG bem configurado resolve com menos complexidade, e o peering em escala pode ser gerenciado pelo Azure Virtual Network Manager.
Conclusão
O Private Link Service é o mecanismo que sustenta o Private Endpoint de praticamente todo serviço PaaS gerenciado do Azure. Aplicá-lo à sua própria aplicação segue o mesmo padrão: Load Balancer interno de um lado, Private Endpoint do outro, conectados sem peering e sem exposição pública. Para o consumidor acessar por nome em vez de IP, registre o endereço do Private Endpoint numa zona DNS privada, como mostrei em DNS privado multi-região. O campo que realmente prova que funcionou não é o apply ter passado, é o connection status — vale sempre checar antes de considerar o cenário validado.
Com ele, fechamos a primeira leva da parte Network desta série. A partir daqui, a série segue para a parte PaaS, começando pelo Azure Functions Premium com integração de VNet.
Referências oficiais
- Azure Private Link service overview — Microsoft Learn
- Create a Private Link service — Microsoft Learn
- azurerm_private_link_service — Terraform Registry
Interessado em saber mais sobre artigos relacionados a Microsoft Azure CLIQUE AQUI
🚀 Vamos nos conectar?
Não perca nenhuma oportunidade! Cadastre-se nas minhas redes e no canal do YouTube para receber conteúdos de TI, Cloud, Azure, AWS, Kubernetes e DevOps em primeira mão.
Dica: no Facebook, todos os artigos do blog são publicados automaticamente. Vale a pena curtir!
💬 Dúvidas ou problemas?
Com o intuito de ajudar a comunidade, caso você tenha dúvidas ou encontre problemas na execução dos comandos deste artigo, deixe um comentário abaixo. Responderei o mais breve possível!
Muito obrigado pela visita e até o próximo post!
Jefferson Castilho Especialista em Cloud & DevOps.Este guia técnico é exclusivo do Blog do Castilho. Explore mais conteúdos sobre Cloud e DevOps.