Skip to content

[IoT Hub] Manage IoT device module twin configuration. #11480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/IotHub/IotHub.Test/ScenarioTests/IotHubDPModuleTests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,34 @@ function Test-AzureRmIotHubModuleLifecycle
Assert-True { $newModule2.DeviceId -eq $device1 }
Assert-True { $newModule2.Authentication.Type -eq 'SelfSigned' }

# Get module twin
$module1twin = Get-AzIotHubModuleTwin -ResourceGroupName $ResourceGroupName -IotHubName $IotHubName -DeviceId $device1 -ModuleId $module1
Assert-True { $module1twin.DeviceId -eq $device1}
Assert-True { $module1twin.ModuleId -eq $module1}

# Partial update module twin
$tags1 = @{}
$tags1.Add('Test1', '1')
$updatedmodule1twin1 = Update-AzIotHubModuleTwin -ResourceGroupName $ResourceGroupName -IotHubName $IotHubName -DeviceId $device1 -ModuleId $module1 -tag $tags1 -Partial
Assert-True { $updatedmodule1twin1.DeviceId -eq $device1}
Assert-True { $updatedmodule1twin1.ModuleId -eq $module1}
Assert-True { $updatedmodule1twin1.tags.Count -eq 1}

$tags2 = @{}
$tags2.Add('Test2', '2')
$updatedmodule1twin2 = Update-AzIotHubModuleTwin -ResourceGroupName $ResourceGroupName -IotHubName $IotHubName -DeviceId $device1 -ModuleId $module1 -tag $tags2 -Partial
Assert-True { $updatedmodule1twin2.DeviceId -eq $device1}
Assert-True { $updatedmodule1twin2.ModuleId -eq $module1}
Assert-True { $updatedmodule1twin2.tags.Count -eq 2}

# Update module twin
$tags3 = @{}
$tags3.Add('Test3', '3')
$updatedmodule1twin3 = Update-AzIotHubModuleTwin -ResourceGroupName $ResourceGroupName -IotHubName $IotHubName -DeviceId $device1 -ModuleId $module1 -tag $tags3
Assert-True { $updatedmodule1twin3.DeviceId -eq $device1}
Assert-True { $updatedmodule1twin3.ModuleId -eq $module1}
Assert-True { $updatedmodule1twin3.tags.Count -eq 1}

# Get all modules
$modules = Get-AzIotHubModule -ResourceGroupName $ResourceGroupName -IotHubName $IotHubName -DeviceId $device1
Assert-True { $modules.Count -eq 2}
Expand Down

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/IotHub/IotHub/Az.IotHub.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ CmdletsToExport = 'Add-AzIotHubKey', 'Get-AzIotHubEventHubConsumerGroup',
'Set-AzIotHubDeviceParent', 'Add-AzIotHubDeviceChildren',
'Remove-AzIotHubDeviceChildren', 'Get-AzIotHubDeviceChildren',
'Get-AzIotHubDistributedTracing', 'Set-AzIotHubDistributedTracing',
'Get-AzIotHubDeviceTwin', 'Update-AzIotHubDeviceTwin', 'Invoke-AzIotHubDeviceMethod'
'Get-AzIotHubDeviceTwin', 'Update-AzIotHubDeviceTwin', 'Invoke-AzIotHubDeviceMethod',
'Get-AzIotHubModuleTwin', 'Update-AzIotHubModuleTwin'
# Variables to export from this module
# VariablesToExport = @()

Expand Down
3 changes: 3 additions & 0 deletions src/IotHub/IotHub/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
- `Get-AzIotHubDeviceTwin`
- `Update-AzIotHubDeviceTwin`
* Added cmdlet to invoke direct method on a device in an Iot Hub.
* Manage IoT device module twin configuration, New cmdlets are:
- `Get-AzIotHubModuleTwin`
- `Update-AzIotHubModuleTwin`

## Version 2.3.0
* Added support to manage distributed settings per-device. New Cmdlets are:
Expand Down
5 changes: 5 additions & 0 deletions src/IotHub/IotHub/Common/IotHubDataPlaneUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ public static PSDeviceTwin ToPSDeviceTwin(Twin deviceTwin)
return IotHubUtils.ConvertObject<Twin, PSDeviceTwin>(deviceTwin);
}

public static PSModuleTwin ToPSModuleTwin(Twin moduleTwin)
{
return IotHubUtils.ConvertObject<Twin, PSModuleTwin>(moduleTwin);
}

