skip to Main Content

I am trying to create a function app on Azure with an Event Hub trigger using a managed identity. I seem to have created everything so that all the references resolve, but when I send a message to the event hub, the function app is not getting triggered (or at least, I’m not seeing the log entry it should be producing).

Trying to follow the documentation here.

Provisioning using Terraform:

main.tf

variable "resource_group_name" {}
variable "location" { default = null }

variable "function_app_name" {}
variable "event_hub_name" {}


resource "azurerm_resource_group" "rg" {
  name     = var.resource_group_name
  location = var.location
}

resource "azurerm_service_plan" "sp" {
  name                = "fnappserviceplan"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  os_type             = "Linux"
  sku_name            = "B1"
}

resource "azurerm_eventhub_namespace" "hub" {
  name                = var.event_hub_name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  sku                 = "Standard"
  capacity            = 1
}

resource "azurerm_eventhub" "hub" {
  name                = "myEventHub"
  namespace_name      = azurerm_eventhub_namespace.hub.name
  resource_group_name = azurerm_resource_group.rg.name
  partition_count     = 2
  message_retention   = 1
}

resource "azurerm_storage_account" "fnapp" {
  name                     = "safnapp"
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_linux_function_app" "fnapp" {
  name                = var.function_app_name
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  service_plan_id     = azurerm_service_plan.sp.id

  storage_account_name       = azurerm_storage_account.fnapp.name
  storage_account_access_key = azurerm_storage_account.fnapp.primary_access_key

  app_settings = {
    # tried this but it doesn't recognize the connection, so I went with the connection string instead
    # "EVENTHUB__fullyQualifiedNamespace" = "${azurerm_eventhub_namespace.hub.name}.servicebus.windows.net"

    "EVENTHUB"                = azurerm_eventhub_namespace.hub.default_primary_connection_string
    "EVENTHUB_CONSUMER_GROUP" = azurerm_eventhub_consumer_group.fnapp.name
  }

  site_config {
    always_on = true

    application_stack {
      python_version = "3.11"
    }
  }

  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_eventhub_consumer_group" "fnapp" {
  name                = "${var.function_app_name}ConsumerGroup"
  namespace_name      = azurerm_eventhub_namespace.hub.name
  eventhub_name       = azurerm_eventhub.hub.name
  resource_group_name = azurerm_resource_group.rg.name
}

And then the main function:

function_app.py
import logging
import os

import azure.functions as func


app = func.FunctionApp()


@app.function_name(name="mylistener")
@app.event_hub_message_trigger(arg_name="hub",
                               event_hub_name="myEventHub",
                               connection="EVENTHUB",
                               consumer_group=os.getenv("EVENTHUB_CONSUMER_GROUP", "$Default"))
def myeventlistener(hub: func.EventHubEvent):
    event_body = hub.get_body().decode('utf-8')
    logging.info(f'Python EventHub trigger processed an event: {event_body}')

After provisioning, I add the "Azure Event Hubs Data Receiver" role to this function app on the resource group. (And "Azure Event Hubs Data Owner" to myself for my own interactions with the event hub.)

I then deploy the code using func azure functionapp publish MyFunctionAppName, and monitor using func azure functionapp logstream MyFunctionAppName. (This logstream is where I expect to see the logging.info() output. Or at least some system logs indicating the function has been triggered. Is this correct?)

When inspecting the connection on the Azure portal (mylistener function > Integration > Triggers > Azure Event Hub) all the parameters appear correct and the Connection seems to be recognized – it’s not giving me "No connection found" or anything, it’s using the named connection I set in the app settings.

After waiting for all the setup/launching/initializing to settle down, I then publish a (plaintext) message to the event hub (not the namespace). I have tried sending specifically to each partition. I expect the log message to appear but it does not. I see no indication that the function is getting triggered.

UPDATE

ChatGPT suggested that this doesn’t use managed identities if I feed it a connection string, and instead I should set EVENTHUB = azurerm_eventhub.hub.name in order to use managed identities. It was double-minded on whether the argument in Python should be connection="EVENTHUB" or connection="EVENTHUB__fullyQualifiedNamespace". I tried both (while uncommenting the one line in the tf app_settings), and with both, it gives me "No existing connections available" when I check the trigger’s connection on the portal. I also get the message "Azure.Messaging.EventHubs: The connection string could not be parsed; either it was malformed or contains no well-known tokens."

2

Answers


  1. Chosen as BEST ANSWER

    Credit to @Pravallika KV's answer for containing the information I needed. TL/DR version follows.

    The update to the OP was the correct configuration. I was looking in the wrong place for logs so it did not appear to be working. Also, with this configuration, the "Integration" tab on the Function shows "No existing connections available" under the Event Hub trigger, implying there is no connection, even though it is in fact connecting and triggering properly. (Bug?)

    The following modification to the OP Terraform setup works:

    main.tf
    resource "azurerm_linux_function_app" "fnapp" {
      # ...
    
      app_settings = {
        "EVENTHUB__fullyQualifiedNamespace" = "${azurerm_eventhub_namespace.hub.name}.servicebus.windows.net"
        "EVENTHUB_CONSUMER_GROUP"           = azurerm_eventhub_consumer_group.fnapp.name
      }
    
      # ...
    }
    

    function_app.py in OP may be used as is. connection should be the portion of the environment variable before the __fullyQualifiedNamespace. So, for example, with an environment variable called EVENTHUB__fullyQualifiedNamespace, you want connection="EVENTHUB". Be sure not to also include an environment variable called EVENTHUB.

    I have not found a way to view the logs from the log stream, even by forcing to stdout. However, the logs are accessible using the (small charge paid service) Application Insights, which must be enabled in order to view them. This can be done using the web portal or through Terraform. Once enabled, invocations are recorded under the "Invocations" tab of the Function. Log entries will appear individually when selecting a specific run.


  2. Follow below steps to establish connection between Azure function app and Event Hub using managed identity.

    • Create an EventHub and Event Hub Trigger in Azure function App.
    • Enable Managed Identity in Azure function App. I have enabled System Managed Identity.
    • Navigate to Event Hub=>Access Role(IAM)=>Add Role Assignment, assign Azure EventHub Data Owner and Azure Event Hub Data Receiver to Function App’s Managed Identity.

    enter image description here

    enter image description here

    • Add the application setting <CONNECTION_NAME_PREFIX>__fullyQualifiedNamespace=<eventhub_name>.servicebus.windows.net under Functionapp=>settings=>Environment Variables=>App Settings:

    CONNECTION_NAME_PREFIX should be the connection value you have used in the function code.

    enter image description here

    • Update EventHub name in the function code.

    Code Snippet:

    import azure.functions as func
    import logging
    
    app = func.FunctionApp()
    
    @app.event_hub_message_trigger(arg_name="azeventhub", event_hub_name="<eventhub_name>",
                                   connection="rkspace_RootManageSharedAccessKey_EVENTHUB") 
    def eventhub_trigger1(azeventhub: func.EventHubEvent):
        logging.info('Python EventHub trigger processed an event: %s',
                    azeventhub.get_body().decode('utf-8'))
    
    • Send the events:

    enter image description here

    • Able to trigger the function:

    You can check the function logs under Function=>Invocation Logs. select the particular log to see the detailed output/invocation.

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search