1. Home
  2. Technical Documentation
  3. How to Add a RabbitMQ Trigger without a Queue Worker

How to Add a RabbitMQ Trigger without a Queue Worker

This article explains how to bridge an existing RabbitMQ broker to the Warewolf Execution Engine without exposing RabbitMQ to Azure or running a queue-worker process per trigger. It’s the right fit when a customer already has RabbitMQ producing messages on-prem and wants those messages to execute a workflow on the engine, with no code change on the producer side.

Download the Deployment Scripts. Download the Warewolf Azure Execution Engine deployment scripts from warewolf.io/release-notes.php and extract the .zip so that the scripts land in D:\ExecutionEngine\Scripts. All examples in this article assume that path. This article assumes the engine itself is already deployed — see Deploy to Azure Functions if it isn’t yet.

Why a bridge, and not a queue-worker container per trigger

RabbitMQ speaks AMQP 0.9.1; Azure Service Bus speaks AMQP 1.0 (or its own protocol). They can’t talk to each other directly. The shovel bridge uses two first-class, independently-provisioned pieces to connect them:

  • RabbitMQ’s own Shovel plugin — a dynamic, broker-managed forwarder that reads from an existing RabbitMQ queue and republishes each message, verbatim, onto a Service Bus queue over AMQP 1.0. No custom forwarding code, no extra process to keep alive.
  • A Service Bus-triggered Function App (Warewolf.Execution.ServiceBusWorker) — the compute step that actually calls the engine. Service Bus is a message broker; it cannot hold an OAuth token or make an outbound HTTP call, so this worker reads each message, authenticates via Managed Identity, and calls the engine’s /secure or /public route on its behalf.
 ┌──────────────┐  publish   ┌─────────────────┐   Shovel    ┌──────────────────┐  trigger   ┌───────────────────┐
 │  RabbitMQ    │ ─────────▶ │  RabbitMQ        │ ──────────▶ │  Azure Service    │ ─────────▶ │  ServiceBusWorker  │
 │  producer    │            │  source queue     │  (AMQP     │  Bus queue        │  (message) │  Function App      │
 └──────────────┘            └─────────────────┘   0.9.1→1.0) └──────────────────┘            └────────┬──────────┘
                                                                                                          │ Managed Identity
                                                                                                          ▼
                                                                                                 Execution Engine
                                                                                                 /secure or /public

Message contract

Shovel forwards message bytes verbatim — it cannot reshape payloads. The RabbitMQ producer must publish exactly the JSON the worker expects:

{ "route": "secure", "workflow": "Hello World", "inputs": { "Name": "FromRabbitMq" } }
  • route (optional) — secure (default) or public.
  • workflow (required) — the workflow name.
  • inputs (optional) — a string map sent as query-string parameters.

If an existing RabbitMQ producer uses a different schema, it must be changed to emit this contract, or an adapter service must sit between the producer and the source queue — Shovel itself cannot transform messages.

Prerequisites

Requirement Notes
PowerShell 7.0+, Azure CLI 2.55.0+ Same as every other script in the deployment scripts zip.
RabbitMQ Shovel plugins enabled on the broker One-time, broker-host admin action: rabbitmq-plugins enable rabbitmq_shovel rabbitmq_shovel_management. Configure-RabbitMqShovel.ps1 probes for this and tells you if it’s missing — it cannot enable it remotely.
A broker-side TLS hostname-check fix Required before any shovel can connect to a real Service Bus namespace — see the callout below. Also broker-host admin, also not automatable remotely.
An Entra “client apps” app role on the engine The worker authenticates as a daemon — see How to configure Client Apps with Azure Entra ID and Easy Auth. Warewolf_ClientApps ships in the example auth config.

Required broker fix — TLS hostname check. Azure Service Bus presents a wildcard certificate (*.servicebus.windows.net). Erlang’s default certificate-hostname check does a literal match, not a wildcard-aware one, so without this fix every shovel connection to a real Service Bus namespace fails with {tls_alert,{bad_certificate,{bad_cert,{hostname_check_failed, ...}}}} in the broker’s own log (not visible via the Management API). Add this to the broker’s advanced.config (%APPDATA%\RabbitMQ\advanced.config on Windows, /etc/rabbitmq/advanced.config on Linux) and restart the broker:

[
  {amqp10_client, [
    {ssl_options, [
      {customize_hostname_check, [
        {match_fun, public_key:pkix_verify_hostname_match_fun(https)}
      ]}
    ]}
  ]}
].

