azureazure-functionsazure-bicepazure-private-link

Unable to deploy private endpoint for flex consumption function app in azure using bicep


resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = {
  name: 'vvkstaccforflexfuncapp'  
  location: 'uksouth'
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  properties: {
    accessTier: 'Hot'
    minimumTlsVersion: 'TLS1_2'
  }
}


resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
  name: 'name'
  location: 'uksouth'
  kind: 'web'
  properties: {
    Application_Type: 'web'
  }
}

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = {
  name: 'vnetforflexfuncapp'
  location: 'uksouth'
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    subnets: [
      {
        name: 'InboundSubnet'
        properties: {
          addressPrefix: '10.0.0.0/24'
        }
      }
      {
        name: 'OutboundSubnet'
        properties: {
          addressPrefix: '10.0.1.0/24'
          delegations: [
            {
              name: 'delegation'
              properties: {
                serviceName: 'Microsoft.App/environments'
              }
            }
          ]
        }
      }
    ]
  }
}


resource flexappserviceplan 'Microsoft.Web/serverfarms@2024-04-01' = {
  name: 'vscodeflexappserviceplan'
  location:  'uksouth'
  sku: {
    name: 'FC1'
    tier: 'FlexConsumption'
    family: 'FC'
    capacity: 0
  }
  kind: 'functionapp'
  properties: {
    perSiteScaling: false
    elasticScaleEnabled: false
    maximumElasticWorkerCount: 1
    isSpot: false
    reserved: true
    isXenon: false
    hyperV: false
    targetWorkerCount: 1
    targetWorkerSizeId: 0
    zoneRedundant: false
  }
}

resource flexfunapp 'Microsoft.Web/sites@2024-04-01' = {
  name: 'vscodeflexfunapp02'
  location: 'uksouth'
  kind: 'functionapp,linux'
  properties: {
    serverFarmId: flexappserviceplan.id
    clientAffinityEnabled: false
    siteConfig: {
      appSettings: [
        {
          name: 'AzureWebJobsStorage'
          value: storageAccount.properties.primaryEndpoints.blob
        }
        {
          name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
          value: appInsights.properties.ConnectionString
        }
      ]
      numberOfWorkers: 1
    }
    functionAppConfig: {
      deployment: {
        storage: {
          type: 'blobcontainer'
          value: '${storageAccount.properties.primaryEndpoints.blob}container'
          authentication: {
            type: 'SystemAssignedIdentity'
          }
          
        }
      }
      scaleAndConcurrency: {
        maximumInstanceCount: 100
        instanceMemoryMB: 4096
      }
      runtime: {
        name: 'python'
        version: '3.11'
      }
    }
    httpsOnly: true
    publicNetworkAccess: 'Disabled'
    virtualNetworkSubnetId: virtualNetwork.properties.subnets[1].id
  }
}

resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2019-11-01' = {
  name: 'privateEndpointNSG'
  location: 'uksouth'
  properties: {
    securityRules: [
      {
        name: 'AllowAzurePrivateLink'
        properties: {
          priority: 100
          access: 'Allow'
          direction: 'Inbound'
          protocol: '*'
          sourceAddressPrefix: 'AzureActiveDirectory'  // Required for Azure Private Link
          sourcePortRange: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '*'
        }
      }
      {
        name: 'AllowAppTraffic'
        properties: {
          priority: 200
          access: 'Allow'
          direction: 'Inbound'
          protocol: 'Tcp'
          sourceAddressPrefix: '10.0.0.0/16'
          sourcePortRange: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '*'
        }
      }
    ]
  }
}


resource subnetAssociation 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' = {
  parent: virtualNetwork
  name: 'InboundSubnet'
  properties: {
    networkSecurityGroup: {
      id: networkSecurityGroup.id
    }
  }
}

resource dnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: 'privatelink.azurewebsites.net' // For Function Apps
  location: 'global'
}

resource dnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
  parent: dnsZone
  name: 'vnetlink'
  properties: {
    virtualNetwork: {
      id: virtualNetwork.id
    }
    registrationEnabled: true
  }
}

