Custom configuration and reporting with Azure IoT and OSConfig

Important

Version 1.0.3 (published 28 June 2022) includes breaking changes to member names which may impact existing users. For more information, see: Member names transition from PascalCase to camelCase in version 1.0.3

This article is designed to support people who provision or manage devices with Azure IoT. If that doesn't sound like you, consider taking a look at Audiences for OSConfig documentation.

The CommandRunner --> RunCommand feature allows to you trade away some simplicity to gain flexibility. When you need to, you can drop down a level of abstraction to perform custom reporting and configuration.

Common examples of custom reporting and configuration

  • Verify device connectivity to key endpoints
  • Verify that specific daemons are running
  • Debug or troubleshoot devices, for example by gathering log files and uploading them to cloud storage
  • Configure all devices to use desired time zone when logging data, generating timestamps, etc.
  • Report on (or configure) your own unique device components which OSConfig would otherwise never know about
  • The possibilities are endless, nearly any task you could do in a shell for one device-- you can do with Azure IoT and OSConfig CommandRunner for fleets of devices

Tip

This article focuses on practical examples with minimal explanation. For technical background on CommandRunner, see: How to interact with the CommandRunner feature of OSConfig and Azure IoT.

Use cases examples

These examples can serve as starting points for you to adapt for your unique environment.

Each example includes steps and screen captures for working in the Azure portal, and for working in bash with Azure CLI.

Each example also includes variations for one device (e.g., a troubleshooting scenario), or for many devices (e.g., a configuration provisioning or reporting scenario).

What to expect:

In the single device instructions, you will read and write reported and desired properties directly in the OSConfig twin for a device. In the at-scale instructions, you will use IoT Hub Configurations (also known as Automatic Device Management or ADM) to push desired properties to many twins, and use IoT Hub Queries (independently, or attached to Configurations as Metrics) to observe the results coming back from devices.

Prerequisites for the following examples

If you are using this article for reference (for example, you are here to copy a property name), there are no pre-requisites.

If you want to try the examples on live systems (recommended), then:

  1. You will need an Azure account with an IoT Hub

    This article assumes some familiarity with IoT Hub and related tools. For example, it assumes you are comfortable creating IoT Hubs and attaching devices. If you prefer a more prescriptive step-by-step introduction to installing and using OSConfig from scratch, see: Quickstart: Manage a single virtual IoT device using Azure CLI instead.

  2. You will need at least one Linux device with the OSConfig agent installed and connected to Azure IoT.

    For more information, see: How and where to install the OSConfig agent for Linux.

  3. You will use Azure Portal or Azure CLI to interact with the devices via your IoT Hub

    For further steps, choose your preferred experience:

  1. Ensure you are signed in to the Azure Portal and can access your IoT Hub's Overview page Screenshot showing IoT Hub and devices from the Azure Portal

Tip

There are two things you should know about CommandRunner behavior to be successful using these examples:

  • As demonstrated in the examples, each new request must include a new value for for commandId (it can be any string, for example "MyCmd01", "MyCmd02").
  • The process is asynchronous, so results are not available instantaneously. The simplest procedure is to wait about one minute after you initiate a CommandRunner request. After that minute, without any extra steps on your part, the results are available in properties.reported.CommandRunner.commandStatus. For more on refresh behavior, async status updates, etc., see: How to interact with the CommandRunner feature of OSConfig and Azure IoT.

Example 1. Check device connectivity to specific endpoints

In this example we ask the device(s) to ping www.github.com 3 times. We observe ping's exit code (success or failure), and we can observe the textual output of the ping command.

  1. From your IoT Hub's portal page, navigate to the OSConfig module twin for the device you wish to manage. Then add the following to the properties.desired section (followed by a comma to separate it from the next item in properties.desired).

      "CommandRunner": {
                 "__t": "c",
                 "commandArguments": {
                     "commandId": "pingcmd",
                     "arguments": "ping -c 3 www.github.com",
                     "timeout": 120,
                     "singleLineTextResult": false,
                     "action": 3
                 }
             }
    

    Screen capture showing the desired twin contents for a ping command using OSConfig module for a single device from Azure Portal

  2. You can verify the device connectivity by checking the response to the ping command from the OSConfig module twin itself. Scroll down the module twin to find reported properties for CommandRunner. Check for resultCode: 0 which indicates the command has succeeded and textResult that shows the output of the ping command.

    Screen capture showing reported twin contents for a ping command using OSConfig module for a single device from Azure Portal

Example 2. Get contents of /etc/ssh/sshd_config

In this example we use CommandRunner to capture the contents of a file from each device. In this example we do not explicitly upload the file to any cloud storage service, but simply capture its contents in the twin.

Important

