Introduction

In modern IoT solutions, especially when working with edge devices, managing configuration and tracking runtime state is just as important as sending telemetry. This is where module twins in Azure IoT Hub play a key role. Module twins act as a bridge between the cloud and individual modules running on a device. They allow you to push configurations from the cloud and receive real-time updates from the module—all without redeploying code.

In this article, we’ll focus on understanding desired properties and reported properties , how they work together, and how to implement them using .NET and Azure CLI.

Prerequisites

Before you begin, make sure you have:

Module Twin Properties

A module twin is a JSON document that represents the state of a module. The most important sections are:

Desired Properties

Desired properties are defined by the cloud and represent the configuration that a module is expected to follow.

These properties are used to:

For example, you might set a desired property like:

  
    {
  "interval": 10
}
  

This tells the module to send telemetry every 10 seconds.

Reported Properties

Reported properties (sometimes referred to as general or runtime properties) are sent by the module back to the cloud . They reflect the current state of the module.

These properties are used to:

For example:

  
    {
  "status": "running",
  "interval": 10
}
  

This confirms that the module is running and using the expected configuration.

Updating Desired Properties using Azure CLI

Update desired properties directly from the cloud using Azure CLI:

az iot hub module-twin update \
  --hub-name MyIoTHubDemo \
  --device-id mydevice001 \
  --module-id module1 \
  --set properties.desired.interval=10

Output

{
  "properties": {
    "desired": {
      "interval": 10
    }
  }
}

This sets the target configuration for the module.

Working with Module Twins in .NET

The following example demonstrates a complete flow:

Sample Code

using System;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Shared;

class Program
{
    private static string connectionString = "<MODULE_CONNECTION_STRING>";
    private static ModuleClient moduleClient;

    static async Task Main(string[] args)
    {
        moduleClient = await ModuleClient.CreateFromConnectionStringAsync(connectionString, TransportType.Mqtt);
        await moduleClient.OpenAsync();

        Console.WriteLine("Module connected.");

        // Read desired properties
        var twin = await moduleClient.GetTwinAsync();
        int interval = twin.Properties.Desired.Contains("interval") ? twin.Properties.Desired["interval"] : 5;

        Console.WriteLine($"Initial interval: {interval}");

        // Update reported properties
        var reported = new TwinCollection();
        reported["status"] = "running";
        reported["interval"] = interval;

        await moduleClient.UpdateReportedPropertiesAsync(reported);
        Console.WriteLine("Reported properties sent.");

        // Handle updates from cloud
        await moduleClient.SetDesiredPropertyUpdateCallbackAsync(OnDesiredChanged, null);

        Console.WriteLine("Waiting for updates...");
        await Task.Delay(-1);
    }

    private static async Task OnDesiredChanged(TwinCollection desiredProperties, object userContext)
    {
        if (desiredProperties.Contains("interval"))
        {
            int newInterval = desiredProperties["interval"];
            Console.WriteLine($"New interval received: {newInterval}");

            var reported = new TwinCollection();
            reported["interval"] = newInterval;
            reported["ack"] = "updated";

            await moduleClient.UpdateReportedPropertiesAsync(reported);
            Console.WriteLine("Reported properties updated.");
        }
    }
}

Output

Module connected.
Initial interval: 10
Reported properties sent.
Waiting for updates...

New interval received: 20
Reported properties updated. 

Code Explanation

Enabling Dynamic Configuration with Module Twins

Desired and reported properties work together to create a feedback loop between cloud and device. The cloud defines what should happen The module reports what is actually happening This approach helps you manage devices remotely, apply updates dynamically, and maintain visibility into system behavior without constant redeployment.

Conclusion

Module twins provide a simple yet powerful way to manage configuration and state in IoT solutions. Desired properties allow you to control modules from the cloud, while reported properties give you visibility into their actual behavior. Using both effectively helps build flexible, scalable, and maintainable IoT systems.