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,282 questions
Azure Event Grid
Azure Event Grid
An Azure event routing service designed for high availability, consistent performance, and dynamic scale.
411 questions
Azure Container Apps
Azure Container Apps
An Azure service that provides a general-purpose, serverless container platform.
493 questions
{count} votes

1 answer

Sort by: Most helpful
  1. VenkateshDodda-MSFT 23,421 Reputation points Microsoft Employee
    2024-12-31T09:26:13.3033333+00:00

    @Adrian Ruchti Thanks for reaching out to Microsoft Q&A, apologize for any inconvenience caused on this.

    From the error message, it looks like your bicep template deployment failed to create event gird event subscription. In your bicep template you are trying to create the Webhook based endpoint type which required authentication to validate the event which is missing in your template.

    As you are creating the Event Grid trigger function you can create Azure Function Endpoint in which underlying infrastructure will take of the event validation and you can ignore the #handle event block in function code as well.

    Hope this helps, let me know if you have any further questions on this.


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.