This sample shows how to use Bicep to create an Azure Private Link Service that can be accessed by a third party via an Azure Private Endpoint. Bicep modules deploy all the Azure resources in the same resource group in the same Azure subscription. In a real-world scenario, service consumer and service provider resources will be hosted distinct Azure subscriptions under the same or a different Azure Active Directory tenant. You can find code on Azure Samples.
The following picture shows the high-level architecture created by the Bicep modules included in this sample:
Figure: network topology and infrastructure architecture.
Multiple Azure resources are defined in the Bicep modules:
imagePublisher
, imageOffer
, and imageSku
to select a different operating system or version.NOTE
You can find the architecture.vsdx
file used for the diagram under the visio
folder under Azure Samples.
You can deploy the Bicep modules in the bicep
folder using either Azure CLI or Azure PowerShell.
az group create \
--name SampleRG \
--location westeurope
az deployment group create \
--resource-group SampleRG \
--template-file main.bicep \
--parameters vmAdminUsername=<admin-user>
New-AzResourceGroup -Name SampleRG -Location westeurope
New-AzResourceGroupDeployment `
-ResourceGroupName SampleRG `
-TemplateFile ./main.bicep `
-vmAdminUsername "<admin-user>"
You can also use the deploy.sh
bash script under the bicep
folder to deploy the infrastructure.
#!/bin/bash
# Template
template="main.bicep"
parameters="main.parameters.json"
# Variables
validateTemplate=1
useWhatIf=1
install=1
# Name and location of the resource group for the Azure Kubernetes Service (AKS) cluster
resourceGroupName="SampleRG"
location="WestEurope"
# 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 exists in the [$subscriptionName] subscription..."
az group show --name $resourceGroupName &>/dev/null
if [[ $? != 0 ]]; then
echo "No [$resourceGroupName] resource group 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 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
# Validate the Bicep template
if [[ $validateTemplate == 1 ]]; then
if [[ $useWhatIf == 1 ]]; then
# Execute a deployment What-If operation at resource group scope
echo "Previewing changes deployed by [$template] Bicep template..."
az deployment group what-if \
--resource-group $resourceGroupName \
--template-file $template \
--parameters $parameters
if [[ $? == 0 ]]; then
echo "[$template] Bicep template validation succeeded"
else
echo "Failed to validate [$template] Bicep template"
exit
fi
else
# Validate the Bicep template
echo "Validating [$template] Bicep template..."
output=$(az deployment group validate \
--resource-group $resourceGroupName \
--template-file $template \
--parameters $parameters)
if [[ $? == 0 ]]; then
echo "[$template] Bicep template validation succeeded"
else
echo "Failed to validate [$template] Bicep template"
echo $output
exit
fi
fi
fi
# Deploy the Bicep template
if [[ $install == 1 ]]; then
echo "Deploying [$template] Bicep template..."
az deployment group create \
--resource-group $resourceGroupName \
--only-show-errors \
--template-file $template \
--parameters $parameters 1>/dev/null
if [[ $? == 0 ]]; then
echo "[$template] Bicep template successfully provisioned"
else
echo "Failed to provision the [$template] Bicep template"
exit
fi
fi
NOTE
Make sure to specify a value for the following parameters in the main.parameters.json
file:
prefix
: specifies a prefix for all the Azure resources.authenticationType
: specifies the type of authentication when accessing the Virtual Machine. SSH key is recommended. Allowed values: sshPublicKey
and password
.vmAdminUsername
: specifies the name of the administrator account of the virtual machine.vmAdminPasswordOrKey
: specifies the SSH Key or password for the virtual machine. SSH key is recommended. We suggest reading the password or SSH Key from Key Vault. For more information, see Use Azure Key Vault to pass secure parameter value during Bicep deployment.Use the Azure portal, Azure CLI, or Azure PowerShell to list the deployed resources in the resource group.
az resource list --resource-group SampleRG
Get-AzResource -ResourceGroupName SampleRG
Figure: Azure Resources in the resource group.
The sample is composed of many Bicep modules, each deploying a different set of resources. The following table contains the Bicep module that deployed the Azure Private Link Service and related Azure Private Endpoint.
// Parameters
@description('Specifies the name of the Azure Private Link Service.')
param privatelinkServiceName string
@description('Specifies the name of the Azure Private Endpoint.')
param privateEndpointName string
@description('Specifies the name of the load balancer.')
param loadBalancerName string
@description('Specifies the name of the client virtual network.')
param virtualNetworkName string
@description('Specifies the name of the subnet used by the load balancer.')
param subnetName string
@description('Specifies the location.')
param location string = resourceGroup().location
@description('Specifies the resource tags.')
param tags object
// Resources
resource loadBalancer 'Microsoft.Network/loadBalancers@2021-08-01' existing = {
name: loadBalancerName
}
resource vnet 'Microsoft.Network/virtualNetworks@2021-08-01' existing = {
name: virtualNetworkName
}
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2021-08-01' existing = {
name: subnetName
parent: vnet
}
resource privatelinkService 'Microsoft.Network/privateLinkServices@2021-05-01' = {
name: privatelinkServiceName
location: location
tags: tags
properties: {
enableProxyProtocol: false
loadBalancerFrontendIpConfigurations: [
{
id: resourceId('Microsoft.Network/loadBalancers/frontendIpConfigurations', loadBalancer.name, loadBalancer.properties.frontendIPConfigurations[0].name)
}
]
ipConfigurations: [
{
name: 'ipConfig'
properties: {
privateIPAllocationMethod: 'Dynamic'
privateIPAddressVersion: 'IPv4'
subnet: {
id: loadBalancer.properties.frontendIPConfigurations[0].properties.subnet.id
}
primary: false
}
}
]
}
}
resource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-05-01' = {
name: privateEndpointName
location: location
properties: {
subnet: {
id: subnet.id
}
privateLinkServiceConnections: [
{
name: privateEndpointName
properties: {
privateLinkServiceId: privatelinkService.id
}
}
]
}
dependsOn: [
subnet
]
}
Connect to to service consumer virtual machine via Bastion from the Azure portal as follows:
Connect
on the Overview
page.Bastion
from the drop-down list.Username
. Authentication Type
, and Password
or SSH Private Key
.Connect
button.Here's how to connect to the http service from the VM by using the private endpoint.
Private IP address
in the Overview
page.curl <private IP address>
command.If everything works as expected, you should see a response message like the following returned by the NGINX website hosted by the service provider virtual machine via Azure Private Link service.
azadmin@MazingaClientVm:~$ curl 10.1.0.4
Hello World from host MazingaServiceVm !
When you no longer need the resources that you created with the private link service, delete the resource group. This will remove all the Azure resources.
Bicep is a domain-specific language (DSL) that uses declarative syntax to deploy Azure resources. It provides concise syntax, reliable type safety, and support for code reuse. Bicep offers the best authoring experience for your infrastructure-as-code solutions in Azure.
Azure Private Link service is the reference to your own service that is powered by Azure Private Link. Your service that is running behind Azure Standard Load Balancer can be enabled for Private Link access so that consumers to your service can access it privately from their own VNets. Your customers can create a private endpoint inside their VNet and map it to this service. This article explains concepts related to the service provider side.
Figure: Azure Private Link Service.
Assuming that the user deploying the solution has Owner
or Contributor
role on the resource group or subscription, the private link connection in this sample will be automatically approved. In a real-world scenario, the private endpoint connection will need to be approved by the service provider. For more information, see the approval workflow under Azure Private Link service .
Figure: Azure Private Link service workflow.
NOTE
Azure Private Link Service is only supported on Standard Load Balancer.
After you create a Private Link service, Azure will generate a globally unique named moniker called "alias" based on the name you provide for your service. You can share either the alias or resource URI of your service with your customers offline. Consumers can start a Private Link connection using the alias or the resource URI.
After a consumer initiates a connection, the service provider can accept or reject the connection request. All connection requests will be listed under the privateendpointconnections
property on the Private Link service. On the Azure portal, you can see select the Private endpoint connections
tab under the Azure Private Link service ro see all pending or approved private endpoint connections.
The following are the known limitations when using the Private Link service:
If you have any feedback, please write a comment below or submit an issue or a PR on GitHub. If you found this article and companion sample useful, please like the article below and give a star to the project on GitHub, thanks.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.