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

2 answers

Sort by: Most helpful
  1. VenkateshDodda-MSFT 25,186 Reputation points Microsoft Employee Moderator
    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.


  2. Adrian Ruchti 20 Reputation points
    2025-08-06T09:15:38.5833333+00:00

    @VenkateshDodda-MSFT
    we ran azure functions on containerapps for the Eventgrid using below bicep. it worked well as you suggested above. But now MSFT recommends transferring to the native Functionsapp on containerapps.

    param name string
    param systemTopicName string
    param functionAppName string
    resource systemTopic 'Microsoft.EventGrid/systemTopics@2024-12-15-preview' existing = {
      name: systemTopicName
    }
    
    
    resource EventSubscription 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2024-12-15-preview' = {
    
      name: '${name}-subscription'
      parent: systemTopic
      properties: {
        destination: {
          endpointType: 'AzureFunction'
          properties: {
            resourceId:resourceId('Microsoft.Web/sites/functions',functionAppName,  'myblobtrigger')
            maxEventsPerBatch: 1
            preferredBatchSizeInKilobytes: 64
          }
        }
        eventDeliverySchema: 'EventGridSchema'
        filter: {
          includedEventTypes: [
            'Microsoft.Storage.BlobCreated'
            'Microsoft.Storage.BlobDeleted'
          ]
          isSubjectCaseSensitive: false
        }
        retryPolicy: {
          eventTimeToLiveInMinutes: 1440
          maxDeliveryAttempts: 30
        }
      }
    }
    
    
    

    new bicep eventgrid sub>

    param name string
    param systemTopicName string
    param functionAppName string
    param storageAccountName string
    resource systemTopic 'Microsoft.EventGrid/systemTopics@2025-04-01-preview' existing = {
      name: systemTopicName
    }
    
    
    resource EventSubscription 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2025-04-01-preview' = {
    
      name: '${name}-subscription'
      parent: systemTopic
      properties: {
        destination: {
          endpointType: 'AzureFunction'
          properties: {
            resourceId: '${resourceId('Microsoft.App/containerApps', functionAppName)}/functions/myblobtrigger'
            maxEventsPerBatch: 1
            preferredBatchSizeInKilobytes: 64
          }
        }
        eventDeliverySchema: 'CloudEventSchemaV1_0'
        
        filter: {
          includedEventTypes: [
            'Microsoft.Storage.BlobCreated'
            'Microsoft.Storage.BlobDeleted'
          ]
          isSubjectCaseSensitive: false
          subjectBeginsWith: '/blobServices/default/containers/in/' 
        }
        retryPolicy: {
          eventTimeToLiveInMinutes: 1440
          maxDeliveryAttempts: 30
        }
        deadLetterDestination: {
          endpointType: 'StorageBlob'
          properties: {
            resourceId: resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', storageAccountName, 'default', 'deadletter')
            blobContainerName: 'deadletter'
          }
        }
      }
      
    }
    

    Bash(az eventgrid system-topic event-subscription create \

            --name blop-trigger-subscription \…)

      ⎿  Error: ERROR: unrecognized arguments: --azure-function-resource-id /subscriptions/041e3b12-1c32-468a-8386-53833397e791/resourceGroups/rg-lentinis-cms/provide

         rs/Microsoft.App/containerApps/lentinis-cms-dvpc56-fn/functions/myblobtrigger

         Examples from AI knowledge base:

         az eventgrid system-topic event-subscription create --endpoint 

         /subscriptions/{SubID}/resourceGroups/{RG}/providers/Microsoft.Web/sites/{functionappname}/functions/{functionname} --endpoint-type webhook 

         --included-event-types Microsoft.Storage.BlobCreated Microsoft.Storage.BlobDeleted --name es1 --resource-group rg1 --system-topic-name systemtopic1

         Create a new event subscription for a system topic (autogenerated)

         az eventgrid system-topic event-subscription create --endpoint 

         /subscriptions/{SubID}/resourceGroups/{RG}/providers/Microsoft.Web/sites/{functionappname}/functions/{functionname} --endpoint-type webhook 

         --event-delivery-schema eventgridschema --included-event-types Microsoft.Storage.BlobCreated Microsoft.Storage.BlobDeleted --name es1 --resource-group rg1 

         --system-topic-name systemtopic1

         Create a new event subscription for a system topic (autogenerated)

         https://docs.microsoft.com/en-US/cli/azure/eventgrid/system-topic/event-subscription#az_eventgrid_system_topic_event_subscription_create This confirms that while Container Apps supports kind: functionapp and can run Functions code, the Azure ARM API doesn't yet expose individual functions as sub-resources under Container Apps. This explains why EventGrid can't validate the resource ID.

    Can you confirm this?:
    Status: The EventGrid service itself hasn't been updated to support Container Apps with native Functions, even though Container Apps can run Functions code.

    At this point, our options are:

    1. Use webhook endpoint type with manual validation - Convert to HTTP trigger that handles EventGrid validation
    2. Use blob storage triggers directly - Skip EventGrid entirely
    3. File a feature request/support ticket with Microsoft about EventGrid + Container Apps native Functions integration
    4. Wait for the integration to be completed - This appears to be a work-in-progress

    Thank you.

    0 comments No comments

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.