The following Jumpstart scenario will guide you on how to use Azure Private Link to securely connect from an Azure Arc-enabled server to Azure using a VPN. This feature allows you to connect privately to Azure Arc without opening up public network access but rather using private endpoints over a VPN or ExpressRoute connection, ensuring that all traffic is being sent to Azure privately.

In this scenario, you will emulate a hybrid environment connected to Azure over a VPN with hybrid resources that will be Azure Arc-enabled, and Azure Private Link Scope will be used to connect over a private connection. To complete this process you deploy a single ARM template that will:

  • Create two separate resource groups:

    • “On-premises” resource group will contain resources that simulate a private on-premises environment with a Windows virtual machine. This VM will not have a public IP address assigned to it so Azure Bastion is deployed to have administrative access to the operating system. The Windows virtual machine is an Azure Arc-enabled server by installing the Azure Arc connected machine agent using Azure Private Link.
    • “Azure” resource group will contain the Azure Arc-enabled server and its Private Link Scope.
  • Both resource groups have their own virtual networks and address spaces, and they are connected via Azure VPN gateways to set up a hybrid private connection.

    Deployment Overview

    NOTE: It is not expected for an Azure VM to be projected as an Azure Arc-enabled server. The below scenario is unsupported and should ONLY be used for demo and testing purposes. NOTE: The below scenario assumes the on-premises VM has outbound internet connectivity for the deployment of the Azure Arc connected machine agent. For internet disconnected environments you will need to adjust the automation to retrieve the agent’s software from locally accessible storage


  • Install or update Azure CLI. Azure CLI should be running version 2.42.0 or later. Use az --version to check your current installed version.

  • Azure Arc-enabled servers depends on the following Azure resource providers in your subscription in order to use this service. Registration is an asynchronous process, and registration may take approximately 10 minutes.

    • Microsoft.HybridCompute

    • Microsoft.GuestConfiguration

    • Microsoft.HybridConnectivity

      az provider register --namespace 'Microsoft.HybridCompute'
      az provider register --namespace 'Microsoft.GuestConfiguration'
      az provider register --namespace 'Microsoft.HybridConnectivity'

      You can monitor the registration process with the following commands:

      az provider show --namespace 'Microsoft.HybridCompute'
      az provider show --namespace 'Microsoft.GuestConfiguration'
      az provider show --namespace 'Microsoft.HybridConnectivity'
  • Create Azure service principal (SP). To deploy this scenario, an Azure service principal assigned with the Contributor Role-based access control (RBAC) role is required. You can use Azure Cloud Shell (or other Bash shell), or PowerShell to create the service principal.

    • (Option 1) Create service principal using Azure Cloud Shell or Bash shell with Azure CLI:

      az login
      subscriptionId=$(az account show --query id --output tsv)
      az ad sp create-for-rbac -n "<Unique SP Name>" --role "Contributor" --scopes /subscriptions/$subscriptionId

      For example:

      az login
      subscriptionId=$(az account show --query id --output tsv)
      az ad sp create-for-rbac -n "JumpstartArc" --role "Contributor" --scopes /subscriptions/$subscriptionId

      Output should look similar to this:

      "displayName": "JumpstartArc",
    • (Option 2) Create service principal using PowerShell. If necessary, follow this documentation to install Azure PowerShell modules.

      $account = Connect-AzAccount
      $spn = New-AzADServicePrincipal -DisplayName "<Unique SPN name>" -Role "Contributor" -Scope "/subscriptions/$($account.Context.Subscription.Id)"
      echo "SPN App id: $($spn.AppId)"
      echo "SPN secret: $($spn.PasswordCredentials.SecretText)"

      For example:

      $account = Connect-AzAccount
      $spn = New-AzADServicePrincipal -DisplayName "JumpstartArcSPN" -Role "Contributor" -Scope "/subscriptions/$($account.Context.Subscription.Id)"
      echo "SPN App id: $($spn.AppId)"
      echo "SPN secret: $($spn.PasswordCredentials.SecretText)"

      Output should look similar to this:

      Screenshot showing creating an SPN with PowerShell

      NOTE: If you create multiple subsequent role assignments on the same service principal, your client secret (password) will be destroyed and recreated each time. Therefore, make sure you grab the correct password..

      NOTE: The Jumpstart scenarios are designed with as much ease of use in-mind and adhering to security-related best practices whenever possible. It is optional but highly recommended to scope the service principal to a specific Azure subscription and resource group as well considering using a less privileged service principal account

