# Private Link Service

Source: /azure/services/private-link-service/

## Introduction

Azure Private Link Service lets you expose your own service, running behind a Standard Load Balancer, to consumers in other virtual networks, subscriptions or tenants through private endpoints.
Consumers connect over a private IP address in their own virtual network, and you control who can see the service and which connection requests are approved. For more information, see [What is Azure Private Link service?](https://learn.microsoft.com/en-us/azure/private-link/private-link-service-overview).

LocalStack for Azure provides a local environment for building and testing applications that make use of Private Link Service.
The supported APIs are available on our [API Coverage section](#api-coverage), which provides information on the extent of Private Link Service's integration with LocalStack.

## Getting started

This guide is designed for users new to Private Link Service and assumes basic knowledge of the Azure CLI and our `lstk az` proxy.

Launch LocalStack using your preferred method. For more information, see [Introduction to LocalStack for Azure](/azure/getting-started/). Once the container is running, enable Azure CLI interception by running:

```bash
lstk az start-interception
```

This command points the `az` CLI away from the public Azure management REST API and toward the LocalStack for Azure emulator API.
To revert this configuration, run:

```bash
lstk az stop-interception
```

This reconfigures the `az` CLI to send commands to the official Azure management REST API.

### Create a resource group and a network

Create a resource group and a virtual network with a subnet for the service. Private link service network policies must be disabled on that subnet:

```bash
az group create \
  --name rg-pls-demo \
  --location westeurope

az network vnet create \
  --name vnet-pls-demo \
  --resource-group rg-pls-demo \
  --location westeurope \
  --address-prefixes 10.2.0.0/16 \
  --subnet-name subnet-service \
  --subnet-prefixes 10.2.1.0/24

az network vnet subnet update \
  --vnet-name vnet-pls-demo \
  --resource-group rg-pls-demo \
  --name subnet-service \
  --private-link-service-network-policies Disabled
```

### Create an internal load balancer

A private link service is attached to the frontend IP configuration of a Standard load balancer. Create an internal load balancer in the service subnet:

```bash
az network lb create \
  --name lb-pls-demo \
  --resource-group rg-pls-demo \
  --location westeurope \
  --sku Standard \
  --vnet-name vnet-pls-demo \
  --subnet subnet-service \
  --frontend-ip-name fe-internal \
  --backend-pool-name be-pool
```

### Create a private link service

Create the private link service, referencing the load balancer's frontend IP configuration by its resource ID:

```bash
az network private-link-service create \
  --name pls-demo \
  --resource-group rg-pls-demo \
  --location westeurope \
  --vnet-name vnet-pls-demo \
  --subnet subnet-service \
  --lb-frontend-ip-configs /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-pls-demo/providers/Microsoft.Network/loadBalancers/lb-pls-demo/frontendIPConfigurations/fe-internal
```

```bash title="Output"
{
  "alias": "pls-demo.e5d53fcc-9fa7-4560-a997-460a6af19667.westeurope.azure.privatelinkservice",
  "enableProxyProtocol": false,
  "fqdns": [],
  "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-pls-demo/providers/Microsoft.Network/privateLinkServices/pls-demo",
  "ipConfigurations": [
    {
      "name": "pls-demo_ipconfig_0",
      "primary": false,
      "privateIPAddressVersion": "IPv4",
      "privateIPAllocationMethod": "Dynamic",
      "provisioningState": "Succeeded",
      "subnet": {
        "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-pls-demo/providers/Microsoft.Network/virtualNetworks/vnet-pls-demo/subnets/subnet-service",
        "resourceGroup": "rg-pls-demo"
      },
      "type": "Microsoft.Network/privateLinkServices/ipConfigurations"
    }
  ],
  "loadBalancerFrontendIpConfigurations": [
    {
      "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-pls-demo/providers/Microsoft.Network/loadBalancers/lb-pls-demo/frontendIPConfigurations/fe-internal",
      "resourceGroup": "rg-pls-demo"
    }
  ],
  "location": "westeurope",
  "name": "pls-demo",
  "networkInterfaces": [],
  "privateEndpointConnections": [],
  "provisioningState": "Succeeded",
  "resourceGroup": "rg-pls-demo",
  "type": "Microsoft.Network/privateLinkServices"
  ...
}
```

The `alias` is the globally unique name that consumers use to request a connection to the service.

:::note
Pass the frontend IP configuration by its full resource ID, as shown above: LocalStack does not resolve a frontend given by name with `--lb-name`.
The load balancer must exist in the same region as the service. Otherwise the request fails with `InvalidResourceReference`.
:::

### List private link services

```bash
az network private-link-service list \
  --resource-group rg-pls-demo \
  --output table
```

```bash title="Output"
Alias                                                                              EnableProxyProtocol    Location    Name      ProvisioningState    ResourceGroup
---------------------------------------------------------------------------------  ---------------------  ----------  --------  -------------------  ---------------
pls-demo.e5d53fcc-9fa7-4560-a997-460a6af19667.westeurope.azure.privatelinkservice  False                  westeurope  pls-demo  Succeeded            rg-pls-demo
```

### Control visibility and auto-approval

Restrict which subscriptions can see the service, and which ones have their connection requests approved automatically:

```bash
az network private-link-service update \
  --name pls-demo \
  --resource-group rg-pls-demo \
  --visibility 00000000-0000-0000-0000-000000000000 \
  --auto-approval 00000000-0000-0000-0000-000000000000
```

```bash title="Output"
{
  "alias": "pls-demo.e5d53fcc-9fa7-4560-a997-460a6af19667.westeurope.azure.privatelinkservice",
  "autoApproval": {
    "subscriptions": [
      "00000000-0000-0000-0000-000000000000"
    ]
  },
  "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-pls-demo/providers/Microsoft.Network/privateLinkServices/pls-demo",
  "name": "pls-demo",
  "provisioningState": "Succeeded",
  "type": "Microsoft.Network/privateLinkServices",
  "visibility": {
    "subscriptions": [
      "00000000-0000-0000-0000-000000000000"
    ]
  }
  ...
}
```

### Delete the private link service

Delete the service, then list the services in the resource group to confirm it was removed:

```bash
az network private-link-service delete \
  --name pls-demo \
  --resource-group rg-pls-demo

az network private-link-service list \
  --resource-group rg-pls-demo
```

```bash title="Output"
[]
```

## Features

The Private Link Service emulator supports the following features:

- **Create and manage private link services**: Full lifecycle management including create, get, update, list, and delete, with subscription-scoped listing.
- **Load balancer frontend validation**: Each referenced load balancer frontend IP configuration must exist, in a load balancer in the same region.
- **Aliases**: Each service gets an alias in the Azure format, `<name>.<guid>.<location>.azure.privatelinkservice`.
- **Visibility and auto-approval**: The subscriptions in `visibility` and `autoApproval` are stored and returned.

## Limitations

- **No connectivity**: A private link service does not route any traffic to its load balancer.
- **Private endpoints do not create connections**: Creating a private endpoint that targets a private link service does not add a connection to the service. The service's `privateEndpointConnections` list stays empty, so there is no connection to approve, reject or delete.
- **Visibility and auto-approval are not enforced**: The subscription lists are stored, but nothing reads them.
- **Limited validation**: The load balancer SKU, the subnet's private link service network policies and the IP configuration's subnet are not checked, and no private IP address is allocated for the service.
- **Visibility checks are not implemented**: The `checkPrivateLinkServiceVisibility` and `autoApprovedPrivateLinkServices` operations are not available.

## API Coverage


### Private Link Service API coverage

Source service: `private-link-service`. 9 of 11 tracked operations are implemented.

Service documentation: /azure/services/private-link-service/

| Operation | Status |
| --- | --- |
| PrivateLinkServices.CreateOrUpdate | Implemented |
| PrivateLinkServices.Delete | Implemented |
| PrivateLinkServices.DeletePrivateEndpointConnection | Implemented |
| PrivateLinkServices.Get | Implemented |
| PrivateLinkServices.GetPrivateEndpointConnection | Implemented |
| PrivateLinkServices.List | Implemented |
| PrivateLinkServices.ListAutoApprovedPrivateLinkServices | Not implemented |
| PrivateLinkServices.ListAutoApprovedPrivateLinkServicesByResourceGroup | Not implemented |
| PrivateLinkServices.ListBySubscription | Implemented |
| PrivateLinkServices.ListPrivateEndpointConnections | Implemented |
| PrivateLinkServices.UpdatePrivateEndpointConnection | Implemented |
