Jump to content

Configure Azure Application Gateway Private Link


Recommended Posts

Guest paolosalvatori
Posted

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:

 

 

 

 

 

 

Architecture

 

 

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.

 

 

 

largevv2px999.png.86a44cd89363fa9b40b9ac61a716f384.png

 

The script creates the following Azure resources for the service provider:

 

 

 

  • A virtual network that contains the following subnets:
    • AppGatewaySubnet: this subnet hosts the Application Gateway
    • BackendSubnet: this subnet hosts Private Link and the virtual machines hosting a 'Hello World' Node.js web application installed via cloud-init

    [*]Application Gateway WAF2 with Private Link

    [*]WAF Policy

    [*]Virtual machines hosting the 'Hello World' Node.js app and related network interfaces

 

 

 

The script creates the following Azure resources for the client provider:

 

 

 

  • A virtual network that contains the following subnets:
    • AzureBastionSubnet: this subnet hosts the Azure Bastion Host to access a client virtual machine.
    • BackendSubnet: this subnet hosts a client virtual machine used to call the sample web application via a Private Endpoint to the Application Gateway Private Link hosted in the same subnet.

    [*]A Private DNS Zone with an A record that is used to resolve a hostname (e.g. app.green.internal) to the private IP address of the Private Endpoint used to connect to the Application Gateway Private Link.

Features and Capabilities

 

 

Private Link allows you to extend private connectivity to Application Gateway via a Private Endpoint in the following scenarios:

 

 

 

  • VNet in the same or different region from Application Gateway
  • VNet in the same or different subscription from Application Gateway
  • VNet in the same or different subscription and the same or different Azure AD tenant from Application Gateway

 

 

 

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.

 

 

 

Private Link components

 

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.
     

 

 

 

Limitations

  • API version 2020-03-01 or later should be used to configure Private Link configurations.
  • The static IP allocation method in the Private Link Configuration object isn't supported.
  • The subnet used for PrivateLinkConfiguration cannot be the same as the Application Gateway subnet.
  • Private link configuration for Application Gateway doesn't expose the "Alias" property and must be referenced via resource URI.
  • Private Endpoint creation doesn't create a *.privatelink DNS record/zone. All DNS records should be entered in existing zones used for your Application Gateway.
  • Azure Front Door and Application Gateway do not support chaining via Private Link.
  • Source IP address and x-forwarded-for headers will contain the Private link IP addresses.

 

 

 

Prerequisites

 

 

 

 

Installation

 

 

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

# Manage network policies for private endpoints - Azure Private Link

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

# Manage network policies for private endpoints - Azure Private Link

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 Configure Azure Application Gateway Private Link (preview)

# 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.

 

 

 

Test the Sample

 

 

If the deployment succeeds, proceed as follows to test the sample:

 

 

  • Navigate to Azure Portal and connect to the client virtual machine via Azure Bastion.
  • Run the the 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.

 

mediumvv2px400.png.3b1f1d7ccf66186df472837194ec85de.png

 

  • Run the curl app.<prefix>.internal; echo command. If the call succeeds, you should see a result like the one in the following figure.

 

mediumvv2px400.png.b09d1625a345f4f67460ac6446ae7e4c.png

 

 

 

Review deployed resources

 

 

Use the Azure portal, Azure CLI, or Azure PowerShell to list the deployed resources in the resource group.

 

 

 

Azure CLI

 

 

 

az resource list --resource-group <resource-group-name>

 

 

 

 

 

PowerShell

 

 

 

Get-AzResource -ResourceGroupName <resource-group-name>

 

 

 

Clean up resources

 

 

Delete the resource group when you no longer need the resources you created. This will remove all the Azure resources.

 

 

 

Azure CLI

 

 

 

az group delete --name <resource-group-name>

 

 

 

PowerShell

 

 

 

Remove-AzResourceGroup -Name <resource-group-name>

 

Continue reading...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...