Terraform Private Endpoint ending in 403 http error

Hello,

I’m using Terraform to build up one of our new product. This product is based on Azure ressources such as API Management and App Services.
In order to secure the access to the app services, we have decided to use the private link service and the private endpoints.

Everything has been set in the terraform code to build up. Unfortunately I’m facing a problem with the result I get with the private endpoints. I always getting an http 4003 error when I tries to access the app services from inside the private subnet I want to use. When I create the private endpoint through Azure Portal, I don’t get this error.

Here is the code I’m using at the moment :
#creation of the internal load balancer required by the private link service
resource “azurerm_lb” “prod_lbi_001_pls” {
name = “prod-lbi-001-pls”
location = data.azurerm_resource_group.fraprod_rg_api.location
resource_group_name = data.azurerm_resource_group.fraprod_rg_api.name
sku = “Standard”

  frontend_ip_configuration {
    name      = data.azurerm_subnet.spoke_fra_snet_api_prod.name
    subnet_id = data.azurerm_subnet.spoke_fra_snet_api_prod.id
  }
  tags = {
    #some tags
  }
}

# creation of the private link service
resource "azurerm_private_link_service" "prod_pls_001" {
  name                = "prod-pls-001-sogetone"
  location            = data.azurerm_resource_group.fraprod_rg_api.location
  resource_group_name = data.azurerm_resource_group.fraprod_rg_api.name

  tags = {
    #some tags
  }

  auto_approval_subscription_ids = [data.azurerm_subscription.current.subscription_id]
  visibility_subscription_ids    = [data.azurerm_subscription.current.subscription_id]
  
  nat_ip_configuration {
    name      = data.azurerm_subnet.spoke_fra_snet_api_prod.name
    subnet_id = data.azurerm_subnet.spoke_fra_snet_api_prod.id
    primary   = true
  }
  
  load_balancer_frontend_ip_configuration_ids = [azurerm_lb.prod_lbi_001_pls.frontend_ip_configuration.0.id]
}
# creation of the private dns zone
resource "azurerm_private_dns_zone" "pls_azurewebsites" {
  name                = "privatelink.azurewebsites.net"
  resource_group_name = data.azurerm_resource_group.fraprod_rg_api.name

  tags = {
    "Kind" = "Private DNS"
  }
}
resource "azurerm_app_service_plan" "prod_plan_001" {
  name                = #internal name
  location            = data.azurerm_resource_group.fraprod_rg_api.location
  resource_group_name = data.azurerm_resource_group.fraprod_rg_api.name
  kind                = "Linux"
  reserved            = true
  tags = {
    # some tags
  }

  sku {
    tier = "PremiumV2"
    size = "P1v2"
  }
}
resource "azurerm_app_service" "prod_app_my_appservice" {
    name                = "prod-app-my-app-service"
    location            = data.azurerm_resource_group.fraprod_rg_api.location
    resource_group_name = data.azurerm_resource_group.fraprod_rg_api.name
    app_service_plan_id = azurerm_app_service_plan.prod_plan_001.id
    https_only          = "true"

    tags = {
         #some tags
    }

    site_config {
        app_command_line = ""
        linux_fx_version = "DOCKER|${var.custom_image_root}/${var.product_image_root}/referential-api:latest"
        ftps_state       = "Disabled"
    }
    app_settings = {
         #here is our internal settings for the app service
    }
}
resource "azurerm_private_endpoint" "prod_pe_app_my_appservice" {
  name                 = "prod-pe-001-app-my-appservice"
  location             = data.azurerm_resource_group.fraprod_rg_api.location
  resource_group_name  = data.azurerm_resource_group.fraprod_rg_api.name
  subnet_id            = data.azurerm_subnet.spoke_fra_snet_app_web_prod.id

  tags = {
    #some internal tags
  }

  private_dns_zone_group {
    name                 = "private-dns-zone-group"
    private_dns_zone_ids = [azurerm_private_dns_zone.pls_azurewebsites.id]
  }

  private_service_connection {
    name                           = "prod_pe_app_my_appservice_svc_connect"
    is_manual_connection           = false
    private_connection_resource_id = azurerm_app_service.prod_app_my_appservice.id
    subresource_names              = ["sites"]
  }
}

I finally found out what I was missing with this error : a missing ressource.
Adding this resource to the code solved the problem

resource “azurerm_private_dns_zone_virtual_network_link” “azurewebsite_dns_zone_vnet_link” {
name = “azurewebsites-dns-vnet-link”
resource_group_name = data.azurerm_resource_group.fraprod_rg_api.name
private_dns_zone_name = azurerm_private_dns_zone.pls_azurewebsites.name
virtual_network_id = data.azurerm_virtual_network.vnet_api_prod.id
registration_enabled = “false”
tags = {
“Kind” = “Private DNS VNet Link”
}
}

With this resource, creating a private endpoint through Terraform also update the Azure DNS, updating the azurewebsite dns default entry to a CName entry.

The default dns entry of an app service is then redirected to the private dns entry.

Was searching a long time to find out this error. It should be added to the online documentation, including the published examples available.

Hope this reply will help some.

Hi Marc,

You could submit a PR like this one to propose a documentation change Update doc for resource_group_name property of azurerm_private_dns_zone_virtual_network_link by neil-yechenwei · Pull Request #7998 · terraform-providers/terraform-provider-azurerm · GitHub