mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 17:22:45 -05:00
Add new wizard for login migrations experience (#21317)
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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 }];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user