mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-26 09:35:38 -05:00
Tde migration integration (#1806)
Adding certificate migration for encrypted databases. Uses notifications to send events back to the client every time each database certificate is migrated.
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
<PackageReference Update="Microsoft.SqlServer.Migration.Assessment" Version="1.0.20221028.23" />
|
||||
<PackageReference Update="Microsoft.SqlServer.Migration.Logins" Version="1.0.20221215.33" />
|
||||
<PackageReference Update="Microsoft.SqlServer.Management.SqlParser" Version="160.22524.0" />
|
||||
<PackageReference Update="Microsoft.SqlServer.Migration.Tde" Version="1.0.20230110.39" />
|
||||
<PackageReference Update="Microsoft.Azure.OperationalInsights" Version="1.0.0" />
|
||||
<PackageReference Update="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" />
|
||||
<PackageReference Update="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.10.0" />
|
||||
|
||||
Binary file not shown.
@@ -54,6 +54,7 @@
|
||||
<PackageReference Include="Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Assessment" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Migration.Logins" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Migration.Tde" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Management.SmoMetadataProvider" />
|
||||
<PackageReference Include="Microsoft.SqlServer.SqlManagementObjects" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Management.SqlParser" />
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for the certificate migration progress event
|
||||
/// </summary>
|
||||
public class CertificateMigrationProgressParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Database name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Message related to the success status. true should describe a positive outcome. false should have an error.
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Result of migration
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a certificate migration request. This should be register at the client.
|
||||
/// </summary>
|
||||
public class CertificateMigrationProgressEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Name and parameters for the event definition.
|
||||
/// </summary>
|
||||
public static readonly
|
||||
EventType<CertificateMigrationProgressParams> Type =
|
||||
EventType<CertificateMigrationProgressParams>.Create("migration/tdemigrationprogress");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for the certificate migration operation
|
||||
/// </summary>
|
||||
public class CertificateMigrationParams
|
||||
{
|
||||
/// <summary>
|
||||
/// List of databses to migrate the certificates
|
||||
/// </summary>
|
||||
public List<string> EncryptedDatabases { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Source connection string to the server
|
||||
/// </summary>
|
||||
public string SourceSqlConnectionString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Target subscription id
|
||||
/// </summary>
|
||||
public string TargetSubscriptionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Target resource group name
|
||||
/// </summary>
|
||||
public string TargetResourceGroupName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Target manages instance name
|
||||
/// </summary>
|
||||
public string TargetManagedInstanceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Place where certificates will be exported
|
||||
/// </summary>
|
||||
public string NetworkSharePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Domain for the user credentials able to read from the shared path
|
||||
/// </summary>
|
||||
public string NetworkShareDomain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Username for the credentials able to read from the shared path
|
||||
/// </summary>
|
||||
public string NetworkShareUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Password for the credentials able to read from the shared path
|
||||
/// </summary>
|
||||
public string NetworkSharePassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Access token for the ARM client
|
||||
/// </summary>
|
||||
public string AccessToken { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result for the certificate migration operation
|
||||
/// </summary>
|
||||
public class CertificateMigrationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// List of the status of each certificate migration result attempted.
|
||||
/// </summary>
|
||||
public List<CertificateMigrationEntryResult> MigrationStatuses { get; set; } = new List<CertificateMigrationEntryResult>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result for an individual database certificate migration
|
||||
/// </summary>
|
||||
public class CertificateMigrationEntryResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the database this result represent
|
||||
/// </summary>
|
||||
public string DbName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The result of the migration.
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Description of the success status or the error message encountered when the migration was not successful
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Certificate migration request definition
|
||||
/// </summary>
|
||||
public class CertificateMigrationRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Name, parameter and return type for the certicate migration operation
|
||||
/// </summary>
|
||||
public static readonly
|
||||
RequestType<CertificateMigrationParams, CertificateMigrationResult> Type =
|
||||
RequestType<CertificateMigrationParams, CertificateMigrationResult>.Create("migration/tdemigration");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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<string> { baselineResults.sqlDbReportPath, baselineResults.sqlMiReportPath, baselineResults.sqlVmReportPath },
|
||||
@@ -336,23 +340,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
||||
}
|
||||
|
||||
internal async Task HandleStartLoginMigration(
|
||||
StartLoginMigrationParams parameters,
|
||||
StartLoginMigrationParams parameters,
|
||||
RequestContext<LoginMigrationResult> requestContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
ILoginsMigration loginMigration = new LoginsMigration(parameters.SourceConnectionString, parameters.TargetConnectionString,
|
||||
null, parameters.LoginList, parameters.AADDomainName);
|
||||
|
||||
|
||||
IDictionary<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
||||
|
||||
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<LoginMigrationResult> requestContext)
|
||||
{
|
||||
{
|
||||
try
|
||||
{
|
||||
ILoginsMigration loginMigration = new LoginsMigration(parameters.SourceConnectionString, parameters.TargetConnectionString,
|
||||
@@ -379,7 +383,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
||||
IDictionary<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
||||
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<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
||||
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<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
||||
|
||||
|
||||
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<SqlInstanceRequirements, List<SkuRecommendationResult>> { { 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<SqlInstanceRequirements, List<SkuRecommendationResult>> { { 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<SqlInstanceRequirements, List<SkuRecommendationResult>> { { 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<SqlInstanceRequirements, List<SkuRecommendationResult>> { { 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<SqlInstanceRequirements, List<SkuRecommendationResult>> { { 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<SqlInstanceRequirements, List<SkuRecommendationResult>> { { 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<string>(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<SqlInstanceRequirements, List<SkuRecommendationResult>> { { req, resultSet.sqlDbResults } },
|
||||
new Dictionary<SqlInstanceRequirements, List<SkuRecommendationResult>> { { 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request handler for the certifica migration operation
|
||||
/// </summary>
|
||||
/// <param name="parameters">Parameters for the operation, as register during the type definition</param>
|
||||
/// <param name="requestContext">Context provided by the framework</param>
|
||||
/// <returns></returns>
|
||||
internal async Task HandleTdeCertificateMigrationRequest(
|
||||
CertificateMigrationParams parameters,
|
||||
RequestContext<CertificateMigrationResult> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Individual certificate migration operation
|
||||
/// </summary>
|
||||
/// <param name="tdeMigrationClient">Instance of the migration client</param>
|
||||
/// <param name="dbName">Name of the database to migrate</param>
|
||||
/// <returns></returns>
|
||||
private async Task<CertificateMigrationEntryResult> 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 };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the Migration Service
|
||||
/// </summary>
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
internal class StaticTokenCredential : TokenCredential
|
||||
{
|
||||
private readonly AccessToken _token;
|
||||
|
||||
/// <summary>
|
||||
/// Build credentials using a token that will not change.
|
||||
/// </summary>
|
||||
/// <param name="accessToken">Newly created token that should last for the duration of the whole operation.</param>
|
||||
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<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
|
||||
=> new ValueTask<AccessToken>(_token);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user