This post was most recently updated on July 26th, 2024.
5 min read.This article describes the gotchas and weird one-offs that I’ve encountered with Azure IoT Edge so far. Consider them not-too-thoroughly tested quick fixes that aren’t significant enough to warrant an actual blog post themselves :)
Okay – let’s get to them gotchas, then!
Random learnings from Azure IoT Edge projects
See the sample below:az iot edge deployment create -d 'unique-deployment-name' -n 'iot-hub-name' --content 'path-to-your-layered-deployment-manifest-file.json' --layered --target-condition "tags.location='stockholm'" --priority 10
You can’t modify the content, but you can tweak labels and the target condition.
See the sample below:az iot edge deployment update -d 'unique-deployment-name' -n 'iot-hub-name' --set targetCondition="tags.location='stockholm' AND tags.environment='dev'" --priority 10
I can’t be the only one googling for this, right? :)
So here it goes (in case WordPress messes up the structure, see at the end of the page for a better copy-pasteable version of the az cli task for Azure IoT Edge deployment using Azure DevOps):task: AzureCLI@2
displayName: Create Edge Deployment using AZ CLI
inputs:
azureSubscription: $(serviceConnection)
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: |
az config set extension.use_dynamic_install=yes_without_prompt
az account set --subscription "subName"
$TargetCondition = "(tags.environment='dev' AND tags.location='stockholm')"
az iot edge deployment create -d 'unique-deployment-name' -n 'iot-hub-name' --content 'path-to-your-layered-deployment-manifest-file.json' --layered --target-condition "$TargetCondition" --priority 10
You don’t. Unless you define a new base/single-device deployment, that is.
You can, however, create a new layered deployment that’ll shut the module down, but I don’t think you can remove it.
Been there, done that. It doesn’t look like IoT Edge validates your manifest beyond some very rudimentary schema checks before trying to apply it.
This will render your Edge device looking somewhat like this in Azure Portal, and configuring it becomes impossible:
Long story short – you’ve got a couple of options:
1. Remove your borked deployment (az iot edge deployment delete -d ‘[deployment-id]’)
2. Fix your environment using one of the available options:
2.1 Deploy a WORKING base/single-device manifest. This’ll overwrite everything you had before.
2.2 Go to “Set Modules” -> “Runtime” and fix your $edgeAgent and $edgeHub configuration (compare with a working device)
2.3 Theoretically, a single-device deployment from VS Code should also overwrite the broken configuration with a functional one, but I haven’t actually verified this.
One of the biggest differences between the schemas of the manifest file for “full” deployment (single-device or base) is the fact the createOptions needs to be an escaped string, not JSON.
So instead of having:createOptions: {}
You need to have:createOptions: "{}"
And obviously adjust any contents to be a properly escaped, stringified JSON. Yeah, that’s going to be a lot of wacks.
Okay, so this one is a bit complicated, but bear with me. What I’ve found out so far – things change depending on whether you’re using PowerShell, bash, or perhaps the az cli task in Azure DevOps, but the basic idea is this:
1) JMESPath usually shows all samples using backticks – that’s this guy: `
2) Values inside backticks are evaluated properly, so comparison operators like this work:--target-condition "tags.priority > `5`"
Without the backticks – or sometimes double backticks – numeric comparison doesn’t seem to work in PowerShell or any Azure DevOps tasks I’ve tried.
3) Doubleticks to the rescue
THAT SAID, backticks don’t always work and your JMESPath queries might fail to parse or produce unexpected results. In that case, try double-backticks. Something like this:--target-condition "tags.priority > ``5``"
4) Single quotes usually work too
All THAT said, you can actually just use single quotes like 99% of the time. String comparisons and such work just fine.
5) Last weird difference – property names with dashes
This is the last weird detail I’ve found – if you have a property name, that has a dash (this one: – ) in it, you’ll need to surround that particular property name with double quotes – and since that probably happens inside a target condition or similar, it’ll be enclosed in quotes already – so your quotes need to be escaped.
So something like this:
--target-condition "\"tags.item-priority\" > ``5``"
This is very unintuitive, but to get Message Routing rules to work for any $body-based routing rules, you’ll need to get the Payload to be rendered as JSON, not a string. But if you just follow instructions or tutorials, it’ll be handled as a string.
See – doing this:var TableName = "table01";
var PartitionKey = "key01";
string messagePayload = $"{{\"tablename\":\"{TableName}\",\"partitionkey\":\"{PartitionKey}\"}}";
using var eventMessage = new Microsoft.Azure.Devices.Client.Message(Encoding.UTF8.GetBytes(messagePayload));
await moduleClient.SendEventAsync(eventMessage);
This will result in this:{
"event": {
"origin": "Simulator",
"module": "ContosoModule",
"interface": "",
"component": "",
"payload": "{\"tablename\":\"table01\",\"partitionkey\":\"key01\"}"
}
}
Routing rules based on the payload – for example $body.tablename == “table01” will not work.
Worse yet, even trying to do it properly and sending an object like this:JsonMessage newMsg = new JsonMessage()
{
tablename = "table01",
partitionkey = "key01",
};
string payload = JsonConvert.SerializeObject(newMsg);
using var eventMessage = new Microsoft.Azure.Devices.Client.Message(Encoding.UTF8.GetBytes(payload));
await moduleClient.SendEventAsync(eventMessage);
Might not work (well, it didn’t for us). It’s still a string, not JSON.
However, adding this should help:eventMessage.ContentEncoding = Encoding.UTF8.ToString(),
eventMessage.ContentType = "application/json"
When the content type of the message is properly configured as application/json, it should render successfully.
And with that, it should look like this:{
"event":{
"origin":"Simulator",
"module":"ContosoModule",
"interface":"",
"component":"",
"payload":{
"tablename":"table01",
"partitionkey":"key01"
}
}
}
Fire up your terminal of choice, make sure you have your Azure CLI installed, and run this:az iot hub monitor-events --hub-name yourhubname d *simulator
In the example, we’re filtering for messages coming from devices with names ending in “simulator”.
Or if you don’t want to log in, select your Azure IoT Hub, browse to Shared Access Policies, and select a connection string for a policy that can listen:az iot hub monitor-events --login "HostName=the-rest-of-your-iot-hub-shared-access-policy-connection-string-here" -d *simulator
Note, however, that this monitors the default endpoint, so any successfully routed messages won’t show up here!
If this FAQ didn’t answer your question, see my other Azure IoT Edge-related posts below!
-
How to roll back an Azure IoT Edge layered deployment?
-
Azure IoT Edge order of deployment operations
-
How to configure Azure IoT Edge deployments in Azure DevOps pipeline?
References and Appendices
Appendix 1 – Azure DevOps az cli deployment task
The sample below will should you an example of an Azure IoT Edge deployment using Azure CLI.
task: AzureCLI@2
displayName: Create Edge Deployment using AZ CLI
inputs:
azureSubscription: $(serviceConnection)
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: |
az config set extension.use_dynamic_install=yes_without_prompt
az account set --subscription "subName"
$TargetCondition = "(tags.environment='dev' AND tags.location='stockholm')"
az iot edge deployment create -d 'unique-deployment-name' -n 'iot-hub-name' --content 'path-to-your-layered-deployment-manifest-file.json' --layered --target-condition "$TargetCondition" --priority 10
References
- Some useful links for investigating Azure IoT Hub message payloads:
- https://stackoverflow.com/questions/68288169/how-to-send-a-json-object-instead-of-a-string-with-azure-client-sdk
- https://stackoverflow.com/questions/65409790/what-is-a-useful-azure-iot-hub-json-message-structure-for-consumption-in-time-se
- https://docs.microsoft.com/en-us/cli/azure/iot/hub?view=azure-cli-latest#az-iot-hub-monitor-events
- “Phone Link” permanently disabled and Windows claiming “Some of these settings are managed by your organization”? (Probably) an easy fix! - December 3, 2024
- Refreshing DefaultAzureCredential in Visual Studio - November 26, 2024
- M365 Copilot claiming “You have turned off web search in the work mode”? Easy fix! - November 19, 2024