Skip to content

[Az.Migrate] To Azure Local - additional validations #28183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/Migrate/Migrate.Autorest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ directive:
- from: Microsoft.OffAzure/stable/2020-01-01/migrate.json
where:
verb: Get
subject: ^HyperV(Cluster|Host|Job|OperationsStatus)$
subject: ^HyperV(Job|OperationsStatus)$
remove: true
- from: Microsoft.OffAzure/stable/2020-01-01/migrate.json
where:
Expand Down Expand Up @@ -477,6 +477,11 @@ directive:
verb: Get$
subject: ^VCenter$
hide: true
- from: Microsoft.OffAzure/stable/2020-01-01/migrate.json
where:
verb: Get$
subject: ^HyperV(Cluster|Host)$
hide: true
- where:
verb: New$|Update$
variant: ^(Update|Create)(?!.*?Expanded)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ $VMwareToolsStatus = @{
NotInstalled = "NotInstalled";
}

$OsType = @{
$OsTypes = @{
LinuxGuest = "linuxguest";
WindowsGuest = "windowsguest";
OtherGuestFamily = "otherguestfamily";
Expand All @@ -111,8 +111,14 @@ $VmReplicationValidationMessage = "Replication could not be initiated. Please en
$VmReplicationValidationMessages = @{
VmPoweredOff = "The VM is currently powered off. $VmReplicationValidationMessage";
AlreadyInReplication = "The VM is already in replication. $VmReplicationValidationMessage";
VmWareToolsNotInstalled = "VMware tools not installed on VM. $VmReplicationValidationMessage";
VmWareToolsNotRunning = "VMware tools not running on VM. $VmReplicationValidationMessage";
VmNotHighlyAvailable = "VM not highly available. $VmReplicationValidationMessage";
OsTypeNotFound = "Hyper-V Integration Services not running on VM. $VmReplicationValidationMessage";
HyperVIntegrationServicesNotRunning = "Hyper-V Integration Services not running on VM. $VmReplicationValidationMessage";
VmWareToolsNotInstalled = "VMware tools not installed on VM. If you plan on migrating static IP of the VMware VM, please ensure VMware Tools are installed on the VM, and allow up to 30 minutes before migrating.";
VmWareToolsNotRunning = "VMware tools not running on VM. If you plan on migrating static IP of the VMware VM, please ensure VMware Tools are running on the VM, and allow up to 30 minutes before migrating.";
OsTypeNotSupported = "The OS type of the VM is not known at time of replication. If it is a custom OS build of either Windows or Linux, please run `Set-AzMigrateLocalServerReplication -TargetObjectID <ProtectedItemId> -OsType <OsType>` to specify the OS type before migrating.";
}

$ArcResourceBridgeValidationMessages = @{
NotRunning = "Arc Resource Bridge is offline. To continue, bring the Arc Resource Bridge online. Wait a few minutes for the status to update and retry.";
NoClusters = "There are no Azure Local clusters found in the selected resource group."
}
26 changes: 19 additions & 7 deletions src/Migrate/Migrate.Autorest/custom/Helper/CommonHelper.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function CheckStorageModuleDependency {
}
}

function GetHCIClusterARGQuery {
function GetARGQueryForArcResourceBridge {
[Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()]
param(
[Parameter(Mandatory)]
Expand Down Expand Up @@ -282,25 +282,37 @@ function ValidateReplication {
throw $VmReplicationValidationMessages.VmPoweredOff
}

# Hyper-V scenario checks
if ($MigrationType -eq $AzLocalInstanceTypes.HyperVToAzLocal) {
# Hyper-V VMs with 'otherguestfamily' OS type and missing OS name could also mean Hyper-V Integration Services are not running
if ([string]::IsNullOrEmpty($Machine.OperatingSystemDetailOSType) -or
($Machine.OperatingSystemDetailOSType -eq $OsType.OtherGuestFamily -and [string]::IsNullOrEmpty($Machine.GuestOSDetailOsname))) {
throw $VmReplicationValidationMessages.OsTypeNotFound
($Machine.OperatingSystemDetailOSType -eq $OsTypes.OtherGuestFamily -and [string]::IsNullOrEmpty($Machine.GuestOSDetailOsname)))
{
throw $VmReplicationValidationMessages.HyperVIntegrationServicesNotRunning
}

if ($Machine.ClusterId -and $Machine.HighAvailability -eq $HighAvailability.NO) {
# Hyper-V VMs should be highly available
if (![string]::IsNullOrEmpty($Machine.ClusterId) -and $Machine.HighAvailability -eq $HighAvailability.NO) {
throw $VmReplicationValidationMessages.VmNotHighlyAvailable
}
}

# VMware scenario checks
if ($MigrationType -eq $AzLocalInstanceTypes.VMwareToAzLocal) {
# Once VMware tools are installed, OS type should be available.
# VMware tools should be running to support static ip migration
if ($Machine.VMwareToolsStatus -eq $VMwareToolsStatus.NotRunning) {
throw $VmReplicationValidationMessages.VmWareToolsNotRunning
Write-Warning $VmReplicationValidationMessages.VmWareToolsNotRunning
}

if ($Machine.VMwareToolsStatus -eq $VMwareToolsStatus.NotInstalled) {
throw $VmReplicationValidationMessages.VmWareToolsNotInstalled
Write-Warning $VmReplicationValidationMessages.VmWareToolsNotInstalled
}
}

# Only OS type of windowsguest and linuxguest are supported for Hyper-V and VMware scenarios
if ($Machine.OperatingSystemDetailOSType -ne $OsTypes.WindowsGuest -and
$Machine.OperatingSystemDetailOSType -ne $OsTypes.LinuxGuest)
{
Write-Warning $VmReplicationValidationMessages.OsTypeNotSupported
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ function New-AzMigrateLocalServerReplication {

if ($SiteType -eq $SiteTypes.HyperVSites) {
$instanceType = $AzLocalInstanceTypes.HyperVToAzLocal

# Get Hyper-V machine
$machine = InvokeAzMigrateGetCommandWithRetries `
-CommandName 'Az.Migrate.Internal\Get-AzMigrateHyperVMachine' `
-Parameters @{
Expand All @@ -209,16 +211,68 @@ function New-AzMigrateLocalServerReplication {
} `
-ErrorMessage "Machine '$MachineName' not found in resource group '$ResourceGroupName' and site '$SiteName'."

# Get Hyper-V site
$siteObject = InvokeAzMigrateGetCommandWithRetries `
-CommandName 'Az.Migrate.Internal\Get-AzMigrateHyperVSite' `
-Parameters @{
'ResourceGroupName' = $ResourceGroupName;
'SiteName' = $SiteName;
} `
-ErrorMessage "Machine site '$SiteName' with Type '$SiteType' not found."

# Get RunAsAccount
if (![string]::IsNullOrEmpty($machine.HostId))
{
# machine is on a single Hyper-V host
$hostIdArray = $machine.HostId.Split("/")
if ($hostIdArray.Length -lt 11) {
throw "Invalid Hyper-V Host ARM ID '$hostIdArray'"
}

$hostResourceGroupName = $hostIdArray[4]
$hostSiteName = $hostIdArray[8]
$hostName = $hostIdArray[10]

$hyperVHost = InvokeAzMigrateGetCommandWithRetries `
-CommandName 'Az.Migrate.Internal\Get-AzMigrateHyperVHost' `
-Parameters @{
'ResourceGroupName' = $hostResourceGroupName;
'SiteName' = $hostSiteName;
'HostName' = $hostName;
} `
-ErrorMessage "Hyper-V host '$hostName' not found in resource group '$hostResourceGroupName' and site '$hostSiteName'."

$runAsAccountId = $hyperVHost.RunAsAccountId
}
elseif(![string]::IsNullOrEmpty($machine.ClusterId))
{
# machine is on a Hyper-V cluster
$clusterIdArray = $machine.ClusterId.Split("/")
if ($clusterIdArray.Length -lt 11) {
throw "Invalid Hyper-V Cluster ARM ID '$clusterIdArray'"
}

$clusterResourceGroupName = $clusterIdArray[4]
$clusterSiteName = $clusterIdArray[8]
$clusterName = $clusterIdArray[10]

$hyperVCluster = InvokeAzMigrateGetCommandWithRetries `
-CommandName 'Az.Migrate.Internal\Get-AzMigrateHyperVCluster' `
-Parameters @{
'ResourceGroupName' = $clusterResourceGroupName;
'SiteName' = $clusterSiteName;
'ClusterName' = $clusterName;
} `
-ErrorMessage "Hyper-V cluster '$clusterName' not found in resource group '$clusterResourceGroupName' and site '$clusterSiteName'."

$runAsAccountId = $hyperVCluster.RunAsAccountId
}
}
else {
else
{
$instanceType = $AzLocalInstanceTypes.VMwareToAzLocal

# Get VMware machine
$machine = InvokeAzMigrateGetCommandWithRetries `
-CommandName 'Az.Migrate.Internal\Get-AzMigrateMachine' `
-Parameters @{
Expand All @@ -228,13 +282,43 @@ function New-AzMigrateLocalServerReplication {
} `
-ErrorMessage "Machine '$MachineName' not found in resource group '$ResourceGroupName' and site '$SiteName'."

# Get VMware site
$siteObject = InvokeAzMigrateGetCommandWithRetries `
-CommandName 'Az.Migrate\Get-AzMigrateSite' `
-Parameters @{
'ResourceGroupName' = $ResourceGroupName;
'SiteName' = $SiteName;
} `
-ErrorMessage "Machine site '$SiteName' with Type '$SiteType' not found."

# Get RunAsAccount
if (![string]::IsNullOrEmpty($machine.VCenterId))
{
# machine is on a single vCenter
$vCenterIdArray = $machine.VCenterId.Split("/")
if ($vCenterIdArray.Length -lt 11) {
throw "Invalid VMware vCenter ARM ID '$vCenterIdArray'"
}

$vCenterResourceGroupName = $vCenterIdArray[4]
$vCenterSiteName = $vCenterIdArray[8]
$vCenterName = $vCenterIdArray[10]

$vmwareVCenter = InvokeAzMigrateGetCommandWithRetries `
-CommandName 'Az.Migrate.Internal\Get-AzMigrateVCenter' `
-Parameters @{
'ResourceGroupName' = $vCenterResourceGroupName;
'SiteName' = $vCenterSiteName;
'Name' = $vCenterName;
} `
-ErrorMessage "VMware vCenter '$vCenterName' not found in resource group '$vCenterResourceGroupName' and site '$vCenterSiteName'."

$runAsAccountId = $vmwareVCenter.RunAsAccountId
}
}

if ([string]::IsNullOrEmpty($runAsAccountId)) {
throw "Unable to determine RunAsAccount for site '$SiteName' from machine '$MachineName'. Please verify your appliance setup."
}

# Validate the VM
Expand Down Expand Up @@ -266,6 +350,9 @@ function New-AzMigrateLocalServerReplication {
"Name" = $replicationVaultName
} `
-ErrorMessage "No Replication Vault '$replicationVaultName' found in Resource Group '$ResourceGroupName'. Please verify your Azure Migrate project setup."
if ($replicationVault.Property.ProvisioningState -ne [ProvisioningState]::Succeeded) {
throw "The Replication Vault '$replicationVaultName' is not in a valid state. The provisioning state is '$($replicationVault.Property.ProvisioningState)'. Please verify your Azure Migrate project setup."
}

# Access Discovery Service
$discoverySolutionName = "Servers-Discovery-ServerDiscovery"
Expand Down Expand Up @@ -420,38 +507,17 @@ function New-AzMigrateLocalServerReplication {
throw "The replication extension '$replicationExtensionName' is not in a valid state. The provisioning state is '$($replicationExtension.Property.ProvisioningState)'. Re-run the Initialize-AzMigrateLocalReplicationInfrastructure command."
}

# Get Target cluster
# Get ARC Resource Bridge info
$targetClusterId = $targetFabric.Property.CustomProperty.Cluster.ResourceName
$targetClusterIdArray = $targetClusterId.Split("/")
$targetSubscription = $targetClusterIdArray[2]
$hciClusterArgQuery = GetHCIClusterARGQuery -HCIClusterID $targetClusterId
$targetCluster = Az.ResourceGraph\Search-AzGraph -Query $hciClusterArgQuery -Subscription $targetSubscription
if ($null -eq $targetCluster) {
throw "Validate target cluster with id '$targetClusterId' exists. Check ARC resource bridge is running on this cluster."
}

# Get source appliance RunAsAccount
if ($SiteType -eq $SiteTypes.HyperVSites) {
$runAsAccounts = InvokeAzMigrateGetCommandWithRetries `
-CommandName 'Az.Migrate.Internal\Get-AzMigrateHyperVRunAsAccount' `
-Parameters @{
ResourceGroupName = $ResourceGroupName;
SiteName = $SiteName;
} `
-ErrorMessage "No run as account found for site '$SiteName'."

$runAsAccount = $runAsAccounts | Where-Object { $_.CredentialType -eq $RunAsAccountCredentialTypes.HyperVFabric }
$arbArgQuery = GetARGQueryForArcResourceBridge -HCIClusterID $targetClusterId
$arbArgResult = Az.ResourceGraph\Search-AzGraph -Query $arbArgQuery -Subscription $targetSubscription
if ($null -eq $arbArgResult) {
throw "$($ArcResourceBridgeValidationMessages.NoClusters). Validate target cluster with id '$targetClusterId' exists."
}
elseif ($SiteType -eq $SiteTypes.VMwareSites) {
$runAsAccounts = InvokeAzMigrateGetCommandWithRetries `
-CommandName 'Az.Migrate\Get-AzMigrateRunAsAccount' `
-Parameters @{
ResourceGroupName = $ResourceGroupName;
SiteName = $SiteName;
} `
-ErrorMessage "No run as account found for site '$SiteName'."

$runAsAccount = $runAsAccounts | Where-Object { $_.CredentialType -eq $RunAsAccountCredentialTypes.VMwareFabric }
elseif ($arbArgResult.statusOfTheBridge -ne "Running") {
throw "$($ArcResourceBridgeValidationMessages.NotRunning). Make sure the Arc Resource Bridge is online before retrying."
}

# Validate TargetVMName
Expand Down Expand Up @@ -480,12 +546,12 @@ function New-AzMigrateLocalServerReplication {
}

$customProperties.InstanceType = $instanceType
$customProperties.CustomLocationRegion = $targetCluster.CustomLocationRegion
$customProperties.CustomLocationRegion = $arbArgResult.CustomLocationRegion
$customProperties.FabricDiscoveryMachineId = $machine.Id
$customProperties.RunAsAccountId = $runAsAccount.Id
$customProperties.RunAsAccountId = $runAsAccountId
$customProperties.SourceFabricAgentName = $sourceDra.Name
$customProperties.StorageContainerId = $TargetStoragePathId
$customProperties.TargetArcClusterCustomLocationId = $targetCluster.CustomLocation
$customProperties.TargetArcClusterCustomLocationId = $arbArgResult.CustomLocation
$customProperties.TargetFabricAgentName = $targetDra.Name
$customProperties.TargetHciClusterId = $targetClusterId
$customProperties.TargetResourceGroupId = $TargetResourceGroupId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ function Set-AzMigrateLocalServerReplication {
# Specifies the nics on the source server to be included for replication.
${NicToInclude},

[Parameter()]
[ValidateSet("WindowsGuest" , "LinuxGuest")]
[ArgumentCompleter( { "WindowsGuest" , "LinuxGuest" })]
[Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')]
[System.String]
# Specifies the OS type of the VM, either WindowsGuest or LinuxGuest.
${OsType},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')]
[Microsoft.Azure.PowerShell.Cmdlets.Migrate.Runtime.DefaultInfo(Script = '(Get-AzContext).Subscription.Id')]
Expand Down Expand Up @@ -133,13 +141,15 @@ function Set-AzMigrateLocalServerReplication {
if ($HasIsDynamicMemoryEnabled) {
$isDynamicRamEnabled = [System.Convert]::ToBoolean($IsDynamicMemoryEnabled)
}
$HasOsType = $PSBoundParameters.ContainsKey('OsType')

$null = $PSBoundParameters.Remove('TargetVMCPUCore')
$null = $PSBoundParameters.Remove('IsDynamicMemoryEnabled')
$null = $PSBoundParameters.Remove('DynamicMemoryConfig')
$null = $PSBoundParameters.Remove('TargetVMRam')
$null = $PSBoundParameters.Remove('NicToInclude')
$null = $PSBoundParameters.Remove('TargetObjectID')
$null = $PSBoundParameters.Remove('OsType')
$null = $PSBoundParameters.Remove('WhatIf')
$null = $PSBoundParameters.Remove('Confirm')

Expand Down Expand Up @@ -301,6 +311,11 @@ function Set-AzMigrateLocalServerReplication {
}
}

# Update OS type
if ($HasOsType) {
$customPropertiesUpdate.OsType = $OsType
}

$protectedItemPropertiesUpdate = [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.Api20240901.ProtectedItemModelPropertiesUpdate]::new()
$protectedItemPropertiesUpdate.CustomProperty = $customPropertiesUpdate

Expand Down
Loading