public static void ValidateDeviceTracing(string DeviceId, string Sku, string Location, bool IsEdgeDevice)
{
if (!TracingAllowedForLocation.Any(location => location.Equals(Location, StringComparison.OrdinalIgnoreCase)))
Expand Down
40 changes: 40 additions & 0 deletions src/IotHub/IotHub/IotHub.format.ps1xml
Original file line number Diff line number Diff line change
Expand Up @@ -987,5 +987,45 @@
</ListEntries>
</ListControl>
</View>
<View>
<Name>
Microsoft.Azure.Commands.Management.IotHub.Models.PSModuleTwin
</Name>
<ViewSelectedBy>
<TypeName>Microsoft.Azure.Commands.Management.IotHub.Models.PSModuleTwin</TypeName>
</ViewSelectedBy>
<ListControl>
<ListEntries>
<ListEntry>
<ListItems>
<ListItem>
<PropertyName>ModuleId</PropertyName>
</ListItem>
<ListItem>
<PropertyName>DeviceId</PropertyName>
</ListItem>
<ListItem>
<Label>Tags</Label>
<ScriptBlock>$_.Tags</ScriptBlock>
</ListItem>
<ListItem>
<Label>Desired</Label>
<ScriptBlock>$_.Properties.Desired</ScriptBlock>
</ListItem>
<ListItem>
<Label>Reported</Label>
<ScriptBlock>$_.Properties.Reported</ScriptBlock>
</ListItem>
<ListItem>
<PropertyName>ETag</PropertyName>
</ListItem>
<ListItem>
<PropertyName>Version</PropertyName>
</ListItem>
</ListItems>
</ListEntry>
</ListEntries>
</ListControl>
</View>
</ViewDefinitions>
</Configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ public override void ExecuteCmdlet()

if (this.Partial.IsPresent)
{
this.WriteObject(registryManager.UpdateTwinAsync(this.DeviceId, deviceTwin, deviceTwin.ETag).GetAwaiter().GetResult());
this.WriteObject(IotHubDataPlaneUtils.ToPSDeviceTwin(registryManager.UpdateTwinAsync(this.DeviceId, deviceTwin, deviceTwin.ETag).GetAwaiter().GetResult()));
}
else
{
this.WriteObject(registryManager.ReplaceTwinAsync(this.DeviceId, deviceTwin, deviceTwin.ETag).GetAwaiter().GetResult());
this.WriteObject(IotHubDataPlaneUtils.ToPSDeviceTwin(registryManager.ReplaceTwinAsync(this.DeviceId, deviceTwin, deviceTwin.ETag).GetAwaiter().GetResult()));
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions src/IotHub/IotHub/IotHub/DataPlane/Models/PSModuleTwin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

namespace Microsoft.Azure.Commands.Management.IotHub.Models
{
/// <summary>
/// Device's Module Twin Representation.
/// </summary>
public class PSModuleTwin : PSDeviceTwin
{
/// <summary>
/// Module ID
/// </summary>
public string ModuleId { get; set; }
}
}
99 changes: 99 additions & 0 deletions src/IotHub/IotHub/IotHub/DataPlane/Module/GetAzIotHubModuleTwin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

namespace Microsoft.Azure.Commands.Management.IotHub
{
using System;
using System.Collections.Generic;
using System.Management.Automation;
using Microsoft.Azure.Commands.Management.IotHub.Common;
using Microsoft.Azure.Commands.Management.IotHub.Models;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
using Microsoft.Azure.Management.IotHub;
using Microsoft.Azure.Management.IotHub.Models;
using ResourceManager.Common.ArgumentCompleters;

[Cmdlet("Get", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "IotHubModuleTwin", DefaultParameterSetName = ResourceParameterSet)]
[OutputType(typeof(PSModuleTwin))]
public class GetAzIotHubModuleTwin : IotHubBaseCmdlet
{
private const string ResourceIdParameterSet = "ResourceIdSet";
private const string ResourceParameterSet = "ResourceSet";
private const string InputObjectParameterSet = "InputObjectSet";

[Parameter(Position = 0, Mandatory = true, ParameterSetName = InputObjectParameterSet, ValueFromPipeline = true, HelpMessage = "IotHub object")]
[ValidateNotNullOrEmpty]
public PSIotHub InputObject { get; set; }

[Parameter(Position = 0, Mandatory = true, ParameterSetName = ResourceParameterSet, HelpMessage = "Name of the Resource Group")]
[ValidateNotNullOrEmpty]
[ResourceGroupCompleter]
public string ResourceGroupName { get; set; }

[Parameter(Position = 0, Mandatory = true, ParameterSetName = ResourceIdParameterSet, ValueFromPipelineByPropertyName = true, HelpMessage = "IotHub Resource Id")]
[ValidateNotNullOrEmpty]
[ResourceIdCompleter("Microsoft.Devices/IotHubs")]
public string ResourceId { get; set; }

[Parameter(Position = 1, Mandatory = true, ParameterSetName = ResourceParameterSet, HelpMessage = "Name of the Iot Hub")]
[ValidateNotNullOrEmpty]
public string IotHubName { get; set; }

[Parameter(Position = 1, Mandatory = true, ParameterSetName = InputObjectParameterSet, HelpMessage = "Target Device Id.")]
[Parameter(Position = 1, Mandatory = true, ParameterSetName = ResourceIdParameterSet, HelpMessage = "Target Device Id.")]
[Parameter(Position = 2, Mandatory = true, ParameterSetName = ResourceParameterSet, HelpMessage = "Target Device Id.")]
[ValidateNotNullOrEmpty]
public string DeviceId { get; set; }

[Parameter(Mandatory = true, HelpMessage = "Target Module Id.")]
[ValidateNotNullOrEmpty]
public string ModuleId { get; set; }

public override void ExecuteCmdlet()
{
IotHubDescription iotHubDescription;
if (ParameterSetName.Equals(InputObjectParameterSet))
{
this.ResourceGroupName = this.InputObject.Resourcegroup;
this.IotHubName = this.InputObject.Name;
iotHubDescription = IotHubUtils.ConvertObject<PSIotHub, IotHubDescription>(this.InputObject);
}
else
{
if (ParameterSetName.Equals(ResourceIdParameterSet))
{
this.ResourceGroupName = IotHubUtils.GetResourceGroupName(this.ResourceId);
this.IotHubName = IotHubUtils.GetIotHubName(this.ResourceId);
}

iotHubDescription = this.IotHubClient.IotHubResource.Get(this.ResourceGroupName, this.IotHubName);
}

IEnumerable<SharedAccessSignatureAuthorizationRule> authPolicies = this.IotHubClient.IotHubResource.ListKeys(this.ResourceGroupName, this.IotHubName);
SharedAccessSignatureAuthorizationRule policy = IotHubUtils.GetPolicy(authPolicies, PSAccessRights.RegistryWrite);
PSIotHubConnectionString psIotHubConnectionString = IotHubUtils.ToPSIotHubConnectionString(policy, iotHubDescription.Properties.HostName);
RegistryManager registryManager = RegistryManager.CreateFromConnectionString(psIotHubConnectionString.PrimaryConnectionString);

Twin moduleTwin = registryManager.GetTwinAsync(this.DeviceId, this.ModuleId).GetAwaiter().GetResult();

if (moduleTwin == null)
{
throw new ArgumentException($"The entered module \"{this.ModuleId}\" doesn't exist.");
}

this.WriteObject(IotHubDataPlaneUtils.ToPSModuleTwin(moduleTwin));
}
}
}
133 changes: 133 additions & 0 deletions src/IotHub/IotHub/IotHub/DataPlane/Module/UpdateAzIotHubModuleTwin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

namespace Microsoft.Azure.Commands.Management.IotHub
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Management.Automation;
using Microsoft.Azure.Commands.Management.IotHub.Common;
using Microsoft.Azure.Commands.Management.IotHub.Models;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
using Microsoft.Azure.Management.IotHub;
using Microsoft.Azure.Management.IotHub.Models;
using Microsoft.WindowsAzure.Commands.Utilities.Common;
using Newtonsoft.Json;
using ResourceManager.Common.ArgumentCompleters;

[Cmdlet("Update", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "IotHubModuleTwin", DefaultParameterSetName = ResourceParameterSet, SupportsShouldProcess = true)]
[OutputType(typeof(PSModuleTwin))]
public class UpdateAzIotHubModuleTwin : IotHubBaseCmdlet
{
private const string ResourceIdParameterSet = "ResourceIdSet";
private const string ResourceParameterSet = "ResourceSet";
private const string InputObjectParameterSet = "InputObjectSet";

[Parameter(Position = 0, Mandatory = true, ParameterSetName = InputObjectParameterSet, ValueFromPipeline = true, HelpMessage = "IotHub object")]
[ValidateNotNullOrEmpty]
public PSIotHub InputObject { get; set; }

[Parameter(Position = 0, Mandatory = true, ParameterSetName = ResourceParameterSet, HelpMessage = "Name of the Resource Group")]
[ValidateNotNullOrEmpty]
[ResourceGroupCompleter]
public string ResourceGroupName { get; set; }

[Parameter(Position = 0, Mandatory = true, ParameterSetName = ResourceIdParameterSet, ValueFromPipelineByPropertyName = true, HelpMessage = "IotHub Resource Id")]
[ValidateNotNullOrEmpty]
[ResourceIdCompleter("Microsoft.Devices/IotHubs")]
public string ResourceId { get; set; }

[Parameter(Position = 1, Mandatory = true, ParameterSetName = ResourceParameterSet, HelpMessage = "Name of the Iot Hub")]
[ValidateNotNullOrEmpty]
public string IotHubName { get; set; }

[Parameter(Position = 1, Mandatory = true, ParameterSetName = InputObjectParameterSet, HelpMessage = "Target Device Id.")]
[Parameter(Position = 1, Mandatory = true, ParameterSetName = ResourceIdParameterSet, HelpMessage = "Target Device Id.")]
[Parameter(Position = 2, Mandatory = true, ParameterSetName = ResourceParameterSet, HelpMessage = "Target Device Id.")]
[ValidateNotNullOrEmpty]
public string DeviceId { get; set; }

[Parameter(Mandatory = true, HelpMessage = "Target Module Id.")]
[ValidateNotNullOrEmpty]
public string ModuleId { get; set; }

[Parameter(Mandatory = false, HelpMessage = "Add or update the tags property in a module twin.")]
[ValidateNotNullOrEmpty]
public Hashtable Tag { get; set; }

[Parameter(Mandatory = false, HelpMessage = "Add or update the desired property in a module twin.")]
[ValidateNotNullOrEmpty]
public Hashtable Desired { get; set; }

[Parameter(Mandatory = false, HelpMessage = "Allows to only partially update the tags and desired properties of a module twin.")]
public SwitchParameter Partial { get; set; }

public override void ExecuteCmdlet()
{
if (ShouldProcess(this.IotHubName, Properties.Resources.UpdateIotHubModuleTwin))
{
IotHubDescription iotHubDescription;
if (ParameterSetName.Equals(InputObjectParameterSet))
{
this.ResourceGroupName = this.InputObject.Resourcegroup;
this.IotHubName = this.InputObject.Name;
iotHubDescription = IotHubUtils.ConvertObject<PSIotHub, IotHubDescription>(this.InputObject);
}
else
{
if (ParameterSetName.Equals(ResourceIdParameterSet))
{
this.ResourceGroupName = IotHubUtils.GetResourceGroupName(this.ResourceId);
this.IotHubName = IotHubUtils.GetIotHubName(this.ResourceId);
}

iotHubDescription = this.IotHubClient.IotHubResource.Get(this.ResourceGroupName, this.IotHubName);
}

IEnumerable<SharedAccessSignatureAuthorizationRule> authPolicies = this.IotHubClient.IotHubResource.ListKeys(this.ResourceGroupName, this.IotHubName);
SharedAccessSignatureAuthorizationRule policy = IotHubUtils.GetPolicy(authPolicies, PSAccessRights.RegistryWrite);
PSIotHubConnectionString psIotHubConnectionString = IotHubUtils.ToPSIotHubConnectionString(policy, iotHubDescription.Properties.HostName);
RegistryManager registryManager = RegistryManager.CreateFromConnectionString(psIotHubConnectionString.PrimaryConnectionString);

Twin moduleTwin = registryManager.GetTwinAsync(this.DeviceId, this.ModuleId).GetAwaiter().GetResult();

if (moduleTwin == null)
{
throw new ArgumentException($"The entered module \"{this.ModuleId}\" doesn't exist.");
}

if (this.IsParameterBound(c => c.Tag))
{
moduleTwin.Tags = new TwinCollection(JsonConvert.SerializeObject(this.Tag));
}

if (this.IsParameterBound(c => c.Desired))
{
moduleTwin.Properties.Desired = new TwinCollection(JsonConvert.SerializeObject(this.Desired));
}

if (this.Partial.IsPresent)
{
this.WriteObject(IotHubDataPlaneUtils.ToPSModuleTwin(registryManager.UpdateTwinAsync(this.DeviceId, this.ModuleId, moduleTwin, moduleTwin.ETag).GetAwaiter().GetResult()));
}
else
{
this.WriteObject(IotHubDataPlaneUtils.ToPSModuleTwin(registryManager.ReplaceTwinAsync(this.DeviceId, this.ModuleId, moduleTwin, moduleTwin.ETag).GetAwaiter().GetResult()));
}
}
}
}
}
Loading