Two weeks ago, I had the privilege of presenting two sessions at Microsoft Build 2026 in San Francisco! I’ve always enjoyed speaking at previous editions of Build, so it was great to be back again. This year, the event moved from Seattle to the beautiful Fort Mason Center in San Francisco. The waterfront location provided an incredible backdrop for a fantastic week of learning, networking, and sharing ideas.
Press enter or click to view image in full size
Fort Mason Center, San Francisco
My first session explored a simple but powerful question:
What happens if we treat Bicep not just as an Azure deployment language, but as a declarative control plane for any system with an API?
To demonstrate the concept, I built a custom Bicep Local Deploy extension that controls a physical Zigbee light through Home Assistant. While the light bulb itself was intentionally simple, the goal was much broader: showing how the same Infrastructure as Code principles we use for Azure can be extended to third-party APIs, edge environments, and virtually any platform that exposes a REST API.
The session, “Deploying Infrastructure and Turning on the Light: Bicep Beyond Azure,” was packed with live demos and practical examples. It generated some great discussions afterward and sparked a lot of ideas about what Bicep extensions can enable.
Press enter or click to view image in full size
Deploying Infrastructure and Turning on the Light: Bicep Beyond Azure
The abstract of the session was as follows:
“Take your Infrastructure as Code workflows to the next level with Bicep Local Deploy. In this demo-driven session, you’ll learn how to extend Bicep beyond Azure resources and treat external systems as declarative infrastructure. We walk through a custom Bicep extension in .NET that exposes new resource types and integrates with external REST APIs. A memorable live demo with practical insights! Leave with both the knowledge and the code needed to start building your own Bicep extensions.” https://build.microsoft.com/en-US/sessions/LTG454
I really enjoyed presenting the session and demonstrating it. The session sparked a lot of ideas in the audience, and I received great feedback afterward.
I also gave away a few copies of my book, as well as some Bicep stickers and pins, which were a great hit!
Press enter or click to view image in full size
Getting started with Bicep — Infrastructure a Code
Press enter or click to view image in full size
Bicep pins and stickers
The demo was structured in three logical steps that demonstrate how Bicep can be extended beyond Azure. In the first step, we built a custom Bicep local-deploy extension by defining a resource model, implementing convergence logic, and creating a client that communicates with Home Assistant to control a Zigbee light.
In the second step, we compiled, published, and packaged the extension so it could be consumed by Bicep as a local extension.
Finally, in the third step, we used the extension from a standard Bicep deployment, where Bicep passed the desired state to the extension, the extension performed the required API calls, and the deployment returned outputs just like any native Azure deployment.
Together, these three steps demonstrate how Bicep can act as a declarative control plane for virtually any platform or system that exposes an API.
🔥 Two weeks ago, I had the privilege of presenting two sessions at Microsoft Build 2026 in San Francisco! I’ve always enjoyed speaking at previous editions of Build, so it was great to be back again. This year, the event moved from Seattle to the beautiful Fort Mason Center in San Francisco. The waterfront location provided an incredible backdrop for a fantastic week of learning, networking, and sharing ideas.
Press enter or click to view image in full size
Fort Mason Center, San Francisco
📍 Before the event started, I spent a few days exploring San Francisco, including an early morning 15km run across the Golden Gate Bridge. Definitely one of the highlights of the trip.
Press enter or click to view image in full size
Early morning run over the Golden Gate Bridge
My session on Microsoft Marketplace was scheduled for Wednesday, just after lunch. The session’s focus was demo-driven and covered the question: how do you take your infrastructure-as-code and turn it into a deployable product experience through the Microsoft Marketplace?
I took the audience on the journey from start to finish as outlined below.
Journey of the session at Microsoft Build
I had the absolute honor of meeting with Elizabeth Beals (McLaughlin) and Felipe Ospina, who reached out prior to the event to sync on the messaging of the Microsoft Marketplace and Build, and exchange ideas.
What I enjoyed most was sharing the lessons learned from real-world projects. The happy path is usually well documented. The validation failures, packaging mistakes, portal UX challenges, and certification surprises are where the real learning happens.
A big thank you to everyone who attended the session, asked questions, shared experiences, and continued the discussion afterward. It was great meeting so many people from around the world who are building on Azure and looking at Microsoft Marketplace as a way to accelerate adoption of their solutions.
One of the coolest additions this year was the physical Microsoft Marketplace on the show floor. It gave attendees a place to ask questions, learn about the latest developments, and continue conversations after sessions. They even had their own Marketplace pins! It also turned out to be the perfect call to action for my talk.
Physical Microsoft Marketplace at Microsoft Build,
Felipe Ospina did a great job presenting on Microsoft Marketplace as well, a session I did not want to miss. He also announced Intelligent Discovery for the Microsoft Marketplace, powered by AI!
Press enter or click to view image in full size
Felipe Ospina presenting a session on Microsoft Marketplace too
I had the great pleasure of meeting the amazing Kristyn Maddox and Trenton Chavez as well. Great conversations and exchanging of ideas!
If something has an API and a meaningful desired state, it can be modeled declaratively. That idea feels obvious in the cloud. We describe virtual machines, networks, load balancers, and container apps in code, and the platform converges reality toward that definition. We call it Infrastructure as Code.
But what happens if you apply that same thinking to something in the physical world?
I decided to find out. Using Bicep’s experimental local extension capability, I built a custom resource provider that treats a Home Assistant light as a first-class Infrastructure-as-Code resource. Running az bicep local-deploy now physically changes the state of a Zigbee light in my office, declaratively and idempotently. Not through a script. Not through an automation. Through a resource definition.
Let’s start by seeing it in action in the video below!
Why This Wasn’t Just a Gimmick
Home automation APIs are almost always imperative. You tell them to “turn on,” “turn off,” or “toggle.” That works perfectly for dashboards and mobile apps, but it clashes with Infrastructure as Code principles. IaC is about convergence. You don’t toggle a VM. You declare its desired configuration. You don’t flip a load balancer. You define its final state and let the system move toward it. So my first rule of this extension was simple: no toggle support. A deployment must explicitly declare “on” or “off”. Running the same deployment twice must result in the same outcome. Anything else would undermine the model.
The Real Hardware Behind It
This isn’t simulated. It’s running on actual hardware. Home Assistant is running locally in a VM on Windows 11. For Zigbee connectivity, I’m using the Sonoff ZBDongle-E as the Zigbee 3.0 coordinator. The light itself is an Aqara T2 Light Bulb, capable of RGB and color temperature modes.
The signal path looks like this: Laptop → Home Assistant VM → Zigbee dongle → Aqara bulb
The extension never talks to Zigbee directly. It communicates with Home Assistant via its REST API. Home Assistant handles the radio communication and device state management. That separation is important. The Bicep extension doesn’t care whether the device is Zigbee, Thread, Wi-Fi, or something else. It only cares that the system exposes a stable API and that the resource has a meaningful desired state. Once you abstract at that level, the pattern becomes clean.
The Enabler: Bicep Local Extensions
The experimental local extension model in Bicep allows you to register a custom executable as a resource provider. Instead of deploying to Azure Resource Manager, az bicep local-deploy invokes your handler locally. The execution flow looks like this:
Bicep CLI parses the template. It discovers the registered extension. It invokes the resource handler. The handler performs the operation locally. Outputs are returned to Bicep.
In this case, the handler calls the Home Assistant REST API, applies the declared state, reads back the entity to confirm convergence, and returns outputs. Everything runs locally. Nothing touches Azure. That’s what makes this interesting. Bicep becomes a declarative engine, not just an Azure deployment tool.
Modeling the Light as Infrastructure
Here’s what the resource definition in main.Bicep looks like:
targetScope = 'local' extension homeassist@secure() @description('Home Assistant long-lived access token') param accessToken string@description('Home Assistant instance URL') param homeAssistantUrl string = 'http://192.168.68.70:8123'@description('The entity ID of the light to control') param lightEntityId string = 'light.aqara_lumi_light_agl003'@description('Desired state of the light') @allowed(['on', 'off']) param lightState string = 'on'@description('Brightness level (0-255)') @minValue(0) @maxValue(255) param brightness int = 100@description('Color temperature mireds (153-500, 0 to skip)') @minValue(0) @maxValue(500) param colorTemp int = 0@description('Hue 0-360 (-1 to skip)') @minValue(-1) @maxValue(360) param hue int = -1@description('Saturation 0-100 (-1 to skip)') @minValue(-1) @maxValue(100) param saturation int = -1resource aqaraLight 'Light' = { entityId: lightEntityId homeAssistantUrl: homeAssistantUrl accessToken: accessToken state: lightState brightness: brightness colorTemp: colorTemp hue: hue saturation: saturation }output currentState string = aqaraLight.currentState output friendlyName string = aqaraLight.friendlyName output entityId string = aqaraLight.entityId
This is the main.bicepparam file, I left out the access token for obvious reasons.
And to be able to import the HomeAssist extension, the following is configured in the bicepconfig.json file. It contains the references to the bin folder where the homeassist extension is located, as well as enabling the localDeploy experimental feature.
Under the hood, the extension is a .NET 9 resource host that registers a Light resource type and implements CreateOrUpdate semantics. Instead of imperative commands, it computes a desired end state from the template properties and applies exactly one color mode per deployment. It deliberately avoids toggle behavior and enforces idempotency at the handler level. The executable is packaged using az bicep publish-extension and invoked locally through the experimental local-deploy runtime, effectively acting as a custom resource provider, just one that happens to manage a Zigbee bulb instead of a cloud resource.
The Color Mode Lesson
One of the more subtle issues during development was color handling. Home Assistant does not allow color_temp and hs_color in the same service call. Sending both results in an HTTP 400. The extension now enforces exclusivity: if colorTemp is set, it uses color_temp. Otherwise, if hue and saturation are set, it uses hs_color. Never both. It’s a small detail, but it reinforces an important principle: declarative modeling only works when you respect the semantics of the underlying API.
Building and Publishing the Extension
Because this relies on the experimental local extension model, the build and registration steps are important. Here’s the exact build process I use on Windows:
Dotnet publish produces the executable that implements the resource handlers. az bicep publish-extension packages and registers it locally so Bicep can discover and invoke it during local-deploy. After that, any Bicep file declaring the extension homeassist can use the custom Light resource. It’s Bicep acting as a declarative runtime locally.
Outputs
One of the things I wanted from the beginning was proper deployment feedback — not just “it worked” or “it failed,” but meaningful outputs that reflect the observed state of the device. The Bicep template defines explicit outputs:
They are returned by the extension itself after it completes the convergence logic. Inside the handler, after calling the appropriate Home Assistant service (light.turn_on or light.turn_off), the extension reads the actual entity state from Home Assistant and maps relevant fields back into the resource outputs.
currentState reflects the actual final state reported by Home Assistant.
entityId confirms the exact resource targeted.
friendlyName is pulled from the entity attributes for human-readable confirmation.
Surfacing real outputs transforms this from “a script that flips a light” into a proper resource model.
The extension applies the declared desired state, reads back the actual state from the source system, and returns that state as outputs to the Bicep runtime.
It’s being declared, converged, and observed.
And It Doesn’t Stop There
The light was just the smallest possible proof. Through Home Assistant, I already have dishwashers, washing machines, printers, motion sensors, smart plugs, and other appliances integrated behind the same API surface. They all expose the state. They all expose actions. They all have configuration that could be expressed declaratively. More fun challenges ahead!
The extension currently supports lights. But there’s nothing fundamentally preventing it from expanding to switches, scenes, or anything else. Once the extension model exists, the boundary moves.
We’ve been thinking about Infrastructure as Code in terms of where it runs: the cloud, the data center, the platform. Maybe we should start thinking about it in terms of what can converge.
Watching a Bicep deployment turn on a light is fun. Realizing that the same pattern could declaratively manage an entire physical environment is the interesting part.
What’s the most unexpected thing you’d model declaratively?
More info and shout-outs to other Bicep Local Deploy resources: