0

I am trying to create a private link to a Microsoft partner service in Azure using Powershell.

When I configure the endpoint through the Azure console, the segment of the template for the endpoint looks as follows:

{
    "type": "Microsoft.Network/privateEndpoints",
    "apiVersion": "2020-11-01",
    "name": "[parameters('privateEndpoints_foo_pl_silverfish_name')]",
    "location": "eastus2",
    "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworks_foo_pl_vnet_name'), 'foo_pl_subnet')]"
    ],
    "tags": {
        "owner": "foo"
    },
    "properties": {
        "privateLinkServiceConnections": [],
        "manualPrivateLinkServiceConnections": [
            {
                "name": "[parameters('privateEndpoints_foo_pl_silverfish_name')]",
                "properties": {
                    "privateLinkServiceId": "xyz-prod.67395a8a-a9d4-4c85-bd01-109a99e7eca2.eastus2.azure.privatelinkservice",
                    "groupIds": [],
                    "privateLinkServiceConnectionState": {
                        "status": "Approved"
                    }
                }
            }
        ],
        "subnet": {
            "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworks_foo_pl_vnet_name'), 'foo_pl_subnet')]"
        },
        "customDnsConfigs": []
    }
}

You can see the privateLinkServiceConnections array is empty. I've got content under the manualPrivateLinkServiceConnections array, which doesn't conform to the options in the New-AzPrivateLinkServiceConnection commandlet.

This is with me creating the private endpoint through the console:

Create Private Endpoint in Console

Can I create a manual private link service connection with the New-AzPrivateLinkServiceConnection and New-AzPrivateEndpoint commandlets?

## Create the private link service configuration
$plink = New-AzPrivateLinkServiceConnection `
-Name 'foo_pl_silverfish_config' `
-PrivateLinkServiceId 'xyz-prod.67395a8a-a9d4-4c85-bd01-109a99e7eca2.eastus2.azure.privatelinkservice'

