0

I'm trying to change the "status" (running or stopped) and the "restartPolicy" of an IoT Edge module programmatically in order to stop a module without having to recreate the whole deployment for the Device.

I've seen that the Edge Agent's Twin has the modules' deployment information in his desiredProperties and I've tried to patch that by using the following code (which uses the Microsoft.Azure.Devices NuGet packages)

   public async Task ShutdownModule(string deviceId, string moduleId)
    {
        var twinEdgeAgent = await _registryManager.GetTwinAsync(deviceId, "$edgeAgent");

        var patchJson = $"{{\"properties\":{{\"desired\":{{\"modules\":{{\"{moduleId}\":{{\"status\": \"stopped\", \"restartPolicy\": \"never\"}}}}}}}}}}";
        await _registryManager.UpdateTwinAsync(deviceId, "$edgeAgent", patchJson, twinEdgeAgent.ETag);
    } 

Unfortunately this doesn't work and I'm getting an UnauthorizedException with the message "ErrorCode:SystemModuleModifyUnauthorizedAccess;Unauthorized to modify reserved module.". It looks like that I can't change the desired properties of the Edge Agent module.

Is there a way to change this property without having to recreate the whole deployment JSON, or at least is there a way to get this deployment JSON so that I can modify the properties I need to change?

Gimly
  • 5,975
  • 3
  • 40
  • 75
  • Did you try the management api? The management URI used by the Edge Agent and 'iotedge' CLI to start, stop, and manage modules. So may be you can try calling this api https://github.com/Azure/iotedge/blob/master/edgelet/management/docs/ModuleApi.md#stop_module – iAviator Nov 21 '20 at 19:41
  • @iAviator is there a way to access this API from the "outside"? I have the impression that this API is accessible from within the IoT Edge "network", but not from the cloud. I'm trying to shutdown a module from the cloud. – Gimly Nov 23 '20 at 06:50
  • Its module specific. How about you trigger a direct method from cloud and call this api from your module? – iAviator Nov 23 '20 at 17:44
  • @iAviator thanks for your help, I ended up using the deployment API and redeploy everything, see my answer for my solution. – Gimly Nov 26 '20 at 08:46

1 Answers1

1

I managed to do it through the deployment API by first reconstructing the existing deployment through the EdgeAgent, EdgeHub and all modules twins.

Here's a resume of the method I ended up writing:

var modulesContent = new Dictionary<string, IDictionary<string, object>>();

var twinEdgeAgent = await _registryManager.GetTwinAsync(deviceId, "$edgeAgent");
var agentModules = twinEdgeAgent.Properties.Desired[ModulesJsonPropertyName];

agentModules[myModuleId]["status"] = "stopped";
agentModules[myModuleId]["restartPolicy"] = "never";

var desiredProperties = twinEdgeAgent.GetDesiredPropertiesDictionary();

modulesContent.Add("$edgeAgent", edgeHubDesiredProperties);

var twinEdgeHub = await _registryManager.GetTwinAsync(deviceId, "$edgeHub");
var edgeHubDesiredProperties = twinEdgeHub.GetDesiredPropertiesDictionary();
modulesContent.Add("$edgeHub", edgeHubDesiredProperties);

// foreach modules contained in agentModules also add 
// the module's twin desired properties in the dictionary (not shown for brevity)

await _registryManager.ApplyConfigurationContentOnDeviceAsync(
            deviceId,
            new ConfigurationContent { ModulesContent = modulesContent });

internal static class TwinExtensions
{
    private const string DesiredPropertiesAttribute = "properties.desired";

    public static IDictionary<string, object> GetDesiredPropertiesDictionary(this Twin twin)
    {
        if (twin == null)
        {
            throw new ArgumentNullException(nameof(twin));
        }

        var twinDesiredProperties = twin.Properties.Desired;
        twinDesiredProperties.ClearMetadata();

        var twinDesiredPropertiesDictionary =
            JsonConvert.DeserializeObject<Dictionary<string, object>>(twinDesiredProperties.ToJson());

        return new Dictionary<string, object> {{DesiredPropertiesAttribute, twinDesiredPropertiesDictionary}};
    }
}

Maybe there's a better / simpler solution but we are using a similar approach to automate the upgrade of the module's runtime image and a few other things, so I was able to regroup all those changes in the same code.

It would be greatly simplified if there was a way to get the deployment JSON directly but I didn't find any.

Gimly
  • 5,975
  • 3
  • 40
  • 75