Restrict network access to PaaS resources with virtual network service endpoints using the Azure CLI

Virtual network service endpoints enable you to limit network access to some Azure service resources to a virtual network subnet. You can also remove internet access to the resources. Service endpoints provide direct connection from your virtual network to supported Azure services, allowing you to use your virtual network's private address space to access the Azure services. Traffic destined to Azure resources through service endpoints always stays on the Microsoft Azure backbone network. In this article, you learn how to:

  • Create a virtual network with one subnet
  • Add a subnet and enable a service endpoint
  • Create an Azure resource and allow network access to it from only a subnet
  • Deploy a virtual machine (VM) to each subnet
  • Confirm access to a resource from a subnet
  • Confirm access is denied to a resource from a subnet and the internet

If you don't have an Azure subscription, create a free account before you begin.

Use Azure Cloud Shell

Azure hosts Azure Cloud Shell, an interactive shell environment that you can use through your browser. Cloud Shell lets you use either bash or PowerShell to work with Azure services. You can use the Cloud Shell pre-installed commands to run the code in this article without having to install anything on your local environment.

To launch Azure Cloud Shell:

Option Example/Link
Select Try It in the upper-right corner of a code block. Selecting Try It doesn't automatically copy the code to Cloud Shell. Example of Try It for Azure Cloud Shell
Go to https://shell.azure.com or select the Launch Cloud Shell button to open Cloud Shell in your browser. Launch Cloud Shell in a new window
Select the Cloud Shell button on the top-right menu bar in the Azure portal. Cloud Shell button in the Azure portal

To run the code in this article in Azure Cloud Shell:

  1. Launch Cloud Shell.

  2. Select the Copy button on a code block to copy the code.

  3. Paste the code into the Cloud Shell session with Ctrl+Shift+V on Windows and Linux, or Cmd+Shift+V on macOS.

  4. Press Enter to run the code.

If you choose to install and use the CLI locally, this quickstart requires that you are running the Azure CLI version 2.0.28 or later. To find the version, run az --version. If you need to install or upgrade, see Install Azure CLI.

Create a virtual network

Before creating a virtual network, you have to create a resource group for the virtual network, and all other resources created in this article. Create a resource group with az group create. The following example creates a resource group named myResourceGroup in the eastus location.

az group create \
  --name myResourceGroup \
  --location eastus

Create a virtual network with one subnet with az network vnet create.

az network vnet create \
  --name myVirtualNetwork \
  --resource-group myResourceGroup \
  --address-prefix 10.0.0.0/16 \
  --subnet-name Public \
  --subnet-prefix 10.0.0.0/24

Enable a service endpoint

You can enable service endpoints only for services that support service endpoints. View service endpoint-enabled services available in an Azure location with az network vnet list-endpoint-services. The following example returns a list of service-endpoint-enabled services available in the eastus region. The list of services returned will grow over time, as more Azure services become service endpoint enabled.

az network vnet list-endpoint-services \
  --location eastus \
  --out table

Create an additional subnet in the virtual network with az network vnet subnet create. In this example, a service endpoint for Microsoft.Storage is created for the subnet:

az network vnet subnet create \
  --vnet-name myVirtualNetwork \
  --resource-group myResourceGroup \
  --name Private \
  --address-prefix 10.0.1.0/24 \
  --service-endpoints Microsoft.Storage

Restrict network access for a subnet

Create a network security group with az network nsg create. The following example creates a network security group named myNsgPrivate.

az network nsg create \
  --resource-group myResourceGroup \
  --name myNsgPrivate

Associate the network security group to the Private subnet with az network vnet subnet update. The following example associates the myNsgPrivate network security group to the Private subnet:

az network vnet subnet update \
  --vnet-name myVirtualNetwork \
  --name Private \
  --resource-group myResourceGroup \
  --network-security-group myNsgPrivate