resource privateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = {
  name: '${flexfunapp.name}-private-endpoint'
  location: 'uksouth'
  properties: {
    subnet: {
      id: virtualNetwork.properties.subnets[0].id // Use the InboundSubnet for the private endpoint
    }
    privateLinkServiceConnections: [
      {
        name: 'functionAppPrivateLink'
        properties: {
          privateLinkServiceId: flexfunapp.id
          groupIds: [
             'web'
          ]
        }
      }
    ]
  }
}

PS C:\Windows\System32> New-AzResourceGroupDeployment -Name 'FlexFuncAppDeployment' -TemplateFile 'D:\LearnBicep\PracticeBicep\flexfunctionapp.bicep' -ResourceGroupName 'vivekchak-rg' -Mode Incremental

New-AzResourceGroupDeployment: 1:41:20 PM - The deployment 'FlexFuncAppDeployment' failed with error(s). Showing 3 out of 3 error(s). Status Message: Call to Microsoft.Web/sites failed. Error message: GroupId is invalid. (Code: BadRequest)

Status Message: Address prefix and ipam pool reference are both empty/null in request payload of resource /subscriptions//resourceGroups/vivekchak-rg/providers/Microsoft.Network/virtualNetworks/vnetforflexfuncapp/subnets/InboundSubnet. Please provide either address prefix or ipam pool reference. (Code: NoAddressPrefixOrPoolProvided)

Status Message: The location property is required for this definition. (Code:LocationRequired)

I'm able to deploy all the resources as given in the bicep except the private endpoint, getting error as shown above.

Unable to figure out the error. please help


