Companion sample for the blog post Fabric: The Hidden Destination. It turns a Microsoft Fabric EventStream into a bidirectional participant in an Azure event-driven architecture, using a Unified Namespace (UNS) as the shared topic convention.
A .NET Aspire AppHost starts the entire round trip above with one command, entirely on your machine - no Azure subscription, no Fabric workspace. Fabric's two EventStream endpoints are just Event Hubs-compatible endpoints under the hood, so the AppHost provisions the Azure Event Hubs emulator in their place. Event Grid's CloudEvents topic is played by the community Event Grid emulator. Neither Function's code knows the difference.
See Using real cloud resources instead if you want to point part of this at Azure once you're past the demo stage. Terraform for the real Event Grid topic is still included for that.
| Path | Purpose |
|---|---|
AppHost |
The Aspire AppHost. Starts Azurite, a Mosquitto broker, both Functions, FabricBridge.UnsPublisher, the Event Hubs emulator (standing in for Fabric), FabricBridge.Emulator, and the Event Grid emulator. |
FabricBridge.Functions |
The two isolated-worker Azure Functions from the article. FabricToEventGrid (egress) and EventGridToFabric (ingress). |
FabricBridge.UnsPublisher |
A small MQTT console publisher that simulates a shop floor and fills the EventStream with UNS-shaped test data (enterprise/site/area/line/tag). |
FabricBridge.Emulator |
Relays raw bytes between Mosquitto and the Event Hubs emulator, standing in for the MQTT source and custom endpoint that live inside a real Fabric EventStream, and logs whatever lands on the emulated ingestion endpoint. |
deploy/infrastructure |
Terraform for a real Event Grid custom topic (CloudEvents schema), plus optional managed-identity role assignment and a sample subscription. |
- .NET 10 SDK.
- Docker Desktop (or another container runtime Aspire supports) - Aspire runs Mosquitto, Azurite, and both emulators as containers.
- Azure Functions Core Tools v4, 4.10.0 or later - older 4.x builds don't start isolated-worker .NET 10 apps:
npm install -g azure-functions-core-tools@4. On 4.13.0+,func startbinds to127.0.0.1by default instead of0.0.0.0, which would stop theeventgrid-emulatorcontainer from reaching the Functions host viahost.docker.internal-local.settings.jsonalready setsHost.LocalHttpAddressback to0.0.0.0to cover it, so no action needed, just don't remove that setting. - The Aspire CLI:
dotnet tool install -g Aspire.Cli(optional -dotnet runworks too, just without the extra CLI conveniences).
That's the whole list: no Azure subscription, no Fabric workspace.
From the repo root:
cd src/AppHost
aspire runThis opens the Aspire dashboard and starts everything. Azurite (the Functions storage emulator), a local Mosquitto broker, the Event Hubs emulator (with a fabric-egress and a fabric-ingress hub), FabricBridge.Emulator, the Event Grid emulator, both FabricToEventGrid and EventGridToFabric, and FabricBridge.UnsPublisher (already publishing simulated UNS tags into Mosquitto). There are no parameters to fill in and nothing to provision first.
Watch uns-publisher push simulated tags into Mosquitto, fabric-emulator-bridge relay them onto the Event Hubs emulator and log what comes back on the ingestion side, and the functions resource's logs show both Functions firing in between. That's the full round trip from the architecture diagram at the top of this README, running end-to-end on one machine.
Everything above is wired up in src/AppHost/Program.cs as two independent swaps - you can make either one, or both:
Remove the fabricEmulator/egressHub block and the FabricBridge.Emulator project resource, and replace the functions.WithEnvironment(...) calls for Fabric__Egress__EventHub__* and Fabric__Ingress__EventHub__* with AddParameter calls, the same way eventgrid-topic-endpoint/eventgrid-topic-key used to work before this AppHost went fully local:
var fabricEgressConnectionString = builder.AddParameter("fabric-egress-eventhub-connection-string", secret: true);
// ...ingress equivalents, then:
functions.WithEnvironment("Fabric__Egress__EventHub__ConnectionString", fabricEgressConnectionString);You'll need a Fabric workspace with Real-Time Intelligence enabled and an EventStream with a custom endpoint destination and, for the return trip, the EventStream's own ingestion endpoint - see Add a custom endpoint destination to an EventStream. Fabric itself isn't something Terraform can provision, so this part stays manual. Since a real Fabric workspace runs in Microsoft's cloud, it also needs a way to reach the Mosquitto broker and, for the return trip, EventGridToFabric's webhook running on your machine via a tunnel. There are many solutions available to establish a tunnel between Fabric and your local machine.
Provide a topic with the included Terraform, then swap the emulator container and its generated endpoint/key for parameters:
cd deploy/infrastructure
cp terraform.tfvars.example terraform.tfvars # then fill in your values
terraform init
terraform apply
terraform output event_grid_topic_endpoint
terraform output event_grid_topic_primary_access_keyvar eventGridTopicEndpoint = builder.AddParameter("eventgrid-topic-endpoint");
var eventGridTopicKey = builder.AddParameter("eventgrid-topic-key", secret: true);
functions
.WithEnvironment("EventGrid__Topic__Endpoint", eventGridTopicEndpoint)
.WithEnvironment("EventGrid__Topic__Key", eventGridTopicKey);Tick Save to user secret on parameters like these in the dashboard's "Unresolved parameters" banner so you don't have to re-enter them next run, or set them upfront:
aspire secret set Parameters:eventgrid-topic-endpoint "https://your-topic.westeurope-1.eventgrid.azure.net/api/events"
aspire secret set Parameters:eventgrid-topic-key "<primary access key>"A real Event Grid topic also needs an event subscription registered against EventGridToFabric's webhook (az eventgrid event-subscription create or the portal), pointed at a publicly reachable URL - again, a tunnel. The unemulated EventGridToFabric webhook route does enforce its system key in that case, unlike the local emulator: <https-url>/runtime/webhooks/eventgrid?functionName=EventGridToFabric&code=<key> (grab <key> from the function's keys in the dashboard, or Core Tools' console output).