Reputation: 4289
I am deploying a langauge service using a bicep file. I set public network access to disabled. In the next step, I setup a private endpoint.
I am getting this error:
Failed to disable Public Access for Azure Search. Additional steps are required to setup a private link to your Azure Cognitive Search service.
I tried deploying without setting public network access to disabled, and it worked. This creates the language service and the private endpoint. Then I changed public network access to disabled and depolyed again, which worked.
I think it will not allow me to disable public network access without the private endpoint in place.
How do I get around this? I don't see a way to disable public network access without copying all the resource settings in another block. That is messy.
Is there a way to update a single setting? I don't want to change anything else by omitting it.
Update: I don't get this problem with I remove the association with the service service. If I remove this section, it works
apiProperties: {
qnaAzureSearchEndpointId: search.id
qnaAzureSearchEndpointKey: search.listAdminKeys().primaryKey
}
Here is the bicep:
param languageServiceName string = 'lg-usas-loginbot-dev-cus-001'
param searchServiceName string = 'ais-usas-loginbot-dev-cus-001'
param location string = 'CentralUS'
param privateEndpointVnetResourceGroup string = 'rg-hrs-usas-np-inf-01'
param privateEndpointVnet string = 'vnet-hrs-usas-dev-cus'
param privateEndpointSubnet string = 'sn-chatbot-dev-002'
resource search 'Microsoft.Search/searchServices@2024-06-01-preview' existing = {
name: searchServiceName
}
resource language 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' = {
name: languageServiceName
location: location
sku: {
name: 'S'
}
kind: 'TextAnalytics'
properties: {
apiProperties: {
qnaAzureSearchEndpointId: search.id
qnaAzureSearchEndpointKey: search.listAdminKeys().primaryKey
}
customSubDomainName: languageServiceName
networkAcls: {
defaultAction: 'Allow'
virtualNetworkRules: []
ipRules: []
}
publicNetworkAccess: 'Disabled'
}
identity: {
type: 'SystemAssigned'
}
}
resource existingPESubnet 'Microsoft.Network/virtualNetworks/subnets@2022-05-01' existing = {
name: '${privateEndpointVnet}/${privateEndpointSubnet}'
scope: resourceGroup(privateEndpointVnetResourceGroup)
}
resource languagePE 'Microsoft.Network/privateEndpoints@2022-05-01' = {
name: 'pe-${languageServiceName}-as'
location: location
properties: {
privateLinkServiceConnections: [
{
name: 'pe-${languageServiceName}-as'
properties: {
privateLinkServiceId: language.id
groupIds: [
'account'
]
privateLinkServiceConnectionState: {
status: 'Approved'
description: 'Auto-approved'
actionsRequired: 'None'
}
}
}
]
manualPrivateLinkServiceConnections: []
customNetworkInterfaceName: 'nic-${languageServiceName}-as'
subnet: {
id: existingPESubnet.id
}
ipConfigurations: []
}
}
Upvotes: 1
Views: 397
Reputation: 29726
Posting a new answer as I think the other one can still be relevant for others but not answering your main issue.
So I found this piece of documentation: Network isolation and private endpoints:
I feel that would be best addressed with az cli because of the multiple steps required but managed to get an almost bicep version. Before the deployment starts, I'm checking if the resources exist to make it idempotent => this also could be done using deploymentScripts but I feel it is over complicated to the purpose of your question.
To piece everything together, I had to create few modules.
search-ai.bicep
param location string = resourceGroup().location
param searchServiceName string
param publicNetworkAccess string
output name string = searchService.name
// Create search service
resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' = {
name: searchServiceName
sku: {
name: 'standard'
}
location: location
properties: {
replicaCount: 1
partitionCount: 1
hostingMode: 'default'
disableLocalAuth: false
semanticSearch: 'disabled'
publicNetworkAccess: publicNetworkAccess
}
}
search-ai-role-assignment.bicep
param searchSearviceName string
param roleId string
param principalId string
param principalType string = 'ServicePrincipal'
resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' existing = {
name: searchSearviceName
}
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(searchService.id, roleId, principalId)
scope: searchService
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleId)
principalId: principalId
principalType: principalType
}
}
language-ai.bicep
param location string = resourceGroup().location
param languageServiceName string
param searchServiceName string
param publicNetworkAccess string = 'Enabled'
output name string = languageService.name
output principalId string = languageService.identity.principalId
// Get a reference to the search service
resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' existing = {
name: searchServiceName
}
// Create language service
resource languageService 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' = {
name: languageServiceName
location: location
kind: 'TextAnalytics'
identity: {
type: 'SystemAssigned'
}
sku: {
name: 'S'
}
properties: {
publicNetworkAccess: publicNetworkAccess
customSubDomainName: toLower(languageServiceName)
apiProperties: {
qnaAzureSearchEndpointId: searchService.id
qnaAzureSearchEndpointKey: searchService.listAdminKeys().primaryKey
}
}
}
main.bicep
param searchServiceName string
param languageServiceName string
param searchServiceExists bool
param languageServiceExists bool
// Create search service with public netwok enable if the service and language service do not exist
var searchService1PublicNetworkAccess = searchServiceExists && languageServiceExists ? 'disabled' : 'enabled'
module searchService1 'search-ai.bicep' = {
name: '${searchServiceName}-1'
params: {
searchServiceName: searchServiceName
publicNetworkAccess: searchService1PublicNetworkAccess
}
}
// Create language service with public netwok enable if the service do not exist
var languageService1PublicNetworkAccess = languageServiceExists ? 'Disabled' : 'Enabled'
module languageService1 'language-ai.bicep' = {
name: '${languageServiceName}-1'
params: {
languageServiceName: languageServiceName
searchServiceName: searchService1.outputs.name
publicNetworkAccess: languageService1PublicNetworkAccess
}
}
// Grant contributor role to the language service identity ove the search service
// => This is required for the creation of the internal private endpoint between the two components
module roleAssignment 'search-ai-role-assignment.bicep' = {
name: 'search-language-rbac-contributor'
params: {
principalId: languageService1.outputs.principalId
roleId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' // Contributor
searchSearviceName: searchService1.outputs.name
}
}
// Disable public network on the search service if we just created the service
module searchService2 'search-ai.bicep' = if (!searchServiceExists) {
name: '${searchServiceName}-2'
params: {
searchServiceName: searchServiceName
publicNetworkAccess: 'disabled'
}
// Explicit dependency
dependsOn: [languageService1]
}
// Disable public network on the language service if we just created the service
module languageService2 'language-ai.bicep' = if (!languageServiceExists) {
name: '${languageServiceName}-2'
params: {
languageServiceName: languageServiceName
searchServiceName: searchService2.outputs.name
publicNetworkAccess: 'Disabled'
}
// Explicit dependency
dependsOn: [roleAssignment, searchService2]
}
Then I can invoke the bicep script like that (using powershell):
$resourceGroupName = "<resource group name>"
$searchServiceName = "<search service name>"
$languageServiceName = "<language service name>"
$searchServiceExists = (az resource list --name "$searchServiceName" --query '[].[id]' | ConvertFrom-Json).Length -gt 0
$languageServiceExists = (az resource list --name "$languageServiceName" --query '[].[id]' | ConvertFrom-Json).Length -gt 0
az deployment group create `
--resource-group "$resourceGroupName" `
--template-file "./main.bicep" `
--parameters `
searchServiceName="$searchServiceName" `
languageServiceName="$languageServiceName" `
searchServiceExists=$searchServiceExists `
languageServiceExists=$languageServiceExists
Upvotes: 0
Reputation: 29726
Just tried with a language service and no error:
param location string = resourceGroup().location
param vnetName string = 'vnet-thomas-test-001'
param languageServiceName string = 'language-ai-thomas-test-001'
var subnetName = 'private-endpoints'
// Create basic vnet
resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [
'10.0.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '10.0.0.0/24'
}
}
{
name: subnetName
properties: {
addressPrefix: '10.0.1.0/24'
}
}
]
}
}
// Create a private dns zone
resource privateDnsZone 'Microsoft.Network/privateDnsZones@2024-06-01' = {
name: 'privatelink.cognitiveservices.azure.com'
location: 'global'
}
// Create a link to the vnet
resource privateDnsZoneVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = {
name: vnet.name
parent: privateDnsZone
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork:{
id: vnet.id
}
}
}
// Create basic language service
resource languageService 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' = {
name: languageServiceName
location: location
kind: 'TextAnalytics'
identity: {
type: 'SystemAssigned'
}
sku: {
name: 'S'
}
properties: {
customSubDomainName: toLower(languageServiceName)
publicNetworkAccess: 'disabled'
}
}
// Create the private endpoint
resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = {
name: 'pe-${languageService.name}'
location: location
properties: {
subnet: {
id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnet.name, subnetName)
}
customNetworkInterfaceName: 'pe-${languageService.name}-nic'
privateLinkServiceConnections: [
{
name: 'plsc-${languageService.name}'
properties: {
privateLinkServiceId: languageService.id
groupIds: [ 'account' ]
}
}
]
}
}
// Associate private link to private dns zone
resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-05-01' = {
name: 'default'
parent: privateEndpoint
properties: {
privateDnsZoneConfigs: [
{
name: replace(privateDnsZone.name, '.', '-')
properties: {
privateDnsZoneId: privateDnsZone.id
}
}
]
}
}
Upvotes: 0