Deployment Options and Automation Flow

This Jumpstart scenario provides multiple paths for deploying and configuring resources. Deployment options include:

  • Azure portal
  • ARM template via Azure CLI

For you to get familiar with the automation and deployment flow, below is an explanation.

  1. User provides the ARM template parameter values, either via the portal or editing the parameters file. These parameter values are being used throughout the deployment.

  2. User deploys the ARM template at subscription level. The ARM template will create two resources groups with:

    • Azure resource group, automated by the template cloudDeploy.json:

      • Azure Arc Private Link Scope
      • Azure Arc-enabled server
      • Azure Private Link Endpoint for the Azure Arc-enabled Server
      • Three Azure Private DNS zones, to overwrite Azure Arc’s public endpoints DNS configuration to connect using a private endpoint
      • Azure VPN Gateway and its public IP address
      • Azure VNET
    • On-premises resource group, automated by the template onPremisesDeploy.json:

      • Azure VNET
      • Azure Bastion
      • Azure VPN Gateway and its public IP address
      • Azure Windows Virtual Machine with a custom script extension that runs the Bootstrap.ps1 script

      NOTE: The installArcAgent.ps1 script will enable the OS firewall and set up new rules for incoming and outgoing connections. By default all incoming and outgoing traffic will be allowed, except blocking Azure IMDS outbound traffic to the remote address.

  3. User logs in to the on-premises VM using Azure Bastion to trigger the Azure Arc onboarding script.

Deployment Option 1: Azure portal

  • Click the button and enter values for the the ARM template parameters.

    Screenshot showing Azure portal deployment

    Screenshot showing Azure portal deployment

Deployment Option 2: ARM template with Azure CLI

As mentioned, this deployment will leverage ARM templates. You will deploy a single ARM template at subscription scope that will deploy resources to the Azure’s resource group as well as the “On-premises” resources that will be onboarded to Azure Arc.

  • Clone the Azure Arc Jumpstart repository

    git clone
  • Before deploying the ARM template, login to Azure using AZ CLI with the az login command.

  • The deployment will use an ARM template parameters file to customize your environment. Before initiating the deployment, edit the azuredeploy.parameters.json file located in your local cloned repository folder. Example parameters files is located here.

  • To deploy the ARM template, navigate to the local cloned deployment folder and run the below command, start deploying resources:

    az deployment sub create \
    --location <Azure Region Location> \
    --template-file <The *azuredeploy.json* template file location> \
    --parameters <The *azuredeploy.parameters.json* parameters file location>

    For example:

    az deployment sub create \
    --location eastus \
    --template-uri \
    --parameters azuredeploy.example.parameters.json

    NOTE: The deployment may take around 45 minutes to complete.

  • Verify the resources are created on the Azure Portal for both resource groups:

    Resources created on Onpremises’s resource group

    Resources created on Azure’s resource group

Windows Login & Post Deployment

  • Now that the Windows Server VM is created and the VPN connections are established, it is time to RDP to it using Azure Bastion.

    • On the “on-premises” resource group select the Windows VM:

      Azure Bastion session 01

    • Under “Connect” choose Bastion:

      Azure Bastion session 02

    • Provide the VM credentials and click on “Connect”:

      Azure Bastion session 03

  • At first login, as mentioned in the “Automation Flow” section, a logon script will get executed. This script was created as part of the automated deployment process.

  • Let the script to run its course and do not close the Powershell session, this will be done for you once completed.

    NOTE: The script run time is ~1-2min long.

    Screenshot script output

  • Upon successful run, a new Azure Arc-enabled server will be added to the resource group.

    Screenshot Azure Arc-enabled server on resource group

To make sure that your Azure Arc-enabled server is using Private Link for its connection. Use your Azure Bastion session to run the command below:

  azcmagent check --location <your Azure Region> --enable-pls-check --verbose

It should show private reachable connections for the agent’s endpoints.

Connected Machine agent using PL

Delete the deployment

The most straightforward way is to delete both resource groups:

Delete Resource Group On-premises

Delete Resource Group Azure