Skip to content

Commit

Permalink
Merge pull request #11425 from anusapan/iot-device-twin
Browse files Browse the repository at this point in the history
[IoT Hub] Manage IoT device twin configuration.
  • Loading branch information
msJinLei authored Mar 27, 2020
2 parents 641a4be + d59a6e9 commit 5dcd3c7
Show file tree
Hide file tree
Showing 15 changed files with 1,757 additions and 428 deletions.
24 changes: 24 additions & 0 deletions src/IotHub/IotHub.Test/ScenarioTests/IotHubDPDeviceTests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,30 @@ function Test-AzureRmIotHubDeviceLifecycle
Assert-True { $newDevice3.Authentication.Type -eq 'CertificateAuthority' }
Assert-False { $newDevice3.Capabilities.IotEdge }

# Get device twin
$device1twin = Get-AzIotHubDeviceTwin -ResourceGroupName $ResourceGroupName -IotHubName $IotHubName -DeviceId $device1
Assert-True { $device1twin.DeviceId -eq $device1}

# Partial update device twin
$tags1 = @{}
$tags1.Add('Test1', '1')
$updateddevice1twin1 = Update-AzIotHubDeviceTwin -ResourceGroupName $ResourceGroupName -IotHubName $IotHubName -DeviceId $device1 -tag $tags1 -Partial
Assert-True { $updateddevice1twin1.DeviceId -eq $device1}
Assert-True { $updateddevice1twin1.tags.Count -eq 1}

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

# Update device twin
$tags3 = @{}
$tags3.Add('Test3', '3')
$updateddevice1twin3 = Update-AzIotHubDeviceTwin -ResourceGroupName $ResourceGroupName -IotHubName $IotHubName -DeviceId $device1 -tag $tags3
Assert-True { $updateddevice1twin3.DeviceId -eq $device1}
Assert-True { $updateddevice1twin3.tags.Count -eq 1}

# Get all devices
$devices = Get-AzIotHubDevice -ResourceGroupName $ResourceGroupName -IotHubName $IotHubName
Assert-True { $devices.Count -eq 3}
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 @@ -100,7 +100,8 @@ CmdletsToExport = 'Add-AzIotHubKey', 'Get-AzIotHubEventHubConsumerGroup',
'Get-AzIotHubModuleConnectionString', 'Get-AzIotHubDeviceParent',
'Set-AzIotHubDeviceParent', 'Add-AzIotHubDeviceChildren',
'Remove-AzIotHubDeviceChildren', 'Get-AzIotHubDeviceChildren',
'Get-AzIotHubDistributedTracing', 'Set-AzIotHubDistributedTracing'
'Get-AzIotHubDistributedTracing', 'Set-AzIotHubDistributedTracing',
'Get-AzIotHubDeviceTwin', 'Update-AzIotHubDeviceTwin'
# 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 @@ -21,6 +21,9 @@
* Added support to manage distributed settings per-device. New Cmdlets are:
- `Get-AzIotHubDistributedTracing`
- `Set-AzIotHubDistributedTracing`
* Manage IoT device twin configuration, New cmdlets are:
- `Get-AzIotHubDeviceTwin`
- `Update-AzIotHubDeviceTwin`

## Version 2.2.0
* Added support to manage devices in an Iot Hub. 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 @@ -70,6 +70,11 @@ public static IEnumerable<PSModules> ToPSModules(IEnumerable<Module> modules)
return IotHubUtils.ConvertObject<IEnumerable<Module>, IEnumerable<PSModules>>(modules.ToList());
}

public static PSDeviceTwin ToPSDeviceTwin(Twin deviceTwin)
{
return IotHubUtils.ConvertObject<Twin, PSDeviceTwin>(deviceTwin);
}

public static void ValidateDeviceTracing(string DeviceId, string Sku, string Location, bool IsEdgeDevice)
{
if (!TracingAllowedForLocation.Any(location => location.Equals(Location, StringComparison.OrdinalIgnoreCase)))
Expand Down
2 changes: 1 addition & 1 deletion src/IotHub/IotHub/Common/IotHubUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ public static string GetSubscriptionId(string Id)
public static string GetIotHubName(string Id)
{
if (string.IsNullOrEmpty(Id)) return null;
Regex r = new Regex(@"(.*?)/IotHubs/(?<iothubname>\S+)/certificates/(.*?)", RegexOptions.IgnoreCase);
Regex r = new Regex(@"(.*?)/IotHubs/(?<iothubname>[^\s/]+)", RegexOptions.IgnoreCase);
Match m = r.Match(Id);
return m.Success ? m.Groups["iothubname"].Value : null;
}
Expand Down
37 changes: 37 additions & 0 deletions src/IotHub/IotHub/IotHub.format.ps1xml
Original file line number Diff line number Diff line change
Expand Up @@ -928,5 +928,42 @@
</ListEntries>
</ListControl>
</View>
<View>
<Name>
Microsoft.Azure.Commands.Management.IotHub.Models.PSDeviceTwin
</Name>
<ViewSelectedBy>
<TypeName>Microsoft.Azure.Commands.Management.IotHub.Models.PSDeviceTwin</TypeName>
</ViewSelectedBy>
<ListControl>
<ListEntries>
<ListEntry>
<ListItems>
<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>
95 changes: 95 additions & 0 deletions src/IotHub/IotHub/IotHub/DataPlane/Device/GetAzIotHubDeviceTwin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// ----------------------------------------------------------------------------------
//
// 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 + "IotHubDeviceTwin", DefaultParameterSetName = ResourceParameterSet)]
[OutputType(typeof(PSDeviceTwin))]
public class GetAzIotHubDeviceTwin : 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; }

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 deviceTwin = registryManager.GetTwinAsync(this.DeviceId).GetAwaiter().GetResult();

if (deviceTwin == null)
{
throw new ArgumentException($"The entered device \"{this.DeviceId}\" doesn't exist.");
}

this.WriteObject(IotHubDataPlaneUtils.ToPSDeviceTwin(deviceTwin));
}
}
}
133 changes: 133 additions & 0 deletions src/IotHub/IotHub/IotHub/DataPlane/Device/UpdateAzIotHubDeviceTwin.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 + "IotHubDeviceTwin", DefaultParameterSetName = ResourceParameterSet, SupportsShouldProcess = true)]
[OutputType(typeof(PSDeviceTwin))]
public class UpdateAzIotHubDeviceTwin : 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 = false, ParameterSetName = InputObjectParameterSet, HelpMessage = "Add or update the tags property in a device twin.")]
[Parameter(Mandatory = false, ParameterSetName = ResourceIdParameterSet, HelpMessage = "Add or update the tags property in a device twin.")]
[Parameter(Mandatory = false, ParameterSetName = ResourceParameterSet, HelpMessage = "Add or update the tags property in a device twin.")]
[ValidateNotNullOrEmpty]
public Hashtable Tag { get; set; }

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

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

public override void ExecuteCmdlet()
{
if (ShouldProcess(this.DeviceId, Properties.Resources.UpdateIotHubDeviceTwin))
{
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 deviceTwin = registryManager.GetTwinAsync(this.DeviceId).GetAwaiter().GetResult();

if (deviceTwin == null)
{
throw new ArgumentException($"The entered device \"{this.DeviceId}\" doesn't exist.");
}

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

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

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

0 comments on commit 5dcd3c7

Please sign in to comment.