diff --git a/extensions/sql-migration/src/api/sqlUtils.ts b/extensions/sql-migration/src/api/sqlUtils.ts index 7c463faaad..8676d708ea 100644 --- a/extensions/sql-migration/src/api/sqlUtils.ts +++ b/extensions/sql-migration/src/api/sqlUtils.ts @@ -571,8 +571,18 @@ export async function canTargetConnectToStorageAccount( break; case MigrationTargetType.SQLVM: - // to-do: VM scenario -- get subnet by first checking underlying compute VM, then its network interface - return true; + const targetVmNetworkInterfaces = Array.from((await NetworkInterfaceModel.getVmNetworkInterfaces(account, subscription, (targetServer as SqlVMServer))).values()); + const targetVmSubnets = targetVmNetworkInterfaces.map(networkInterface => { + const ipConfigurations = networkInterface.properties.ipConfigurations ?? []; + return ipConfigurations.map(ipConfiguration => ipConfiguration.properties.subnet.id.toLowerCase()); + }).flat(); + + // 2) check for access from whitelisted vnet + if (storageAccountWhitelistedVNets.length > 0) { + enabledFromWhitelistedVNet = storageAccountWhitelistedVNets.some(vnet => targetVmSubnets.some(targetVnet => vnet.toLowerCase() === targetVnet.toLowerCase())); + } + + break; default: return true; } diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts index 3e84482760..58a032ebcf 100644 --- a/extensions/sql-migration/src/constants/strings.ts +++ b/extensions/sql-migration/src/constants/strings.ts @@ -623,10 +623,14 @@ export const INVALID_RESOURCE_GROUP_ERROR = localize('sql.migration.invalid.reso export const INVALID_STORAGE_ACCOUNT_ERROR = localize('sql.migration.invalid.storageAccount.error', "To continue, select a valid storage account."); export const MISSING_TARGET_USERNAME_ERROR = localize('sql.migration.missing.targetUserName.error', "To continue, enter a valid target user name."); export const MISSING_TARGET_PASSWORD_ERROR = localize('sql.migration.missing.targetPassword.error', "To continue, enter a valid target password."); -export function STORAGE_ACCOUNT_CONNECTIVITY_WARNING(targetServer: string, databases: string[]): string { - return databases.length === 1 - ? localize('sql.migration.storageAccount.warning.many', "Target instance '{0}' may not be able to access storage account '{1}'. Ensure that the subnet of the target instance is whitelisted on the storage account, and if applicable, that the private endpoint is in the same virtual network as the target server.", targetServer, databases[0]) - : localize('sql.migration.storageAccount.warning.one', "Target instance '{0}' may not be able to access storage accounts '{1}'. Ensure that the subnet of the target instance is whitelisted on the storage accounts, and if applicable, that the private endpoints are on the same virtual network as the target server.", targetServer, databases.join("', '")); +export function STORAGE_ACCOUNT_CONNECTIVITY_WARNING(targetServer: string, databases: string[], isSqlMiTarget: boolean): string { + return isSqlMiTarget + ? databases.length === 1 + ? localize('sql.migration.storageAccount.warning.many', "Target instance '{0}' may not be able to access storage account '{1}'. Ensure that the subnet of the target instance is whitelisted on the storage account, and if applicable, that the private endpoint is in the same virtual network as the target server.", targetServer, databases[0]) + : localize('sql.migration.storageAccount.warning.one', "Target instance '{0}' may not be able to access storage accounts '{1}'. Ensure that the subnet of the target instance is whitelisted on the storage accounts, and if applicable, that the private endpoints are on the same virtual network as the target server.", targetServer, databases.join("', '")) + : databases.length === 1 + ? localize('sql.migration.storageAccount.warning.vm.many', "Target server '{0}' may not be able to access storage account '{1}'. Ensure that the subnet of the target server is whitelisted on the storage account.", targetServer, databases[0]) + : localize('sql.migration.storageAccount.warning.vm.one', "Target server '{0}' may not be able to access storage accounts '{1}'. Ensure that the subnet of the target server is whitelisted on the storage accounts.", targetServer, databases.join("', '")); } export const TARGET_TABLE_NOT_EMPTY = localize('sql.migration.target.table.not.empty', "Target table is not empty."); diff --git a/extensions/sql-migration/src/wizard/databaseBackupPage.ts b/extensions/sql-migration/src/wizard/databaseBackupPage.ts index 89ecb9984d..43e827559d 100644 --- a/extensions/sql-migration/src/wizard/databaseBackupPage.ts +++ b/extensions/sql-migration/src/wizard/databaseBackupPage.ts @@ -641,7 +641,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { this.wizard.message = { text: this._inaccessibleStorageAccounts.length > 0 - ? constants.STORAGE_ACCOUNT_CONNECTIVITY_WARNING(this.migrationStateModel._targetServerInstance.name, this._inaccessibleStorageAccounts) + ? constants.STORAGE_ACCOUNT_CONNECTIVITY_WARNING(this.migrationStateModel._targetServerInstance.name, this._inaccessibleStorageAccounts, this.migrationStateModel.isSqlMiTarget) : '', level: azdata.window.MessageLevel.Warning } @@ -679,7 +679,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { this.wizard.message = { text: this._inaccessibleStorageAccounts.length > 0 - ? constants.STORAGE_ACCOUNT_CONNECTIVITY_WARNING(this.migrationStateModel._targetServerInstance.name, this._inaccessibleStorageAccounts) + ? constants.STORAGE_ACCOUNT_CONNECTIVITY_WARNING(this.migrationStateModel._targetServerInstance.name, this._inaccessibleStorageAccounts, this.migrationStateModel.isSqlMiTarget) : '', level: azdata.window.MessageLevel.Warning } @@ -1152,7 +1152,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { this.wizard.message = { text: this._inaccessibleStorageAccounts.length > 0 - ? constants.STORAGE_ACCOUNT_CONNECTIVITY_WARNING(this.migrationStateModel._targetServerInstance.name, this._inaccessibleStorageAccounts) + ? constants.STORAGE_ACCOUNT_CONNECTIVITY_WARNING(this.migrationStateModel._targetServerInstance.name, this._inaccessibleStorageAccounts, this.migrationStateModel.isSqlMiTarget) : '', level: azdata.window.MessageLevel.Warning }