Skip to content

[IoT Hub] Manage IoT device twin configuration. #11425

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 2 commits into from
Mar 27, 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
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