using System; using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Table; // same SDK as your sample using Microsoft.Azure.Devices; // Install-Package Microsoft.Azure.Devices using Microsoft.Azure.Devices.Common.Exceptions; namespace Tango.MachineService.Telemetry { internal sealed class TelemetryDeviceRegistrationEntity : TableEntity { public TelemetryDeviceRegistrationEntity() { } public TelemetryDeviceRegistrationEntity(string machineGuid, string serialNumber) { PartitionKey = machineGuid; RowKey = SanitizeRowKey(serialNumber); } public String Environment { get; set; } public string SerialNumber { get; set; } public string DeviceId { get; set; } public string ConnectionString { get; set; } public DateTime CreatedUtc { get; set; } public DateTime UpdatedUtc { get; set; } internal static string SanitizeRowKey(string value) => (value ?? string.Empty) .Replace("/", "_").Replace("\\", "_") .Replace("#", "_").Replace("?", "_"); } /// /// Creates/gets IoT Hub device identities and caches the per-device connection string /// in Azure Table Storage ("TelemetryDeviceRegistrations"). /// PartitionKey = MachineGuid, RowKey = SerialNumber (sanitized). /// public sealed class TelemetryDeviceRegistrationManager { private readonly string _iotHubServiceConnectionString; private readonly string _iotHubHostName; private readonly CloudTable _table; private readonly object _locker = new object(); public TelemetryDeviceRegistrationManager( string storageAccountConnectionString, string iotHubServiceConnectionString, string tableName = "TelemetryDeviceRegistrations") { if (string.IsNullOrWhiteSpace(storageAccountConnectionString)) throw new ArgumentNullException(nameof(storageAccountConnectionString)); if (string.IsNullOrWhiteSpace(iotHubServiceConnectionString)) throw new ArgumentNullException(nameof(iotHubServiceConnectionString)); if (string.IsNullOrWhiteSpace(tableName)) throw new ArgumentNullException(nameof(tableName)); _iotHubServiceConnectionString = iotHubServiceConnectionString; _iotHubHostName = ExtractHostName(iotHubServiceConnectionString); var storageAccount = CloudStorageAccount.Parse(storageAccountConnectionString); var tableClient = storageAccount.CreateCloudTableClient(); _table = tableClient.GetTableReference(tableName); _table.CreateIfNotExists(); // sync, as in your sample } /// /// Returns the per-device IoT Hub connection string for (machineGuid, serialNumber). /// Creates the IoT Hub device (DeviceId = serialNumber) if needed, persists the connection string, /// then returns the cached value next time. /// public string GetOrCreateDeviceConnectionString(string machineGuid, string serialNumber) { if (string.IsNullOrWhiteSpace(machineGuid)) throw new ArgumentNullException(nameof(machineGuid)); if (string.IsNullOrWhiteSpace(serialNumber)) throw new ArgumentNullException(nameof(serialNumber)); // 1) Try table cache var cached = TryGetFromTable(machineGuid, serialNumber); if (!string.IsNullOrEmpty(cached)) return cached; // 2) Create/get in IoT Hub (guard against concurrent callers) lock (_locker) { // double-check after lock cached = TryGetFromTable(machineGuid, serialNumber); if (!string.IsNullOrEmpty(cached)) return cached; var registry = RegistryManager.CreateFromConnectionString(_iotHubServiceConnectionString); Device device; try { device = registry.AddDeviceAsync(new Device(serialNumber)).GetAwaiter().GetResult(); } catch (DeviceAlreadyExistsException) { device = registry.GetDeviceAsync(serialNumber).GetAwaiter().GetResult(); } if (device == null) throw new InvalidOperationException("Failed to get or create IoT Hub device."); var primaryKey = device.Authentication?.SymmetricKey?.PrimaryKey; if (string.IsNullOrEmpty(primaryKey)) throw new InvalidOperationException("Device has no symmetric key."); var deviceConnectionString = $"HostName={_iotHubHostName};DeviceId={serialNumber};SharedAccessKey={primaryKey}"; UpsertToTable(machineGuid, serialNumber, deviceConnectionString); return deviceConnectionString; } } // ---- Table helpers (sync) ---- private string TryGetFromTable(string machineGuid, string serialNumber) { var rowKey = TelemetryDeviceRegistrationEntity.SanitizeRowKey(serialNumber); var retrieve = TableOperation.Retrieve(machineGuid, rowKey); var result = _table.Execute(retrieve); var entity = result.Result as TelemetryDeviceRegistrationEntity; return entity?.ConnectionString; } private void UpsertToTable(string machineGuid, string serialNumber, string connectionString) { var entity = new TelemetryDeviceRegistrationEntity(machineGuid, serialNumber) { SerialNumber = serialNumber, DeviceId = serialNumber, Environment = MachineServiceConfig.DEPLOYMENT_SLOT.ToString(), ConnectionString = connectionString, CreatedUtc = DateTime.UtcNow, UpdatedUtc = DateTime.UtcNow }; var op = TableOperation.InsertOrReplace(entity); _table.Execute(op); } // ---- Utility ---- private static string ExtractHostName(string iotHubServiceConnectionString) { var parts = iotHubServiceConnectionString.Split(';'); foreach (var p in parts) if (p.StartsWith("HostName=", StringComparison.OrdinalIgnoreCase)) return p.Substring("HostName=".Length); throw new ArgumentException("IoT Hub service connection string is missing HostName=…"); } } }