private async Task EnrollTenantForService(
            string enrollingTenantName,
            string serviceTenantName,
            string?enrollmentConfigurationName = null)
        {
            ITenantStore managementService =
                ContainerBindings.GetServiceProvider(this.scenarioContext).GetRequiredService <ITenantStore>();
            ITenantProvider tenantProvider = ContainerBindings.GetServiceProvider(this.scenarioContext).GetRequiredService <ITenantProvider>();

            ITenant enrollingTenant = await tenantProvider.GetTenantAsync(this.scenarioContext.Get <string>(enrollingTenantName)).ConfigureAwait(false);

            ITenant serviceTenant = await tenantProvider.GetTenantAsync(this.scenarioContext.Get <string>(serviceTenantName)).ConfigureAwait(false);

            List <EnrollmentConfigurationItem> enrollmentConfiguration =
                string.IsNullOrEmpty(enrollmentConfigurationName)
                ? new List <EnrollmentConfigurationItem>()
                : this.scenarioContext.Get <List <EnrollmentConfigurationItem> >(enrollmentConfigurationName);

            await CatchException.AndStoreInScenarioContextAsync(
                this.scenarioContext,
                () => managementService.EnrollInServiceAsync(
                    enrollingTenant,
                    serviceTenant,
                    enrollmentConfiguration.ToArray())).ConfigureAwait(false);
        }
        /// <summary>
        /// Enrolls the specified tenant in the service.
        /// </summary>
        /// <param name="tenantStore">The <see cref="ITenantStore"/>.</param>
        /// <param name="enrollingTenantId">The Id of the tenant to enroll.</param>
        /// <param name="serviceTenantId">The Id of the service to enroll in.</param>
        /// <param name="configurationItems">Configuration for the enrollment.</param>
        /// <returns>A task which completes when the enrollment has finished.</returns>
        public static async Task EnrollInServiceAsync(
            this ITenantStore tenantStore,
            string enrollingTenantId,
            string serviceTenantId,
            EnrollmentConfigurationItem[] configurationItems)
        {
            if (string.IsNullOrWhiteSpace(enrollingTenantId))
            {
                throw new ArgumentException(nameof(enrollingTenantId));
            }

            if (string.IsNullOrWhiteSpace(serviceTenantId))
            {
                throw new ArgumentException(nameof(serviceTenantId));
            }

            if (configurationItems == null)
            {
                throw new ArgumentNullException(nameof(configurationItems));
            }

            // Enrolling tenant can be either a Client tenant or a Delegated tenant.
            ITenant enrollingTenant = await tenantStore.GetTenantOfTypeAsync(
                enrollingTenantId,
                MarainTenantType.Client,
                MarainTenantType.Delegated).ConfigureAwait(false);

            ITenant serviceTenant = await tenantStore.GetServiceTenantAsync(serviceTenantId).ConfigureAwait(false);

            await tenantStore.EnrollInServiceAsync(enrollingTenant, serviceTenant, configurationItems).ConfigureAwait(false);
        }
        /// <summary>
        /// Enrolls the specified tenant in the service.
        /// </summary>
        /// <param name="tenantStore">The <see cref="ITenantStore"/>.</param>
        /// <param name="enrollingTenant">The tenant to enroll.</param>
        /// <param name="serviceTenantId">The Id of the service to enroll in.</param>
        /// <param name="configurationItems">Configuration for the enrollment.</param>
        /// <returns>A task which completes when the enrollment has finished.</returns>
        public static async Task EnrollInServiceAsync(
            this ITenantStore tenantStore,
            ITenant enrollingTenant,
            string serviceTenantId,
            EnrollmentConfigurationItem[] configurationItems)
        {
            // Enrolling tenant validation will happen when we call through to the next method to do the enrollment, so no
            // need to do it here as well.
            if (string.IsNullOrWhiteSpace(serviceTenantId))
            {
                throw new ArgumentException(nameof(serviceTenantId));
            }

            if (configurationItems == null)
            {
                throw new ArgumentNullException(nameof(configurationItems));
            }

            ITenant serviceTenant = await tenantStore.GetServiceTenantAsync(serviceTenantId).ConfigureAwait(false);

            await tenantStore.EnrollInServiceAsync(enrollingTenant, serviceTenant, configurationItems).ConfigureAwait(false);
        }
        /// <summary>
        /// Enrolls the specified tenant in the service.
        /// </summary>
        /// <param name="tenantStore">The tenant store.</param>
        /// <param name="enrollingTenant">The tenant to enroll.</param>
        /// <param name="serviceTenant">The service to enroll in.</param>
        /// <param name="configurationItems">Configuration for the enrollment.</param>
        /// <param name="logger">Optional logger.</param>
        /// <returns>A task which completes when the enrollment has finished.</returns>
        public static async Task EnrollInServiceAsync(
            this ITenantStore tenantStore,
            ITenant enrollingTenant,
            ITenant serviceTenant,
            EnrollmentConfigurationItem[] configurationItems,
            ILogger?logger = null)
        {
            if (enrollingTenant == null)
            {
                throw new ArgumentNullException(nameof(enrollingTenant));
            }

            enrollingTenant.EnsureTenantIsOfType(MarainTenantType.Client, MarainTenantType.Delegated);

            if (serviceTenant == null)
            {
                throw new ArgumentNullException(nameof(serviceTenant));
            }

            serviceTenant.EnsureTenantIsOfType(MarainTenantType.Service);

            if (configurationItems == null)
            {
                throw new ArgumentNullException(nameof(configurationItems));
            }

            logger?.LogDebug(
                "Enrolling tenant '{enrollingTenantName}' with Id '{enrollingTenantId}' from service '{serviceTenantName}' with Id '{serviceTenantId}'",
                enrollingTenant.Name,
                enrollingTenant.Id,
                serviceTenant.Name,
                serviceTenant.Id);

            // First we need to ensure that all the required config items for both the service being enrolled in,
            // as well as any dependent services is provided.
            ServiceManifestRequiredConfigurationEntry[] requiredConfig = await tenantStore.GetServiceEnrollmentConfigurationRequirementsAsync(serviceTenant).ConfigureAwait(false);

            logger?.LogDebug("Validating supplied configuration against required config.");
            configurationItems.ValidateAndThrow(requiredConfig);

            ServiceManifest manifest = serviceTenant.GetServiceManifest();

            // Now, match up the required config items for this service to the relevent supplied config (we may
            // have been supplied with config for dependent services as well, so we can't just attach all
            // of the supplied config items to the enrolling tenant - some of it could belong on delegated
            // tenants.
            logger?.LogDebug(
                "Attaching required configuration items to tenant '{serviceTenantName}' with Id '{serviceTenantId}'",
                serviceTenant.Name,
                serviceTenant.Id);

            IEnumerable <(ServiceManifestRequiredConfigurationEntry RequiredConfigurationEntry, EnrollmentConfigurationItem ProvidedConfigurationItem)> matchedConfigItems =
                manifest.RequiredConfigurationEntries.Select(
                    requiredConfigItem =>
                    (requiredConfigItem, configurationItems.Single(item => item.Key == requiredConfigItem.Key)));

            IEnumerable <KeyValuePair <string, object> > propertiesToAddToEnrollingTenant = PropertyBagValues.Empty;

            foreach ((ServiceManifestRequiredConfigurationEntry RequiredConfigurationEntry, EnrollmentConfigurationItem ProvidedConfigurationItem)current in matchedConfigItems)
            {
                logger?.LogDebug(
                    "Adding configuration entry '{requiredConfigurationEntryKey}' to tenant '{serviceTenantName}' with Id '{serviceTenantId}'",
                    current.RequiredConfigurationEntry.Key,
                    serviceTenant.Name,
                    serviceTenant.Id);

                propertiesToAddToEnrollingTenant = current.RequiredConfigurationEntry.AddToTenantProperties(
                    propertiesToAddToEnrollingTenant, current.ProvidedConfigurationItem);
            }

            // Add an enrollment entry to the tenant.
            logger?.LogDebug(
                "Adding service enrollment to tenant '{serviceTenantName}' with Id '{serviceTenantId}'",
                serviceTenant.Name,
                serviceTenant.Id);

            propertiesToAddToEnrollingTenant = propertiesToAddToEnrollingTenant.AddServiceEnrollment(
                enrollingTenant, serviceTenant.Id);

            // Update the tenant now, so that the tenant type is correctly set - otherwise
            // recursive enrollments will fail
            logger?.LogDebug(
                "Updating tenant '{enrollingTenantName}' with Id '{enrollingTenantId}'",
                enrollingTenant.Name,
                enrollingTenant.Id);

            enrollingTenant = await tenantStore.UpdateTenantAsync(
                enrollingTenant.Id,
                propertiesToSetOrAdd : propertiesToAddToEnrollingTenant)
                              .ConfigureAwait(false);

            propertiesToAddToEnrollingTenant = PropertyBagValues.Empty;

            // If this service has dependencies, we need to create a new delegated tenant for the service to use when
            // accessing those dependencies.
            if (manifest.DependsOnServiceTenants.Count > 0)
            {
                logger?.LogDebug(
                    "Service '{serviceTenantName}' has dependencies. Creating delegated tenant for enrollment.",
                    serviceTenant.Name);

                ITenant delegatedTenant = await tenantStore.CreateDelegatedTenant(enrollingTenant, serviceTenant).ConfigureAwait(false);

                // Now enroll the new delegated tenant for all of the dependent services.
                await Task.WhenAll(manifest.DependsOnServiceTenants.Select(
                                       dependsOnService => tenantStore.EnrollInServiceAsync(
                                           delegatedTenant,
                                           dependsOnService.Id,
                                           configurationItems))).ConfigureAwait(false);

                // Add the delegated tenant Id to the enrolling tenant
                logger?.LogDebug(
                    "Setting delegated tenant for client '{enrollingTenantName}' with Id '{enrollingTenantId}' in service '{serviceTenantName}' with Id '{serviceTenantId}' to new tenant '{delegatedTenantName}' with Id '{delegatedTenantId}'.",
                    enrollingTenant.Name,
                    enrollingTenant.Id,
                    serviceTenant.Name,
                    serviceTenant.Id,
                    delegatedTenant.Name,
                    delegatedTenant.Id);

                propertiesToAddToEnrollingTenant = propertiesToAddToEnrollingTenant.SetDelegatedTenantForService(
                    enrollingTenant, serviceTenant, delegatedTenant);

                logger?.LogDebug(
                    "Updating tenant '{enrollingTenantName}' with Id '{enrollingTenantId}'",
                    enrollingTenant.Name,
                    enrollingTenant.Id);

                await tenantStore.UpdateTenantAsync(
                    enrollingTenant.Id,
                    propertiesToSetOrAdd : propertiesToAddToEnrollingTenant)
                .ConfigureAwait(false);
            }

            logger?.LogInformation(
                "Successfully enrolled tenant '{enrollingTenantName}' with Id '{enrollingTenant.Id}' for service '{serviceTenantName}' with Id '{serviceTenantId}'",
                enrollingTenant.Name,
                enrollingTenant.Id,
                serviceTenant.Name,
                serviceTenant.Id);
        }