Tutorial: Restrict network access to PaaS resources with virtual network service endpoints
Article
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 tutorial, 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.
Azure Cloud Shell
Azure hosts Azure Cloud Shell, an interactive shell environment that you can use through your browser. You can use either Bash or PowerShell with Cloud Shell to work with Azure services. You can use the Cloud Shell preinstalled commands to run the code in this article, without having to install anything on your local environment.
To start Azure Cloud Shell:
Option
Example/Link
Select Try It in the upper-right corner of a code or command block. Selecting Try It doesn't automatically copy the code or command to Cloud Shell.
Go to https://shell.azure.com, or select the Launch Cloud Shell button to open Cloud Shell in your browser.
Select the Cloud Shell button on the menu bar at the upper right in the Azure portal.
To use Azure Cloud Shell:
Start Cloud Shell.
Select the Copy button on a code block (or command block) to copy the code or command.
Paste the code or command into the Cloud Shell session by selecting Ctrl+Shift+V on Windows and Linux, or by selecting Cmd+Shift+V on macOS.
Select Enter to run the code or command.
If you choose to install and use PowerShell locally, this article requires the Azure PowerShell module version 1.0.0 or later. Run Get-Module -ListAvailable Az to find the installed version. If you need to upgrade, see Install Azure PowerShell module. If you're running PowerShell locally, you also need to run Connect-AzAccount to create a connection with Azure.
If you prefer to run CLI reference commands locally, install the Azure CLI. If you're running on Windows or macOS, consider running Azure CLI in a Docker container. For more information, see How to run the Azure CLI in a Docker container.
If you're using a local installation, sign in to the Azure CLI by using the az login command. To finish the authentication process, follow the steps displayed in your terminal. For other sign-in options, see Sign in with the Azure CLI.
When you're prompted, install the Azure CLI extension on first use. For more information about extensions, see Use extensions with the Azure CLI.
Run az version to find the version and dependent libraries that are installed. To upgrade to the latest version, run az upgrade.
This article requires version 2.0.28 or later of the Azure CLI. If using Azure Cloud Shell, the latest version is already installed.
Create a virtual network and an Azure Bastion host
The following procedure creates a virtual network with a resource subnet, an Azure Bastion subnet, and a Bastion host:
In the portal, search for and select Virtual networks.
On the Virtual networks page, select + Create.
On the Basics tab of Create virtual network, enter, or select the following information:
Setting
Value
Project details
Subscription
Select your subscription.
Resource group
Select Create new. Enter test-rg for the name. Select OK.
Instance details
Name
Enter vnet-1.
Region
Select East US 2.
Select Next to proceed to the Security tab.
In the Azure Bastion section, select Enable Azure Bastion.
Bastion uses your browser to connect to VMs in your virtual network over Secure Shell (SSH) or Remote Desktop Protocol (RDP) by using their private IP addresses. The VMs don't need public IP addresses, client software, or special configuration. For more information, see What is Azure Bastion?.
Note
Hourly pricing starts from the moment that Bastion is deployed, regardless of outbound data usage. For more information, see Pricing and SKUs. If you're deploying Bastion as part of a tutorial or test, we recommend that you delete this resource after you finish using it.
In Azure Bastion, enter or select the following information:
Setting
Value
Azure Bastion host name
Enter bastion.
Azure Bastion public IP address
Select Create a public IP address. Enter public-ip-bastion in Name. Select OK.
Select Next to proceed to the IP Addresses tab.
In the address space box in Subnets, select the default subnet.
In Edit subnet, enter or select the following information:
Setting
Value
Subnet purpose
Leave the default of Default.
Name
Enter subnet-1.
IPv4
IPv4 address range
Leave the default of 10.0.0.0/16.
Starting address
Leave the default of 10.0.0.0.
Size
Leave the default of /24 (256 addresses).
Select Save.
Select Review + create at the bottom of the window. When validation passes, select Create.
Service endpoints are enabled per service, per subnet.
In the search box at the top of the portal page, search for Virtual network. Select Virtual networks in the search results.
In Virtual networks, select vnet-1.
In the Settings section of vnet-1, select Subnets.
Select + Subnet.
On the Add subnet page, enter, or select the following information:
Setting
Value
Name
subnet-private
Subnet address range
Leave the default of 10.0.2.0/24.
SERVICE ENDPOINTS
Services
Select Microsoft.Storage
Select Save.
Caution
Before enabling a service endpoint for an existing subnet that has resources in it, see Change subnet settings.
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 New-AzResourceGroup. The following example creates a resource group named test-rg:
Create a subnet configuration with New-AzVirtualNetworkSubnetConfig. The following example creates a subnet configuration for a subnet named subnet-public:
Azure Bastion uses your browser to connect to VMs in your virtual network over Secure Shell (SSH) or Remote Desktop Protocol (RDP) by using their private IP addresses. The VMs don't need public IP addresses, client software, or special configuration. For more information about Bastion, see What is Azure Bastion?.
Hourly pricing starts from the moment that Bastion is deployed, regardless of outbound data usage. For more information, see Pricing and SKUs. If you're deploying Bastion as part of a tutorial or test, we recommend that you delete this resource after you finish using it.
Configure a Bastion subnet for your virtual network. This subnet is reserved exclusively for Bastion resources and must be named AzureBastionSubnet.
It takes about 10 minutes to deploy the Bastion resources. You can create VMs in the next section while Bastion deploys to your virtual network.
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 test-rg in the westus2 location.
az group create \
--name test-rg \
--location westus2
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 westus2 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 westus2 \
--out table
Create another 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:
By default, all virtual machine instances in a subnet can communicate with any resources. You can limit communication to and from all resources in a subnet by creating a network security group, and associating it to the subnet.
In the search box at the top of the portal page, search for Network security group. Select Network security groups in the search results.
In Network security groups, select + Create.
In the Basics tab of Create network security group, enter, or select the following information:
Setting
Value
Project details
Subscription
Select your subscription.
Resource group
Select test-rg.
Instance details
Name
Enter nsg-storage.
Region
Select East US 2.
Select Review + create, then select Create.
Create a network security group with New-AzNetworkSecurityGroup. The following example creates a network security group named nsg-private.
In the search box at the top of the portal page, search for Network security group. Select Network security groups in the search results.
Select nsg-storage.
Select Outbound security rules in Settings.
Select + Add.
Create a rule that allows outbound communication to the Azure Storage service. Enter or select the following information in Add outbound security rule:
Setting
Value
Source
Select Service Tag.
Source service tag
Select VirtualNetwork.
Source port ranges
Leave the default of *.
Destination
Select Service Tag.
Destination service tag
Select Storage.
Service
Leave default of Custom.
Destination port ranges
Enter 445.
Protocol
Select Any.
Action
Select Allow.
Priority
Leave the default of 100.
Name
Enter allow-storage-all.
Select + Add.
Create another outbound security rule that denies communication to the internet. This rule overrides a default rule in all network security groups that allows outbound internet communication. Complete the previous steps with the following values in Add outbound security rule:
Setting
Value
Source
Select Service Tag.
Source service tag
Select VirtualNetwork.
Source port ranges
Leave the default of *.
Destination
Select Service Tag.
Destination service tag
Select Internet.
Service
Leave default of Custom.
Destination port ranges
Enter *.
Protocol
Select Any.
Action
Select Deny.
Priority
Leave the default 110.
Name
Enter deny-internet-all.
Select Add.
In the search box at the top of the portal page, search for Network security group. Select Network security groups in the search results.
Select nsg-storage.
Select Subnets in Settings.
Select + Associate.
In Associate subnet, select vnet-1 in Virtual network. Select subnet-private in Subnet.
Select OK.
Create network security group security rules with New-AzNetworkSecurityRuleConfig. The following rule allows outbound access to the public IP addresses assigned to the Azure Storage service:
The following rule denies 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.
# Retrieve the existing network security group
$nsgpriv = @{
ResourceGroupName = 'test-rg'
Name = 'nsg-private'
}
$nsg = Get-AzNetworkSecurityGroup @nsgpriv
# Add the new rules to the security group
$nsg.SecurityRules += $rule1
$nsg.SecurityRules += $rule2
# Update the network security group with the new rules
Set-AzNetworkSecurityGroup -NetworkSecurityGroup $nsg
Associate the network security group to the subnet-private subnet with Set-AzVirtualNetworkSubnetConfig and then write the subnet configuration to the virtual network. The following example associates the nsg-private network security group to the subnet-private subnet:
Create security rules with az network nsg rule create. The following rule allows outbound access to the public IP addresses assigned to the Azure Storage service:
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.
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.
Associate the network security group to the subnet-private subnet with az network vnet subnet update. The following example associates the nsg-private network security group to the subnet-private subnet:
The steps required to restrict network access to resources created through Azure services, which are enabled for service endpoints vary across services. See the documentation for individual services for specific steps for each service. The rest of this tutorial includes steps to restrict network access for an Azure Storage account, as an example.
Create a storage account
Create an Azure Storage account for the steps in this article. If you already have a storage account, you can use it instead.
In the search box at the top of the portal, enter Storage account. Select Storage accounts in the search results.
Select + Create.
On the Basics tab of Create a storage account, enter or select the following information:
Setting
Value
Project Details
Subscription
Select your Azure subscription.
Resource Group
Select test-rg.
Instance details
Storage account name
Enter storage1. If the name is unavailable, enter a unique name.
Location
Select (US) East US 2.
Performance
Leave the default Standard.
Redundancy
Select Locally-redundant storage (LRS).
Select Review.
Select Create.
Create an Azure storage account with New-AzStorageAccount. 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.
For the purposes of this tutorial, the connection string is used to connect to the storage account. Microsoft recommends that you use the most secure authentication flow available. The authentication flow described in this procedure requires a high degree of trust in the application, and carries risks that aren't present in other flows. You should only use this flow when other more secure flows, such as managed identities, aren't viable.
The key is used to create a file share in a later step. Enter $storageAcctKey and note the value. You manually enter it in a later step when you map the file share to a drive in a virtual machine.
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.
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.
For the purposes of this tutorial, the connection string is used to connect to the storage account. Microsoft recommends that you use the most secure authentication flow available. The authentication flow described in this procedure requires a high degree of trust in the application, and carries risks that aren't present in other flows. You should only use this flow when other more secure flows, such as managed identities, aren't viable.
By default, storage accounts accept network connections from clients in any network, including the internet. You can restrict network access from the internet, and all other subnets in all virtual networks (except the subnet-private subnet in the vnet-1 virtual network.)
To restrict network access to a subnet:
In the search box at the top of the portal, enter Storage account. Select Storage accounts in the search results.
Select your storage account.
In Security + networking, select Networking.
In the Firewalls and virtual networks tab, select Enabled from selected virtual networks and IP addresses in Public network access.
In Virtual networks, select + Add existing virtual network.
In Add networks, enter or select the following information:
Setting
Value
Subscription
Select your subscription.
Virtual networks
Select vnet-1.
Subnets
Select subnet-private.
Select Add.
Select Save to save the virtual network configurations.
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 Update-AzStorageAccountNetworkRuleSet. Once network access is denied, the storage account isn't accessible from any network.
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 isn't accessible from any network.
To test network access to a storage account, deploy a virtual machine to each subnet.
Create test virtual machine
The following procedure creates a test virtual machine (VM) named vm-1 in the virtual network.
In the portal, search for and select Virtual machines.
In Virtual machines, select + Create, then Azure virtual machine.
On the Basics tab of Create a virtual machine, enter or select the following information:
Setting
Value
Project details
Subscription
Select your subscription.
Resource group
Select test-rg.
Instance details
Virtual machine name
Enter vm-1.
Region
Select East US 2.
Availability options
Select No infrastructure redundancy required.
Security type
Leave the default of Standard.
Image
Select Windows Server 2022 Datacenter - x64 Gen2.
VM architecture
Leave the default of x64.
Size
Select a size.
Administrator account
Authentication type
Select Password.
Username
Enter azureuser.
Password
Enter a password.
Confirm password
Reenter the password.
Inbound port rules
Public inbound ports
Select None.
Select the Networking tab at the top of the page.
Enter or select the following information in the Networking tab:
Setting
Value
Network interface
Virtual network
Select vnet-1.
Subnet
Select subnet-1 (10.0.0.0/24).
Public IP
Select None.
NIC network security group
Select Advanced.
Configure network security group
Select Create new. Enter nsg-1 for the name. Leave the rest at the defaults and select OK.
Leave the rest of the settings at the defaults and select Review + create.
Review the settings and select Create.
Note
Virtual machines in a virtual network with a bastion host don't need public IP addresses. Bastion provides the public IP, and the VMs use private IPs to communicate within the network. You can remove the public IPs from any VMs in bastion hosted virtual networks. For more information, see Dissociate a public IP address from an Azure VM.
Note
Azure provides a default outbound access IP for VMs that either aren't assigned a public IP address or are in the backend pool of an internal basic Azure load balancer. The default outbound access IP mechanism provides an outbound IP address that isn't configurable.
The default outbound access IP is disabled when one of the following events happens:
A public IP address is assigned to the VM.
The VM is placed in the backend pool of a standard load balancer, with or without outbound rules.
Create a second virtual machine repeating the steps in the previous section. Replace the following values in Create a virtual machine:
Setting
Value
Virtual machine name
Enter vm-private.
Subnet
Select subnet-private.
Public IP
Select None.
NIC network security group
Select None.
Warning
Do not continue to the next step until the deployment is completed.
Create the first virtual machine
Create a virtual machine in the subnet-public subnet with New-AzVM. When running the command that follows, you're prompted for credentials. The values that you enter are configured as the user name and password for the VM.
It takes a few minutes for Azure to create the VM. Don't continue to the next step until Azure finishes creating the VM and returns output to PowerShell.
To test network access to a storage account, deploy a VM to each subnet.
Create the first virtual machine
Create a VM in the subnet-public subnet with az vm create. If SSH keys don't already exist in a default key location, the command creates them. To use a specific set of keys, use the --ssh-key-value option.
The virtual machine you created earlier that is assigned to the subnet-private subnet is used to confirm access to the storage account. The virtual machine you created in the previous section that is assigned to the subnet-1 subnet is used to confirm that access to the storage account is blocked.
Get storage account access key
In the search box at the top of the portal, enter Storage account. Select Storage accounts in the search results.
In Storage accounts, select your storage account.
In Security + networking, select Access keys.
Copy the value of key1. You might need to select the Show button to display the key.
In the search box at the top of the portal, enter Virtual machine. Select Virtual machines in the search results.
Select vm-private.
Select Bastion in Operations.
Enter the username and password you specified when creating the virtual machine. Select Connect.
Open Windows PowerShell. Use the following script to map the Azure file share to drive Z.
Replace <storage-account-key> with the key you copied in the previous step.
Replace <storage-account-name> with the name of your storage account. In this example, it's storage8675.
PowerShell returns output similar to the following example output:
Name Used (GB) Free (GB) Provider Root
---- --------- --------- -------- ----
Z FileSystem \\storage8675.file.core.windows.net\f...
The Azure file share successfully mapped to the Z drive.
Close the Bastion connection to vm-private.
The virtual machine you created earlier that is assigned to the subnet-private subnet is used to confirm access to the storage account. The virtual machine you created in the previous section that is assigned to the subnet-1 subnet is used to confirm that access to the storage account is blocked.
PowerShell returns output similar to the following example output:
Name Used (GB) Free (GB) Provider Root
---- --------- --------- -------- ----
Z FileSystem \\storage8675.file.core.windows.net\f...
The Azure file share successfully mapped to the Z drive.
Confirm that the VM has no outbound connectivity to any other public IP addresses:
ping bing.com
You receive no replies, because the network security group associated to the Private subnet doesn't allow outbound access to public IP addresses other than the addresses assigned to the Azure Storage service.
Close the Bastion connection to vm-private.
SSH into the vm-private VM.
Run the following command to store the IP address of the VM as an environment variable:
export IP_ADDRESS=$(az vm show --show-details --resource-group test-rg --name vm-private --query publicIps --output tsv)
ssh -o StrictHostKeyChecking=no azureuser@$IP_ADDRESS
Create a folder for a mount point:
sudo mkdir /mnt/file-share
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/file-share --options vers=3.0,username=<storage-account-name>,password=<storage-account-key>,dir_mode=0777,file_mode=0777,serverino
You receive the user@vm-private:~$ prompt. The Azure file share successfully mounted to /mnt/file-share.
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 subnet-private subnet doesn't allow outbound access to public IP addresses other than the addresses assigned to the Azure Storage service.
In the search box at the top of the portal, enter Virtual machine. Select Virtual machines in the search results.
Select vm-1.
Select Bastion in Operations.
Enter the username and password you specified when creating the virtual machine. Select Connect.
Repeat the previous command to attempt to map the drive to the file share in the storage account. You might need to copy the storage account access key again for this procedure:
In the search box at the top of the portal, enter Storage account. Select Storage accounts in the search results.
In Storage accounts, select your storage account.
In Data storage, select File shares.
Select file-share.
Select Browse in the left-hand menu.
You should receive the following error message:
Note
The access is denied because your computer isn't in the subnet-private subnet of the vnet-1 virtual network.
From vm-1
In the search box at the top of the portal, enter Virtual machine. Select Virtual machines in the search results.
Select vm-1.
Select Bastion in Operations.
Enter the username and password you specified when creating the virtual machine. Select Connect.
Repeat the previous command to attempt to map the drive to the file share in the storage account. You might need to copy the storage account access key again for this procedure:
Access is denied. You receive an output similar to the following example.
Get-AzStorageFile : The remote server returned an error: (403) Forbidden. HTTP Status Code: 403 - HTTP Error Message: This request isn't authorized to perform this operation
Your computer isn't in the subnet-private subnet of the vnet-1 virtual network.
SSH into the vm-public VM.
Run the following command to store the IP address of the VM as an environment variable:
export IP_ADDRESS=$(az vm show --show-details --resource-group test-rg --name vm-public --query publicIps --output tsv)
ssh -o StrictHostKeyChecking=no azureuser@$IP_ADDRESS
Create a directory for a mount point:
sudo mkdir /mnt/file-share
Attempt to mount the Azure file share to the directory you created. This article assumes you deployed the latest version of Ubuntu. If you're using earlier versions of Ubuntu, see Mount on Linux for more 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/file-share /mnt/file-share --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 vm-public VM is deployed within the subnet-public subnet. The subnet-public subnet doesn't have a service endpoint enabled for Azure Storage, and the storage account only allows network access from the subnet-private subnet, not the subnet-public subnet.
Exit the SSH session to the vm-public 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 isn't authorized to perform this operation error, because your computer isn't in the subnet-private subnet of the vnet-1 virtual network.
If you have multiple virtual networks in your account, you might want to establish connectivity between them so that resources can communicate with each other. To learn how to connect virtual networks, advance to the next tutorial.
Find out how Azure network security groups and service endpoints help you secure your virtual machines and Azure services from unauthorized network access.