Solution

  • Few things:

    resource dnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
      parent: dnsZone
      location: 'global'
      name: 'vnetlink'
      properties: {
        virtualNetwork: {
          id: virtualNetwork.id
        }
        registrationEnabled: true
      }
    }
    
    resource privateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = {
      name: '${flexfunapp.name}-private-endpoint'
      location: 'uksouth'
      properties: {
        subnet: {
          id: virtualNetwork.properties.subnets[0].id // Use the InboundSubnet for the private endpoint
        }
        privateLinkServiceConnections: [
          {
            name: 'functionAppPrivateLink'
            properties: {
              privateLinkServiceId: flexfunapp.id
              groupIds: [
                 'sites'
              ]
            }
          }
        ]
      }
    }
    
    resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = {
      name: 'thomastest-001-uks-vnet'
      location: 'uksouth'
      properties: {
        addressSpace: {
          addressPrefixes: [
            '10.0.0.0/16'
          ]
        }
        subnets: [
          {
            name: 'InboundSubnet'
            properties: {
              addressPrefix: '10.0.0.0/24'
              networkSecurityGroup: {
                id: networkSecurityGroup.id
              }
            }
          }
          ...
        ]
      }
    }
    

    enter image description here

    resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
      name: 'thomastest-001-uks-log'
      location: 'uksouth'
      properties: {
        sku: {
          name: 'PerGB2018'
        }
      }
    }
    
    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
      name: 'thomastest-001-uks-appi'
      location: 'uksouth'
      kind: 'web'
      properties: {
        Application_Type: 'web'
        WorkspaceResourceId: logAnalyticsWorkspace.id
      }
    }
    

    Full bicep sample:

    resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = {
      name: 'thomastest001uksst'  
      location: 'uksouth'
      kind: 'StorageV2'
      sku: {
        name: 'Standard_LRS'
      }
      properties: {
        accessTier: 'Hot'
        minimumTlsVersion: 'TLS1_2'
      }
    }
    
    resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
      name: 'thomastest-001-uks-log'
      location: 'uksouth'
      properties: {
        sku: {
          name: 'PerGB2018'
        }
      }
    }
    
    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
      name: 'thomastest-001-uks-appi'
      location: 'uksouth'
      kind: 'web'
      properties: {
        Application_Type: 'web'
        WorkspaceResourceId: logAnalyticsWorkspace.id
      }
    }
    
    resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2019-11-01' = {
      name: 'thomastest-001-uks-nsg'
      location: 'uksouth'
      properties: {
        securityRules: [
          {
            name: 'AllowAzurePrivateLink'
            properties: {
              priority: 100
              access: 'Allow'
              direction: 'Inbound'
              protocol: '*'
              sourceAddressPrefix: 'AzureActiveDirectory'  // Required for Azure Private Link
              sourcePortRange: '*'
              destinationAddressPrefix: '*'
              destinationPortRange: '*'
            }
          }
          {
            name: 'AllowAppTraffic'
            properties: {
              priority: 200
              access: 'Allow'
              direction: 'Inbound'
              protocol: 'Tcp'
              sourceAddressPrefix: '10.0.0.0/16'
              sourcePortRange: '*'
              destinationAddressPrefix: '*'
              destinationPortRange: '*'
            }
          }
        ]
      }
    }
    
    resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = {
      name: 'thomastest-001-uks-vnet'
      location: 'uksouth'
      properties: {
        addressSpace: {
          addressPrefixes: [
            '10.0.0.0/16'
          ]
        }
        subnets: [
          {
            name: 'InboundSubnet'
            properties: {
              addressPrefix: '10.0.0.0/24'
              networkSecurityGroup: {
                id: networkSecurityGroup.id
              }
            }
          }
          {
            name: 'OutboundSubnet'
            properties: {
              addressPrefix: '10.0.1.0/24'
              delegations: [
                {
                  name: 'delegation'
                  properties: {
                    serviceName: 'Microsoft.App/environments'
                  }
                }
              ]
            }
          }
        ]
      }
    }
    
    
    resource flexappserviceplan 'Microsoft.Web/serverfarms@2024-04-01' = {
      name: 'thomastest-001-uks-asp'
      location:  'uksouth'
      sku: {
        name: 'FC1'
        tier: 'FlexConsumption'
        family: 'FC'
        capacity: 0
      }
      kind: 'functionapp'
      properties: {
        reserved: true // linux
      }
    }
    
    resource flexfunapp 'Microsoft.Web/sites@2024-04-01' = {
      name: 'thomastest-001-uks-flex'
      location: 'uksouth'
      kind: 'functionapp,linux'
      properties: {
        serverFarmId: flexappserviceplan.id
        clientAffinityEnabled: false
        siteConfig: {
          appSettings: [
            {
              name: 'AzureWebJobsStorage'
              value: storageAccount.properties.primaryEndpoints.blob
            }
            {
              name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
              value: appInsights.properties.ConnectionString
            }
          ]
          numberOfWorkers: 1
        }
        functionAppConfig: {
          deployment: {
            storage: {
              type: 'blobcontainer'
              value: '${storageAccount.properties.primaryEndpoints.blob}container'
              authentication: {
                type: 'SystemAssignedIdentity'
              }
              
            }
          }
          scaleAndConcurrency: {
            maximumInstanceCount: 100
            instanceMemoryMB: 4096
          }
          runtime: {
            name: 'python'
            version: '3.11'
          }
        }
        httpsOnly: true
        publicNetworkAccess: 'Disabled'
        virtualNetworkSubnetId: virtualNetwork.properties.subnets[1].id
      }
    }
    
    resource dnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
      name: 'privatelink.azurewebsites.net' // For Function Apps
      location: 'global'
    }
    
    resource dnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
      parent: dnsZone
      location: 'global'
      name: 'vnetlink'
      properties: {
        virtualNetwork: {
          id: virtualNetwork.id
        }
        registrationEnabled: true
      }
    }
    
    resource privateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = {
      name: '${flexfunapp.name}-private-endpoint'
      location: 'uksouth'
      properties: {
        subnet: {
          id: virtualNetwork.properties.subnets[0].id // Use the InboundSubnet for the private endpoint
        }
        privateLinkServiceConnections: [
          {
            name: 'functionAppPrivateLink'
            properties: {
              privateLinkServiceId: flexfunapp.id
              groupIds: [
                 'sites'
              ]
            }
          }
        ]
      }
    }