Twin property updates are limited to 4KB. The approach shown here captures the file contents (from cat) inline into the twin. For very small files this approach has the benefit of not requiring any outside storage services or credentials. For larger files this approach is not applicable. Instead, you would include logic in your script/command to upload the file to on-prem or cloud storage of your choice. For example you might connect to a cifs share and copy the file there, or push the file contents to Azure Storage.

  1. From your IoT Hub's portal page, navigate to the OSConfig twin for the device you wish to manage. Then add the following to the properties.desired section (followed by a comma to separate it from the next item in properties.desired). You can replace the filename of your choice in the arguments field below.

      "CommandRunner": {
                 "__t": "c",
                 "commandArguments": {
                     "commandId": "sshdconfig",
                     "arguments": "cat /etc/ssh/sshd_config",
                     "timeout": 30,
                     "singleLineTextResult": false,
                     "action": 3
                 }
             }
    

    Screen capture showing how to set the desired property of OSConfig module twin to read file contents from a single device using OSConfig module from Azure Portal

  2. You can see the file contents from the module twin itself. Scroll down the module twin to find commandStatus under reported properties for CommandRunner, here you will see this in the textResult. Check for resultCode: 0 which indicates the command has succeeded and textResult that will show the desired file contents.

    "CommandRunner": {
                 "__t": "c",
                 "commandStatus": {
                     "commandId": "sshdconfig",
                     "resultCode": 0,
                     "textResult": "<sshd_config_file_contents>",
                     "currentState": 2
                 }
              }   
    

    Screen capture showing OSConfig module twin that shows the requested file contents from a single device from Azure Portal

Example 3. Deploy a simple inline script to set time zone to UTC and report on time zone

This example illustrates a simple use-case where both the script and the results can be handled inline as part of the twin. The example here will set the time zone to UTC and then query for the timezone once it is set.

  1. From your IoT Hub's portal page, navigate to the OSConfig twin for the device you wish to manage, and add the following to the properties.desired section, followed by a comma to separate it from the next item in properties.desired. action=3 below specifies RunCommand action.

    "CommandRunner": {
       "__t": "c",
       "commandArguments": {
          "commandId": "settimezonecmd",
          "arguments": "timedatectl set-timezone Etc/UTC | timedatectl | grep Time",
          "timeout": 30,
          "singleLineTextResult": false,
          "action": 3
       }
    }
    

    Screen capture showing how to set desired property to update timezone on a device using OSConfig module from Azure Portal

  2. Once the command is executed, scroll down the module twin to find commandStatus under reported properties for CommandRunner, here you will see the textResult display the current timezone set on the device.

    "CommandRunner": {
       "__t": "c",
       "commandStatus": {
          "commandId": "settimezonecmd",
          "resultCode": 0,
          "textResult": " Time zone: Etc/UTC (UTC, +0000)",
          "currentState": 2
       }
    }   
    

    Screen capture showing the response of setting the timezone command on a single device from Azure Portal

Example 4. Deploy a custom reporting script from an online repository

This example illustrates calling a script located outside of the twin. For example, you might place your scripts in GitHub. This pattern can arise out of necessity (script is too big for twin), or out of preference. The command in the twin is a simple wrapper. It download the main script and runs it.

Important

To support this document, we have published a sample script. This sample script is provided as a stand-in for your own script in GitHub or elsewhere. It gathers a few data points from the device, including a time stamp, daemon status, and free disk space. You should inspect scripts from the internet, including this one, before running them on your devices.

  1. From your IoT Hub's portal page, navigate to the OSConfig twin for the device you wish to manage, and add the following to the properties.desired section, followed by a comma to separate it from the next item in properties.desired.

    "CommandRunner": {
        "__t": "c",
       "commandArguments": {
          "commandId": "runcustomscript",
          "arguments": "curl -s -L https://learn.microsoft.com/azure/osconfig/samples/report-multiple-datapoints-from-device.sh | tr -d \r| bash",
          "timeout": 60,
          "singleLineTextResult": false,
          "action": 3
       }
    }
    

    Screen capture showing how to update twin contents to run a custom script on a single device using Azure Portal

  2. Scroll down the module twin to find CommandRunner --> commandStatus in the reported properties section of the twin. Check for resultCode: 0 which indicates the command has succeeded and textResult that shows the output from the script executed. The following is example output taken from a device's OSConfig twin:

    "CommandRunner": {
       "__t": "c",
       "commandStatus": {
          "commandId": "runcustomscript",
          "resultCode": 0,
          "textResult": "+TIMESTAMP: 'Fri Jul  8 19:01:15 UTC 2022'  +DAEMON_STATUS: 'Active: active (running) since Mon 2022-06-27 19:02:46 UTC; 1 weeks 3 days ago'  +FREE_SPACE: '/dev/sda1       30309264 8811724  21481156  30% /'"
          "currentState": 2
       },
    }   
    

    Screen capture showing how to check twin contents after a custom script is run on a single device using Azure Portal

Next steps

For an overview of OSConfig scenarios and capabilities, see:

For specific practical examples, see: