Пример #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);
            }
        }
Пример #2
0
        private static async Task <TestDevice> CreateDeviceAsync(TestDeviceType type, string prefix)
        {
            string deviceName = "E2E_" + prefix + Guid.NewGuid();

            // Delete existing devices named this way and create a new one.
            using var rm = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString);
            _logger.Trace($"{nameof(GetTestDeviceAsync)}: Creating device {deviceName} with type {type}.");

            Client.IAuthenticationMethod auth = null;

            var requestDevice = new Device(deviceName);

            if (type == TestDeviceType.X509)
            {
                requestDevice.Authentication = new AuthenticationMechanism
                {
                    X509Thumbprint = new X509Thumbprint
                    {
                        PrimaryThumbprint = Configuration.IoTHub.GetCertificateWithPrivateKey().Thumbprint
                    }
                };

                auth = new DeviceAuthenticationWithX509Certificate(deviceName, Configuration.IoTHub.GetCertificateWithPrivateKey());
            }

            Device device = await rm.AddDeviceAsync(requestDevice).ConfigureAwait(false);

            _logger.Trace($"{nameof(GetTestDeviceAsync)}: Pausing for {DelayAfterDeviceCreationSeconds}s after device was created.");
            await Task.Delay(DelayAfterDeviceCreationSeconds * 1000).ConfigureAwait(false);

            await rm.CloseAsync().ConfigureAwait(false);

            return(new TestDevice(device, auth));
        }
Пример #3
0
        public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok(
            string transportType,
            string securityType,
            X509EnrollmentType?x509EnrollmentType,
            TransportFallbackType?transportFallback,
            bool setCustomProxy,
            string proxyServerAddress = null)
        {
            using (ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportType, transportFallback))
                using (SecurityProvider security = await CreateSecurityProviderFromName(securityType, x509EnrollmentType).ConfigureAwait(false))
                {
                    _verboseLog.WriteLine("Creating device");

                    if (ImplementsWebProxy(transportType, transportFallback) && setCustomProxy)
                    {
                        transport.Proxy = (proxyServerAddress != null) ? new WebProxy(proxyServerAddress) : null;
                    }

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

                    var cts = new CancellationTokenSource(PassingTimeoutMiliseconds);

                    _log.WriteLine("ProvisioningDeviceClient RegisterAsync . . . ");
                    DeviceRegistrationResult result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false);

                    Assert.IsNotNull(result);
                    _log.WriteLine($"{result.Status} (Error Code: {result.ErrorCode}; Error Message: {result.ErrorMessage})");
                    _log.WriteLine($"ProvisioningDeviceClient AssignedHub: {result.AssignedHub}; DeviceID: {result.DeviceId}");

                    Assert.AreEqual(ProvisioningRegistrationStatusType.Assigned, result.Status);
                    Assert.IsNotNull(result.AssignedHub);
                    Assert.IsNotNull(result.DeviceId);

                    Client.IAuthenticationMethod auth = CreateAuthenticationMethodFromSecurityProvider(security, result.DeviceId);

                    // TODO: #591 - ProvisioningClient and DeviceClient should use the same protocol.
                    using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, Client.TransportType.Http1))
                    {
                        _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);

                        _log.WriteLine("DeviceClient CloseAsync.");
                        await iotClient.CloseAsync().ConfigureAwait(false);
                    }
                }
        }
Пример #4
0
        public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok(
            string transportType,
            string securityType,
            X509EnrollmentType?x509EnrollmentType,
            TransportFallbackType?transportFallback)
        {
            if (!ConfigurationFound())
            {
                _log.WriteLine("Provisioning test configuration not found. Result inconclusive.");
                return;
            }

            using (ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportType, transportFallback))
                using (SecurityProvider security = CreateSecurityProviderFromName(securityType, x509EnrollmentType))
                {
                    _verboseLog.WriteLine("Creating device");

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

                    var cts = new CancellationTokenSource(PassingTimeoutMiliseconds);

                    _log.WriteLine("ProvisioningClient RegisterAsync . . . ");
                    DeviceRegistrationResult result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false);

                    Assert.IsNotNull(result);
                    _log.WriteLine($"{result.Status} (Error Code: {result.ErrorCode}; Error Message: {result.ErrorMessage})");
                    _log.WriteLine($"ProvisioningClient AssignedHub: {result.AssignedHub}; DeviceID: {result.DeviceId}");

                    Assert.AreEqual(ProvisioningRegistrationStatusType.Assigned, result.Status);
                    Assert.IsNotNull(result.AssignedHub);
                    Assert.IsNotNull(result.DeviceId);

                    Client.IAuthenticationMethod auth = CreateAuthenticationMethodFromSecurityProvider(security, result.DeviceId);

                    using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, Client.TransportType.Mqtt_Tcp_Only))
                    {
                        _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);

                        _log.WriteLine("DeviceClient CloseAsync.");
                        await iotClient.CloseAsync().ConfigureAwait(false);
                    }
                }
        }
Пример #5
0
        private static async Task <TestDevice> CreateDeviceAsync(TestDeviceType type, string prefix)
        {
            string deviceName = "E2E_" + prefix + Guid.NewGuid();

            // Delete existing devices named this way and create a new one.
            using var rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString);
            s_logger.Trace($"{nameof(GetTestDeviceAsync)}: Creating device {deviceName} with type {type}.");

            Client.IAuthenticationMethod auth = null;

            var requestDevice = new Device(deviceName);
            X509Certificate2 authCertificate = null;

            if (type == TestDeviceType.X509)
            {
                requestDevice.Authentication = new AuthenticationMechanism
                {
                    X509Thumbprint = new X509Thumbprint
                    {
                        PrimaryThumbprint = TestConfiguration.IoTHub.GetCertificateWithPrivateKey().Thumbprint
                    }
                };

#pragma warning disable CA2000 // Dispose objects before losing scope - X509Certificate and DeviceAuthenticationWithX509Certificate are disposed when TestDevice is disposed.
                authCertificate = TestConfiguration.IoTHub.GetCertificateWithPrivateKey();
                auth            = new DeviceAuthenticationWithX509Certificate(deviceName, authCertificate);
#pragma warning restore CA2000 // Dispose objects before losing scope - X509Certificate and DeviceAuthenticationWithX509Certificate are disposed when TestDevice is disposed.
            }

            Device device = null;

            await RetryOperationHelper
            .RetryOperationsAsync(
                async() =>
            {
                device = await rm.AddDeviceAsync(requestDevice).ConfigureAwait(false);
            },
                s_exponentialBackoffRetryStrategy,
                s_retryableExceptions,
                s_logger)
            .ConfigureAwait(false);

            await rm.CloseAsync().ConfigureAwait(false);

            return(device == null
                ? throw new Exception($"Exhausted attempts for creating device {device.Id}, requests got throttled.")
                : new TestDevice(device, auth)
            {
                _authCertificate = authCertificate,
            });
        }
Пример #6
0
        /// <summary>
        /// Factory method.
        /// IMPORTANT: Not thread safe!
        /// </summary>
        /// <param name="namePrefix"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static async Task <TestDevice> GetTestDeviceAsync(string namePrefix, TestDeviceType type = TestDeviceType.Sasl)
        {
            string prefix = namePrefix + type + "_";

            if (!s_deviceCache.TryGetValue(prefix, out TestDevice testDevice))
            {
                string deviceName = prefix + Guid.NewGuid();
                s_log.WriteLine($"{nameof(GetTestDeviceAsync)}: Device with prefix {prefix} not found. Removing old devices.");

                // Delete existing devices named this way and create a new one.
                RegistryManager rm = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString);

                s_log.WriteLine($"{nameof(GetTestDeviceAsync)}: Creating device {deviceName} with type {type}.");

                Client.IAuthenticationMethod auth = null;

                Device requestDevice = new Device(deviceName);
                if (type == TestDeviceType.X509)
                {
                    requestDevice.Authentication = new AuthenticationMechanism()
                    {
                        X509Thumbprint = new X509Thumbprint()
                        {
                            PrimaryThumbprint = Configuration.IoTHub.GetCertificateWithPrivateKey().Thumbprint
                        }
                    };

                    auth = new DeviceAuthenticationWithX509Certificate(deviceName, Configuration.IoTHub.GetCertificateWithPrivateKey());
                }

                Device device = await rm.AddDeviceAsync(requestDevice).ConfigureAwait(false);

                s_log.WriteLine($"{nameof(GetTestDeviceAsync)}: Pausing for {DelayAfterDeviceCreationSeconds}s after device was created.");
                await Task.Delay(DelayAfterDeviceCreationSeconds * 1000).ConfigureAwait(false);

                s_deviceCache[prefix] = new TestDevice(device, auth);

                await rm.CloseAsync().ConfigureAwait(false);
            }

            TestDevice ret = s_deviceCache[prefix];

            s_log.WriteLine($"{nameof(GetTestDeviceAsync)}: Using device {ret.Id}.");
            return(ret);
        }
