Guest paolosalvatori Posted April 20, 2023 Posted April 20, 2023 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: Application Gateway Private Link Configure Azure Application Gateway Private Link 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. 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 An active Azure subscription. If you don't have one, create a free Azure account before you begin. If you run the Bash script locally, ensure the latest version of the Azure CLI is installed. 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. Run the curl app.<prefix>.internal; echo command. If the call succeeds, you should see a result like the one in the following figure. 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... Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.