Private Link for Application Gateway allows you to connect workloads over a private connection spanning across VNets and subscriptions. When configured, a private endpoint will be placed into a defined virtual network's subnet, providing a private IP address for clients looking to communicate with the gateway. For a list of other PaaS services that support Private Link functionality, see What is Azure Private Link?. For more information on Application Gateway Private Link, see the following resources:
The following picture shows the architecture of the demo built by the bash script below. The sample represents a scenario where a SaaS provider exposes a service application to a third party via Application Gateway Private Link.
The script creates the following Azure resources for the service provider:
The script creates the following Azure resources for the client provider:
app.green.internal
) to the private IP address of the Private Endpoint used to connect to the Application Gateway Private Link.Private Link allows you to extend private connectivity to Application Gateway via a Private Endpoint in the following scenarios:
You may also block inbound public (Internet) access to Application Gateway and allow access only via private endpoints. Inbound management traffic still needs to be allowed to the application gateway. For more information, see Application Gateway infrastructure configuration.
All features supported by Application Gateway are supported when accessed through a private endpoint, including support for AGIC. For more information, see How to call an AKS-hosted workload via Application Gateway Private Link and AGIC.
Four components are required to implement Private Link with Application Gateway:
Application Gateway Private Link Configuration
A Private link configuration can be associated with an Application Gateway Frontend IP address, which can then be used to establish a connection using a Private Endpoint. The Private Link feature won't be enabled if there's no association to an Application Gateway frontend IP address.
Application Gateway Frontend IP address
The public or private IP address where the Application Gateway Private Link Configuration needs to be associated with enabling the Private Link Capabilities.
Private Endpoint
An Azure network resource that allocates a private IP address in your VNet address space. It connects to the Application Gateway via the private IP address similar to many other Azure Services like Storage, KeyVault, etc., that provide private link access.
Private Endpoint Connection
A connection on Application Gateway originated by Private Endpoints. You can auto-approve, manually approve, or reject connections to grant or deny access.
The following cloud-init configuration installs the required packages, creates a Node.js app, then initializes and starts the web application.
At your bash prompt or in the Cloud Shell, create a file named cloud-init.txt and paste the following configuration.
#cloud-config
package_upgrade: true
packages:
- nginx
- nodejs
- npm
write_files:
- owner: www-data:www-data
- path: /etc/nginx/sites-available/default
content: |
server {
listen 80;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
- owner: azureuser:azureuser
- path: /home/azureuser/myapp/index.js
content: |
var express = require('express')
var app = express()
var os = require('os');
app.get('/', function (req, res) {
res.send('Hello World from host ' + os.hostname() + '!')
})
app.listen(3000, function () {
console.log('Hello world app listening on port 3000!')
})
runcmd:
- service nginx restart
- cd "/home/azureuser/myapp"
- npm init
- npm install express -y
- nodejs index.js
Then create the bash script called deploy.sh and paste the following code. Choose a value for the prefix variable. Before running the script, customize the az vm create that creates the client virtual machine and refer your private SSH key in the --ssh-key-values parameter. This will be necessary as credentials when connecting to the virtual machine using the azureuser admin user via Azure Bastion Host.
#!/bin/bash
# Variables
prefix="Green"
resourceGroupName="${prefix}RG"
bastionName="${prefix}Bastion"
bastionPublicIpName="${prefix}BastionPublicIp"
applicationGatewayName="${prefix}ApplicationGateway"
applicationGatewayProbeName="httpDefaultProbe"
applicationGatewayHttpSettingsName="appGatewayBackendHttpSettings"
applicationGatewayPublicIpName="${prefix}ApplicationGatewayPublicIp"
applicationGatewaySubnetName="AppGatewaySubnet"
applicationGatewaySubnetPrefix="10.21.0.0/24"
backendSubnetName="BackendSubnet"
backendSubnetPrefix="10.21.1.0/24"
defaultSubnetName="DefaultSubnet"
defaultSubnetPrefix="10.22.0.0/24"
bastionSubnetName="AzureBastionSubnet"
bastionSubnetPrefix="10.22.1.0/24"
serverVirtualNetworkName="${prefix}ServerVNet"
serverVirtualNetworkAddressPrefix="10.21.0.0/16"
clientVirtualNetworkName="${prefix}ClientVNet"
clientVirtualNetworkAddressPrefix="10.22.0.0/16"
virtualMachinePrefix="${prefix}"
location="eastus2"
privateLinkServiceName="${prefix}PrivateLink"
privateEndpointName="${prefix}PrivateEndpoint"
privateDnsZoneName="${prefix,,}.internal"
privateDnsZoneVirtualNetworkLinkName="LinkTo${serverVirtualNetworkName}"
aRecordName="app"
wafPolicyName="${prefix}WafPolicy"
# Subscription id, subscription name, and tenant id of the current subscription
subscriptionId=$(az account show --query id --output tsv)
subscriptionName=$(az account show --query name --output tsv)
tenantId=$(az account show --query tenantId --output tsv)
# Check if the resource group already exists
echo "Checking if [$resourceGroupName] resource group actually exists in the [$subscriptionName] subscription..."
az group show --name $resourceGroupName &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$resourceGroupName] resource group actually exists in the [$subscriptionName] subscription"
echo "Creating [$resourceGroupName] resource group in the [$subscriptionName] subscription..."
# Create the resource group
az group create --name $resourceGroupName --location $location 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$resourceGroupName] resource group successfully created in the [$subscriptionName] subscription"
else
echo "Failed to create [$resourceGroupName] resource group in the [$subscriptionName] subscription"
exit
fi
else
echo "[$resourceGroupName] resource group already exists in the [$subscriptionName] subscription"
fi
# Check if the server virtual network already exists
echo "Checking if [$serverVirtualNetworkName] virtual network actually exists in the [$resourceGroupName] resource group..."
az network vnet show \
--name $serverVirtualNetworkName \
--only-show-errors \
--resource-group $resourceGroupName &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$serverVirtualNetworkName] virtual network actually exists in the [$resourceGroupName] resource group"
echo "Creating [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group..."
# Create the server virtual network
az network vnet create \
--name $serverVirtualNetworkName \
--resource-group $resourceGroupName \
--address-prefixes $serverVirtualNetworkAddressPrefix \
--subnet-name $applicationGatewaySubnetName \
--subnet-prefix $applicationGatewaySubnetPrefix \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$serverVirtualNetworkName] virtual network successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group"
exit
fi
# Check if the backend subnet already exists
echo "Checking if [$backendSubnetName] backend subnet actually exists in the [$serverVirtualNetworkName] virtual network..."
az network vnet subnet show \
--name $backendSubnetName \
--vnet-name $serverVirtualNetworkName \
--resource-group $resourceGroupName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$backendSubnetName] backend subnet actually exists in the [$serverVirtualNetworkName] virtual network"
echo "Creating [$backendSubnetName] backend subnet in the [$serverVirtualNetworkName] virtual network..."
# Create the backend subnet
az network vnet subnet create \
--name $backendSubnetName \
--vnet-name $serverVirtualNetworkName \
--resource-group $resourceGroupName \
--address-prefix $backendSubnetPrefix \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$backendSubnetName] backend subnet successfully created in the [$serverVirtualNetworkName] virtual network"
else
echo "Failed to create [$backendSubnetName] backend subnet in the [$serverVirtualNetworkName] virtual network"
exit
fi
else
echo "[$backendSubnetName] backend subnet already exists in the [$serverVirtualNetworkName] virtual network"
fi
else
echo "[$serverVirtualNetworkName] virtual network already exists in the [$resourceGroupName] resource group"
fi
# Check if the client virtual network already exists
echo "Checking if [$clientVirtualNetworkName] virtual network actually exists in the [$resourceGroupName] resource group..."
az network vnet show \
--name $clientVirtualNetworkName \
--resource-group $resourceGroupName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$clientVirtualNetworkName] virtual network actually exists in the [$resourceGroupName] resource group"
echo "Creating [$clientVirtualNetworkName] virtual network in the [$resourceGroupName] resource group..."
# Create the client virtual network
az network vnet create \
--name $clientVirtualNetworkName \
--resource-group $resourceGroupName \
--address-prefixes $clientVirtualNetworkAddressPrefix \
--subnet-name $defaultSubnetName \
--subnet-prefix $defaultSubnetPrefix \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$clientVirtualNetworkName] virtual network successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$clientVirtualNetworkName] virtual network in the [$resourceGroupName] resource group"
exit
fi
# Check if the bastion subnet already exists
echo "Checking if [$bastionSubnetName] bastion subnet actually exists in the [$clientVirtualNetworkName] virtual network..."
az network vnet subnet show \
--name $bastionSubnetName \
--vnet-name $clientVirtualNetworkName \
--resource-group $resourceGroupName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$bastionSubnetName] bastion subnet actually exists in the [$clientVirtualNetworkName] virtual network"
echo "Creating [$bastionSubnetName] bastion subnet in the [$clientVirtualNetworkName] virtual network..."
# Create the bastion subnet
az network vnet subnet create \
--name $bastionSubnetName \
--vnet-name $clientVirtualNetworkName \
--resource-group $resourceGroupName \
--address-prefix $bastionSubnetPrefix \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$bastionSubnetName] bastion subnet successfully created in the [$clientVirtualNetworkName] virtual network"
else
echo "Failed to create [$bastionSubnetName] bastion subnet in the [$clientVirtualNetworkName] virtual network"
exit
fi
else
echo "[$bastionSubnetName] bastion subnet already exists in the [$clientVirtualNetworkName] virtual network"
fi
else
echo "[$clientVirtualNetworkName] virtual network already exists in the [$resourceGroupName] resource group"
fi
# Check if the application gateway public ip address already exists
echo "Checking if [$applicationGatewayPublicIpName] application gateway public ip address actually exists in the [$resourceGroupName] resource group..."
az network public-ip show \
--name $applicationGatewayPublicIpName \
--resource-group $resourceGroupName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$applicationGatewayPublicIpName] application gateway public ip address actually exists in the [$resourceGroupName] resource group"
echo "Creating [$applicationGatewayPublicIpName] application gateway public ip address in the [$resourceGroupName] resource group..."
# Create the application gateway public ip address
az network public-ip create \
--name $applicationGatewayPublicIpName \
--resource-group $resourceGroupName \
--sku Standard \
--allocation-method Static \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$applicationGatewayPublicIpName] application gateway public ip address successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$applicationGatewayPublicIpName] application gateway public ip address in the [$resourceGroupName] resource group"
exit
fi
else
echo "[$applicationGatewayPublicIpName] application gateway public ip address already exists in the [$resourceGroupName] resource group"
fi
# Create an eopty array to store the private ip addresses of the backend VMs
privateIpAddresses=()
# Create backend VMs
for i in $(seq 1 2); do
# Check if the virtual machine already exists
virtualMachineName="$virtualMachinePrefix${i}Vm"
echo "Checking if [$virtualMachineName] virtual machine actually exists in the [$resourceGroupName] resource group..."
az vm show \
--name $virtualMachineName \
--resource-group $resourceGroupName &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$virtualMachineName] virtual machine actually exists in the [$resourceGroupName] resource group"
echo "Creating the network interface for the [$virtualMachineName] virtual machine in the [$resourceGroupName] resource group..."
# Create the virtual machine network interface
nicName="${virtualMachineName}Nic"
az network nic create \
--name $nicName \
--resource-group $resourceGroupName \
--vnet-name $serverVirtualNetworkName \
--subnet $backendSubnetName \
--only-show-errors 1>/dev/null
if [[ $? != 0 ]]; then
echo "Failed to create [$nicName] network interface in the [$resourceGroupName] resource group"
exit
fi
# Retrieve the private ip address of the virtual machine network interface
privateIpAddress=$(az network nic show \
--name $nicName \
--resource-group $resourceGroupName \
--only-show-errors \
--query ipConfigurations[0].privateIPAddress \
--output tsv)
echo "[$nicName] network interface private ip address: $privateIpAddress"
# Add private ip address to the array
privateIpAddresses+=($privateIpAddress)
echo "Creating the [$virtualMachineName] virtual machine in the [$resourceGroupName] resource group..."
# Create the virtual machine
az vm create \
--name $virtualMachineName \
--resource-group $resourceGroupName \
--nics $nicName \
--image UbuntuLTS \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub \
--custom-data ./cloud-init.txt \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$virtualMachineName] virtual machine successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$virtualMachineName] virtual machine in the [$resourceGroupName] resource group"
exit
fi
else
# Retrieve the private ip address of the virtual machine network interface
nicName="${virtualMachineName}Nic"
privateIpAddress=$(az network nic show \
--name $nicName \
--resource-group $resourceGroupName \
--only-show-errors \
--query ipConfigurations[0].privateIPAddress \
--output tsv)
echo "[$nicName] network interface private ip address: $privateIpAddress"
# Add private ip address to the array
privateIpAddresses+=($privateIpAddress)
echo "[$virtualMachineName] virtual machine already exists in the [$resourceGroupName] resource group"
fi
done
# Create a space-separated list of IP addresses of private IP addresses of the backend VMs from the array
privateIpAddressesList=$(
IFS=' '
echo "${privateIpAddresses[*]}"
)
# Check if the waf policy already exists
echo "Checking if [$wafPolicyName] waf policy actually exists in the [$resourceGroupName] resource group..."
az network application-gateway waf-policy show \
--name $wafPolicyName \
--resource-group $resourceGroupName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$wafPolicyName] waf policy actually exists in the [$resourceGroupName] resource group"
echo "Creating [$wafPolicyName] waf policy in the [$resourceGroupName] resource group..."
# Create the waf policy
az network application-gateway waf-policy create \
--name $wafPolicyName \
--resource-group $resourceGroupName \
--location $location \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$wafPolicyName] waf policy successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$wafPolicyName] waf policy in the [$resourceGroupName] resource group"
exit
fi
else
echo "[$wafPolicyName] waf policy already exists in the [$resourceGroupName] resource group"
fi
# Check if the application gateway already exists
echo "Checking if [$applicationGatewayName] application gateway actually exists in the [$resourceGroupName] resource group..."
az network application-gateway show \
--name $applicationGatewayName \
--resource-group $resourceGroupName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$applicationGatewayName] application gateway actually exists in the [$resourceGroupName] resource group"
echo "Creating [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group..."
# Create the application gateway
az network application-gateway create \
--name $applicationGatewayName \
--resource-group $resourceGroupName \
--location $location \
--capacity 2 \
--sku WAF_v2 \
--waf-policy $wafPolicyName \
--public-ip-address $applicationGatewayPublicIpName \
--vnet-name $serverVirtualNetworkName \
--subnet $applicationGatewaySubnetName \
--servers $privateIpAddressesList \
--priority 100 \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$applicationGatewayName] application gateway successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group"
exit
fi
# Create http probe
echo "Creating [$applicationGatewayProbeName] http probe in the [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group..."
az network application-gateway probe create \
--name $applicationGatewayProbeName \
--gateway-name $applicationGatewayName \
--resource-group $resourceGroupName \
--protocol http \
--port 80 \
--host 127.0.0.1 \
--timeout 30 \
--path / \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$applicationGatewayProbeName] http probe successfully created in the [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group"
else
echo "Failed to create [$applicationGatewayProbeName] http probe in the [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group"
exit
fi
# Update the http settings
echo "Updating [$applicationGatewayHttpSettingsName] http settings in the [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group..."
az network application-gateway http-settings update \
--name $applicationGatewayHttpSettingsName \
--gateway-name $applicationGatewayName \
--resource-group $resourceGroupName \
--port 80 \
--protocol http \
--probe $applicationGatewayProbeName \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$applicationGatewayHttpSettingsName] http settings successfully updated in the [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group"
else
echo "Failed to update [$applicationGatewayHttpSettingsName] http settings in the [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group"
exit
fi
else
echo "[$applicationGatewayName] application gateway already exists in the [$resourceGroupName] resource group"
fi
# Get the resource id of the application gateway
echo "Getting the resource id of the [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group..."
applicationGatewayResourceId=$(az network application-gateway show \
--name $applicationGatewayName \
--resource-group $resourceGroupName \
--only-show-errors \
--query id \
--output tsv)
if [[ -z $applicationGatewayResourceId ]]; then
echo "Failed to get the resource id of the [$applicationGatewayName] application gateway in the [$resourceGroupName] resource group"
exit
else
echo "[$applicationGatewayName] application gateway resource id successfully retrieved"
fi
# Check if the private link service network policies are disabled on the backend subnet that will host the application gateway private link
echo "Checking if Private Link Service Network Policies are disabled on the [$backendSubnetName] subnet of the [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group..."
result=$(az network vnet subnet show \
--name $backendSubnetName \
--vnet-name $serverVirtualNetworkName \
--resource-group $resourceGroupName \
--only-show-errors \
--query privateLinkServiceNetworkPolicies \
--output tsv)
if [[ $result != "Disabled" ]]; then
# Disable Private Link Service Network Policies
# https://learn.microsoft.com/azure/private-link/disable-private-endpoint-network-policy
echo "Disabling Private Link Service Network Policies on the [$backendSubnetName] subnet of the [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group..."
az network vnet subnet update \
--name $backendSubnetName \
--vnet-name $serverVirtualNetworkName \
--resource-group $resourceGroupName \
--only-show-errors \
--disable-private-link-service-network-policies true 1>/dev/null
if [[ $? == 0 ]]; then
echo "Private Link Service Network Policies successfully disabled on the [$backendSubnetName] subnet of the [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group"
else
echo "Failed to disable Private Link Service Network Policies on the [$backendSubnetName] subnet of the [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group"
exit
fi
else
echo "Private Link Service Network Policies are already disabled on the [$backendSubnetName] subnet of the [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group"
fi
# Check if the private link service network policies are disabled on the application gateway subnet
echo "Checking if Private Link Service Network Policies are disabled on the [$defaultSubnetName] subnet of the [$clientVirtualNetworkName] virtual network in the [$resourceGroupName] resource group..."
result=$(az network vnet subnet show \
--name $defaultSubnetName \
--vnet-name $clientVirtualNetworkName \
--resource-group $resourceGroupName \
--only-show-errors \
--query privateLinkServiceNetworkPolicies \
--output tsv)
if [[ $result != "Disabled" ]]; then
# Disable Private Link Service Network Policies
# https://learn.microsoft.com/azure/private-link/disable-private-endpoint-network-policy
echo "Disabling Private Link Service Network Policies on the [$defaultSubnetName] subnet of the [$clientVirtualNetworkName] virtual network in the [$resourceGroupName] resource group..."
az network vnet subnet update \
--name $defaultSubnetName \
--vnet-name $clientVirtualNetworkName \
--resource-group $resourceGroupName \
--disable-private-link-service-network-policies true \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "Private Link Service Network Policies successfully disabled on the [$defaultSubnetName] subnet of the [$clientVirtualNetworkName] virtual network in the [$resourceGroupName] resource group"
else
echo "Failed to disable Private Link Service Network Policies on the [$defaultSubnetName] subnet of the [$clientVirtualNetworkName] virtual network in the [$resourceGroupName] resource group"
exit
fi
else
echo "Private Link Service Network Policies are already disabled on the [$defaultSubnetName] subnet of the [$clientVirtualNetworkName] virtual network in the [$resourceGroupName] resource group"
fi
# Get Application Gateway Frontend IP Name
echo "Getting Application Gateway Frontend IP Configuration name..."
frontendIpConfigurationName=$(az network application-gateway frontend-ip list \
--gateway-name $applicationGatewayName \
--resource-group $resourceGroupName \
--only-show-errors \
--query [0].name \
--output tsv)
if [[ -n $frontendIpConfigurationName ]]; then
echo "Frontend IP configuration [$frontendIpConfigurationName] name successfully retrieved"
else
echo "Failed to retrieve the name of the frontend IP configuration of the [$applicationGatewayName] application gateway"
exit
fi
# Get the resource id of the backend subnet that will host the application gateway private link. This subnet should be different from the one hosting the application gateway
echo "Getting the resource id of the [$applicationGatewaySubnetName] subnet of the [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group..."
backendSubnetId=$(az network vnet subnet show \
--name $backendSubnetName \
--vnet-name $serverVirtualNetworkName \
--resource-group $resourceGroupName \
--only-show-errors \
--query id \
--output tsv)
if [[ -n $backendSubnetId ]]; then
echo "Resource id of the [$backendSubnetName] subnet of the [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group successfully retrieved"
else
echo "Failed to retrieve the resource id of the [$backendSubnetName] subnet of the [$serverVirtualNetworkName] virtual network in the [$resourceGroupName] resource group"
exit
fi
# Check if the private link configuration already exists
echo "Checking if the private link configuration [$privateLinkServiceName] already exists..."
privateLinkId=$(az network application-gateway private-link list \
--gateway-name $applicationGatewayName \
--resource-group $resourceGroupName \
--only-show-errors \
--query "[?name=='$privateLinkServiceName'].id" \
--output tsv)
if [[ -z $privateLinkId ]]; then
echo "Private link configuration [$privateLinkServiceName] does not exist"
echo "Creating private link configuration [$privateLinkServiceName]..."
# Add a new Private Link configuration and associate it with an existing Frontend IP
echo "Adding a new private link configuration and associating it with the [$frontendIpConfigurationName] frontend IP configuration of the [$applicationGatewayName] application gateway..."
az network application-gateway private-link add \
--frontend-ip $frontendIpConfigurationName \
--name $privateLinkServiceName \
--subnet $backendSubnetId \
--gateway-name $applicationGatewayName \
--resource-group $resourceGroupName \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "Private link configuration successfully added and associated with the [$frontendIpConfigurationName] frontend IP configuration of the [$applicationGatewayName] application gateway"
else
echo "Failed to add a new private link configuration and associate it with the [$frontendIpConfigurationName] frontend IP configuration of the [$applicationGatewayName] application gateway"
exit
fi
else
echo "Private link configuration [$privateLinkServiceName] already exists"
fi
# Get Private Link resource ID
echo "Getting the resource id of the [$privateLinkServiceName] private link configuration of the [$applicationGatewayName] application gateway..."
privateLinkId=(az network application-gateway private-link list
--gateway-name $applicationGatewayName
--resource-group $resourceGroupName
--only-show-errors
--query "[?name=='$privateLinkServiceName'].id"
--output tsv)
if [[ -n $privateLinkId ]]; then
echo "Resource id of the [$privateLinkServiceName] private link configuration of the [$applicationGatewayName] application gateway successfully retrieved"
else
echo "Failed to retrieve the resource id of the [$privateLinkServiceName] private link configuration of the [$applicationGatewayName] application gateway"
exit
fi
# Check if the private endpoint already exists
privateEndpointId=$(az network private-endpoint list \
--resource-group $resourceGroupName \
--only-show-errors \
--query "[?name=='$privateEndpointName'].id" \
--output tsv)
if [[ -z $privateEndpointId ]]; then
echo "Private endpoint [$privateEndpointName] does not exist"
echo "Creating a private endpoint for the [$privateLinkServiceName] private link configuration of the [$applicationGatewayName] application gateway..."
# Create a private endpoint for the private link configuration
# NOTE: as documented in https://learn.microsoft.com/en-us/azure/application-gateway/private-link-configure?tabs=cli
# the value of the --group-id parameter needs to be eqaul to the frontend IP configuration of the Application Gateway
# use by the Private Link. The defalt name of the frontend IP configuration is "appGatewayFrontendIP"
az network private-endpoint create \
--name $privateEndpointName \
--resource-group $resourceGroupName \
--vnet-name $clientVirtualNetworkName \
--subnet $defaultSubnetName \
--group-id appGatewayFrontendIP \
--private-connection-resource-id $applicationGatewayResourceId \
--connection-name "${applicationGatewayName}Connection" \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "Private endpoint successfully created for the [$privateLinkServiceName] private link configuration of the [$applicationGatewayName] application gateway"
else
echo "Failed to create a private endpoint for the [$privateLinkServiceName] private link configuration of the [$applicationGatewayName] application gateway"
exit
fi
else
echo "Private endpoint [$privateEndpointName] already exists"
fi
# Check if the virtual machine already exists
virtualMachineName="${virtualMachinePrefix}TestVm"
echo "Checking if [$virtualMachineName] virtual machine actually exists in the [$resourceGroupName] resource group..."
az vm show \
--name $virtualMachineName \
--resource-group $resourceGroupName &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$virtualMachineName] virtual machine actually exists in the [$resourceGroupName] resource group"
echo "Creating the network interface for the [$virtualMachineName] virtual machine in the [$resourceGroupName] resource group..."
# Create the virtual machine network interface
nicName="${virtualMachineName}Nic"
az network nic create \
--name $nicName \
--resource-group $resourceGroupName \
--vnet-name $clientVirtualNetworkName \
--subnet $defaultSubnetName \
--only-show-errors 1>/dev/null
if [[ $? != 0 ]]; then
echo "Failed to create [$nicName] network interface in the [$resourceGroupName] resource group"
exit
fi
echo "Creating [$virtualMachineName] virtual machine in the [$resourceGroupName] resource group..."
# Create the virtual machine
az vm create \
--name $virtualMachineName \
--resource-group $resourceGroupName \
--nics $nicName \
--image UbuntuLTS \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$virtualMachineName] virtual machine successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$virtualMachineName] virtual machine in the [$resourceGroupName] resource group"
exit
fi
else
echo "[$virtualMachineName] virtual machine already exists in the [$resourceGroupName] resource group"
fi
# Check if the bastion public ip address already exists
echo "Checking if [$bastionPublicIpName] bastion public ip address actually exists in the [$resourceGroupName] resource group..."
az network public-ip show \
--name $bastionPublicIpName \
--resource-group $resourceGroupName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$bastionPublicIpName] bastion public ip address actually exists in the [$resourceGroupName] resource group"
echo "Creating [$bastionPublicIpName] bastion public ip address in the [$resourceGroupName] resource group..."
# Create the bastion public ip address
az network public-ip create \
--name $bastionPublicIpName \
--resource-group $resourceGroupName \
--sku Standard \
--allocation-method Static \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$bastionPublicIpName] bastion public ip address successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$bastionPublicIpName] bastion public ip address in the [$resourceGroupName] resource group"
exit
fi
else
echo "[$bastionPublicIpName] bastion public ip address already exists in the [$resourceGroupName] resource group"
fi
# Check if bastion exists
echo "Checking if [$bastionName] bastion actually exists in the [$resourceGroupName] resource group..."
az network bastion show \
--name $bastionName \
--resource-group $resourceGroupName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$bastionName] bastion actually exists in the [$resourceGroupName] resource group"
echo "Creating [$bastionName] bastion in the [$resourceGroupName] resource group..."
# Create the bastion subnet
az network bastion create \
--name $bastionName \
--vnet-name $clientVirtualNetworkName \
--public-ip-address $bastionPublicIpName \
--resource-group $resourceGroupName \
--location $location \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$bastionName] bastion successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$bastionName] bastion in the [$resourceGroupName] resource group"
exit
fi
else
echo "[$bastionName] bastion already exists in the [$resourceGroupName] resource group"
fi
# Check if the private DNS Zone already exists
az network private-dns zone show \
--name $privateDnsZoneName \
--resource-group $resourceGroupName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$privateDnsZoneName] private DNS zone actually exists in the [$resourceGroupName] resource group"
echo "Creating [$privateDnsZoneName] private DNS zone in the [$resourceGroupName] resource group..."
# Create the private DNS Zone
az network private-dns zone create \
--name $privateDnsZoneName \
--resource-group $resourceGroupName \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$privateDnsZoneName] private DNS zone successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$privateDnsZoneName] private DNS zone in the [$resourceGroupName] resource group"
exit
fi
else
echo "[$privateDnsZoneName] private DNS zone already exists in the [$resourceGroupName] resource group"
fi
# Check if the private DNS Zone virtual network link already exists
az network private-dns link vnet show \
--name $privateDnsZoneVirtualNetworkLinkName \
--resource-group $resourceGroupName \
--zone-name $privateDnsZoneName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$privateDnsZoneVirtualNetworkLinkName] private DNS zone virtual network link actually exists in the [$resourceGroupName] resource group"
# Retrieve the client virtual network resource id
clientVirtualNetworkResourceId=$(az network vnet show \
--name $clientVirtualNetworkName \
--resource-group $resourceGroupName \
--only-show-errors \
--query id \
--output tsv)
if [[ -z $clientVirtualNetworkResourceId ]]; then
echo "Failed to retrieve [$clientVirtualNetworkName] client virtual network resource id in the [$resourceGroupName] resource group"
exit
fi
echo "Creating [$privateDnsZoneVirtualNetworkLinkName] private DNS zone virtual network link in the [$resourceGroupName] resource group..."
# Create the private DNS Zone virtual network link
az network private-dns link vnet create \
--name $privateDnsZoneVirtualNetworkLinkName \
--resource-group $resourceGroupName \
--zone-name $privateDnsZoneName \
--virtual-network $clientVirtualNetworkResourceId \
--registration-enabled false \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$privateDnsZoneVirtualNetworkLinkName] private DNS zone virtual network link successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$privateDnsZoneVirtualNetworkLinkName] private DNS zone virtual network link in the [$resourceGroupName] resource group"
exit
fi
else
echo "[$privateDnsZoneVirtualNetworkLinkName] private DNS zone virtual network link already exists in the [$resourceGroupName] resource group"
fi
# Check if the A record already exists
az network private-dns record-set a show \
--name $aRecordName \
--resource-group $resourceGroupName \
--zone-name $privateDnsZoneName \
--only-show-errors &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$aRecordName] A record actually exists in the [$resourceGroupName] resource group"
# Get the resource id of the network interface used by the private endpoint
echo "Retrieving [$privateEndpointName] private endpoint network interface resource id in the [$resourceGroupName] resource group..."
privateEndpointNetworkInterfaceResourceId=$(az network private-endpoint show \
--name $privateEndpointName \
--resource-group $resourceGroupName \
--only-show-errors \
--query "networkInterfaces[0].id" \
--output tsv)
if [[ -z $privateEndpointNetworkInterfaceResourceId ]]; then
echo "Failed to retrieve [$privateEndpointName] private endpoint network interface resource id in the [$resourceGroupName] resource group"
exit
fi
echo $privateEndpointNetworkInterfaceResourceId
# Get the private IP address of the network interface used by the private endpoint
echo "Retrieving [$privateEndpointName] private endpoint network interface private IP address in the [$resourceGroupName] resource group..."
privateEndpointNetworkInterfacePrivateIpAddress=$(az network nic show \
--ids $privateEndpointNetworkInterfaceResourceId \
--only-show-errors \
--query "ipConfigurations[0].privateIPAddress" \
--output tsv)
if [[ -z $privateEndpointNetworkInterfacePrivateIpAddress ]]; then
echo "Failed to retrieve [$privateEndpointName] private endpoint network interface private IP address in the [$resourceGroupName] resource group"
exit
fi
# Create the A record
echo "Creating [$aRecordName] A record in the [$resourceGroupName] resource group..."
az network private-dns record-set a create \
--name $aRecordName \
--resource-group $resourceGroupName \
--zone-name $privateDnsZoneName \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$aRecordName] A record successfully created in the [$resourceGroupName] resource group"
else
echo "Failed to create [$aRecordName] A record in the [$resourceGroupName] resource group"
exit
fi
# Add record to record set
echo "Adding [$privateEndpointNetworkInterfacePrivateIpAddress] private IP address to the [$aRecordName] A record..."
az network private-dns record-set a add-record \
--record-set-name $aRecordName \
--resource-group $resourceGroupName \
--zone-name $privateDnsZoneName \
--ipv4-address $privateEndpointNetworkInterfacePrivateIpAddress \
--only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$privateEndpointNetworkInterfacePrivateIpAddress] private IP address successfully added to the [$aRecordName] A record"
else
echo "Failed to add [$privateEndpointNetworkInterfacePrivateIpAddress] private IP address to the [$aRecordName] A record"
exit
fi
else
echo "[$aRecordName] A record already exists in the [$resourceGroupName] resource group"
fi
When done, run the deploy.sh script to create Azure resources in a resource group.
If the deployment succeeds, proceed as follows to test the sample:
nslookup app.<prefix>.internal
command. The name of the Private DNS zone depends on the value you chose for the prefix variable in the deploy.sh script. curl app.<prefix>.internal; echo
command. If the call succeeds, you should see a result like the one in the following figure.
Use the Azure portal, Azure CLI, or Azure PowerShell to list the deployed resources in the resource group.
az resource list --resource-group <resource-group-name>
PowerShell
Get-AzResource -ResourceGroupName <resource-group-name>
Delete the resource group when you no longer need the resources you created. This will remove all the Azure resources.
az group delete --name <resource-group-name>
PowerShell
Remove-AzResourceGroup -Name <resource-group-name>
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.