$privateEndpoint = New-AzPrivateEndpoint `
-ResourceGroupName 'foo_private_link' `
-Name 'foo_pl_db' `
-Location 'eastus2' `
-Subnet $sub `
-PrivateLinkServiceConnection $plink

I get the following error when running the above:

New-AzPrivateEndpoint: Operation returned an invalid status code 'BadRequest'
StatusCode: 400
ReasonPhrase: Bad Request
ErrorCode: LinkedInvalidPropertyId
ErrorMessage: Property id 'xyz-prod.67395a8a-a9d4-4c85-bd01-109a99e7eca2.eastus2.azure.privatelinkservice' at path 'properties.privateLinkServiceConnections[0].properties.privateLinkServiceId' is invalid. Expect fully qualified resource Id that start with '/subscriptions/{subscriptionId}' or '/providers/{resourceProviderNamespace}/'.
Adam
  • 3,891
  • 3
  • 19
  • 42

2 Answers2

0

ErrorMessage: Property id 'xyz-prod.67395a8a-a9d4-4c85-bd01-109a99e7eca2.eastus2.azure.privatelinkservice' at path 'properties.privateLinkServiceConnections[0].properties.privateLinkServiceId' is invalid. Expect fully qualified resource Id that start with '/subscriptions/{subscriptionId}' or '/providers/{resourceProviderNamespace}/'.

You need to pass the resourceId for the property PrivateLinkServiceId to the New-AzPrivateLinkServiceConnection cmdlet.

Can I create a manual private link service connection with the New-AzPrivateLinkServiceConnection and New-AzPrivateEndpoint commandlets?

Yes, you can create manualPrivateLinkServiceConnections using -ByManualRequest in the New-AzPrivateLinkServiceConnection powershell cmdlet.

Here is the reference documentation for more information about the supported properties for the above cmdlet.

I have tested this in my local environment by creating a private endpoint to storage table in one of the storage accounts in my subscription.

Here is the cmdlet that I have used:

$virtualNetwork = Get-AzVirtualNetwork -ResourceName '<VirtualNetworkName>' -ResourceGroupName '<resourceGroupName>'
$subnet = $virtualNetwork | Select-Object -ExpandProperty subnets | Where-Object Name -eq '<SubnetName>'
$plsConnection= New-AzPrivateLinkServiceConnection -Name '<PLSConnectionsName>' -PrivateLinkServiceId '<ResourceID to which resource you want to create endpoint connection>' -RequestMessage 'Please Approve my request' -GroupId 'queue'
New-AzPrivateEndpoint -Name '<PrivateEndpointName>' -ResourceGroupName '<ResourceGroupName>' -Location 'eastus' -PrivateLinkServiceConnection $plsConnection -Subnet $subnet -ByManualRequest

Here is the sample output for your reference:

enter image description here


Updated Answer: If you want to create the private endpoint using the private link service alias(foo..<azure_region>.azure.privatelinkservice) you can use the below powershell script.

$virtualNetwork = Get-AzVirtualNetwork -ResourceName '<VirtualNetworkName>' -ResourceGroupName '<resourceGroupName>'
$subnet = $virtualNetwork | Select-Object -ExpandProperty subnets | Where-Object Name -eq '<SubnetName>'
$privatelinkAlias= Get-AzPrivateLinkService -Name '<privateServiceLinkName>' -ResourceGroupName '<resourceGroupName>'
$plsConnection= New-AzPrivateLinkServiceConnection -Name '<PLSConnectionsName>' -PrivateLinkServiceId $privatelinkAlias.Alias -RequestMessage 'Please Approve my request'
New-AzPrivateEndpoint -Name '<PrivateEndpointName>' -ResourceGroupName '<ResourceGroupName>' -Location 'eastus' -PrivateLinkServiceConnection $plsConnection -Subnet $subnet -ByManualRequest

Here is the sample output screenshot:

enter image description here

VenkateshDodda
  • 4,723
  • 1
  • 3
  • 12
  • I think there is a typo in your post. You explain, "Yes, you can create manualPrivateLinkServiceConnections using -ByManualRequest in the New-AzPrivateLinkServiceConnection powershell cmdlet." But, your example code includes the `ByManualRequest` switch in the call to `New-AzPrivateEndpoint`, which looks like the right command let for that param. – Adam Aug 23 '22 at 04:33
  • I really really appreciate your help @VenkateshDodda-MSFT! The suggestion didn't quite work for me. I updated my code to include the `-ByManualRequest` in the `New-AzurePrivateEndpoint` call. I still get a 400 error, but the error message reads, "ErrorMessage: Cannot parse the request.". In my attempt, the `-PrivateLinkServiceId` parameter looks different. I have something that fits the format `foo...azure.privatelinkservice` not the backslash delimited string like in your example. I think I have a resource id not a service id - see Azure console screen shot in my post. – Adam Aug 23 '22 at 04:40
  • 1
    @Adam please take a look into the above update answer and respective powershell sript as well – VenkateshDodda Aug 23 '22 at 12:19
  • I don't have the resource group for the service's load balancer. All I have is an alias/resource-id for the service load balancer. – Adam Aug 30 '22 at 00:24
0

The only way I was able to build the private endpoint was using an ARM template that I deployed via Powershell script.

The ARM template looks like the following:

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]",
      "metadata": {
        "description": "The location for resources created as part of the deployment."
      }
    },
    "virtualNetworkName": {
      "type": "string",
      "metadata": {
        "description": "The name of the virtual network hosting the private endpoint for the Astra private link."
      }
    },
    "privatelinkEndpointSubnetName": {
      "type": "string",
      "metadata": {
        "description": "The name of the subnet hosting the private endpoint."
      }
    },
    "privatelinkEndpointSubnetCIDR": {
      "type": "string",
      "metadata": {
        "description": "The CIDR range associated with the subnet hosting the private endpoint."
      }
    },
    "privateEndpointName": {
      "type": "string",
      "metadata": {
        "description": "The name of the private endpoint associated with the database's private link."
      }
    },
    "AstraDBServiceName": {
      "type": "string",
      "metadata": {
        "description": "The 'Service Name' for the subject Astra DB database."
      }
    }
  },
  "variables": {
    "PrivatelinkEndpointSubnetNameRef": "[concat(parameters('virtualNetworkName'), '/', parameters('privatelinkEndpointSubnetName'))]",
    "PrivatelinkEndpointSubnetIdRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('privatelinkEndpointSubnetName'))]"
  },
  "resources": [{
      "apiVersion": "2018-04-01",
      "type": "Microsoft.Network/virtualNetworks/subnets",
      "name": "[variables('PrivatelinkEndpointSubnetNameRef')]",
      "location": "[parameters('location')]",
      "properties": {
        "addressPrefix": "[concat(parameters('privatelinkEndpointSubnetCIDR'))]",
        "privateLinkServiceNetworkPolicies": "Disabled",
        "privateEndpointNetworkPolicies": "Disabled"
      }
    },
    {
      "dependsOn": [
        "[variables('PrivatelinkEndpointSubnetIdRef')]"
      ],
      "apiVersion": "2019-02-01",
      "type": "Microsoft.Network/privateEndpoints",
      "name": "[parameters('privateEndpointName')]",
      "location": "[parameters('location')]",
      "properties": {
        "manualPrivateLinkServiceConnections": [{
          "name": "{plsConnection}",
          "properties": {
            "privateLinkServiceId": "[parameters('AstraDBServiceName')]",
            "requestMessage": "Please approve my connection, thanks."
          }
        }],
        "subnet": {
          "id": "[variables('PrivatelinkEndpointSubnetIdRef')]"
        }
      }
    }
  ]
}

Here is the code I would use to build the template...

$arm_template_parameters = @{
    'AstraDBServiceName'  = 'abc.593caf89-071c-4be7-f5ce9d3f9bc6
.eastus.azure.privatelinkservice'
    'privateEndpointName' = 'myEndpoint'
    'virtualNetworkName'  = 'myVNET'
    'privatelinkEndpointSubnetName' = 'mySubnet'
    'privatelinkEndpointSubnetCIDR'  = '10.0.2.0/24'
}

New-AzResourceGroupDeployment `
    -Name 'myDeployment' `
    -ResourceGroupName 'myResourceGroup' `
    -TemplateFile '.\template.json' `
    -TemplateParameterObject $arm_template_parameters `
    -verbose

The problem for me is I ONLY have a resource id or alias. I can build from an ARM template or the Azure console. All the examples building the PE using Powershell require me to know the details about the service's load balancer, which I don't.

Adam
  • 3,891
  • 3
  • 19
  • 42