Azure Function in Container App Webhook validation handshake fails
Hello
Trying to provision a eventgrid subscription I get the following error:
ERROR: {"status":"Failed","error":{"code":"DeploymentFailed","target":"/subscriptions/ad6ec112-366b-4221-9b70-cf3ccc089a41/resourceGroups/timescale-azfunctions-rg/providers/Microsoft.Resources/deployments/eventGridSubscriptionDeployment","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-deployment-operations for usage details.","details":[{"code":"ResourceDeploymentFailure","target":"/subscriptions/ad6ec112-366b-4221-9b70-cf3ccc089a41/resourceGroups/timescale-azfunctions-rg/providers/Microsoft.EventGrid/systemTopics/system-blob-trigger-topic/eventSubscriptions/blop-trigger-systemtopic-subscription","message":"The resource write operation failed to complete successfully, because it reached terminal provisioning state 'Failed'.","details":[{"code":"URL validation","message":"Webhook validation handshake failed for https://timescale-azfunctio-fa.salmonbeach-eec8bd4a.switzerlandnorth.azurecontainerapps.io/runtime/webhooks/eventgrid. Http POST request failed with response code Unknown. For troubleshooting, visit https://aka.ms/esvalidation. Activity id:d8da7ac7-3ec9-4724-915e-02c82ae85ca7, timestamp: 12/27/2024 11:19:36 AM (UTC)."}]}]}}
function_app.py
@app.function_name(name="myblobtrigger")
@app.event_grid_trigger(arg_name="event")
async def myblobtrigger(event: func.EventGridEvent):
try:
logging.info('Python EventGrid trigger processed an event: %s', event.get_json())
# Handle validation event
if event.event_type == 'Microsoft.EventGrid.SubscriptionValidationEvent':
validation_code = event.get_json()['data']['validationCode']
return func.HttpResponse(
body=json.dumps({'validationResponse': validation_code}),
status_code=200,
mimetype='application/json'
)
# Handle blob events
if event.event_type in ['Microsoft.Storage.BlobCreated', 'Microsoft.Storage.BlobDeleted']:
event_data = event.get_json()
logging.info(f'Processing blob event: {event_data}')
return func.HttpResponse(status_code=200)
return func.HttpResponse(status_code=400, body="Unsupported event type")
except Exception as e:
logging.error(f'Error processing event: {str(e)}')
return func.HttpResponse(
body=str(e),
status_code=500
)
functionsapp.bicep with functionEndpoint
param name string
param location string = resourceGroup().location
param tags object = {}
param identityName string
param containerAppsEnvironmentName string
param containerRegistryName string
param serviceName string = 'func'
param exists bool
param keyVaultName string
param authClientSecretName string
param authClientId string
param authAuthority string
param environmentVariables array = []
param serverAppId string
param serverAppSecretName string
param authTenantId string
@secure()
param secrets object
resource funcIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: identityName
location: location
}
module app '../container-app-upsert.bicep' = {
name: '${serviceName}-function-app-module'
params: {
name: name
location: location
tags: union(tags, { 'azd-service-name': serviceName })
identityName: funcIdentity.name
exists: exists
containerAppsEnvironmentName: containerAppsEnvironmentName
containerRegistryName: containerRegistryName
env: union(
environmentVariables,
[
{
name: 'RUNNING_IN_PRODUCTION'
value: 'true'
}
{
name: 'AZURE_CLIENT_ID'
value: funcIdentity.properties.clientId
}
{
name: 'AZURE_AUTH_CLIENT_SECRET_NAME'
value: authClientSecretName
}
{
name: 'AZURE_AUTH_CLIENT_ID'
value: authClientId
}
{
name: 'AZURE_AUTH_AUTHORITY'
value: authAuthority
}
{
name: 'AZURE_KEY_VAULT_NAME'
value: keyVaultName
}
{
name: 'AZURE_SERVER_APP_ID'
value: serverAppId
}
{
name: 'AZURE_SERVER_APP_SECRET_NAME'
value: serverAppSecretName
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
{
name: 'AZURE_AUTH_TENANT_ID'
value: authTenantId
}
]
)
secrets: secrets
targetPort: 80
}
}
output SERVICE_FUNC_IDENTITY_PRINCIPAL_ID string = funcIdentity.properties.principalId
output SERVICE_FUNC_IDENTITY_NAME string = funcIdentity.name
output SERVICE_FUNC_NAME string = app.outputs.name
output SERVICE_FUNC_URI string = app.outputs.uri
output SERVICE_FUNC_IMAGE_NAME string = app.outputs.imageName
output uri string = app.outputs.uri
output name string = app.outputs.name
output imageName string = app.outputs.imageName
output AZURE_CLIENT_ID string = funcIdentity.properties.clientId
output functionEndpoint string = '${app.outputs.uri}/runtime/webhooks/eventgrid?functionName=myblobtrigger'
output functionAppId string = app.outputs.containerAppId
eventgrid.bicep
param name string
param functionEndpoint string
param systemTopicName string
resource systemTopic 'Microsoft.EventGrid/systemTopics@2024-12-15-preview' existing = {
name: systemTopicName
}
resource EventSubscription 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2024-12-15-preview' = {
parent: systemTopic
name: '${name}-systemtopic-subscription'
properties: {
destination: {
endpointType: 'WebHook'
properties: {
endpointUrl: functionEndpoint
maxEventsPerBatch: 1
preferredBatchSizeInKilobytes: 64
}
}
eventDeliverySchema: 'EventGridSchema'
filter: {
includedEventTypes: [
'Microsoft.Storage.BlobCreated'
'Microsoft.Storage.BlobDeleted'
]
isSubjectCaseSensitive: false
}
retryPolicy: {
eventTimeToLiveInMinutes: 1440
maxDeliveryAttempts: 30
}
}
}
output AZURE_EVENTGRID_SUBSCRIPTION_NAME string = EventSubscription.name
is the approach to provision the function app in a containerapp then using a webhook for the function endpoint correct? what is the correct endpoint url (for validation and handshake)?
PLEASE NOTE THAT THE FUNCTION APP IS RUNNING IN A CONTAINER APP CONTAINER.
Thank you for your help.
Kind regards
Adrian