Пример #7
0
        public void Dispose()
        {
            // X509Certificate needs to be disposed for implementations !NET451 (NET451 doesn't implement X509Certificates as IDisposable).

            // Normally we wouldn't be disposing the X509 Certificates here, but rather delegate that to whoever was creating the TestDevice.
            // For the design that our test suite follows, it is ok to dispose the X509 certificate here since it won't be referenced by anyone else
            // within the scope of the test using this TestDevice.
            if (_authCertificate is IDisposable disposableCert)
            {
                disposableCert?.Dispose();
            }
            _authCertificate = null;

            if (AuthenticationMethod is DeviceAuthenticationWithX509Certificate x509Auth)
            {
                x509Auth?.Dispose();
            }
            AuthenticationMethod = null;
        }
Пример #8
0
 private TestDevice(Device device, Client.IAuthenticationMethod authenticationMethod)
 {
     Device = device;
     AuthenticationMethod = authenticationMethod;
 }
Пример #9
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);
            }
        }
Пример #10
0
        private async Task ConfirmExpectedDeviceCapabilities(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, DeviceCapabilities capabilities)
        {
            if (capabilities != null)
            {
                //hardcoding amqp since http does not support twin, but tests that call into this may use http
                using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, Client.TransportType.Amqp))
                {
                    //Confirm that the device twin reflects what the enrollment dictated
                    Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false);

                    Assert.AreEqual(capabilities.IotEdge, twin.Capabilities.IotEdge);
                }
            }
        }
Пример #11
0
        /// <summary>
        /// Attempt to create device client instance from provided arguments, ensure that it can open a
        /// connection, ensure that it can send telemetry, and (optionally) send a reported property update
        /// </summary>
        private async Task ConfirmRegisteredDeviceWorks(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, Client.TransportType transportProtocol, bool sendReportedPropertiesUpdate)
        {
            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);

                if (sendReportedPropertiesUpdate)
                {
                    _log.WriteLine("DeviceClient updating desired properties.");
                    Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false);

                    await iotClient.UpdateReportedPropertiesAsync(new TwinCollection($"{{\"{new Guid()}\":\"{new Guid()}\"}}")).ConfigureAwait(false);
                }

                _log.WriteLine("DeviceClient CloseAsync.");
                await iotClient.CloseAsync().ConfigureAwait(false);
            }
        }
Пример #12
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);
            }
        }
Пример #13
0
        /// <summary>
        /// Attempt to create device client instance from provided arguments, ensure that it can open a
        /// connection, ensure that it can send telemetry, and (optionally) send a reported property update
        /// </summary>
        private async Task ConfirmRegisteredDeviceWorks(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, Client.TransportType transportProtocol, 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 message = new Client.Message(Encoding.UTF8.GetBytes("TestMessage"));
                await iotClient.SendEventAsync(message).ConfigureAwait(false);

                if (transportProtocolSupportsTwinOperations)
                {
                    Logger.Trace("DeviceClient updating reported property.");
                    Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false);
                    await iotClient.UpdateReportedPropertiesAsync(new TwinCollection($"{{\"{new Guid()}\":\"{new Guid()}\"}}")).ConfigureAwait(false);
                }

                Logger.Trace("DeviceClient CloseAsync.");
                await iotClient.CloseAsync().ConfigureAwait(false);
            }
        }
Пример #14
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();
            }
        }