mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 01:25:38 -05:00
[SQL Migration] Add storage/MI connectivity validation (#22410)
* wip * Add SQL VM POC * Undo azurecore changes * Add warning banner instead of blocking on next * Fix warning banner behavior * Add private endpoint support * Fix navigation issue * Add offline scenario * Address PR comments * Fix merge conflicts
This commit is contained in:
@@ -32,7 +32,8 @@ export interface NetworkInterfaceIpConfiguration extends NetworkResource {
|
||||
privateIPAddress: string,
|
||||
privateIPAddressVersion: string,
|
||||
provisioningState: string,
|
||||
publicIPAddress: NetworkResource
|
||||
publicIPAddress: NetworkResource,
|
||||
subnet: { id: string }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +43,19 @@ export interface PublicIpAddress extends NetworkResource {
|
||||
}
|
||||
}
|
||||
|
||||
export interface PrivateEndpointConnection extends NetworkResource {
|
||||
properties: {
|
||||
privateEndpoint: { id: string },
|
||||
privateLinkServiceConnectionState: { description: string, status: string }
|
||||
}
|
||||
}
|
||||
|
||||
export interface PrivateEndpoint extends NetworkResource {
|
||||
properties: {
|
||||
subnet: { id: string }
|
||||
}
|
||||
}
|
||||
|
||||
export class NetworkInterfaceModel {
|
||||
public static IPv4VersionType = "IPv4".toLocaleLowerCase();
|
||||
private static NETWORK_API_VERSION = '2022-09-01';
|
||||
@@ -145,4 +159,13 @@ export class NetworkInterfaceModel {
|
||||
|
||||
return networkInterfaces;
|
||||
}
|
||||
|
||||
|
||||
public static async getPrivateEndpoint(account: azdata.Account, subscription: Subscription, privateEndpointId: string): Promise<PrivateEndpoint> {
|
||||
return getAzureResourceGivenId(account, subscription, privateEndpointId, this.NETWORK_API_VERSION);
|
||||
}
|
||||
|
||||
public static getVirtualNetworkFromSubnet(subnetId: string): string {
|
||||
return subnetId.replace(RegExp('^(.*?)/virtualNetworks/'), '').replace(RegExp('/subnets/.*'), '').toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { azureResource } from 'azurecore';
|
||||
import { AzureSqlDatabase, AzureSqlDatabaseServer } from './azure';
|
||||
import { generateGuid } from './utils';
|
||||
import { AzureSqlDatabase, AzureSqlDatabaseServer, SqlManagedInstance, SqlVMServer, StorageAccount, Subscription } from './azure';
|
||||
import { generateGuid, MigrationTargetType } from './utils';
|
||||
import * as utils from '../api/utils';
|
||||
import { TelemetryAction, TelemetryViews, logError } from '../telemetry';
|
||||
import * as constants from '../constants/strings';
|
||||
import { NetworkInterfaceModel, PrivateEndpointConnection } from './dataModels/azure/networkInterfaceModel';
|
||||
|
||||
const query_database_tables_sql = `
|
||||
SELECT
|
||||
@@ -509,3 +510,72 @@ export async function isSourceConnectionSysAdmin(): Promise<boolean> {
|
||||
|
||||
return getSqlBoolean(results.rows[0][0]);
|
||||
}
|
||||
|
||||
export async function canTargetConnectToStorageAccount(
|
||||
targetType: MigrationTargetType,
|
||||
targetServer: SqlManagedInstance | SqlVMServer | AzureSqlDatabaseServer,
|
||||
storageAccount: StorageAccount,
|
||||
account: azdata.Account,
|
||||
subscription: Subscription): Promise<boolean> {
|
||||
|
||||
// additional ARM properties of storage accounts which aren't exposed in azurecore
|
||||
interface StorageAccountAdditionalProperties {
|
||||
publicNetworkAccess: string,
|
||||
networkAcls: NetworkRuleSet,
|
||||
privateEndpointConnections: PrivateEndpointConnection[]
|
||||
}
|
||||
interface NetworkRuleSet {
|
||||
virtualNetworkRules: VirtualNetworkRule[],
|
||||
defaultAction: string
|
||||
}
|
||||
interface VirtualNetworkRule {
|
||||
id: string,
|
||||
state: string,
|
||||
action: string
|
||||
}
|
||||
|
||||
const ENABLED = 'Enabled';
|
||||
const ALLOW = 'Allow';
|
||||
|
||||
const storageAccountProperties: StorageAccountAdditionalProperties = (storageAccount as any)['properties'];
|
||||
const storageAccountPublicAccessEnabled: boolean = storageAccountProperties.publicNetworkAccess ? storageAccountProperties.publicNetworkAccess.toLowerCase() === ENABLED.toLowerCase() : true;
|
||||
const storageAccountDefaultIsAllow: boolean = storageAccountProperties.networkAcls ? storageAccountProperties.networkAcls.defaultAction.toLowerCase() === ALLOW.toLowerCase() : true;
|
||||
const storageAccountWhitelistedVNets: string[] = storageAccountProperties.networkAcls ? storageAccountProperties.networkAcls.virtualNetworkRules.filter(rule => rule.action.toLowerCase() === ALLOW.toLowerCase()).map(rule => rule.id) : [];
|
||||
|
||||
var enabledFromAllNetworks: boolean = false;
|
||||
var enabledFromWhitelistedVNet: boolean = false;
|
||||
var enabledFromPrivateEndpoint: boolean = false;
|
||||
|
||||
// 1) check for access from all networks
|
||||
enabledFromAllNetworks = storageAccountPublicAccessEnabled && storageAccountDefaultIsAllow;
|
||||
|
||||
switch (targetType) {
|
||||
case MigrationTargetType.SQLMI:
|
||||
const targetManagedInstanceVNet: string = (targetServer.properties as any)['subnetId'] ?? '';
|
||||
const targetManagedInstancePrivateEndpointConnections: PrivateEndpointConnection[] = (targetServer.properties as any)['privateEndpointConnections'] ?? [];
|
||||
const storageAccountPrivateEndpointConnections: PrivateEndpointConnection[] = storageAccountProperties.privateEndpointConnections ?? [];
|
||||
|
||||
// 2) check for access from whitelisted vnet
|
||||
if (storageAccountWhitelistedVNets.length > 0) {
|
||||
enabledFromWhitelistedVNet = storageAccountWhitelistedVNets.some(vnet => vnet.toLowerCase() === targetManagedInstanceVNet.toLowerCase());
|
||||
}
|
||||
|
||||
// 3) check for access from private endpoint
|
||||
if (targetManagedInstancePrivateEndpointConnections.length > 0) {
|
||||
enabledFromPrivateEndpoint = storageAccountPrivateEndpointConnections.some(async privateEndpointConnection => {
|
||||
const privateEndpoint = await NetworkInterfaceModel.getPrivateEndpoint(account, subscription, privateEndpointConnection.id);
|
||||
const privateEndpointSubnet = privateEndpoint.properties.subnet ? privateEndpoint.properties.subnet.id : '';
|
||||
return NetworkInterfaceModel.getVirtualNetworkFromSubnet(privateEndpointSubnet).toLowerCase() === NetworkInterfaceModel.getVirtualNetworkFromSubnet(targetManagedInstanceVNet).toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
case MigrationTargetType.SQLVM:
|
||||
// to-do: VM scenario -- get subnet by first checking underlying compute VM, then its network interface
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return enabledFromAllNetworks || enabledFromWhitelistedVNet || enabledFromPrivateEndpoint;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user