This keeps full TLS peer/chain validation enabled — it only fixes the hostname match, it doesn’t weaken certificate checking. On Erlang/OTP 26+ brokers you’ll also need to pass a CA bundle on the shovel’s destination URI (-DestUriCaCertFile on Configure-RabbitMqShovel.ps1, step 3 below) or the shovel crash-loops with {cacerts, undefined} — OTP 26 stopped falling back to an implicit trust store.

Step 1 — Deploy the Service Bus worker

Publish first — the script does not build, and its publish output must be a different folder from the engine’s own:

dotnet publish Dev/Warewolf.Execution.ServiceBusWorker/Warewolf.Execution.ServiceBusWorker.csproj -c Release -o D:\ServiceBusWorker\Publish

.\Deploy-WwExecutionServiceBusWorker.ps1 `
  -ResourceGroup $ResourceGroup -Location $Location `
  -StorageAccount stwwsbworker -AppName $ServiceBusWorkerApp `
  -PublishPath D:\ServiceBusWorker\Publish `
  -ServiceBusNamespace $ServiceBusNamespace -ServiceBusQueueName wwexecution-queue `
  -CreateShovelSendRule $true `
  -WwExecutionBaseUrl $EngineUrl -WwExecutionTenantId $TenantId `
  -WwExecutionResourceAppId $ResourceAppId

This provisions, in order: the Service Bus namespace and destination queue (dead-lettering, configurable max delivery count), the worker’s Function App with a system-assigned Managed Identity for its listen connection (no SAS secret in its own app settings), and a queue-scoped Send-only SAS authorization rule (default name shovel-send) — the least-privilege credential the RabbitMQ Shovel plugin will use as its destination. Like every other script here, it’s params-first, prompt-if-missing, supports -DryRun, and writes a masked summary + transcript.

Configurable trigger binding

-ServiceBusQueueName (default wwexecution-queue) isn’t just a provisioning detail — it’s applied as an app setting the worker’s trigger actually binds to, so overriding it retargets the trigger, not only the queue that gets created. Four more parameters tune the trigger’s binding, which otherwise defaults to the worker’s committed values:

Parameter Default What it controls
-ServiceBusTriggerMaxConcurrentCalls 16 Max messages processed concurrently
-ServiceBusTriggerPrefetchCount 0 Messages pre-fetched per replica ahead of the concurrency limit
-ServiceBusTriggerMaxAutoLockRenewalMinutes 5 How long the worker keeps renewing a message’s peek-lock while processing it
-ServiceBusTriggerAutoCompleteMessages true Whether the host auto-completes a message on successful return

Most deploys don’t need these — the defaults match production traffic patterns. They’re there for higher-throughput or slower-downstream scenarios. All five parameters above are only available on this standalone invocation; they aren’t forwarded when the worker is deployed as a companion of the engine (Deploy-WwExecutionEngine.ps1 -DeployServiceBusWorker) — that switch only carries shared subscription/tenant/resource-group/engine-URL context.

Step 2 — Authorize the worker’s Managed Identity

The worker is a daemon caller of the engine, same as any other app-only client — mirror the registration from How to configure Client Apps. -AppRolesToAssign fails loudly if Warewolf_ClientApps doesn’t already exist on the engine:

.\Configure-WwExecutionAuth-Clients.ps1 `
  -ResourceAppId $ResourceAppId -TenantId $TenantId `
  -ClientType Daemon -DaemonUseManagedIdentity `
  -DaemonFunctionAppName $ServiceBusWorkerApp `
  -DaemonFunctionAppResourceGroup $ResourceGroup `
  -AppRolesToAssign Warewolf_ClientApps `
  -NonInteractive

You’ll also need a matching secure.config WindowsGroupPermissions row granting Warewolf_ClientApps Execute=true on every workflow the bridge calls — same requirement as any other daemon caller, see Security — secure.config.

Step 3 — Configure the RabbitMQ shovel

This configures a dynamic shovel via the RabbitMQ Management HTTP API — nothing is written to the broker’s static config, and it’s safe to re-run. The destination SAS key is fetched live via az and never written to disk:

.\Configure-RabbitMqShovel.ps1 `
  -RabbitMqManagementUri "https://:15671" `
  -RabbitMqUsername  -RabbitMqPassword  `
  -SourceQueue  `
  -ServiceBusNamespace $ServiceBusNamespace -ServiceBusQueueName wwexecution-queue `
  -ServiceBusResourceGroup $ResourceGroup -ServiceBusSasKeyName shovel-send

If your broker is Erlang/OTP 26+, append -DestUriCaCertFile /etc/ssl/certs/ca-certificates.crt (or your platform’s CA bundle path) — see the TLS callout above. -DestUriVerifyNone exists as a last-resort escape hatch that disables all peer certificate validation; it’s a diagnostic tool, not a recommended setting.

Step 4 — Verify

