mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-13 17:23:02 -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.Assessment" Version="1.0.20221028.23" />
|
||||||
<PackageReference Update="Microsoft.SqlServer.Migration.Logins" Version="1.0.20221215.33" />
|
<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.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.Azure.OperationalInsights" Version="1.0.0" />
|
||||||
<PackageReference Update="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" />
|
<PackageReference Update="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" />
|
||||||
<PackageReference Update="Microsoft.CodeAnalysis.CSharp.Workspaces" 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.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider" />
|
||||||
<PackageReference Include="Microsoft.SqlServer.Assessment" />
|
<PackageReference Include="Microsoft.SqlServer.Assessment" />
|
||||||
<PackageReference Include="Microsoft.SqlServer.Migration.Logins" />
|
<PackageReference Include="Microsoft.SqlServer.Migration.Logins" />
|
||||||
|
<PackageReference Include="Microsoft.SqlServer.Migration.Tde" />
|
||||||
<PackageReference Include="Microsoft.SqlServer.Management.SmoMetadataProvider" />
|
<PackageReference Include="Microsoft.SqlServer.Management.SmoMetadataProvider" />
|
||||||
<PackageReference Include="Microsoft.SqlServer.SqlManagementObjects" />
|
<PackageReference Include="Microsoft.SqlServer.SqlManagementObjects" />
|
||||||
<PackageReference Include="Microsoft.SqlServer.Management.SqlParser" />
|
<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.Models;
|
||||||
using Microsoft.SqlServer.Migration.Assessment.Common.Utils;
|
using Microsoft.SqlServer.Migration.Assessment.Common.Utils;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Migration.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
|
namespace Microsoft.SqlTools.ServiceLayer.Migration
|
||||||
{
|
{
|
||||||
@@ -131,6 +134,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
this.ServiceHost.SetRequestHandler(MigrateLoginsRequest.Type, HandleMigrateLogins, true);
|
this.ServiceHost.SetRequestHandler(MigrateLoginsRequest.Type, HandleMigrateLogins, true);
|
||||||
this.ServiceHost.SetRequestHandler(EstablishUserMappingRequest.Type, HandleEstablishUserMapping, true);
|
this.ServiceHost.SetRequestHandler(EstablishUserMappingRequest.Type, HandleEstablishUserMapping, true);
|
||||||
this.ServiceHost.SetRequestHandler(MigrateServerRolesAndSetPermissionsRequest.Type, HandleMigrateServerRolesAndSetPermissions, true);
|
this.ServiceHost.SetRequestHandler(MigrateServerRolesAndSetPermissionsRequest.Type, HandleMigrateServerRolesAndSetPermissions, true);
|
||||||
|
this.ServiceHost.SetRequestHandler(CertificateMigrationRequest.Type, HandleTdeCertificateMigrationRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -316,7 +320,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
ElasticSqlDbRecommendationDurationInMs = elasticResults.sqlDbDurationInMs,
|
ElasticSqlDbRecommendationDurationInMs = elasticResults.sqlDbDurationInMs,
|
||||||
ElasticSqlMiRecommendationResults = elasticResults.sqlMiResults,
|
ElasticSqlMiRecommendationResults = elasticResults.sqlMiResults,
|
||||||
ElasticSqlMiRecommendationDurationInMs = elasticResults.sqlMiDurationInMs,
|
ElasticSqlMiRecommendationDurationInMs = elasticResults.sqlMiDurationInMs,
|
||||||
ElasticSqlVmRecommendationResults = elasticResults.sqlVmResults,
|
ElasticSqlVmRecommendationResults = elasticResults.sqlVmResults,
|
||||||
ElasticSqlVmRecommendationDurationInMs = elasticResults.sqlVmDurationInMs,
|
ElasticSqlVmRecommendationDurationInMs = elasticResults.sqlVmDurationInMs,
|
||||||
InstanceRequirements = req,
|
InstanceRequirements = req,
|
||||||
SkuRecommendationReportPaths = new List<string> { baselineResults.sqlDbReportPath, baselineResults.sqlMiReportPath, baselineResults.sqlVmReportPath },
|
SkuRecommendationReportPaths = new List<string> { baselineResults.sqlDbReportPath, baselineResults.sqlMiReportPath, baselineResults.sqlVmReportPath },
|
||||||
@@ -336,23 +340,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal async Task HandleStartLoginMigration(
|
internal async Task HandleStartLoginMigration(
|
||||||
StartLoginMigrationParams parameters,
|
StartLoginMigrationParams parameters,
|
||||||
RequestContext<LoginMigrationResult> requestContext)
|
RequestContext<LoginMigrationResult> requestContext)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ILoginsMigration loginMigration = new LoginsMigration(parameters.SourceConnectionString, parameters.TargetConnectionString,
|
ILoginsMigration loginMigration = new LoginsMigration(parameters.SourceConnectionString, parameters.TargetConnectionString,
|
||||||
null, parameters.LoginList, parameters.AADDomainName);
|
null, parameters.LoginList, parameters.AADDomainName);
|
||||||
|
|
||||||
IDictionary<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
IDictionary<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
||||||
|
|
||||||
exceptionMap.AddExceptions( await loginMigration.StartValidations(CancellationToken.None) );
|
exceptionMap.AddExceptions(await loginMigration.StartValidations(CancellationToken.None));
|
||||||
exceptionMap.AddExceptions( await loginMigration.MigrateLogins(CancellationToken.None) );
|
exceptionMap.AddExceptions(await loginMigration.MigrateLogins(CancellationToken.None));
|
||||||
exceptionMap.AddExceptions( loginMigration.MigrateServerRoles(CancellationToken.None) );
|
exceptionMap.AddExceptions(loginMigration.MigrateServerRoles(CancellationToken.None));
|
||||||
exceptionMap.AddExceptions( loginMigration.EstablishUserMapping(CancellationToken.None) );
|
exceptionMap.AddExceptions(loginMigration.EstablishUserMapping(CancellationToken.None));
|
||||||
exceptionMap.AddExceptions( await loginMigration.EstablishServerRoleMapping(CancellationToken.None) );
|
exceptionMap.AddExceptions(await loginMigration.EstablishServerRoleMapping(CancellationToken.None));
|
||||||
exceptionMap.AddExceptions( loginMigration.SetLoginPermissions(CancellationToken.None) );
|
exceptionMap.AddExceptions(loginMigration.SetLoginPermissions(CancellationToken.None));
|
||||||
exceptionMap.AddExceptions( loginMigration.SetServerRolePermissions(CancellationToken.None) );
|
exceptionMap.AddExceptions(loginMigration.SetServerRolePermissions(CancellationToken.None));
|
||||||
|
|
||||||
LoginMigrationResult results = new LoginMigrationResult()
|
LoginMigrationResult results = new LoginMigrationResult()
|
||||||
{
|
{
|
||||||
@@ -370,7 +374,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
internal async Task HandleValidateLoginMigration(
|
internal async Task HandleValidateLoginMigration(
|
||||||
StartLoginMigrationParams parameters,
|
StartLoginMigrationParams parameters,
|
||||||
RequestContext<LoginMigrationResult> requestContext)
|
RequestContext<LoginMigrationResult> requestContext)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ILoginsMigration loginMigration = new LoginsMigration(parameters.SourceConnectionString, parameters.TargetConnectionString,
|
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>>();
|
IDictionary<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
||||||
Stopwatch stopWatch = new Stopwatch();
|
Stopwatch stopWatch = new Stopwatch();
|
||||||
stopWatch.Start();
|
stopWatch.Start();
|
||||||
exceptionMap.AddExceptions( await loginMigration.StartValidations(CancellationToken.None) );
|
exceptionMap.AddExceptions(await loginMigration.StartValidations(CancellationToken.None));
|
||||||
stopWatch.Stop();
|
stopWatch.Stop();
|
||||||
TimeSpan elapsedTime = stopWatch.Elapsed;
|
TimeSpan elapsedTime = stopWatch.Elapsed;
|
||||||
|
|
||||||
@@ -411,8 +415,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
IDictionary<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
IDictionary<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
||||||
Stopwatch stopWatch = new Stopwatch();
|
Stopwatch stopWatch = new Stopwatch();
|
||||||
stopWatch.Start();
|
stopWatch.Start();
|
||||||
exceptionMap.AddExceptions( await loginMigration.StartValidations(CancellationToken.None) );
|
exceptionMap.AddExceptions(await loginMigration.StartValidations(CancellationToken.None));
|
||||||
exceptionMap.AddExceptions( await loginMigration.MigrateLogins(CancellationToken.None) );
|
exceptionMap.AddExceptions(await loginMigration.MigrateLogins(CancellationToken.None));
|
||||||
stopWatch.Stop();
|
stopWatch.Stop();
|
||||||
TimeSpan elapsedTime = stopWatch.Elapsed;
|
TimeSpan elapsedTime = stopWatch.Elapsed;
|
||||||
|
|
||||||
@@ -441,11 +445,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
null, parameters.LoginList, parameters.AADDomainName);
|
null, parameters.LoginList, parameters.AADDomainName);
|
||||||
|
|
||||||
IDictionary<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
IDictionary<string, IEnumerable<ReportableException>> exceptionMap = new Dictionary<string, IEnumerable<ReportableException>>();
|
||||||
|
|
||||||
Stopwatch stopWatch = new Stopwatch();
|
Stopwatch stopWatch = new Stopwatch();
|
||||||
stopWatch.Start();
|
stopWatch.Start();
|
||||||
exceptionMap.AddExceptions( await loginMigration.StartValidations(CancellationToken.None) );
|
exceptionMap.AddExceptions(await loginMigration.StartValidations(CancellationToken.None));
|
||||||
exceptionMap.AddExceptions( loginMigration.EstablishUserMapping(CancellationToken.None) );
|
exceptionMap.AddExceptions(loginMigration.EstablishUserMapping(CancellationToken.None));
|
||||||
stopWatch.Stop();
|
stopWatch.Stop();
|
||||||
TimeSpan elapsedTime = stopWatch.Elapsed;
|
TimeSpan elapsedTime = stopWatch.Elapsed;
|
||||||
|
|
||||||
@@ -501,7 +505,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
ExceptionMap = exceptionMap,
|
ExceptionMap = exceptionMap,
|
||||||
CompletedStep = LoginMigrationStep.MigrateServerRoles,
|
CompletedStep = LoginMigrationStep.MigrateServerRoles,
|
||||||
ElapsedTime = MigrationServiceHelper.FormatTimeSpan(elapsedTime)
|
ElapsedTime = MigrationServiceHelper.FormatTimeSpan(elapsedTime)
|
||||||
});
|
});
|
||||||
|
|
||||||
stopWatch.Restart();
|
stopWatch.Restart();
|
||||||
exceptionMap.AddExceptions(await loginMigration.EstablishServerRoleMapping(CancellationToken.None));
|
exceptionMap.AddExceptions(await loginMigration.EstablishServerRoleMapping(CancellationToken.None));
|
||||||
@@ -560,120 +564,123 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal RecommendationResultSet GenerateBaselineRecommendations(SqlInstanceRequirements req, GetSkuRecommendationsParams parameters) {
|
internal RecommendationResultSet GenerateBaselineRecommendations(SqlInstanceRequirements req, GetSkuRecommendationsParams parameters)
|
||||||
RecommendationResultSet resultSet = new RecommendationResultSet();
|
{
|
||||||
|
RecommendationResultSet resultSet = new RecommendationResultSet();
|
||||||
|
|
||||||
SkuRecommendationServiceProvider provider = new SkuRecommendationServiceProvider(new AzureSqlSkuBillingServiceProvider());
|
SkuRecommendationServiceProvider provider = new SkuRecommendationServiceProvider(new AzureSqlSkuBillingServiceProvider());
|
||||||
AzurePreferences prefs = new AzurePreferences() {
|
AzurePreferences prefs = new AzurePreferences()
|
||||||
EligibleSkuCategories = null, // eligible SKU list will be adjusted with each recommendation type
|
{
|
||||||
ScalingFactor = parameters.ScalingFactor / 100.0,
|
EligibleSkuCategories = null, // eligible SKU list will be adjusted with each recommendation type
|
||||||
TargetEnvironment = TargetEnvironmentType.Production
|
ScalingFactor = parameters.ScalingFactor / 100.0,
|
||||||
};
|
TargetEnvironment = TargetEnvironmentType.Production
|
||||||
|
};
|
||||||
// generate SQL DB recommendations, if applicable
|
|
||||||
if (parameters.TargetPlatforms.Contains("AzureSqlDatabase"))
|
// 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();
|
resultSet.sqlMiResults.Add(new SkuRecommendationResult()
|
||||||
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()
|
SqlInstanceName = parameters.TargetSqlInstance,
|
||||||
{
|
DatabaseName = null,
|
||||||
SqlInstanceName = parameters.TargetSqlInstance,
|
TargetSku = null,
|
||||||
DatabaseName = null,
|
MonthlyCost = null,
|
||||||
TargetSku = null,
|
Ranking = -1,
|
||||||
MonthlyCost = null,
|
PositiveJustifications = null,
|
||||||
Ranking = -1,
|
NegativeJustifications = null,
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate SQL VM recommendations, if applicable
|
sqlMiStopwatch.Stop();
|
||||||
if (parameters.TargetPlatforms.Contains("AzureSqlVirtualMachine"))
|
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();
|
resultSet.sqlVmResults.Add(new SkuRecommendationResult()
|
||||||
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()
|
SqlInstanceName = parameters.TargetSqlInstance,
|
||||||
{
|
DatabaseName = null,
|
||||||
SqlInstanceName = parameters.TargetSqlInstance,
|
TargetSku = null,
|
||||||
DatabaseName = null,
|
MonthlyCost = null,
|
||||||
TargetSku = null,
|
Ranking = -1,
|
||||||
MonthlyCost = null,
|
PositiveJustifications = null,
|
||||||
Ranking = -1,
|
NegativeJustifications = null,
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
RecommendationResultSet resultSet = new RecommendationResultSet();
|
||||||
|
|
||||||
CsvAggregatorForElasticStrategy elasticaggregator = new CsvAggregatorForElasticStrategy(
|
CsvAggregatorForElasticStrategy elasticaggregator = new CsvAggregatorForElasticStrategy(
|
||||||
instanceId: parameters.TargetSqlInstance,
|
instanceId: parameters.TargetSqlInstance,
|
||||||
directory: parameters.DataFolder,
|
directory: parameters.DataFolder,
|
||||||
queryInterval: parameters.PerfQueryIntervalInSec,
|
queryInterval: parameters.PerfQueryIntervalInSec,
|
||||||
logger: null,
|
logger: null,
|
||||||
dbsToInclude: new HashSet<string>(parameters.DatabaseAllowList));
|
dbsToInclude: new HashSet<string>(parameters.DatabaseAllowList));
|
||||||
|
|
||||||
// generate SQL DB recommendations, if applicable
|
// generate SQL DB recommendations, if applicable
|
||||||
@@ -698,7 +705,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
resultSet.sqlDbDurationInMs = sqlDbStopwatch.ElapsedMilliseconds;
|
resultSet.sqlDbDurationInMs = sqlDbStopwatch.ElapsedMilliseconds;
|
||||||
|
|
||||||
SkuRecommendationReport sqlDbReport = new SkuRecommendationReport(
|
SkuRecommendationReport sqlDbReport = new SkuRecommendationReport(
|
||||||
new Dictionary<SqlInstanceRequirements, List<SkuRecommendationResult>> { { req, resultSet.sqlDbResults } },
|
new Dictionary<SqlInstanceRequirements, List<SkuRecommendationResult>> { { req, resultSet.sqlDbResults } },
|
||||||
AzureSqlTargetPlatform.AzureSqlDatabase.ToString());
|
AzureSqlTargetPlatform.AzureSqlDatabase.ToString());
|
||||||
var sqlDbRecommendationReportFileName = String.Format("SkuRecommendationReport-AzureSqlDatabase-Elastic-{0}", DateTime.UtcNow.ToString("yyyyMMddHH-mmss", CultureInfo.InvariantCulture));
|
var sqlDbRecommendationReportFileName = String.Format("SkuRecommendationReport-AzureSqlDatabase-Elastic-{0}", DateTime.UtcNow.ToString("yyyyMMddHH-mmss", CultureInfo.InvariantCulture));
|
||||||
var sqlDbRecommendationReportFullPath = Path.Combine(SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, sqlDbRecommendationReportFileName);
|
var sqlDbRecommendationReportFullPath = Path.Combine(SqlAssessmentConfiguration.ReportsAndLogsRootFolderPath, sqlDbRecommendationReportFileName);
|
||||||
@@ -989,7 +996,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
AzureSqlPaaSHardwareType.PremiumSeries));
|
AzureSqlPaaSHardwareType.PremiumSeries));
|
||||||
|
|
||||||
if (includePreviewSkus)
|
if (includePreviewSkus)
|
||||||
{
|
{
|
||||||
// Premium Memory Optimized BC/GP
|
// Premium Memory Optimized BC/GP
|
||||||
eligibleSkuCategories.Add(new AzureSqlSkuPaaSCategory(
|
eligibleSkuCategories.Add(new AzureSqlSkuPaaSCategory(
|
||||||
AzureSqlTargetPlatform.AzureSqlManagedInstance,
|
AzureSqlTargetPlatform.AzureSqlManagedInstance,
|
||||||
@@ -1043,6 +1050,78 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
return eligibleSkuCategories;
|
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>
|
/// <summary>
|
||||||
/// Disposes the Migration Service
|
/// Disposes the Migration Service
|
||||||
/// </summary>
|
/// </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