Create security rules with az network nsg rule create. The rule that follows allows outbound access to the public IP addresses assigned to the Azure Storage service:

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name myNsgPrivate \
  --name Allow-Storage-All \
  --access Allow \
  --protocol "*" \
  --direction Outbound \
  --priority 100 \
  --source-address-prefix "VirtualNetwork" \
  --source-port-range "*" \
  --destination-address-prefix "Storage" \
  --destination-port-range "*"

Each network security group contains several default security rules. The rule that follows overrides a default security rule that allows outbound access to all public IP addresses. The destination-address-prefix "Internet" option denies outbound access to all public IP addresses. The previous rule overrides this rule, due to its higher priority, which allows access to the public IP addresses of Azure Storage.

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name myNsgPrivate \
  --name Deny-Internet-All \
  --access Deny \
  --protocol "*" \
  --direction Outbound \
  --priority 110 \
  --source-address-prefix "VirtualNetwork" \
  --source-port-range "*" \
  --destination-address-prefix "Internet" \
  --destination-port-range "*"

The following rule allows SSH traffic inbound to the subnet from anywhere. The rule overrides a default security rule that denies all inbound traffic from the internet. SSH is allowed to the subnet so that connectivity can be tested in a later step.

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name myNsgPrivate \
  --name Allow-SSH-All \
  --access Allow \
  --protocol Tcp \
  --direction Inbound \
  --priority 120 \
  --source-address-prefix "*" \
  --source-port-range "*" \
  --destination-address-prefix "VirtualNetwork" \
  --destination-port-range "22"

Restrict network access to a resource

The steps necessary to restrict network access to resources created through Azure services enabled for service endpoints varies across services. See the documentation for individual services for specific steps for each service. The remainder of this article includes steps to restrict network access for an Azure Storage account, as an example.

Create a storage account

Create an Azure storage account with az storage account create. Replace <replace-with-your-unique-storage-account-name> with a name that is unique across all Azure locations, between 3-24 characters in length, using only numbers and lower-case letters.

storageAcctName="<replace-with-your-unique-storage-account-name>"

az storage account create \
  --name $storageAcctName \
  --resource-group myResourceGroup \
  --sku Standard_LRS \
  --kind StorageV2

After the storage account is created, retrieve the connection string for the storage account into a variable with az storage account show-connection-string. The connection string is used to create a file share in a later step.

saConnectionString=$(az storage account show-connection-string \
  --name $storageAcctName \
  --resource-group myResourceGroup \
  --query 'connectionString' \
  --out tsv)

View the contents of the variable and note the value for AccountKey returned in the output, because it's used in a later step.

echo $saConnectionString

Create a file share in the storage account

Create a file share in the storage account with az storage share create. In a later step, this file share is mounted to confirm network access to it.

az storage share create \
  --name my-file-share \
  --quota 2048 \
  --connection-string $saConnectionString > /dev/null

Deny all network access to a storage account

By default, storage accounts accept network connections from clients in any network. To limit access to selected networks, change the default action to Deny with az storage account update. Once network access is denied, the storage account is not accessible from any network.

az storage account update \
  --name $storageAcctName \
  --resource-group myResourceGroup \
  --default-action Deny

Enable network access from a subnet

Allow network access to the storage account from the Private subnet with az storage account network-rule add.

az storage account network-rule add \
  --resource-group myResourceGroup \
  --account-name $storageAcctName \
  --vnet-name myVirtualNetwork \
  --subnet Private

Create virtual machines

To test network access to a storage account, deploy a VM to each subnet.

Create the first virtual machine

Create a VM in the Public subnet with az vm create. If SSH keys do not already exist in a default key location, the command creates them. To use a specific set of keys, use the --ssh-key-value option.

az vm create \
  --resource-group myResourceGroup \
  --name myVmPublic \
  --image UbuntuLTS \
  --vnet-name myVirtualNetwork \
  --subnet Public \
  --generate-ssh-keys

The VM takes a few minutes to create. After the VM is created, the Azure CLI shows information similar to the following example:

{
  "fqdns": "",
  "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVmPublic",
  "location": "eastus",
  "macAddress": "00-0D-3A-23-9A-49",
  "powerState": "VM running",
  "privateIpAddress": "10.0.0.4",
  "publicIpAddress": "13.90.242.231",
  "resourceGroup": "myResourceGroup"
}

Take note of the publicIpAddress in the returned output. This address is used to access the VM from the internet in a later step.

Create the second virtual machine

az vm create \
  --resource-group myResourceGroup \
  --name myVmPrivate \
  --image UbuntuLTS \
  --vnet-name myVirtualNetwork \
  --subnet Private \
  --generate-ssh-keys

The VM takes a few minutes to create. After creation, take note of the publicIpAddress in the output returned. This address is used to access the VM from the internet in a later step.

Confirm access to storage account

SSH into the myVmPrivate VM. Replace <publicIpAddress> with the public IP address of your myVmPrivate VM.

ssh <publicIpAddress>

Create a folder for a mount point:

sudo mkdir /mnt/MyAzureFileShare

Mount the Azure file share to the directory you created. Before running the following command, replace <storage-account-name> with the account name and <storage-account-key> with the key you retrieved in Create a storage account.

sudo mount --types cifs //<storage-account-name>.file.core.windows.net/my-file-share /mnt/MyAzureFileShare --options vers=3.0,username=<storage-account-name>,password=<storage-account-key>,dir_mode=0777,file_mode=0777,serverino

You receive the user@myVmPrivate:~$ prompt. The Azure file share successfully mounted to /mnt/MyAzureFileShare.

Confirm that the VM has no outbound connectivity to any other public IP addresses:

ping bing.com -c 4

You receive no replies, because the network security group associated to the Private subnet does not allow outbound access to public IP addresses other than the addresses assigned to the Azure Storage service.

Exit the SSH session to the myVmPrivate VM.

Confirm access is denied to storage account

Use the following command to create an SSH session with the myVmPublic VM. Replace <publicIpAddress> with the public IP address of your myVmPublic VM:

ssh <publicIpAddress>

Create a directory for a mount point:

sudo mkdir /mnt/MyAzureFileShare

Attempt to mount the Azure file share to the directory you created. This article assumes you deployed the latest version of Ubuntu. If you are using earlier versions of Ubuntu, see Mount on Linux for additional instructions about mounting file shares. Before running the following command, replace <storage-account-name> with the account name and <storage-account-key> with the key you retrieved in Create a storage account:

sudo mount --types cifs //storage-account-name>.file.core.windows.net/my-file-share /mnt/MyAzureFileShare --options vers=3.0,username=<storage-account-name>,password=<storage-account-key>,dir_mode=0777,file_mode=0777,serverino

Access is denied, and you receive a mount error(13): Permission denied error, because the myVmPublic VM is deployed within the Public subnet. The Public subnet does not have a service endpoint enabled for Azure Storage, and the storage account only allows network access from the Private subnet, not the Public subnet.

Exit the SSH session to the myVmPublic VM.

From your computer, attempt to view the shares in your storage account with az storage share list. Replace <account-name> and <account-key> with the storage account name and key from Create a storage account:

az storage share list \
  --account-name <account-name> \
  --account-key <account-key>

Access is denied and you receive a This request is not authorized to perform this operation error, because your computer is not in the Private subnet of the MyVirtualNetwork virtual network.

Clean up resources

When no longer needed, use az group delete to remove the resource group and all of the resources it contains.

az group delete --name myResourceGroup --yes

Next steps

In this article, you enabled a service endpoint for a virtual network subnet. You learned that service endpoints can be enabled for resources deployed with multiple Azure services. You created an Azure Storage account and limited network access to the storage account to only resources within a virtual network subnet. To learn more about service endpoints, see Service endpoints overview and Manage subnets.

If you have multiple virtual networks in your account, you may want to connect two virtual networks together so the resources within each virtual network can communicate with each other. To learn how, see Connect virtual networks.