Add new wizard for login migrations experience (#21317)

This commit is contained in:
AkshayMata
2022-12-17 12:11:25 -05:00
committed by GitHub
parent ff4dc9fe86
commit e223b4e870
22 changed files with 2569 additions and 50 deletions

View File

@@ -74,6 +74,10 @@ export async function createResourceGroup(account: azdata.Account, subscription:
}
export type SqlManagedInstance = azurecore.azureResource.AzureSqlManagedInstance;
export function isSqlManagedInstance(instance: any): instance is SqlManagedInstance {
return (instance as SqlManagedInstance) !== undefined;
}
export async function getAvailableManagedInstanceProducts(account: azdata.Account, subscription: Subscription): Promise<SqlManagedInstance[]> {
const api = await getAzureCoreAPI();
const result = await api.getSqlManagedInstances(account, [subscription], false);
@@ -165,6 +169,10 @@ export interface ServerPrivateEndpointConnection {
readonly properties?: PrivateEndpointConnectionProperties;
}
export function isAzureSqlDatabaseServer(instance: any): instance is AzureSqlDatabaseServer {
return (instance as AzureSqlDatabaseServer) !== undefined;
}
export interface AzureSqlDatabaseServer {
id: string,
name: string,

View File

@@ -58,6 +58,22 @@ const query_databases_with_size = `
WHERE sys.databases.state = 0
`;
const query_login_tables_sql = `
SELECT
sp.name as login,
sp.type_desc as login_type,
sp.default_database_name,
case when sp.is_disabled = 1 then 'Disabled' else 'Enabled' end as status
FROM sys.server_principals sp
LEFT JOIN sys.sql_logins sl ON sp.principal_id = sl.principal_id
WHERE sp.type NOT IN ('G', 'R') AND sp.type_desc IN (
'SQL_LOGIN'
--, 'WINDOWS_LOGIN'
) AND sp.name NOT LIKE '##%##'
ORDER BY sp.name;`;
const query_is_sys_admin_sql = `SELECT IS_SRVROLEMEMBER('sysadmin');`;
export const excludeDatabases: string[] = [
'master',
'tempdb',
@@ -85,6 +101,13 @@ export interface TargetDatabaseInfo {
targetTables: Map<string, TableInfo>;
}
export interface LoginTableInfo {
loginName: string;
loginType: string;
defaultDatabaseName: string;
status: string;
}
function getSqlDbConnectionProfile(
serverName: string,
tenantId: string,
@@ -123,7 +146,7 @@ function getSqlDbConnectionProfile(
};
}
function getConnectionProfile(
export function getConnectionProfile(
serverName: string,
azureResourceId: string,
userName: string,
@@ -319,3 +342,64 @@ export async function getDatabasesList(connectionProfile: azdata.connection.Conn
return [];
}
}
export async function collectSourceLogins(sourceConnectionId: string): Promise<LoginTableInfo[]> {
const ownerUri = await azdata.connection.getUriForConnection(sourceConnectionId);
const queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(
'MSSQL',
azdata.DataProviderType.QueryProvider);
const results = await queryProvider.runQueryAndReturn(
ownerUri,
query_login_tables_sql);
return results.rows.map(row => {
return {
loginName: getSqlString(row[0]),
loginType: getSqlString(row[1]),
defaultDatabaseName: getSqlString(row[2]),
status: getSqlString(row[3]),
};
}) ?? [];
}
export async function collectTargetLogins(
targetServer: AzureSqlDatabaseServer,
userName: string,
password: string): Promise<string[]> {
const connectionProfile = getConnectionProfile(
targetServer.properties.fullyQualifiedDomainName,
targetServer.id,
userName,
password);
const result = await azdata.connection.connect(connectionProfile, false, false);
if (result.connected && result.connectionId) {
const queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(
'MSSQL',
azdata.DataProviderType.QueryProvider);
const ownerUri = await azdata.connection.getUriForConnection(result.connectionId);
const results = await queryProvider.runQueryAndReturn(
ownerUri,
query_login_tables_sql);
return results.rows.map(row => getSqlString(row[0])) ?? [];
}
throw new Error(result.errorMessage);
}
export async function isSysAdmin(sourceConnectionId: string): Promise<boolean> {
const ownerUri = await azdata.connection.getUriForConnection(sourceConnectionId);
const queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(
'MSSQL',
azdata.DataProviderType.QueryProvider);
const results = await queryProvider.runQueryAndReturn(
ownerUri,
query_is_sys_admin_sql);
return getSqlBoolean(results.rows[0][0]);
}

View File

@@ -28,6 +28,7 @@ export const MenuCommands = {
CancelMigration: 'sqlmigration.cancel.migration',
RetryMigration: 'sqlmigration.retry.migration',
StartMigration: 'sqlmigration.start',
StartLoginMigration: 'sqlmigration.login.start',
IssueReporter: 'workbench.action.openIssueReporter',
OpenNotebooks: 'sqlmigration.openNotebooks',
NewSupportRequest: 'sqlmigration.newsupportrequest',
@@ -295,6 +296,22 @@ export function getMigrationStatusWithErrors(migration: azure.DatabaseMigration)
return constants.STATUS_VALUE(migrationStatus) + (constants.STATUS_WARNING_COUNT(migrationStatus, warningCount) ?? '');
}
export function getLoginStatusMessage(loginFound: boolean): string {
if (loginFound) {
return constants.LOGINS_FOUND;
} else {
return constants.LOGINS_NOT_FOUND;
}
}
export function getLoginStatusImage(loginFound: boolean): IconPath {
if (loginFound) {
return IconPathHelper.completedMigration;
} else {
return IconPathHelper.notFound;
}
}
export function getPipelineStatusImage(status: string | undefined): IconPath {
// status codes: 'PreparingForCopy' | 'Copying' | 'CopyFinished' | 'RebuildingIndexes' | 'Succeeded' | 'Failed' | 'Canceled',
switch (status) {
@@ -677,6 +694,10 @@ export function getAzureResourceDropdownValues(
}
export function getResourceDropdownValues(resources: { id: string, name: string }[], resourceNotFoundMessage: string): CategoryValue[] {
if (!resources || !resources.length) {
return [{ name: '', displayName: resourceNotFoundMessage }];
}
return resources?.map(resource => { return { name: resource.id, displayName: resource.name }; })
|| [{ name: '', displayName: resourceNotFoundMessage }];
}
@@ -687,6 +708,10 @@ export async function getAzureTenantsDropdownValues(tenants: Tenant[]): Promise<
}
export async function getAzureLocationsDropdownValues(locations: azureResource.AzureLocation[]): Promise<CategoryValue[]> {
if (!locations || !locations.length) {
return [{ name: '', displayName: constants.NO_LOCATION_FOUND }];
}
return locations?.map(location => { return { name: location.name, displayName: location.displayName }; })
|| [{ name: '', displayName: constants.NO_LOCATION_FOUND }];
}