Azure Function in Container App Webhook validation handshake fails

Adrian Ruchti 20 Reputation points
2024-12-27T11:23:02.6333333+00:00

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

Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,257 questions
Azure Event Grid
Azure Event Grid
An Azure event routing service designed for high availability, consistent performance, and dynamic scale.
408 questions
Azure Container Apps
Azure Container Apps
An Azure service that provides a general-purpose, serverless container platform.
487 questions
0 comments No comments
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.