I would like to use best practices. I don't have express route. Was planning on Azure VPN Gateway to connect via the Azure VPN Client. I made a first pass, by putting the gateway in the hub vnet, but ran into connectivity issues as I was attempting to access eastus.pl-auth.privatelink.azuredatabricks.net (I had to move all resources to eastus closer to me). Anyway, the issue I ran into was the below. But unlike other custom apps, I don't have an app registration I can update the redirect URI (all I see is a single enterprise app record).
Any advice is appreciated.
AADSTS50011: The redirect URI 'https://eastus.pl-auth.privatelink.azuredatabricks.net/login.html' specified in the request does not match the redirect URIs configured for the application '2ff814a6-3304-4ab8-85cb-cd0e6f879c1d'. Make sure the redirect URI sent in the request matches one added to your application in the Azure portal. Navigate to https://aka.ms/redirectUriMismatchError to learn more about how to fix this.
Below is my current scripts:
vpn_gateway.tf
# --- VPN Gateway Configuration ---
# Create a public IP address for VPN Gateway
resource "azurerm_public_ip" "vpn_gateway_ip" {
name = "vpnGatewayPublicIP"
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
allocation_method = "Dynamic"
sku = "Basic"
}
# Create the VPN Gateway
resource "azurerm_virtual_network_gateway" "vpn_gateway" {
name = "vpnGateway"
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
type = "Vpn"
vpn_type = "RouteBased"
sku = "VpnGw1"
ip_configuration {
name = "vpnGatewayConfig"
public_ip_address_id = azurerm_public_ip.vpn_gateway_ip.id
private_ip_address_allocation = "Dynamic"
subnet_id = azurerm_subnet.hubvpngw.id # Referencing the GatewaySubnet here
}
vpn_client_configuration {
address_space = ["172.16.0.0/24"] # Client address pool
vpn_client_protocols = ["OpenVPN"]
# Azure AD Configuration for Authentication
aad_tenant = "https://login.microsoftonline.com/${var.AZURE_TENANT_ID}" # Azure AD tenant ID
aad_issuer = "https://sts.windows.net/${var.AZURE_TENANT_ID}/" # Azure AD issuer URL
aad_audience = "41b23e61-6c1e-4545-b367-cd054e0ed4b4" # Azure AD audience
}
}
My vnet.tf script is below (edited to include the gateway subnet expected by my vpn gateway):
vnet.tf
resource "azurerm_virtual_network" "this" {
name = "${local.prefix}-vnet"
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
address_space = [local.cidr]
tags = local.tags
}
resource "azurerm_network_security_group" "this" {
name = "${local.prefix}-nsg"
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
tags = local.tags
}
resource "azurerm_network_security_rule" "aad" {
name = "AllowAAD"
priority = 200
direction = "Outbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "AzureActiveDirectory"
resource_group_name = azurerm_resource_group.this.name
network_security_group_name = azurerm_network_security_group.this.name
}
resource "azurerm_network_security_rule" "azfrontdoor" {
name = "AllowAzureFrontDoor"
priority = 201
direction = "Outbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "AzureFrontDoor.Frontend"
resource_group_name = azurerm_resource_group.this.name
network_security_group_name = azurerm_network_security_group.this.name
}
resource "azurerm_subnet" "public" {
name = "${local.prefix}-public"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.this.name
address_prefixes = [cidrsubnet(local.cidr, 3, 0)]
delegation {
name = "databricks"
service_delegation {
name = "Microsoft.Databricks/workspaces"
actions = [
"Microsoft.Network/virtualNetworks/subnets/join/action",
"Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action",
"Microsoft.Network/virtualNetworks/subnets/unprepareNetworkPolicies/action"]
}
}
}
resource "azurerm_subnet_network_security_group_association" "public" {
subnet_id = azurerm_subnet.public.id
network_security_group_id = azurerm_network_security_group.this.id
}
variable "private_subnet_endpoints" {
default = []
}
resource "azurerm_subnet" "private" {
name = "${local.prefix}-private"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.this.name
address_prefixes = [cidrsubnet(local.cidr, 3, 1)]
enforce_private_link_endpoint_network_policies = true
enforce_private_link_service_network_policies = true
delegation {
name = "databricks"
service_delegation {
name = "Microsoft.Databricks/workspaces"
actions = [
"Microsoft.Network/virtualNetworks/subnets/join/action",
"Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action",
"Microsoft.Network/virtualNetworks/subnets/unprepareNetworkPolicies/action"]
}
}
service_endpoints = var.private_subnet_endpoints
}
resource "azurerm_subnet_network_security_group_association" "private" {
subnet_id = azurerm_subnet.private.id
network_security_group_id = azurerm_network_security_group.this.id
}
resource "azurerm_subnet" "plsubnet" {
name = "${local.prefix}-privatelink"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.this.name
address_prefixes = [cidrsubnet(local.cidr, 3, 2)]
enforce_private_link_endpoint_network_policies = true // set to true to disable subnet policy
}
resource "azurerm_virtual_network" "hubvnet" {
name = "${local.prefix}-hub-vnet"
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
address_space = [var.hubcidr]
tags = local.tags
}
resource "azurerm_subnet" "hubfw" {
//name must be fixed as AzureFirewallSubnet
name = "AzureFirewallSubnet"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.hubvnet.name
address_prefixes = [cidrsubnet(var.hubcidr, 3, 0)]
}
resource "azurerm_virtual_network_peering" "hubvnet" {
name = "peerhubtospoke"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.hubvnet.name
remote_virtual_network_id = azurerm_virtual_network.this.id
allow_gateway_transit = true
}
resource "azurerm_virtual_network_peering" "spokevnet" {
name = "peerspoketohub"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.this.name
remote_virtual_network_id = azurerm_virtual_network.hubvnet.id
use_remote_gateways = true
}
resource "azurerm_subnet" "hubvpngw" {
name = "GatewaySubnet"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.hubvnet.name
address_prefixes = [cidrsubnet(var.hubcidr, 3, 1)] # You can change the subnet range accordingly
}
And my firewall.tf updates as well:
firweall.tf
resource "azurerm_public_ip" "fwpublicip" {
name = "hubfirewallpublicip"
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
allocation_method = "Static"
sku = "Standard"
}
resource "azurerm_firewall" "hubfw" {
name = "hubfirewall"
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
sku_name = "AZFW_VNet"
sku_tier = "Standard"
ip_configuration {
name = "configuration"
subnet_id = azurerm_subnet.hubfw.id
public_ip_address_id = azurerm_public_ip.fwpublicip.id
}
}
resource "azurerm_firewall_network_rule_collection" "adbfnetwork" {
name = "adbcontrolplanenetwork"
azure_firewall_name = azurerm_firewall.hubfw.name
resource_group_name = azurerm_resource_group.this.name
priority = 200
action = "Allow"
rule {
name = "databricks-metastore"
source_addresses = [
join(", ", azurerm_subnet.public.address_prefixes),
join(", ", azurerm_subnet.private.address_prefixes),
]
destination_ports = ["3306"]
destination_addresses = [var.metastoreip]
protocols = ["TCP"]
}
}
resource "azurerm_firewall_application_rule_collection" "adbfqdn" {
name = "adbcontrolplanefqdn"
azure_firewall_name = azurerm_firewall.hubfw.name
resource_group_name = azurerm_resource_group.this.name
priority = 200
action = "Allow"
rule {
name = "databricks-control-plane-services"
source_addresses = [
join(", ", azurerm_subnet.public.address_prefixes),
join(", ", azurerm_subnet.private.address_prefixes),
]
target_fqdns = concat(var.firewallfqdn, ["*.azuredatabricks.net"])
protocol {
port = "443"
type = "Https"
}
}
}
resource "azurerm_route_table" "adbroute" {
name = "spoke-routetable"
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
route {
name = "to-firewall"
address_prefix = "0.0.0.0/0"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = azurerm_firewall.hubfw.ip_configuration.0.private_ip_address
}
}
resource "azurerm_subnet_route_table_association" "publicudr" {
subnet_id = azurerm_subnet.public.id
route_table_id = azurerm_route_table.adbroute.id
}
resource "azurerm_subnet_route_table_association" "privateudr" {
subnet_id = azurerm_subnet.private.id
route_table_id = azurerm_route_table.adbroute.id
}