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
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:
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.
You will need at least one Linux device with the OSConfig agent installed and connected to Azure IoT.
Ensure you are signed in to the Azure Portal and can access your IoT Hub's Overview page
Sign in to the Azure Portal with the account you wish to use
Launch Azure Cloud Shell in bash mode
(optional) Use the command az account show to ensure you are signed into the context you wish to use for the examples.
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.
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).
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.
From your Azure IoT Hub's portal page, choose Device management --> Configurations --> Add Module Configuration.
Specify a name and set the Twin Settings with the desired package manager configuration. Set Module Twin Property to properties.desired.CommandRunner and set the Module Twin Property Content to the following:
Create a Custom Metric with name successfulpings and the Metric Criteria as shown below. This metric query will identify all devices that have successfully pinged the endpoint.
SELECT deviceId FROM devices.modules
WHERE properties.reported.CommandRunner.commandStatus.resultCode = 0
AND properties.reported.CommandRunner.commandStatus.commandId = 'pingcmd'
For the Target Modules, specify the criteria for which devices are in scope. In this example, we will target all OSConfig-enabled devices, by specifying a Target Condition of FROM devices.modules where moduleId='osconfig'
To observe which devices' twins have been updated with the desired twin property content (cloud side), see the Applied metric (also known as appliedCount)
To observe which devices have successfully pinged the endpoint, see the custom successfulpings metric that was created above.
You can also verify the output of the ping command used in this example using the following query. From your Azure IoT Hub's portal page, choose Device management --> Queries in the left hand navigation.
SELECT deviceId, properties.reported.CommandRunner.commandStatus
FROM devices.modules
WHERE properties.reported.CommandRunner.commandStatus.commandId = 'pingcmd'
Use the following example command to set a single device's OSConfig twin, replacing <device id> and/or <hub name> to match your environment.
You can verify the device connectivity by checking the response to the ping command. Use the following example command which gets the result of the ping command, replacing <device id> and/or <hub name> to match your environment. Check for resultCode: 0 which indicates the command has succeeded and textResult that shows the output of the ping command.
az iot hub query -q \
"SELECT properties.reported.CommandRunner.commandStatus.resultCode,
properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE moduleId='osconfig'
AND deviceId='<device id>'" --hub-name <hub name> --output table
Use the following example command to create an IoT Hub Configuration to check the network connectivity for a fleet of devices connected to an IoT Hub. This Configuration includes targeting criteria, custom metrics and the desired configuration. Be sure to replace the <hub name> with your IoT Hub name.
az iot hub configuration create -c "checkconnectivity_devicefleet" --content \
'{"moduleContent":
{"properties.desired.CommandRunner":
{"commandArguments":
{"commandId": "checkconnectivity_devicefleet",
"arguments": "ping -c 3 www.github.com",
"timeout": 120,
"singleLineTextResult": false,
"action": 3
}}}}' \
--target-condition "from devices.modules where moduleId='osconfig'" --priority 10 \
--metrics "{\"metrics\": {\"queries\": {\"successfulpings\":\"select deviceId from devices.modules \
where moduleId='osconfig' AND properties.reported.CommandRunner.commandStatus.resultCode=0 \
and properties.reported.CommandRunner.commandStatus.commandId='checkconnectivity_devicefleet'\"}}}" \
--hub-name <hub name>
To observe which devices' twins have been updated with the desired twin property content (cloud side), see the Applied metric (also known as appliedCount). To observe which devices have successfully pinged the endpoint, see the custom successfulpings metric that was created above.
Use the following commands to check for both Applied and custom metrics successfulpings.
az iot hub configuration show-metric --metric-id appliedCount \
-c "checkconnectivity_devicefleet" \
-n <hub name> --metric-type system
az iot hub configuration show-metric --metric-id successfulpings \
-c "checkconnectivity_devicefleet" \
-n <hub name> --metric-type user
Using the following command, you can verify the results from the ping command for each device to see if it was successful or not. Check for resultCode: 0 which indicates the command has succeeded and textResult that shows the output of the ping command.
az iot hub query -q \
"SELECT deviceId, properties.reported.CommandRunner.commandStatus.resultCode,
properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE moduleId='osconfig'" \
--output table --hub-name <hub name> --output table
You can also verify the output of the ping command used in this example using the following query. From your Azure IoT Hub's portal page, choose Device management --> Queries in the left hand navigation. textResult from the output will show the desired file contents.
SELECT deviceId, properties.reported.CommandRunner.commandStatus
FROM devices.modules
WHERE properties.reported.CommandRunner.commandStatus.commandId = 'checkconnectivity_devicefleet'
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.
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.
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.
From your Azure IoT Hub's portal page, choose Device management --> Configurations --> Add Module Configuration.
Specify a name and set the Twin Settings with the desired package manager configuration. Set Module Twin Property to properties.desired.CommandRunner and set the Module Twin Property Content to the following:
For the Target Modules, specify the criteria for which devices are in scope. In this example, we will target all OSConfig-enabled devices, by specifying a Target Condition of FROM devices.modules where moduleId='osconfig'
To observe which devices' twins have been updated with the desired twin property content (cloud side), see the Applied metric (also known as appliedCount)
You can also read the file contents in this example using the following query. From your Azure IoT Hub's portal page, choose Device management --> Queries in the left hand navigation. Actual file contents can be seen in commandStatus.textResult.
SELECT deviceId, properties.reported.CommandRunner.commandStatus
FROM devices.modules
WHERE properties.reported.CommandRunner.commandStatus.commandId = 'sshdconfigfile_contents'
Use the following example command to initiate request (by placing your request in the properties.desired section of the twin).
Use the following command to see the results. Check for resultCode: 0 which indicates the command has succeeded and textResult that will show the desired file contents.
az iot hub query --output table -q \
"SELECT properties.reported.CommandRunner.commandStatus.resultCode,
properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE moduleId='osconfig'
AND deviceID='<device id>'" --hub-name <hub name>
Use the following example command to create an IoT Hub Configuration. This Configuration includes targeting criteria, the desired configuration. Be sure to replace hub-name with your IoT Hub name.
Use the following command to see the results. Check for resultCode: 0 which indicates the command has succeeded and textResult that shows the file contents.
az iot hub query -q \
"SELECT deviceId, properties.reported.CommandRunner.commandStatus.resultCode,
properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE moduleId='osconfig'" \
--output table --hub-name <hub name>
To observe which devices' twins have been updated with the desired twin property content (cloud side), see the Applied metric (also known as appliedCount).
Use the following command to check for the Applied metric.
az iot hub configuration show-metric --metric-id appliedCount \
-c "sshdconfigfile_contents" \
-n <hub name> --metric-type system
You can also read the file contents using the following query. From your Azure IoT Hub's portal page, choose Device management --> Queries in the left hand navigation.
SELECT deviceId, properties.reported.CommandRunner.commandStatus,
properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE properties.reported.CommandRunner.commandStatus.commandId = 'sshdconfigfile_contents'
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.
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.
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.
To set or update the timezone for a fleet of devices to UTC, as in this example, you will create an IoT Hub Configuration (also known as IoT Hub Automatic Device Management [ADM]).
From your Azure IoT Hub's portal page, choose Device management --> Configurations --> Add Module Configuration.
Specify a name and set the Twin Settings with the desired package manager configuration. Set Module Twin Property to properties.desired.CommandRunner and set the Module Twin Property Content to the following:
For the Target Modules, specify the criteria for which devices are in scope. In this example, we will target all OSConfig-enabled devices, by specifying a Target Condition of FROM devices.modules where moduleId='osconfig'
Create a Custom Metric with name successfullyconfigured and the Metric Criteria as shown below. This metric query will identify all devices that have successfully updated the timezone.
SELECT deviceId FROM devices.modules
WHERE properties.reported.CommandRunner.commandStatus.resultCode = 0
AND properties.reported.CommandRunner.commandStatus.commandId = 'settimezone_config'
Once the configuration is applied to the devices, you can verify which devices' twins have been updated with the desired twin property content (cloud side), by checking for the Applied metric (also known as appliedCount). To see which devices have successfully updated the timezone, see the custom successfullyconfigured metric that was created above.
You can also read the current timezone on these devices from this example using the following query. From your Azure IoT Hub's portal page, choose Device management --> Queries in the left hand navigation. You can check the current timezone on these devices from commandStatus.textResult.
SELECT deviceId, properties.reported.CommandRunner.commandStatus
FROM devices.modules
WHERE properties.reported.CommandRunner.commandStatus.commandId = 'settimezone_config'
Use the following example to set the timezone on the device and then read the current timezone. action=3 below specifies RunCommand action. replacing <device id> and/or <hub name> to match your environment..
You can query the result of the above command and check the current timezone on the device using the following example. Check for resultCode: 0 which indicates the command has succeeded and textResult will show the current timezone.
az iot hub query -q \
"SELECT properties.reported.CommandRunner.commandStatus.resultCode,
properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE moduleId='osconfig'
AND deviceID='<device id>'" --hub-name <hub name> --output table
Use the following example command to create an IoT Hub Configuration. This Configuration includes targeting criteria, the desired configuration. Be sure to replace hub name with your IoT Hub name.
az iot hub configuration create -c "settimezone_config" --content \
'{"moduleContent":
{"properties.desired.CommandRunner":
{"commandArguments":
{"commandId": "settimezone_cmd",
"arguments": "timedatectl set-timezone Etc/UTC | timedatectl | grep Time",
"timeout": 30,
"singleLineTextResult": false,
"action": 3
}}}}' \
--target-condition "from devices.modules where moduleId='osconfig'" --priority 10 \
--metrics "{\"metrics\": {\"queries\": {\"successfullyconfigured\":\"select deviceId from devices.modules \
where moduleId='osconfig' AND properties.reported.CommandRunner.commandStatus.resultCode=0 \
and properties.reported.CommandRunner.commandStatus.commandId='settimezone_cmd'\"}}}" \
--hub-name <hub-name>
You can query that the timezone is set on the devices using the following example. The current timezone can be seen in commandStatus.textResult.
az iot hub query -q \
"SELECT deviceId, properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE moduleId='osconfig'" \
--output table --hub-name <hub name>
To observe which devices' twins have been updated with the desired twin property content (cloud side), see the Applied metric (also known as appliedCount). To observe which devices have successfully pinged the endpoint, see the custom successfullyconfigured metric that was created above.
Use the following commands to check for both these metrics.
az iot hub configuration show-metric --metric-id appliedCount \
-c "settimezone_config" \
-n <hub name> --metric-type system
az iot hub configuration show-metric --metric-id successfullyconfigured \
-c "settimezone_config" \
-n <hub name> --metric-type user
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.
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.
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:
To download and install an app or execute a script on a fleet of devices, as in this example, you will create an IoT Hub Configuration (also known as IoT Hub Automatic Device Management [ADM]).
From your Azure IoT Hub's portal page, choose Device management --> Configurations --> Add Module Configuration.
Specify a name and set the Twin Settings with the desired package manager configuration. Set Module Twin Property to properties.desired.CommandRunner and set the Module Twin Property Content to the following.
For the Target Modules, specify the criteria for which devices are in scope. In this example, we will target all OSConfig-enabled devices, by specifying a Target Condition of FROM devices.modules where moduleId='osconfig'
Create a Custom Metric with name successfullyrun and the Metric Criteria as shown below. This metric query will identify all devices where the script was successfully run or executed.
SELECT deviceId
FROM devices.modules
WHERE properties.reported.CommandRunner.commandStatus.resultCode = 0
AND properties.reported.CommandRunner.commandStatus.commandId = 'runcustomscript_configuration'
Once the configuration is applied to the devices, you can verify which devices' twins have been updated with the desired twin property content (cloud side), by checking for the Applied metric (also known as appliedCount). To see which devices have successfully updated the timezone, see the custom successfullyrun metric that was created above.
You can also check that the above configuration was successfully applied and that the install was successful by using the following query. From your Azure IoT Hub's portal page, choose Device management --> Queries in the left hand navigation. Check for resultCode: 0 which indicates the command has succeeded and textResult that shows the output from the script.
SELECT deviceId, properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE properties.reported.CommandRunner.commandStatus.commandId = 'runcustomscript_configuration'
The following example shows the download and execution of a simple script on a specific device. replacing <device id> and/or <hub name> to match your environment..
You can query the result of the above script on the device using the following example. Check for resultCode: 0 which indicates the command has succeeded and textResult that shows the output of the script.
az iot hub query -q \
"SELECT properties.reported.CommandRunner.commandStatus.resultCode,
properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE moduleId='osconfig' AND properties.reported.CommandRunner.commandStatus.commandId = 'runcustomscript'
AND deviceId = '<device id>'" \
--hub-name <hub name> --output table
Use the following example command to create an IoT Hub Configuration to download and run a custom script on multiple devices. This Configuration includes targeting criteria and the desired configuration. Be sure to replace hub name with your IoT Hub name.
az iot hub configuration create -c "runcustomscript_multipledevices" --content \
'{"moduleContent":
{"properties.desired.CommandRunner":
{"commandArguments":
{"commandId": "runcustomscript_multipledevices",
"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
}}}}' \
--target-condition "from devices.modules where moduleId='osconfig'" --priority 10 \
--metrics "{\"metrics\": {\"queries\": {\"successfulruns\":\"select deviceId from devices.modules \
where moduleId='osconfig' AND properties.reported.CommandRunner.commandStatus.resultCode=0 \
and properties.reported.CommandRunner.commandStatus.commandId='runcustomscript_multipledevices'\"}}}" \
--hub-name <hub-name>
You can verify the result of the script execution on the devices using the following example. Check for resultCode: 0 which indicates the command has succeeded and textResult that shows the output of the script executed.
az iot hub query -q \
"SELECT deviceId, properties.reported.CommandRunner.commandStatus.resultCode,
properties.reported.CommandRunner.commandStatus.textResult
FROM devices.modules
WHERE moduleId='osconfig' AND properties.reported.CommandRunner.commandStatus.commandId = 'runcustomscript_multipledevices'" \
--output table --hub-name <hub name>
Next steps
For an overview of OSConfig scenarios and capabilities, see: