Exemplo n.º 1
0
        /// <summary>
        /// Provisions a device to a starting hub, tries to open a connection, send telemetry,
        /// and (if supported by the protocol) send a twin update. Then, this method updates the enrollment
        /// to provision the device to a different hub. Based on the provided reprovisioning settings, this
        /// method then checks that the device was/was not reprovisioned as expected, and that the device
        /// did/did not migrate twin data as expected.
        /// </summary>
        public async Task ProvisioningDeviceClient_ReprovisioningFlow(
            Client.TransportType transportProtocol,
            AttestationMechanismType attestationType,
            EnrollmentType?enrollmentType,
            bool setCustomProxy,
            ReprovisionPolicy reprovisionPolicy,
            AllocationPolicy allocationPolicy,
            CustomAllocationDefinition customAllocationDefinition,
            ICollection <string> iotHubsToStartAt,
            ICollection <string> iotHubsToReprovisionTo,
            string proxyServerAddress = null)
        {
            ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(s_proxyServerAddress);
            string groupId = _devicePrefix + AttestationTypeToString(attestationType) + "-" + Guid.NewGuid();

            bool twinOperationsAllowed = transportProtocol != Client.TransportType.Http1;

            using ProvisioningTransportHandler transport = ProvisioningE2ETests.CreateTransportHandlerFromName(transportProtocol);
            using SecurityProvider security = await CreateSecurityProviderFromName(
                      attestationType,
                      enrollmentType,
                      groupId,
                      reprovisionPolicy,
                      allocationPolicy,
                      customAllocationDefinition,
                      iotHubsToStartAt)
                                              .ConfigureAwait(false);

            //Check basic provisioning
            if (ProvisioningE2ETests.ImplementsWebProxy(transportProtocol) && setCustomProxy)
            {
                transport.Proxy = (proxyServerAddress != null) ? new WebProxy(s_proxyServerAddress) : null;
            }

            ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create(
                s_globalDeviceEndpoint,
                Configuration.Provisioning.IdScope,
                security,
                transport);

            using var cts = new CancellationTokenSource(PassingTimeoutMiliseconds);
            DeviceRegistrationResult result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false);

            ValidateDeviceRegistrationResult(result);
            Client.IAuthenticationMethod auth = CreateAuthenticationMethodFromSecurityProvider(security, result.DeviceId);
            await ConfirmRegisteredDeviceWorks(result, auth, transportProtocol, twinOperationsAllowed).ConfigureAwait(false);

            //Check reprovisioning
            await UpdateEnrollmentToForceReprovision(enrollmentType, provisioningServiceClient, iotHubsToReprovisionTo, security, groupId).ConfigureAwait(false);

            result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false);

            ConfirmDeviceInExpectedHub(result, reprovisionPolicy, iotHubsToStartAt, iotHubsToReprovisionTo, allocationPolicy);
            await ConfirmDeviceWorksAfterReprovisioning(result, auth, transportProtocol, reprovisionPolicy, twinOperationsAllowed).ConfigureAwait(false);

            if (attestationType != AttestationMechanismType.X509) //x509 enrollments are hardcoded, should never be deleted
            {
                await ProvisioningE2ETests.DeleteCreatedEnrollmentAsync(enrollmentType, provisioningServiceClient, security, groupId).ConfigureAwait(false);
            }
        }
        public async Task ProvisioningServiceClient_GroupEnrollments_Create_Ok(
            string proxyServerAddress,
            AttestationMechanismType attestationType,
            ReprovisionPolicy reprovisionPolicy,
            AllocationPolicy allocationPolicy,
            CustomAllocationDefinition customAllocationDefinition,
            ICollection <string> iothubs)
        {
            string groupId = s_devicePrefix + AttestationTypeToString(attestationType) + "-" + Guid.NewGuid();

            using (ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(proxyServerAddress))
            {
                EnrollmentGroup enrollmentGroup = await CreateEnrollmentGroupAsync(
                    provisioningServiceClient,
                    attestationType,
                    groupId,
                    reprovisionPolicy,
                    allocationPolicy,
                    customAllocationDefinition,
                    iothubs,
                    null,
                    Logger).ConfigureAwait(false);

                EnrollmentGroup enrollmentGroupResult = null;
                await RetryOperationHelper
                .RetryOperationsAsync(
                    async() =>
                {
                    enrollmentGroupResult = await provisioningServiceClient.GetEnrollmentGroupAsync(enrollmentGroup.EnrollmentGroupId).ConfigureAwait(false);
                },
                    s_provisioningServiceRetryPolicy,
                    s_retryableExceptions,
                    Logger)
                .ConfigureAwait(false);

                if (enrollmentGroupResult == null)
                {
                    throw new ArgumentException($"The enrollment group with group Id {enrollmentGroup.EnrollmentGroupId} could not retrieved, exiting test.");
                }

                Assert.AreEqual(enrollmentGroupResult.ProvisioningStatus, ProvisioningStatus.Enabled);

                if (reprovisionPolicy != null)
                {
                    Assert.AreEqual(reprovisionPolicy.MigrateDeviceData, enrollmentGroupResult.ReprovisionPolicy.MigrateDeviceData);
                    Assert.AreEqual(reprovisionPolicy.UpdateHubAssignment, enrollmentGroupResult.ReprovisionPolicy.UpdateHubAssignment);
                }

                if (customAllocationDefinition != null)
                {
                    Assert.AreEqual(customAllocationDefinition.WebhookUrl, enrollmentGroupResult.CustomAllocationDefinition.WebhookUrl);
                    Assert.AreEqual(customAllocationDefinition.ApiVersion, enrollmentGroupResult.CustomAllocationDefinition.ApiVersion);
                }

                Assert.AreEqual(allocationPolicy, enrollmentGroup.AllocationPolicy);

                await DeleteCreatedEnrollmentAsync(EnrollmentType.Group, "", enrollmentGroup.EnrollmentGroupId, Logger).ConfigureAwait(false);
            }
        }
        public async Task ProvisioningServiceClient_SymmetricKey_IndividualEnrollment_Create_Http_Ok_WithReprovisioningFields()
        {
            //This webhook won't actually work for reprovisioning, but this test is only testing that the field is accepted by the service
            var customAllocationDefinition = new CustomAllocationDefinition() { ApiVersion = "2019-03-31", WebhookUrl = "https://www.microsoft.com" };
            var reprovisionPolicy = new ReprovisionPolicy() { MigrateDeviceData = false, UpdateHubAssignment = true };
            AllocationPolicy allocationPolicy = AllocationPolicy.GeoLatency;

            await ProvisioningServiceClient_IndividualEnrollments_Create_Ok("", AttestationMechanismType.SymmetricKey, reprovisionPolicy, allocationPolicy, customAllocationDefinition, null).ConfigureAwait(false);
        }
        public static async Task <EnrollmentGroup> CreateEnrollmentGroupAsync(
            ProvisioningServiceClient provisioningServiceClient,
            AttestationMechanismType attestationType,
            string groupId,
            ReprovisionPolicy reprovisionPolicy,
            AllocationPolicy allocationPolicy,
            CustomAllocationDefinition customAllocationDefinition,
            ICollection <string> iothubs,
            DeviceCapabilities capabilities,
            MsTestLogger logger)
        {
            Attestation attestation;

            switch (attestationType)
            {
            case AttestationMechanismType.Tpm:
                throw new NotSupportedException("Group enrollments do not support tpm attestation");

            case AttestationMechanismType.SymmetricKey:
                string primaryKey   = CryptoKeyGenerator.GenerateKey(32);
                string secondaryKey = CryptoKeyGenerator.GenerateKey(32);
                attestation = new SymmetricKeyAttestation(primaryKey, secondaryKey);
                break;

            case AttestationMechanismType.X509:
            default:
                throw new NotSupportedException("Test code has not been written for testing this attestation type yet");
            }

            var enrollmentGroup = new EnrollmentGroup(groupId, attestation)
            {
                Capabilities               = capabilities,
                ReprovisionPolicy          = reprovisionPolicy,
                AllocationPolicy           = allocationPolicy,
                CustomAllocationDefinition = customAllocationDefinition,
                IotHubs = iothubs,
            };

            EnrollmentGroup createdEnrollmentGroup = null;
            await RetryOperationHelper
            .RetryOperationsAsync(
                async() =>
            {
                createdEnrollmentGroup = await provisioningServiceClient.CreateOrUpdateEnrollmentGroupAsync(enrollmentGroup).ConfigureAwait(false);
            },
                s_provisioningServiceRetryPolicy,
                s_retryableExceptions,
                logger)
            .ConfigureAwait(false);

            if (createdEnrollmentGroup == null)
            {
                throw new ArgumentException($"The enrollment entry with group Id {groupId} could not be created, exiting test.");
            }

            return(createdEnrollmentGroup);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Confirm that the hub the device belongs to did or did not change, depending on the reprovision policy
        /// </summary>
        private void ConfirmDeviceInExpectedHub(DeviceRegistrationResult result, ReprovisionPolicy reprovisionPolicy, ICollection <string> iotHubsToStartAt, ICollection <string> iotHubsToReprovisionTo, AllocationPolicy allocationPolicy)
        {
            if (reprovisionPolicy.UpdateHubAssignment)
            {
                Assert.IsTrue(iotHubsToReprovisionTo.Contains(result.AssignedHub));
                Assert.IsFalse(iotHubsToStartAt.Contains(result.AssignedHub));

                if (allocationPolicy == AllocationPolicy.GeoLatency)
                {
                    Assert.AreNotEqual(result.AssignedHub, Configuration.Provisioning.FarAwayIotHubHostName);
                }
            }
            else
            {
                Assert.IsFalse(iotHubsToReprovisionTo.Contains(result.AssignedHub));
                Assert.IsTrue(iotHubsToStartAt.Contains(result.AssignedHub));
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Confirm that the hub the device belongs to did or did not change, depending on the reprovision policy
        /// </summary>
        private void ConfirmDeviceInExpectedHub(DeviceRegistrationResult result, ReprovisionPolicy reprovisionPolicy, ICollection<string> iotHubsToStartAt, ICollection<string> iotHubsToReprovisionTo, AllocationPolicy allocationPolicy)
        {
            if (reprovisionPolicy.UpdateHubAssignment)
            {
                iotHubsToReprovisionTo.Should().Contain(result.AssignedHub);
                iotHubsToStartAt.Should().NotContain(result.AssignedHub);

                if (allocationPolicy == AllocationPolicy.GeoLatency)
                {
                    result.AssignedHub.Should().NotBe(TestConfiguration.Provisioning.FarAwayIotHubHostName);
                }
            }
            else
            {
                iotHubsToReprovisionTo.Should().NotContain(result.AssignedHub);
                iotHubsToStartAt.Should().Contain(result.AssignedHub);
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Provisions a device to a starting hub, tries to open a connection, send telemetry,
        /// and (if supported by the protocol) send a twin update. Then, this method updates the enrollment
        /// to provision the device to a different hub. Based on the provided reprovisioning settings, this
        /// method then checks that the device was/was not reprovisioned as expected, and that the device
        /// did/did not migrate twin data as expected.
        /// </summary>
        public async Task ProvisioningDeviceClient_ReprovisioningFlow(
            Client.TransportType transportProtocol,
            AttestationMechanismType attestationType,
            EnrollmentType? enrollmentType,
            bool setCustomProxy,
            ReprovisionPolicy reprovisionPolicy,
            AllocationPolicy allocationPolicy,
            CustomAllocationDefinition customAllocationDefinition,
            ICollection<string> iotHubsToStartAt,
            ICollection<string> iotHubsToReprovisionTo,
            string proxyServerAddress = null)
        {
            using ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(s_proxyServerAddress);

            string groupId = null;
            if (enrollmentType == EnrollmentType.Group)
            {
                if (attestationType == AttestationMechanismType.X509)
                {
                    groupId = TestConfiguration.Provisioning.X509GroupEnrollmentName;
                }
                else
                {
                    groupId = _idPrefix + AttestationTypeToString(attestationType) + "-" + Guid.NewGuid();
                }
            }

            bool transportProtocolSupportsTwinOperations = transportProtocol != Client.TransportType.Http1;

            using ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportProtocol);
            using SecurityProvider security = await CreateSecurityProviderFromNameAsync(
                    attestationType,
                    enrollmentType,
                    groupId,
                    reprovisionPolicy,
                    allocationPolicy,
                    customAllocationDefinition,
                    iotHubsToStartAt)
                .ConfigureAwait(false);

            //Check basic provisioning
            if (ImplementsWebProxy(transportProtocol) && setCustomProxy)
            {
                transport.Proxy = (proxyServerAddress != null) ? new WebProxy(s_proxyServerAddress) : null;
            }

            var provClient = ProvisioningDeviceClient.Create(
                s_globalDeviceEndpoint,
                TestConfiguration.Provisioning.IdScope,
                security,
                transport);
            using var cts = new CancellationTokenSource(PassingTimeoutMiliseconds);
            DeviceRegistrationResult result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false);
            ValidateDeviceRegistrationResult(result);

#pragma warning disable CA2000 // Dispose objects before losing scope
            // The certificate instance referenced in the DeviceAuthenticationWithX509Certificate instance is common for all tests in this class. It is disposed during class cleanup.
            Client.IAuthenticationMethod auth = CreateAuthenticationMethodFromSecurityProvider(security, result.DeviceId);
#pragma warning restore CA2000 // Dispose objects before losing scope

            await ConfirmRegisteredDeviceWorks(result, auth, transportProtocol, transportProtocolSupportsTwinOperations).ConfigureAwait(false);

            //Check reprovisioning
            await UpdateEnrollmentToForceReprovision(enrollmentType, provisioningServiceClient, iotHubsToReprovisionTo, security, groupId).ConfigureAwait(false);
            result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false);
            ConfirmDeviceInExpectedHub(result, reprovisionPolicy, iotHubsToStartAt, iotHubsToReprovisionTo, allocationPolicy);
            await ConfirmDeviceWorksAfterReprovisioning(result, auth, transportProtocol, reprovisionPolicy, transportProtocolSupportsTwinOperations).ConfigureAwait(false);

            if (attestationType == AttestationMechanismType.X509 && enrollmentType == EnrollmentType.Group)
            {
                Logger.Trace($"The test enrollment type {attestationType}-{enrollmentType} with group Id {groupId} is currently hardcoded - do not delete.");
            }
            else
            {
                Logger.Trace($"Deleting test enrollment type {attestationType}-{enrollmentType} with registration Id {security.GetRegistrationID()}.");
                await DeleteCreatedEnrollmentAsync(enrollmentType, security, groupId, Logger).ConfigureAwait(false);
            }

            if (security is SecurityProviderX509 x509Security)
            {
                X509Certificate2 deviceCertificate = x509Security.GetAuthenticationCertificate();
                deviceCertificate?.Dispose();
            }

            if (auth != null && auth is IDisposable disposableAuth)
            {
                disposableAuth?.Dispose();
            }
        }
        public static async Task<EnrollmentGroup> CreateEnrollmentGroup(ProvisioningServiceClient provisioningServiceClient, AttestationMechanismType attestationType, string groupId, ReprovisionPolicy reprovisionPolicy, AllocationPolicy allocationPolicy, CustomAllocationDefinition customAllocationDefinition, ICollection<string> iothubs, DeviceCapabilities capabilities)
        {
            Attestation attestation;
            switch (attestationType)
            {
                case AttestationMechanismType.Tpm:
                    throw new NotSupportedException("Group enrollments do not support tpm attestation");
                case AttestationMechanismType.SymmetricKey:
                    string primaryKey = CryptoKeyGenerator.GenerateKey(32);
                    string secondaryKey = CryptoKeyGenerator.GenerateKey(32);
                    attestation = new SymmetricKeyAttestation(primaryKey, secondaryKey);
                    break;

                case AttestationMechanismType.X509:
                default:
                    throw new NotSupportedException("Test code has not been written for testing this attestation type yet");
            }

            var enrollmentGroup = new EnrollmentGroup(groupId, attestation)
            {
                Capabilities = capabilities,
                ReprovisionPolicy = reprovisionPolicy,
                AllocationPolicy = allocationPolicy,
                CustomAllocationDefinition = customAllocationDefinition,
                IotHubs = iothubs,
            };

            return await provisioningServiceClient.CreateOrUpdateEnrollmentGroupAsync(enrollmentGroup).ConfigureAwait(false);
        }
        public static async Task<IndividualEnrollment> CreateIndividualEnrollment(ProvisioningServiceClient provisioningServiceClient, AttestationMechanismType attestationType, ReprovisionPolicy reprovisionPolicy, AllocationPolicy allocationPolicy, CustomAllocationDefinition customAllocationDefinition, ICollection<string> iotHubsToProvisionTo, DeviceCapabilities capabilities)
        {
            string registrationId = AttestationTypeToString(attestationType) + "-registration-id-" + Guid.NewGuid();
            Attestation attestation;
            IndividualEnrollment individualEnrollment;
            switch (attestationType)
            {
                case AttestationMechanismType.Tpm:
                    using (var tpmSim = new SecurityProviderTpmSimulator(registrationId))
                    {
                        string base64Ek = Convert.ToBase64String(tpmSim.GetEndorsementKey());
                        var provisioningService = ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString);
                        individualEnrollment = new IndividualEnrollment(registrationId, new TpmAttestation(base64Ek))
                        {
                            Capabilities = capabilities,
                            AllocationPolicy = allocationPolicy,
                            ReprovisionPolicy = reprovisionPolicy,
                            CustomAllocationDefinition = customAllocationDefinition,
                            IotHubs = iotHubsToProvisionTo
                        };

                        IndividualEnrollment enrollment = await provisioningService.CreateOrUpdateIndividualEnrollmentAsync(individualEnrollment).ConfigureAwait(false);
                        attestation = new TpmAttestation(base64Ek);
                        enrollment.Attestation = attestation;
                        return await provisioningService.CreateOrUpdateIndividualEnrollmentAsync(enrollment).ConfigureAwait(false);
                    }
                case AttestationMechanismType.SymmetricKey:
                    string primaryKey = CryptoKeyGenerator.GenerateKey(32);
                    string secondaryKey = CryptoKeyGenerator.GenerateKey(32);
                    attestation = new SymmetricKeyAttestation(primaryKey, secondaryKey);
                    break;

                case AttestationMechanismType.X509:
                default:
                    throw new NotSupportedException("Test code has not been written for testing this attestation type yet");
            }

            individualEnrollment = new IndividualEnrollment(registrationId, attestation);
            individualEnrollment.Capabilities = capabilities;
            individualEnrollment.CustomAllocationDefinition = customAllocationDefinition;
            individualEnrollment.ReprovisionPolicy = reprovisionPolicy;
            individualEnrollment.IotHubs = iotHubsToProvisionTo;
            individualEnrollment.AllocationPolicy = allocationPolicy;
            return await provisioningServiceClient.CreateOrUpdateIndividualEnrollmentAsync(individualEnrollment).ConfigureAwait(false);
        }
        public static async Task ProvisioningServiceClient_GroupEnrollments_Create_Ok(string proxyServerAddress, AttestationMechanismType attestationType, ReprovisionPolicy reprovisionPolicy, AllocationPolicy allocationPolicy, CustomAllocationDefinition customAllocationDefinition, ICollection<string> iothubs)
        {
            string groupId = s_devicePrefix + AttestationTypeToString(attestationType) + "-" + Guid.NewGuid();
            using (ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(proxyServerAddress))
            {
                EnrollmentGroup enrollmentGroup = await CreateEnrollmentGroup(provisioningServiceClient, attestationType, groupId, reprovisionPolicy, allocationPolicy, customAllocationDefinition, iothubs, null).ConfigureAwait(false);
                EnrollmentGroup enrollmentGroupResult = await provisioningServiceClient.GetEnrollmentGroupAsync(enrollmentGroup.EnrollmentGroupId).ConfigureAwait(false);
                Assert.AreEqual(enrollmentGroupResult.ProvisioningStatus, ProvisioningStatus.Enabled);

                if (reprovisionPolicy != null)
                {
                    Assert.AreEqual(reprovisionPolicy.MigrateDeviceData, enrollmentGroupResult.ReprovisionPolicy.MigrateDeviceData);
                    Assert.AreEqual(reprovisionPolicy.UpdateHubAssignment, enrollmentGroupResult.ReprovisionPolicy.UpdateHubAssignment);
                }

                if (customAllocationDefinition != null)
                {
                    Assert.AreEqual(customAllocationDefinition.WebhookUrl, enrollmentGroupResult.CustomAllocationDefinition.WebhookUrl);
                    Assert.AreEqual(customAllocationDefinition.ApiVersion, enrollmentGroupResult.CustomAllocationDefinition.ApiVersion);
                }

                Assert.AreEqual(allocationPolicy, enrollmentGroup.AllocationPolicy);

                try
                {
                    await provisioningServiceClient.DeleteEnrollmentGroupAsync(enrollmentGroup.EnrollmentGroupId).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Cleanup of enrollment group failed due to {ex}");
                }
            }
        }
        public static async Task ProvisioningServiceClient_IndividualEnrollments_Create_Ok(string proxyServerAddress, AttestationMechanismType attestationType, ReprovisionPolicy reprovisionPolicy, AllocationPolicy allocationPolicy, CustomAllocationDefinition customAllocationDefinition, ICollection<string> iotHubsToProvisionTo)
        {
            using (ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(proxyServerAddress))
            {
                IndividualEnrollment individualEnrollment = await CreateIndividualEnrollment(provisioningServiceClient, attestationType, reprovisionPolicy, allocationPolicy, customAllocationDefinition, iotHubsToProvisionTo, null).ConfigureAwait(false);
                IndividualEnrollment individualEnrollmentResult = await provisioningServiceClient.GetIndividualEnrollmentAsync(individualEnrollment.RegistrationId).ConfigureAwait(false);
                Assert.AreEqual(individualEnrollmentResult.ProvisioningStatus, ProvisioningStatus.Enabled);

                if (reprovisionPolicy != null)
                {
                    Assert.AreEqual(reprovisionPolicy.UpdateHubAssignment, individualEnrollmentResult.ReprovisionPolicy.UpdateHubAssignment);
                    Assert.AreEqual(reprovisionPolicy.MigrateDeviceData, individualEnrollmentResult.ReprovisionPolicy.MigrateDeviceData);
                }

                if (customAllocationDefinition != null)
                {
                    Assert.AreEqual(customAllocationDefinition.WebhookUrl, individualEnrollmentResult.CustomAllocationDefinition.WebhookUrl);
                    Assert.AreEqual(customAllocationDefinition.ApiVersion, individualEnrollmentResult.CustomAllocationDefinition.ApiVersion);
                }

                //allocation policy is never null
                Assert.AreEqual(allocationPolicy, individualEnrollmentResult.AllocationPolicy);

                await provisioningServiceClient.DeleteIndividualEnrollmentAsync(individualEnrollment.RegistrationId).ConfigureAwait(false);
            }
        }
Exemplo n.º 12
0
        private async Task ConfirmDeviceWorksAfterReprovisioning(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, Client.TransportType transportProtocol, ReprovisionPolicy reprovisionPolicy, bool twinOperationsAllowed)
        {
            using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol))
            {
                _log.WriteLine("DeviceClient OpenAsync.");
                await iotClient.OpenAsync().ConfigureAwait(false);

                _log.WriteLine("DeviceClient SendEventAsync.");
                await iotClient.SendEventAsync(
                    new Client.Message(Encoding.UTF8.GetBytes("TestMessage"))).ConfigureAwait(false);

                //twin can be configured to revert back to default twin when provisioned, or to keep twin
                // from previous hub's records.
                if (twinOperationsAllowed)
                {
                    Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false);

                    if (reprovisionPolicy.MigrateDeviceData)
                    {
                        Assert.AreNotEqual(1, twin.Properties.Desired.Count);
                        Assert.AreNotEqual(0, twin.Properties.Desired.Version);
                        Assert.AreEqual(ProvisioningRegistrationSubstatusType.DeviceDataMigrated, result.Substatus);
                    }
                    else if (reprovisionPolicy.UpdateHubAssignment)
                    {
                        Assert.AreEqual(twin.Properties.Desired.Count, 0);
                        Assert.AreEqual(ProvisioningRegistrationSubstatusType.DeviceDataReset, result.Substatus);
                    }
                    else
                    {
                        Assert.AreNotEqual(twin.Properties.Desired.Count, 1);
                        Assert.AreEqual(ProvisioningRegistrationSubstatusType.InitialAssignment, result.Substatus);
                    }
                }

                _log.WriteLine("DeviceClient CloseAsync.");
                await iotClient.CloseAsync().ConfigureAwait(false);
            }
        }
Exemplo n.º 13
0
        private async Task <SecurityProvider> CreateSecurityProviderFromName(AttestationType attestationType, EnrollmentType?enrollmentType, string groupId, ReprovisionPolicy reprovisionPolicy, AllocationPolicy allocationPolicy, CustomAllocationDefinition customAllocationDefinition, ICollection <string> iothubs, DeviceCapabilities capabilities = null)
        {
            _verboseLog.WriteLine($"{nameof(CreateSecurityProviderFromName)}({attestationType})");

            var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString);

            switch (attestationType)
            {
            case AttestationType.Tpm:
                string registrationId = AttestationTypeToString(attestationType) + "-registration-id-" + Guid.NewGuid();
                var    tpmSim         = new SecurityProviderTpmSimulator(registrationId);

                string base64Ek = Convert.ToBase64String(tpmSim.GetEndorsementKey());


                var provisioningService = ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString);

                _log.WriteLine($"Getting enrollment: RegistrationID = {registrationId}");
                IndividualEnrollment individualEnrollment = new IndividualEnrollment(registrationId, new TpmAttestation(base64Ek))
                {
                    AllocationPolicy = allocationPolicy, ReprovisionPolicy = reprovisionPolicy, IotHubs = iothubs, CustomAllocationDefinition = customAllocationDefinition, Capabilities = capabilities
                };
                IndividualEnrollment enrollment = await provisioningService.CreateOrUpdateIndividualEnrollmentAsync(individualEnrollment).ConfigureAwait(false);

                var attestation = new TpmAttestation(base64Ek);
                enrollment.Attestation = attestation;
                _log.WriteLine($"Updating enrollment: RegistrationID = {registrationId} EK = '{base64Ek}'");
                await provisioningService.CreateOrUpdateIndividualEnrollmentAsync(enrollment).ConfigureAwait(false);

                return(tpmSim);

            case AttestationType.x509:

                X509Certificate2           certificate = null;
                X509Certificate2Collection collection  = null;
                switch (enrollmentType)
                {
                case EnrollmentType.Individual:
                    certificate = Configuration.Provisioning.GetIndividualEnrollmentCertificate();
                    break;

                case EnrollmentType.Group:
                    certificate = Configuration.Provisioning.GetGroupEnrollmentCertificate();
                    collection  = Configuration.Provisioning.GetGroupEnrollmentChain();
                    break;

                default:
                    throw new NotSupportedException($"Unknown X509 type: '{enrollmentType}'");
                }

                return(new SecurityProviderX509Certificate(certificate, collection));

            case AttestationType.SymmetricKey:
                switch (enrollmentType)
                {
                case EnrollmentType.Group:
                    EnrollmentGroup symmetricKeyEnrollmentGroup = await CreateEnrollmentGroup(provisioningServiceClient, AttestationType.SymmetricKey, groupId, reprovisionPolicy, allocationPolicy, customAllocationDefinition, iothubs, capabilities).ConfigureAwait(false);

                    Assert.IsTrue(symmetricKeyEnrollmentGroup.Attestation is SymmetricKeyAttestation);
                    SymmetricKeyAttestation symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollmentGroup.Attestation;
                    string registrationIdSymmetricKey  = IdPrefix + Guid.NewGuid();
                    string primaryKeyEnrollmentGroup   = symmetricKeyAttestation.PrimaryKey;
                    string secondaryKeyEnrollmentGroup = symmetricKeyAttestation.SecondaryKey;

                    string primaryKeyIndividual   = ProvisioningE2ETests.ComputeDerivedSymmetricKey(Convert.FromBase64String(primaryKeyEnrollmentGroup), registrationIdSymmetricKey);
                    string secondaryKeyIndividual = ProvisioningE2ETests.ComputeDerivedSymmetricKey(Convert.FromBase64String(secondaryKeyEnrollmentGroup), registrationIdSymmetricKey);

                    return(new SecurityProviderSymmetricKey(registrationIdSymmetricKey, primaryKeyIndividual, secondaryKeyIndividual));

                case EnrollmentType.Individual:
                    IndividualEnrollment symmetricKeyEnrollment = await CreateIndividualEnrollment(provisioningServiceClient, AttestationType.SymmetricKey, reprovisionPolicy, allocationPolicy, customAllocationDefinition, iothubs, capabilities).ConfigureAwait(false);

                    Assert.IsTrue(symmetricKeyEnrollment.Attestation is SymmetricKeyAttestation);
                    symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollment.Attestation;

                    registrationIdSymmetricKey = symmetricKeyEnrollment.RegistrationId;
                    string primaryKey   = symmetricKeyAttestation.PrimaryKey;
                    string secondaryKey = symmetricKeyAttestation.SecondaryKey;
                    return(new SecurityProviderSymmetricKey(registrationIdSymmetricKey, primaryKey, secondaryKey));

                default:
                    throw new NotSupportedException("Unrecognized enrollment type");
                }

            default:
                throw new NotSupportedException("Unrecognized attestation type");
            }

            throw new NotSupportedException($"Unknown security type: '{attestationType}'.");
        }
Exemplo n.º 14
0
        private async Task<SecurityProvider> CreateSecurityProviderFromNameAsync(
            AttestationMechanismType attestationType,
            EnrollmentType? enrollmentType,
            string groupId,
            ReprovisionPolicy reprovisionPolicy,
            AllocationPolicy allocationPolicy,
            CustomAllocationDefinition customAllocationDefinition,
            ICollection<string> iothubs,
            DeviceCapabilities capabilities = null)
        {
            _verboseLog.WriteLine($"{nameof(CreateSecurityProviderFromNameAsync)}({attestationType})");

            string registrationId = AttestationTypeToString(attestationType) + "-" + Guid.NewGuid();
            using var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString);

            switch (attestationType)
            {
                case AttestationMechanismType.Tpm:
                    IndividualEnrollment tpmEnrollment = await CreateIndividualEnrollmentAsync(
                        provisioningServiceClient,
                        registrationId,
                        AttestationMechanismType.Tpm,
                        null,
                        reprovisionPolicy,
                        allocationPolicy,
                        customAllocationDefinition,
                        iothubs,
                        capabilities,
                        Logger).ConfigureAwait(false);

                    return new SecurityProviderTpmSimulator(tpmEnrollment.RegistrationId);

                case AttestationMechanismType.X509:
                    X509Certificate2 certificate = null;
                    X509Certificate2Collection collection = null;
                    switch (enrollmentType)
                    {
                        case EnrollmentType.Individual:
                            X509Certificate2Helper.GenerateSelfSignedCertificateFiles(registrationId, s_x509CertificatesFolder, Logger);

#pragma warning disable CA2000 // Dispose objects before losing scope
                            // This certificate is used for authentication with IoT hub and is returned to the caller of this method.
                            // It is disposed when the caller to this method is disposed, at the end of the test method.
                            certificate = X509Certificate2Helper.CreateX509Certificate2FromPfxFile(registrationId, s_x509CertificatesFolder);
#pragma warning restore CA2000 // Dispose objects before losing scope

                            using (X509Certificate2 publicCertificate = X509Certificate2Helper.CreateX509Certificate2FromCerFile(registrationId, s_x509CertificatesFolder))
                            {
                                IndividualEnrollment x509IndividualEnrollment = await CreateIndividualEnrollmentAsync(
                                    provisioningServiceClient,
                                    registrationId,
                                    AttestationMechanismType.X509,
                                    publicCertificate,
                                    reprovisionPolicy,
                                    allocationPolicy,
                                    customAllocationDefinition,
                                    iothubs,
                                    capabilities,
                                    Logger).ConfigureAwait(false);

                                x509IndividualEnrollment.Attestation.Should().BeAssignableTo<X509Attestation>();
                            }

                            break;

                        case EnrollmentType.Group:
                            // The X509 enrollment group has been hardcoded for the purpose of E2E tests and the root certificate has been verified on DPS.
                            // Each device identity provisioning through the above enrollment group is created on-demand.

                            X509Certificate2Helper.GenerateIntermediateCertificateSignedCertificateFiles(
                                registrationId,
                                s_intermediateCertificateSubject,
                                s_x509CertificatesFolder,
                                Logger);

#pragma warning disable CA2000 // Dispose objects before losing scope
                            // This certificate is used for authentication with IoT hub and is returned to the caller of this method.
                            // It is disposed when the caller to this method is disposed, at the end of the test method.
                            certificate = X509Certificate2Helper.CreateX509Certificate2FromPfxFile(registrationId, s_x509CertificatesFolder);
#pragma warning restore CA2000 // Dispose objects before losing scope

                            collection = new X509Certificate2Collection
                            {
                                TestConfiguration.CommonCertificates.GetRootCaCertificate(),
                                TestConfiguration.CommonCertificates.GetIntermediate1Certificate(),
                                TestConfiguration.CommonCertificates.GetIntermediate2Certificate(),
                                X509Certificate2Helper.CreateX509Certificate2FromCerFile(registrationId, s_x509CertificatesFolder)
                            };
                            break;

                        default:
                            throw new NotSupportedException($"Unknown X509 type: '{enrollmentType}'");
                    }

                    return new SecurityProviderX509Certificate(certificate, collection);

                case AttestationMechanismType.SymmetricKey:
                    switch (enrollmentType)
                    {
                        case EnrollmentType.Group:
                            EnrollmentGroup symmetricKeyEnrollmentGroup = await CreateEnrollmentGroupAsync(
                                    provisioningServiceClient,
                                    AttestationMechanismType.SymmetricKey,
                                    groupId,
                                    reprovisionPolicy,
                                    allocationPolicy,
                                    customAllocationDefinition,
                                    iothubs,
                                    capabilities,
                                    Logger)
                                .ConfigureAwait(false);
                            Assert.IsTrue(symmetricKeyEnrollmentGroup.Attestation is SymmetricKeyAttestation);
                            var symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollmentGroup.Attestation;
                            string registrationIdSymmetricKey = _idPrefix + Guid.NewGuid();
                            string primaryKeyEnrollmentGroup = symmetricKeyAttestation.PrimaryKey;
                            string secondaryKeyEnrollmentGroup = symmetricKeyAttestation.SecondaryKey;

                            string primaryKeyIndividual = ComputeDerivedSymmetricKey(Convert.FromBase64String(primaryKeyEnrollmentGroup), registrationIdSymmetricKey);
                            string secondaryKeyIndividual = ComputeDerivedSymmetricKey(Convert.FromBase64String(secondaryKeyEnrollmentGroup), registrationIdSymmetricKey);

                            return new SecurityProviderSymmetricKey(registrationIdSymmetricKey, primaryKeyIndividual, secondaryKeyIndividual);

                        case EnrollmentType.Individual:
                            IndividualEnrollment symmetricKeyEnrollment = await CreateIndividualEnrollmentAsync(
                                provisioningServiceClient,
                                registrationId,
                                AttestationMechanismType.SymmetricKey,
                                null,
                                reprovisionPolicy,
                                allocationPolicy,
                                customAllocationDefinition,
                                iothubs,
                                capabilities,
                                Logger).ConfigureAwait(false);

                            Assert.IsTrue(symmetricKeyEnrollment.Attestation is SymmetricKeyAttestation);
                            symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollment.Attestation;

                            registrationIdSymmetricKey = symmetricKeyEnrollment.RegistrationId;
                            string primaryKey = symmetricKeyAttestation.PrimaryKey;
                            string secondaryKey = symmetricKeyAttestation.SecondaryKey;
                            return new SecurityProviderSymmetricKey(registrationIdSymmetricKey, primaryKey, secondaryKey);

                        default:
                            throw new NotSupportedException("Unrecognized enrollment type");
                    }
                default:
                    throw new NotSupportedException("Unrecognized attestation type");
            }

            throw new NotSupportedException($"Unknown security type: '{attestationType}'.");
        }
Exemplo n.º 15
0
        public static async Task ProvisioningServiceClient_GroupEnrollments_Create_Ok(string proxyServerAddress, AttestationType attestationType, ReprovisionPolicy reprovisionPolicy, AllocationPolicy allocationPolicy, CustomAllocationDefinition customAllocationDefinition, ICollection <string> iothubs)
        {
            string groupId = "some-valid-group-id-" + attestationTypeToString(attestationType) + "-" + Guid.NewGuid();

            using (ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(proxyServerAddress))
            {
                EnrollmentGroup enrollmentGroup = await CreateEnrollmentGroup(provisioningServiceClient, attestationType, groupId, reprovisionPolicy, allocationPolicy, customAllocationDefinition, iothubs).ConfigureAwait(false);

                EnrollmentGroup enrollmentGroupResult = await provisioningServiceClient.GetEnrollmentGroupAsync(enrollmentGroup.EnrollmentGroupId).ConfigureAwait(false);

                Assert.AreEqual(enrollmentGroupResult.ProvisioningStatus, ProvisioningStatus.Enabled);

                if (reprovisionPolicy != null)
                {
                    Assert.AreEqual(reprovisionPolicy.MigrateDeviceData, enrollmentGroupResult.ReprovisionPolicy.MigrateDeviceData);
                    Assert.AreEqual(reprovisionPolicy.UpdateHubAssignment, enrollmentGroupResult.ReprovisionPolicy.UpdateHubAssignment);
                }

                if (customAllocationDefinition != null)
                {
                    Assert.AreEqual(customAllocationDefinition.WebhookUrl, enrollmentGroupResult.CustomAllocationDefinition.WebhookUrl);
                    Assert.AreEqual(customAllocationDefinition.ApiVersion, enrollmentGroupResult.CustomAllocationDefinition.ApiVersion);
                }

                Assert.AreEqual(allocationPolicy, enrollmentGroup.AllocationPolicy);

                await provisioningServiceClient.DeleteEnrollmentGroupAsync(enrollmentGroup.EnrollmentGroupId).ConfigureAwait(false);
            }
        }
        public static async Task <IndividualEnrollment> CreateIndividualEnrollmentAsync(
            ProvisioningServiceClient provisioningServiceClient,
            string registrationId,
            AttestationMechanismType attestationType,
            X509Certificate2 authenticationCertificate,
            ReprovisionPolicy reprovisionPolicy,
            AllocationPolicy allocationPolicy,
            CustomAllocationDefinition customAllocationDefinition,
            ICollection <string> iotHubsToProvisionTo,
            DeviceCapabilities capabilities,
            MsTestLogger logger)
        {
            Attestation          attestation;
            IndividualEnrollment individualEnrollment;
            IndividualEnrollment createdEnrollment = null;

            switch (attestationType)
            {
            case AttestationMechanismType.Tpm:
                using (var tpmSim = new SecurityProviderTpmSimulator(registrationId))
                {
                    string base64Ek = Convert.ToBase64String(tpmSim.GetEndorsementKey());
                    individualEnrollment = new IndividualEnrollment(registrationId, new TpmAttestation(base64Ek))
                    {
                        Capabilities               = capabilities,
                        AllocationPolicy           = allocationPolicy,
                        ReprovisionPolicy          = reprovisionPolicy,
                        CustomAllocationDefinition = customAllocationDefinition,
                        IotHubs = iotHubsToProvisionTo
                    };

                    IndividualEnrollment temporaryCreatedEnrollment = null;
                    await RetryOperationHelper
                    .RetryOperationsAsync(
                        async() =>
                    {
                        temporaryCreatedEnrollment = await provisioningServiceClient.CreateOrUpdateIndividualEnrollmentAsync(individualEnrollment).ConfigureAwait(false);
                    },
                        s_provisioningServiceRetryPolicy,
                        s_retryableExceptions,
                        logger)
                    .ConfigureAwait(false);

                    if (temporaryCreatedEnrollment == null)
                    {
                        throw new ArgumentException($"The enrollment entry with registration Id {registrationId} could not be created, exiting test.");
                    }

                    attestation = new TpmAttestation(base64Ek);
                    temporaryCreatedEnrollment.Attestation = attestation;

                    await RetryOperationHelper
                    .RetryOperationsAsync(
                        async() =>
                    {
                        createdEnrollment = await provisioningServiceClient.CreateOrUpdateIndividualEnrollmentAsync(temporaryCreatedEnrollment).ConfigureAwait(false);
                    },
                        s_provisioningServiceRetryPolicy,
                        s_retryableExceptions,
                        logger)
                    .ConfigureAwait(false);

                    if (createdEnrollment == null)
                    {
                        throw new ArgumentException($"The enrollment entry with registration Id {registrationId} could not be updated, exiting test.");
                    }

                    return(createdEnrollment);
                }

            case AttestationMechanismType.SymmetricKey:
                string primaryKey   = CryptoKeyGenerator.GenerateKey(32);
                string secondaryKey = CryptoKeyGenerator.GenerateKey(32);
                attestation = new SymmetricKeyAttestation(primaryKey, secondaryKey);
                break;

            case AttestationMechanismType.X509:
                attestation = X509Attestation.CreateFromClientCertificates(authenticationCertificate);
                break;

            default:
                throw new NotSupportedException("Test code has not been written for testing this attestation type yet");
            }

            individualEnrollment = new IndividualEnrollment(registrationId, attestation)
            {
                Capabilities               = capabilities,
                AllocationPolicy           = allocationPolicy,
                ReprovisionPolicy          = reprovisionPolicy,
                CustomAllocationDefinition = customAllocationDefinition,
                IotHubs = iotHubsToProvisionTo,
            };

            await RetryOperationHelper
            .RetryOperationsAsync(
                async() =>
            {
                createdEnrollment = await provisioningServiceClient.CreateOrUpdateIndividualEnrollmentAsync(individualEnrollment).ConfigureAwait(false);
            },
                s_provisioningServiceRetryPolicy,
                s_retryableExceptions,
                logger)
            .ConfigureAwait(false);

            if (createdEnrollment == null)
            {
                throw new ArgumentException($"The enrollment entry with registration Id {registrationId} could not be created, exiting test.");
            }

            return(createdEnrollment);
        }
        public async Task ProvisioningServiceClient_IndividualEnrollments_Create_Ok(
            string proxyServerAddress,
            AttestationMechanismType attestationType,
            ReprovisionPolicy reprovisionPolicy,
            AllocationPolicy allocationPolicy,
            CustomAllocationDefinition customAllocationDefinition,
            ICollection <string> iotHubsToProvisionTo)
        {
            using (ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(proxyServerAddress))
            {
                string registrationId = AttestationTypeToString(attestationType) + "-" + Guid.NewGuid();

                IndividualEnrollment individualEnrollment = await CreateIndividualEnrollmentAsync(
                    provisioningServiceClient,
                    registrationId,
                    attestationType,
                    null,
                    reprovisionPolicy,
                    allocationPolicy,
                    customAllocationDefinition,
                    iotHubsToProvisionTo,
                    null,
                    Logger).ConfigureAwait(false);

                IndividualEnrollment individualEnrollmentResult = null;
                await RetryOperationHelper
                .RetryOperationsAsync(
                    async() =>
                {
                    individualEnrollmentResult = await provisioningServiceClient.GetIndividualEnrollmentAsync(individualEnrollment.RegistrationId).ConfigureAwait(false);
                },
                    s_provisioningServiceRetryPolicy,
                    s_retryableExceptions,
                    Logger)
                .ConfigureAwait(false);

                if (individualEnrollmentResult == null)
                {
                    throw new ArgumentException($"The individual enrollment with registration Id {individualEnrollment.RegistrationId} could not retrieved, exiting test.");
                }

                Assert.AreEqual(individualEnrollmentResult.ProvisioningStatus, ProvisioningStatus.Enabled);

                if (reprovisionPolicy != null)
                {
                    Assert.AreEqual(reprovisionPolicy.UpdateHubAssignment, individualEnrollmentResult.ReprovisionPolicy.UpdateHubAssignment);
                    Assert.AreEqual(reprovisionPolicy.MigrateDeviceData, individualEnrollmentResult.ReprovisionPolicy.MigrateDeviceData);
                }

                if (customAllocationDefinition != null)
                {
                    Assert.AreEqual(customAllocationDefinition.WebhookUrl, individualEnrollmentResult.CustomAllocationDefinition.WebhookUrl);
                    Assert.AreEqual(customAllocationDefinition.ApiVersion, individualEnrollmentResult.CustomAllocationDefinition.ApiVersion);
                }

                //allocation policy is never null
                Assert.AreEqual(allocationPolicy, individualEnrollmentResult.AllocationPolicy);

                await DeleteCreatedEnrollmentAsync(EnrollmentType.Individual, individualEnrollment.RegistrationId, null, Logger);
            }
        }
Exemplo n.º 18
0
        private async Task ConfirmDeviceWorksAfterReprovisioning(
            DeviceRegistrationResult result,
            Client.IAuthenticationMethod auth,
            Client.TransportType transportProtocol,
            ReprovisionPolicy reprovisionPolicy,
            bool transportProtocolSupportsTwinOperations)
        {
            using (var iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol))
            {
                Logger.Trace("DeviceClient OpenAsync.");
                await iotClient.OpenAsync().ConfigureAwait(false);
                Logger.Trace("DeviceClient SendEventAsync.");

                using var testMessage = new Client.Message(Encoding.UTF8.GetBytes("TestMessage"));
                await iotClient.SendEventAsync(testMessage).ConfigureAwait(false);

                // Twin can be configured to revert back to default twin when provisioned, or to keep twin
                // from previous hub's records.
                if (transportProtocolSupportsTwinOperations)
                {
                    Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false);

                    // Reprovision
                    if (reprovisionPolicy.UpdateHubAssignment)
                    {
                        // Migrate data
                        if (reprovisionPolicy.MigrateDeviceData)
                        {
                            // The reprovisioned twin should have an entry for the reprorted property updated previously in the test
                            // as a part of ConfirmRegisteredDeviceWorks.
                            // On device creation the twin reported property version starts at 1. For this scenario the reported property update
                            // operation should increment the version to 2.
                            twin.Properties.Reported.Count.Should().Be(1);
                            twin.Properties.Reported.Version.Should().Be(2);
                            result.Substatus.Should().Be(ProvisioningRegistrationSubstatusType.DeviceDataMigrated);
                        }
                        // Reset to initial configuration
                        else
                        {
                            // The reprovisioned twin should not have an entry for the reprorted property updated previously in the test
                            // as a part of ConfirmRegisteredDeviceWorks.
                            // On device creation the twin reported property version starts at 1.
                            twin.Properties.Reported.Count.Should().Be(0);
                            twin.Properties.Reported.Version.Should().Be(1);
                            result.Substatus.Should().Be(ProvisioningRegistrationSubstatusType.DeviceDataReset);
                        }
                    }
                    // Do not reprovision
                    else
                    {
                        // The reprovisioned twin should have an entry for the reprorted property updated previously in the test
                        // as a part of ConfirmRegisteredDeviceWorks.
                        // On device creation the twin reported property version starts at 1. For this scenario the reported property update
                        // operation should increment the version to 2.
                        twin.Properties.Reported.Count.Should().Be(1);
                        twin.Properties.Reported.Version.Should().Be(2);
                        result.Substatus.Should().Be(ProvisioningRegistrationSubstatusType.InitialAssignment);
                    }
                }

                Logger.Trace("DeviceClient CloseAsync.");
                await iotClient.CloseAsync().ConfigureAwait(false);
            }
        }