# Confirm the queue exists and check its depth:
az servicebus queue show --namespace-name $ServiceBusNamespace --resource-group $ResourceGroup --name wwexecution-queue -o table

# Or schedule the standalone health monitor (Task Scheduler/cron/Azure Automation —
# RabbitMQ is customer/on-prem infra, not something a Function can reliably poll):
.\Monitor-RabbitMqShovel.ps1 -RabbitMqManagementUri "https://:15671" `
  -RabbitMqUsername  -RabbitMqPassword  -ShovelName wwexecution-shovel

Publish a message to the RabbitMQ source queue and confirm it arrives on the Service Bus queue and is executed on the engine. A 500 from the engine means the worker’s Managed Identity lacks Warewolf_ClientApps, or secure.config has no matching Execute row for that workflow — engine authorization denials are wrapped as 500, not 403 (WOLF-8418), same as every other daemon caller.

The Management API reports a healthy, actively-forwarding shovel as either running or flowflow just means RabbitMQ’s own flow control is briefly throttling it (e.g. transient backpressure from the destination). It is not a failure state; don’t alert on it.

Security model

  • The Shovel’s destination credential is a queue-scoped, Send-only SAS rule — it can never Listen or Manage.
  • The worker’s own Service Bus listen connection uses Managed Identity by default — no SAS secret in the worker’s app settings at all.
  • The worker calls the engine via Managed Identity too — see How to configure Client Apps and Security — secure.config.
  • The destination Service Bus namespace must keep local (SAS) authentication enabled (disableLocalAuth = false). RabbitMQ’s Shovel plugin only supports SASL PLAIN with a policy-name/key credential — it has no Entra/OAuth client, so it cannot use Managed Identity. If a security baseline flips this to true, every shovel to that namespace stops connecting. This is independent of the worker’s own Managed Identity listen connection above — only the Shovel’s send-side credential needs SAS.
  • Rotate the shovel-send SAS key per your normal key-rotation schedule; rotating it requires re-running Configure-RabbitMqShovel.ps1 so the Shovel picks up the new key.

Troubleshooting

Symptom Likely cause Fix
Shovel terminated, reason "failed to connect to destination" Either the TLS hostname-check fix isn’t applied, or disableLocalAuth has drifted to true on the namespace — both produce this identical message. Check the broker’s own log for a hostname_check_failed TLS alert (apply the advanced.config fix above) vs. run az servicebus namespace show --query disableLocalAuth and restore false if it drifted.
Shovel stuck at "starting", never reaches running/flow OTP 26+ broker missing a CA bundle on the destination URI — crash-loops with {cacerts, undefined} in the broker log, too fast for the Management API to catch between retries. Re-run Configure-RabbitMqShovel.ps1 with -DestUriCaCertFile pointing at your platform’s CA bundle.
500 from the engine after a message arrives Worker’s Managed Identity lacks Warewolf_ClientApps, or secure.config has no Execute=true row for the workflow. Re-check Step 2’s role assignment and the matching secure.config row.
rabbitmq-plugins enable instructions on every Configure-RabbitMqShovel.ps1 run rabbitmq_shovel/rabbitmq_shovel_management aren’t enabled on the broker. Run the exact rabbitmq-plugins enable command the script prints, once, on the broker host.

Teardown

The Service Bus worker has no dedicated rollback companion — its run summary JSON records what to remove manually (Function App, storage account, Service Bus namespace/queue, SAS rules). The shovel itself can be removed via the RabbitMQ Management UI or API (DELETE /api/parameters/shovel/{vhost}/{name}) — deleting it does not touch the Service Bus queue or the worker.

See also

  • Scripts/Deploy-WwExecutionServiceBusWorker.ps1 — provisions the Azure side.
  • Scripts/Configure-RabbitMqShovel.ps1 — configures the RabbitMQ side.
  • Scripts/Monitor-RabbitMqShovel.ps1 — standalone health monitor for scheduled polling.
  • docs/ShovelBridge-Architecture.md — full internal architecture reference, including every root-caused failure mode this article’s troubleshooting table summarizes.
  • docs/Deploy-EndToEnd-Runbook.md §8.5 — the full copy-paste runbook this article is drawn from.
  • How to configure Client Apps with Azure Entra ID and Easy Auth — the daemon registration Step 2 depends on, and the AzureServiceBus reference client this worker is built from.
  • Security — secure.config — the permission model the worker’s role resolves against.
  • Deploy to Azure Functions — deploying the engine itself, if it isn’t already up.
FacebookTwitterLinkedInGoogle+Email
Updated on August 26, 2026

Was this article helpful?

Related Articles

Enjoying Warewolf?

Write a review on G2 Crowd
Stars