diff --git a/Packages.props b/Packages.props index 64fcaa34..57cdecbd 100644 --- a/Packages.props +++ b/Packages.props @@ -26,6 +26,7 @@ + diff --git a/bin/nuget/Microsoft.SqlServer.Migration.Tde.1.0.20230110.39.nupkg b/bin/nuget/Microsoft.SqlServer.Migration.Tde.1.0.20230110.39.nupkg new file mode 100644 index 00000000..b3d57e0d Binary files /dev/null and b/bin/nuget/Microsoft.SqlServer.Migration.Tde.1.0.20230110.39.nupkg differ diff --git a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj index 725d9674..7b5b427a 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj +++ b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj @@ -54,6 +54,7 @@ + diff --git a/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/CertificateMigrationProgressEvent.cs b/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/CertificateMigrationProgressEvent.cs new file mode 100644 index 00000000..cd987c86 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/CertificateMigrationProgressEvent.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.SqlTools.Hosting.Protocol.Contracts; + +namespace Microsoft.SqlTools.ServiceLayer.Migration.Contracts +{ + /// + /// Parameters for the certificate migration progress event + /// + public class CertificateMigrationProgressParams + { + /// + /// Database name + /// + public string Name { get; set; } + + /// + /// Message related to the success status. true should describe a positive outcome. false should have an error. + /// + public string Message { get; set; } + + /// + /// Result of migration + /// + public bool Success { get; set; } + } + + + /// + /// Create a certificate migration request. This should be register at the client. + /// + public class CertificateMigrationProgressEvent + { + /// + /// Name and parameters for the event definition. + /// + public static readonly + EventType Type = + EventType.Create("migration/tdemigrationprogress"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/CertificateMigrationRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/CertificateMigrationRequest.cs new file mode 100644 index 00000000..2e4d117c --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/CertificateMigrationRequest.cs @@ -0,0 +1,111 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; +using Microsoft.SqlTools.Hosting.Protocol.Contracts; + +namespace Microsoft.SqlTools.ServiceLayer.Migration.Contracts +{ + /// + /// Parameters for the certificate migration operation + /// + public class CertificateMigrationParams + { + /// + /// List of databses to migrate the certificates + /// + public List EncryptedDatabases { get; set; } = new List(); + + /// + /// Source connection string to the server + /// + public string SourceSqlConnectionString { get; set; } + + /// + /// Target subscription id + /// + public string TargetSubscriptionId { get; set; } + + /// + /// Target resource group name + /// + public string TargetResourceGroupName { get; set; } + + /// + /// Target manages instance name + /// + public string TargetManagedInstanceName { get; set; } + + /// + /// Place where certificates will be exported + /// + public string NetworkSharePath { get; set; } + + /// + /// Domain for the user credentials able to read from the shared path + /// + public string NetworkShareDomain { get; set; } + + /// + /// Username for the credentials able to read from the shared path + /// + public string NetworkShareUserName { get; set; } + + /// + /// Password for the credentials able to read from the shared path + /// + public string NetworkSharePassword { get; set; } + + /// + /// Access token for the ARM client + /// + public string AccessToken { get; set; } + } + + /// + /// Result for the certificate migration operation + /// + public class CertificateMigrationResult + { + /// + /// List of the status of each certificate migration result attempted. + /// + public List MigrationStatuses { get; set; } = new List(); + } + + /// + /// Result for an individual database certificate migration + /// + public class CertificateMigrationEntryResult + { + /// + /// The name of the database this result represent + /// + public string DbName { get; set; } + + /// + /// The result of the migration. + /// + public bool Success { get; set; } + + /// + /// Description of the success status or the error message encountered when the migration was not successful + /// + public string Message { get; set; } + } + + /// + /// Certificate migration request definition + /// + public class CertificateMigrationRequest + { + /// + /// Name, parameter and return type for the certicate migration operation + /// + public static readonly + RequestType Type = + RequestType.Create("migration/tdemigration"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/Migration/MigrationService.cs b/src/Microsoft.SqlTools.ServiceLayer/Migration/MigrationService.cs index 4f6c1f1b..e62f951a 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Migration/MigrationService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Migration/MigrationService.cs @@ -46,6 +46,9 @@ using Microsoft.SqlServer.Migration.Logins.Contracts; using Microsoft.SqlServer.Migration.Assessment.Common.Models; using Microsoft.SqlServer.Migration.Assessment.Common.Utils; using Microsoft.SqlTools.ServiceLayer.Migration.Utils; +using Microsoft.SqlServer.Migration.Tde; +using Microsoft.SqlServer.Migration.Tde.Common; +using Microsoft.SqlTools.ServiceLayer.Migration.Models; namespace Microsoft.SqlTools.ServiceLayer.Migration { @@ -131,6 +134,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration this.ServiceHost.SetRequestHandler(MigrateLoginsRequest.Type, HandleMigrateLogins, true); this.ServiceHost.SetRequestHandler(EstablishUserMappingRequest.Type, HandleEstablishUserMapping, true); this.ServiceHost.SetRequestHandler(MigrateServerRolesAndSetPermissionsRequest.Type, HandleMigrateServerRolesAndSetPermissions, true); + this.ServiceHost.SetRequestHandler(CertificateMigrationRequest.Type, HandleTdeCertificateMigrationRequest); } /// @@ -316,7 +320,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration ElasticSqlDbRecommendationDurationInMs = elasticResults.sqlDbDurationInMs, ElasticSqlMiRecommendationResults = elasticResults.sqlMiResults, ElasticSqlMiRecommendationDurationInMs = elasticResults.sqlMiDurationInMs, - ElasticSqlVmRecommendationResults = elasticResults.sqlVmResults, + ElasticSqlVmRecommendationResults = elasticResults.sqlVmResults, ElasticSqlVmRecommendationDurationInMs = elasticResults.sqlVmDurationInMs, InstanceRequirements = req, SkuRecommendationReportPaths = new List { baselineResults.sqlDbReportPath, baselineResults.sqlMiReportPath, baselineResults.sqlVmReportPath }, @@ -336,23 +340,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration } internal async Task HandleStartLoginMigration( - StartLoginMigrationParams parameters, + StartLoginMigrationParams parameters, RequestContext requestContext) { try { ILoginsMigration loginMigration = new LoginsMigration(parameters.SourceConnectionString, parameters.TargetConnectionString, null, parameters.LoginList, parameters.AADDomainName); - + IDictionary> exceptionMap = new Dictionary>(); - exceptionMap.AddExceptions( await loginMigration.StartValidations(CancellationToken.None) ); - exceptionMap.AddExceptions( await loginMigration.MigrateLogins(CancellationToken.None) ); - exceptionMap.AddExceptions( loginMigration.MigrateServerRoles(CancellationToken.None) ); - exceptionMap.AddExceptions( loginMigration.EstablishUserMapping(CancellationToken.None) ); - exceptionMap.AddExceptions( await loginMigration.EstablishServerRoleMapping(CancellationToken.None) ); - exceptionMap.AddExceptions( loginMigration.SetLoginPermissions(CancellationToken.None) ); - exceptionMap.AddExceptions( loginMigration.SetServerRolePermissions(CancellationToken.None) ); + exceptionMap.AddExceptions(await loginMigration.StartValidations(CancellationToken.None)); + exceptionMap.AddExceptions(await loginMigration.MigrateLogins(CancellationToken.None)); + exceptionMap.AddExceptions(loginMigration.MigrateServerRoles(CancellationToken.None)); + exceptionMap.AddExceptions(loginMigration.EstablishUserMapping(CancellationToken.None)); + exceptionMap.AddExceptions(await loginMigration.EstablishServerRoleMapping(CancellationToken.None)); + exceptionMap.AddExceptions(loginMigration.SetLoginPermissions(CancellationToken.None)); + exceptionMap.AddExceptions(loginMigration.SetServerRolePermissions(CancellationToken.None)); LoginMigrationResult results = new LoginMigrationResult() { @@ -370,7 +374,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration internal async Task HandleValidateLoginMigration( StartLoginMigrationParams parameters, RequestContext requestContext) - { + { try { ILoginsMigration loginMigration = new LoginsMigration(parameters.SourceConnectionString, parameters.TargetConnectionString, @@ -379,7 +383,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration IDictionary> exceptionMap = new Dictionary>(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); - exceptionMap.AddExceptions( await loginMigration.StartValidations(CancellationToken.None) ); + exceptionMap.AddExceptions(await loginMigration.StartValidations(CancellationToken.None)); stopWatch.Stop(); TimeSpan elapsedTime = stopWatch.Elapsed; @@ -411,8 +415,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration IDictionary> exceptionMap = new Dictionary>(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); - exceptionMap.AddExceptions( await loginMigration.StartValidations(CancellationToken.None) ); - exceptionMap.AddExceptions( await loginMigration.MigrateLogins(CancellationToken.None) ); + exceptionMap.AddExceptions(await loginMigration.StartValidations(CancellationToken.None)); + exceptionMap.AddExceptions(await loginMigration.MigrateLogins(CancellationToken.None)); stopWatch.Stop(); TimeSpan elapsedTime = stopWatch.Elapsed; @@ -441,11 +445,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration null, parameters.LoginList, parameters.AADDomainName); IDictionary> exceptionMap = new Dictionary>(); - + Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); - exceptionMap.AddExceptions( await loginMigration.StartValidations(CancellationToken.None) ); - exceptionMap.AddExceptions( loginMigration.EstablishUserMapping(CancellationToken.None) ); + exceptionMap.AddExceptions(await loginMigration.StartValidations(CancellationToken.None)); + exceptionMap.AddExceptions(loginMigration.EstablishUserMapping(CancellationToken.None)); stopWatch.Stop(); TimeSpan elapsedTime = stopWatch.Elapsed; @@ -501,7 +505,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration ExceptionMap = exceptionMap, CompletedStep = LoginMigrationStep.MigrateServerRoles, ElapsedTime = MigrationServiceHelper.FormatTimeSpan(elapsedTime) - }); + }); stopWatch.Restart(); exceptionMap.AddExceptions(await loginMigration.EstablishServerRoleMapping(CancellationToken.None)); @@ -560,120 +564,123 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration } } - internal RecommendationResultSet GenerateBaselineRecommendations(SqlInstanceRequirements req, GetSkuRecommendationsParams parameters) { - RecommendationResultSet resultSet = new RecommendationResultSet(); + internal RecommendationResultSet GenerateBaselineRecommendations(SqlInstanceRequirements req, GetSkuRecommendationsParams parameters) + { + RecommendationResultSet resultSet = new RecommendationResultSet(); - SkuRecommendationServiceProvider provider = new SkuRecommendationServiceProvider(new AzureSqlSkuBillingServiceProvider()); - AzurePreferences prefs = new AzurePreferences() { - EligibleSkuCategories = null, // eligible SKU list will be adjusted with each recommendation type - ScalingFactor = parameters.ScalingFactor / 100.0, - TargetEnvironment = TargetEnvironmentType.Production - }; - - // generate SQL DB recommendations, if applicable - if (parameters.TargetPlatforms.Contains("AzureSqlDatabase")) + SkuRecommendationServiceProvider provider = new SkuRecommendationServiceProvider(new AzureSqlSkuBillingServiceProvider()); + AzurePreferences prefs = new AzurePreferences() + { + EligibleSkuCategories = null, // eligible SKU list will be adjusted with each recommendation type + ScalingFactor = parameters.ScalingFactor / 100.0, + TargetEnvironment = TargetEnvironmentType.Production + }; + + // generate SQL DB recommendations, if applicable + if (parameters.TargetPlatforms.Contains("AzureSqlDatabase")) + { + Stopwatch sqlDbStopwatch = new Stopwatch(); + sqlDbStopwatch.Start(); + + prefs.EligibleSkuCategories = GetEligibleSkuCategories("AzureSqlDatabase", parameters.IncludePreviewSkus); + resultSet.sqlDbResults = provider.GetSkuRecommendation(prefs, req); + + sqlDbStopwatch.Stop(); + resultSet.sqlDbDurationInMs = sqlDbStopwatch.ElapsedMilliseconds; + + SkuRecommendationReport sqlDbReport = new SkuRecommendationReport( + new Dictionary> { { req, resultSet.sqlDbResults } }, + AzureSqlTargetPlatform.AzureSqlDatabase.ToString()); + var sqlDbRecommendationReportFileName = String.Format("SkuRecommendationReport-AzureSqlDatabase-Baseline-{0}", DateTime.UtcNow.ToString("yyyyMMddHH-mmss", CultureInfo.InvariantCulture)); + var sqlDbRecommendationReportFullPath = Path.Combine(SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, sqlDbRecommendationReportFileName); + ExportRecommendationResultsAction.ExportRecommendationResults(sqlDbReport, SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, false, sqlDbRecommendationReportFileName); + resultSet.sqlDbReportPath = sqlDbRecommendationReportFullPath + ".html"; + } + + // generate SQL MI recommendations, if applicable + if (parameters.TargetPlatforms.Contains("AzureSqlManagedInstance")) + { + Stopwatch sqlMiStopwatch = new Stopwatch(); + sqlMiStopwatch.Start(); + + prefs.EligibleSkuCategories = GetEligibleSkuCategories("AzureSqlManagedInstance", parameters.IncludePreviewSkus); + resultSet.sqlMiResults = provider.GetSkuRecommendation(prefs, req); + + // if no result was generated, create a result with a null SKU + if (!resultSet.sqlMiResults.Any()) { - Stopwatch sqlDbStopwatch = new Stopwatch(); - sqlDbStopwatch.Start(); - - prefs.EligibleSkuCategories = GetEligibleSkuCategories("AzureSqlDatabase", parameters.IncludePreviewSkus); - resultSet.sqlDbResults = provider.GetSkuRecommendation(prefs, req); - - sqlDbStopwatch.Stop(); - resultSet.sqlDbDurationInMs = sqlDbStopwatch.ElapsedMilliseconds; - - SkuRecommendationReport sqlDbReport = new SkuRecommendationReport( - new Dictionary> { { req, resultSet.sqlDbResults } }, - AzureSqlTargetPlatform.AzureSqlDatabase.ToString()); - var sqlDbRecommendationReportFileName = String.Format("SkuRecommendationReport-AzureSqlDatabase-Baseline-{0}", DateTime.UtcNow.ToString("yyyyMMddHH-mmss", CultureInfo.InvariantCulture)); - var sqlDbRecommendationReportFullPath = Path.Combine(SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, sqlDbRecommendationReportFileName); - ExportRecommendationResultsAction.ExportRecommendationResults(sqlDbReport, SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, false, sqlDbRecommendationReportFileName); - resultSet.sqlDbReportPath = sqlDbRecommendationReportFullPath + ".html"; - } - - // generate SQL MI recommendations, if applicable - if (parameters.TargetPlatforms.Contains("AzureSqlManagedInstance")) - { - Stopwatch sqlMiStopwatch = new Stopwatch(); - sqlMiStopwatch.Start(); - - prefs.EligibleSkuCategories = GetEligibleSkuCategories("AzureSqlManagedInstance", parameters.IncludePreviewSkus); - resultSet.sqlMiResults = provider.GetSkuRecommendation(prefs, req); - - // if no result was generated, create a result with a null SKU - if (!resultSet.sqlMiResults.Any()) + resultSet.sqlMiResults.Add(new SkuRecommendationResult() { - resultSet.sqlMiResults.Add(new SkuRecommendationResult() - { - SqlInstanceName = parameters.TargetSqlInstance, - DatabaseName = null, - TargetSku = null, - MonthlyCost = null, - Ranking = -1, - PositiveJustifications = null, - NegativeJustifications = null, - }); - } - - sqlMiStopwatch.Stop(); - resultSet.sqlMiDurationInMs = sqlMiStopwatch.ElapsedMilliseconds; - - SkuRecommendationReport sqlMiReport = new SkuRecommendationReport( - new Dictionary> { { req, resultSet.sqlMiResults } }, - AzureSqlTargetPlatform.AzureSqlManagedInstance.ToString()); - var sqlMiRecommendationReportFileName = String.Format("SkuRecommendationReport-AzureSqlManagedInstance-Baseline-{0}", DateTime.UtcNow.ToString("yyyyMMddHH-mmss", CultureInfo.InvariantCulture)); - var sqlMiRecommendationReportFullPath = Path.Combine(SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, sqlMiRecommendationReportFileName); - ExportRecommendationResultsAction.ExportRecommendationResults(sqlMiReport, SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, false, sqlMiRecommendationReportFileName); - resultSet.sqlMiReportPath = sqlMiRecommendationReportFullPath + ".html"; + SqlInstanceName = parameters.TargetSqlInstance, + DatabaseName = null, + TargetSku = null, + MonthlyCost = null, + Ranking = -1, + PositiveJustifications = null, + NegativeJustifications = null, + }); } - // generate SQL VM recommendations, if applicable - if (parameters.TargetPlatforms.Contains("AzureSqlVirtualMachine")) + sqlMiStopwatch.Stop(); + resultSet.sqlMiDurationInMs = sqlMiStopwatch.ElapsedMilliseconds; + + SkuRecommendationReport sqlMiReport = new SkuRecommendationReport( + new Dictionary> { { req, resultSet.sqlMiResults } }, + AzureSqlTargetPlatform.AzureSqlManagedInstance.ToString()); + var sqlMiRecommendationReportFileName = String.Format("SkuRecommendationReport-AzureSqlManagedInstance-Baseline-{0}", DateTime.UtcNow.ToString("yyyyMMddHH-mmss", CultureInfo.InvariantCulture)); + var sqlMiRecommendationReportFullPath = Path.Combine(SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, sqlMiRecommendationReportFileName); + ExportRecommendationResultsAction.ExportRecommendationResults(sqlMiReport, SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, false, sqlMiRecommendationReportFileName); + resultSet.sqlMiReportPath = sqlMiRecommendationReportFullPath + ".html"; + } + + // generate SQL VM recommendations, if applicable + if (parameters.TargetPlatforms.Contains("AzureSqlVirtualMachine")) + { + Stopwatch sqlVmStopwatch = new Stopwatch(); + sqlVmStopwatch.Start(); + + prefs.EligibleSkuCategories = GetEligibleSkuCategories("AzureSqlVirtualMachine", parameters.IncludePreviewSkus); + resultSet.sqlVmResults = provider.GetSkuRecommendation(prefs, req); + + // if no result was generated, create a result with a null SKU + if (!resultSet.sqlVmResults.Any()) { - Stopwatch sqlVmStopwatch = new Stopwatch(); - sqlVmStopwatch.Start(); - - prefs.EligibleSkuCategories = GetEligibleSkuCategories("AzureSqlVirtualMachine", parameters.IncludePreviewSkus); - resultSet.sqlVmResults = provider.GetSkuRecommendation(prefs, req); - - // if no result was generated, create a result with a null SKU - if (!resultSet.sqlVmResults.Any()) + resultSet.sqlVmResults.Add(new SkuRecommendationResult() { - resultSet.sqlVmResults.Add(new SkuRecommendationResult() - { - SqlInstanceName = parameters.TargetSqlInstance, - DatabaseName = null, - TargetSku = null, - MonthlyCost = null, - Ranking = -1, - PositiveJustifications = null, - NegativeJustifications = null, - }); - } - - sqlVmStopwatch.Stop(); - resultSet.sqlVmDurationInMs = sqlVmStopwatch.ElapsedMilliseconds; - - SkuRecommendationReport sqlVmReport = new SkuRecommendationReport( - new Dictionary> { { req, resultSet.sqlVmResults } }, - AzureSqlTargetPlatform.AzureSqlVirtualMachine.ToString()); - var sqlVmRecommendationReportFileName = String.Format("SkuRecommendationReport-AzureSqlVirtualMachine-Baseline-{0}", DateTime.UtcNow.ToString("yyyyMMddHH-mmss", CultureInfo.InvariantCulture)); - var sqlVmRecommendationReportFullPath = Path.Combine(SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, sqlVmRecommendationReportFileName); - ExportRecommendationResultsAction.ExportRecommendationResults(sqlVmReport, SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, false, sqlVmRecommendationReportFileName); - resultSet.sqlVmReportPath = sqlVmRecommendationReportFullPath + ".html"; + SqlInstanceName = parameters.TargetSqlInstance, + DatabaseName = null, + TargetSku = null, + MonthlyCost = null, + Ranking = -1, + PositiveJustifications = null, + NegativeJustifications = null, + }); } - return resultSet; + sqlVmStopwatch.Stop(); + resultSet.sqlVmDurationInMs = sqlVmStopwatch.ElapsedMilliseconds; + + SkuRecommendationReport sqlVmReport = new SkuRecommendationReport( + new Dictionary> { { req, resultSet.sqlVmResults } }, + AzureSqlTargetPlatform.AzureSqlVirtualMachine.ToString()); + var sqlVmRecommendationReportFileName = String.Format("SkuRecommendationReport-AzureSqlVirtualMachine-Baseline-{0}", DateTime.UtcNow.ToString("yyyyMMddHH-mmss", CultureInfo.InvariantCulture)); + var sqlVmRecommendationReportFullPath = Path.Combine(SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, sqlVmRecommendationReportFileName); + ExportRecommendationResultsAction.ExportRecommendationResults(sqlVmReport, SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, false, sqlVmRecommendationReportFileName); + resultSet.sqlVmReportPath = sqlVmRecommendationReportFullPath + ".html"; + } + + return resultSet; } - internal RecommendationResultSet GenerateElasticRecommendations(SqlInstanceRequirements req, GetSkuRecommendationsParams parameters) { + internal RecommendationResultSet GenerateElasticRecommendations(SqlInstanceRequirements req, GetSkuRecommendationsParams parameters) + { RecommendationResultSet resultSet = new RecommendationResultSet(); CsvAggregatorForElasticStrategy elasticaggregator = new CsvAggregatorForElasticStrategy( instanceId: parameters.TargetSqlInstance, - directory: parameters.DataFolder, - queryInterval: parameters.PerfQueryIntervalInSec, - logger: null, + directory: parameters.DataFolder, + queryInterval: parameters.PerfQueryIntervalInSec, + logger: null, dbsToInclude: new HashSet(parameters.DatabaseAllowList)); // generate SQL DB recommendations, if applicable @@ -698,7 +705,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration resultSet.sqlDbDurationInMs = sqlDbStopwatch.ElapsedMilliseconds; SkuRecommendationReport sqlDbReport = new SkuRecommendationReport( - new Dictionary> { { req, resultSet.sqlDbResults } }, + new Dictionary> { { req, resultSet.sqlDbResults } }, AzureSqlTargetPlatform.AzureSqlDatabase.ToString()); var sqlDbRecommendationReportFileName = String.Format("SkuRecommendationReport-AzureSqlDatabase-Elastic-{0}", DateTime.UtcNow.ToString("yyyyMMddHH-mmss", CultureInfo.InvariantCulture)); var sqlDbRecommendationReportFullPath = Path.Combine(SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, sqlDbRecommendationReportFileName); @@ -989,7 +996,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration AzureSqlPaaSHardwareType.PremiumSeries)); if (includePreviewSkus) - { + { // Premium Memory Optimized BC/GP eligibleSkuCategories.Add(new AzureSqlSkuPaaSCategory( AzureSqlTargetPlatform.AzureSqlManagedInstance, @@ -1043,6 +1050,78 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration return eligibleSkuCategories; } + /// + /// Request handler for the certifica migration operation + /// + /// Parameters for the operation, as register during the type definition + /// Context provided by the framework + /// + internal async Task HandleTdeCertificateMigrationRequest( + CertificateMigrationParams parameters, + RequestContext requestContext) + { + var result = new CertificateMigrationResult(); + + var credentials = new StaticTokenCredential(parameters.AccessToken); //New token provided, will change to shared ADS cache later. + + // Reuse the tde migration client + var tdeMigrationClient = new TdeMigration( + parameters.SourceSqlConnectionString, + parameters.TargetSubscriptionId, + parameters.TargetResourceGroupName, + parameters.TargetManagedInstanceName, + parameters.NetworkSharePath, + parameters.NetworkShareDomain, + parameters.NetworkShareUserName, + parameters.NetworkSharePassword, + credentials + ); + + foreach (var dbName in parameters.EncryptedDatabases) + { + var migrationResult = await MigrateCertificate(tdeMigrationClient, dbName); + + var eventData = new CertificateMigrationProgressParams + { + Name = dbName, + Success = migrationResult.Success, + Message = migrationResult.Message + }; + await requestContext.SendEvent(CertificateMigrationProgressEvent.Type, eventData); + + result.MigrationStatuses.Add(migrationResult); + } + + await requestContext.SendResult(result); + } + + /// + /// Individual certificate migration operation + /// + /// Instance of the migration client + /// Name of the database to migrate + /// + private async Task MigrateCertificate(TdeMigration tdeMigrationClient, string dbName) + { + try + { + var result = await tdeMigrationClient.MigrateTdeCertificate(dbName, CancellationToken.None); + + if (result is TdeExceptionResult tdeExceptionResult) + { + return new CertificateMigrationEntryResult { DbName = dbName, Success = result.IsSuccess, Message = tdeExceptionResult.Exception.Message }; + } + else + { + return new CertificateMigrationEntryResult { DbName = dbName, Success = result.IsSuccess, Message = result.UserFriendlyMessage }; + } + } + catch (Exception ex) + { + return new CertificateMigrationEntryResult { DbName = dbName, Success = false, Message = ex.Message }; + } + } + /// /// Disposes the Migration Service /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/Migration/Models/StaticTokenCredential.cs b/src/Microsoft.SqlTools.ServiceLayer/Migration/Models/StaticTokenCredential.cs new file mode 100644 index 00000000..2723e69a --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/Migration/Models/StaticTokenCredential.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +using System; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; + +namespace Microsoft.SqlTools.ServiceLayer.Migration.Models +{ + /// + /// Temp token creadentials to interact with ArmClient class. + /// The token passed to this class should be a newly request token, because this class doesn't renew the token. + /// Once MSAL is rolled out on ADS, we will implement a way to use the same ADS token cache configured by ADS. + /// + internal class StaticTokenCredential : TokenCredential + { + private readonly AccessToken _token; + + /// + /// Build credentials using a token that will not change. + /// + /// Newly created token that should last for the duration of the whole operation. + public StaticTokenCredential(string accessToken) => _token = new AccessToken( + accessToken: accessToken, + expiresOn: DateTimeOffset.Now.AddHours(1)); //Default to an hour, the current duration of a newly create token. + + public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) + => _token; + + public override ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) + => new ValueTask(_token); + } +}