Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -302,9 +302,15 @@ function Test-ReplicationPrequisites {
throw $VmReplicationValidationMessages.HyperVIntegrationServicesNotRunning
}

# Hyper-V VMs should be highly available
if (![string]::IsNullOrEmpty($Machine.ClusterId) -and $Machine.HighAvailability -eq $HighAvailability.NO) {
throw $VmReplicationValidationMessages.VmNotHighlyAvailable
# Hyper-V VMs on cluster should be highly available
if (![string]::IsNullOrEmpty($Machine.ClusterId)) {
if ($Machine.HighAvailability -eq $HighAvailability.NO) {
throw $VmReplicationValidationMessages.VmNotHighlyAvailable
}
elseif ($Machine.HighAvailability -ne $HighAvailability.YES) {
# Unknown or unexpected value
throw $VmReplicationValidationMessages.VmUnknownHighlyAvailable
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ $PowerStatus = @{
}

$HighAvailability = @{
Unknown = "Unknown";
NO = "No";
YES = "Yes";
}
Expand All @@ -111,7 +112,8 @@ $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";
VmNotHighlyAvailable = "VM not highly available. $VmReplicationValidationMessage";
VmNotHighlyAvailable = "The VM is not highly available. $VmReplicationValidationMessage";
VmUnknownHighlyAvailable = "The VM has unknown high availability status. $VmReplicationValidationMessage";
HyperVIntegrationServicesNotRunning = "Hyper-V Integration Services are not running on VM. $VmReplicationValidationMessage";
VmWareToolsNotInstalled = "VMware Tools are not installed on the VM. To preserve static IPs during migration, install VMware Tools and wait up to 30 minutes for the system to detect the changes.";
VmWareToolsNotRunning = "VMware Tools are not running on the VM. To preserve static IPs during migration, ensure VMware Tools are running and wait up to 30 minutes for the system to detect the changes.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,27 +165,27 @@ function Initialize-AzMigrateLocalReplicationInfrastructure {
}
Write-Host "*Selected Resource Group: '$($ResourceGroupName)'"

# Verify user validity
$userObject = Get-AzADUser -UserPrincipalName $context.Subscription.ExtendedProperties.Account

if (-not $userObject) {
$userObject = Get-AzADUser -Mail $context.Subscription.ExtendedProperties.Account
# Verify caller identity
if ($context.Account.Type -eq 'ServicePrincipal') {
$userObject = Get-AzADServicePrincipal -ApplicationID $context.Account.Id
}

if (-not $userObject) {
$mailNickname = "{0}#EXT#" -f $($context.Account.Id -replace '@', '_')

$userObject = Get-AzADUser |
Where-Object { $_.MailNickname -eq $mailNickname }
elseif ($context.Account.Type -eq 'ManagedService' -or $context.Account.Id.StartsWith("MSI@")) {
$hostname = if ($env:COMPUTERNAME) { $env:COMPUTERNAME } else { $env:HOSTNAME }
if ([string]::IsNullOrEmpty($hostname)) {
throw "Unable to determine the hostname for Managed Identity resolution. Please set the COMPUTERNAME or HOSTNAME environment variable."
}
$userObject = Get-AzADServicePrincipal -DisplayName $hostname
}
else {
$userObject = Get-AzADUser -UserPrincipalName $context.Subscription.ExtendedProperties.Account

if (-not $userObject) {
if ($context.Account.Id.StartsWith("MSI@")) {
$hostname = $env:COMPUTERNAME
$userObject = Get-AzADServicePrincipal -DisplayName $hostname
if (-not $userObject) {
$userObject = Get-AzADUser -Mail $context.Subscription.ExtendedProperties.Account
}
else {
$userObject = Get-AzADServicePrincipal -ApplicationID $context.Account.Id

if (-not $userObject) {
$mailNickname = "{0}#EXT#" -f $($context.Account.Id -replace '@', '_')
$userObject = Get-AzADUser -Filter "mailNickname eq '$mailNickname'"
Comment thread
minhsuanlee marked this conversation as resolved.
Outdated
}
}

Expand Down Expand Up @@ -760,7 +760,7 @@ function Initialize-AzMigrateLocalReplicationInfrastructure {
-Location $params.location `
-Kind $params.kind `
-Tags $params.tags `
-AllowBlobPublicAccess $true
-AllowBlobPublicAccess $false

if ($null -ne $cacheStorageAccount -and
$null -ne $cacheStorageAccount.ProvisioningState -and
Expand Down Expand Up @@ -799,6 +799,17 @@ function Initialize-AzMigrateLocalReplicationInfrastructure {
throw "Unexpected error occurs during Cache Storage Account selection process. Please re-run this command or contact support if help needed."
}

# Validate Cache Storage Account SKU tier is Standard (not Premium)
if ($cacheStorageAccount.Sku.Tier -ne "Standard") {
throw "Cache Storage Account '$($cacheStorageAccount.StorageAccountName)' uses an unsupported SKU tier '$($cacheStorageAccount.Sku.Tier)'. Only 'Standard' tier storage accounts are supported. Please provide a Standard tier storage account."
}

# Validate public network access should not be disabled even for private endpoint
if (![string]::IsNullOrEmpty($cacheStorageAccount.PublicNetworkAccess) -and
$cacheStorageAccount.PublicNetworkAccess -eq "Disabled") {
throw "Cache Storage Account '$($cacheStorageAccount.StorageAccountName)' does not allow public network access. Please enable 'Public network access' on the storage account and re-run this command."
Comment thread
minhsuanlee marked this conversation as resolved.
}
Comment thread
minhsuanlee marked this conversation as resolved.

$params = @{
contributorRoleDefId = [System.Guid]::parse($RoleDefinitionIds.ContributorId);
storageBlobDataContributorRoleDefId = [System.Guid]::parse($RoleDefinitionIds.StorageBlobDataContributorId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,6 @@ function New-AzMigrateLocalServerReplication {
CheckResourceGraphModuleDependency
CheckResourcesModuleDependency

$HasMachineId = $PSBoundParameters.ContainsKey('MachineId')
$HasTargetStoragePathId = $PSBoundParameters.ContainsKey('TargetStoragePathId')
$HasTargetResourceGroupId = $PSBoundParameters.ContainsKey('TargetResourceGroupId')
$HasTargetVMCPUCore = $PSBoundParameters.ContainsKey('TargetVMCPUCore')
$HasIsDynamicMemoryEnabled = $PSBoundParameters.ContainsKey('IsDynamicMemoryEnabled')
if ($HasIsDynamicMemoryEnabled) {
Expand Down Expand Up @@ -212,22 +209,22 @@ function New-AzMigrateLocalServerReplication {
$null = $PSBoundParameters.Add('ErrorAction', 'SilentlyContinue')

# Validate ARM ID format from inputs
if ($HasMachineId -and !(Test-AzureResourceIdFormat -Data $MachineId -Format $IdFormats.MachineArmIdTemplate))
if (!(Test-AzureResourceIdFormat -Data $MachineId -Format $IdFormats.MachineArmIdTemplate))
{
throw New-InvalidResourceIdProvidedException `
-ResourceId $MachineId `
-ResourceType "DiscoveredMachine" `
-Format $IdFormats.MachineArmIdTemplate
}

if ($HasTargetStoragePathId -and !(Test-AzureResourceIdFormat -Data $TargetStoragePathId -Format $IdFormats.StoragePathArmIdTemplate)) {
if (!(Test-AzureResourceIdFormat -Data $TargetStoragePathId -Format $IdFormats.StoragePathArmIdTemplate)) {
throw New-InvalidResourceIdProvidedException `
-ResourceId $TargetStoragePathId `
-ResourceType "StorageContainer" `
-Format $IdFormats.StoragePathArmIdTemplate
}

if ($HasTargetResourceGroupId -and !(Test-AzureResourceIdFormat -Data $TargetResourceGroupId -Format $IdFormats.ResourceGroupArmIdTemplate)) {
if (!(Test-AzureResourceIdFormat -Data $TargetResourceGroupId -Format $IdFormats.ResourceGroupArmIdTemplate)) {
throw New-InvalidResourceIdProvidedException `
-ResourceId $TargetResourceGroupId `
-ResourceType "ResourceGroup" `
Expand Down Expand Up @@ -651,6 +648,30 @@ function New-AzMigrateLocalServerReplication {
$customProperties.FabricDiscoveryMachineId = $machine.Id
$customProperties.RunAsAccountId = $runAsAccountId
$customProperties.SourceFabricAgentName = $sourceDra.Name

# Validate storage path exists and is in a usable state
$storagePath = Get-AzResource `
-ResourceId $TargetStoragePathId `
-ErrorVariable notPresent `
-ErrorAction SilentlyContinue
if ($null -eq $storagePath) {
throw "Storage path with Id '$TargetStoragePathId' not found. Please provide a valid storage path ARM ID."
Comment thread
minhsuanlee marked this conversation as resolved.
}

# Creation must have succeeded for the storage path to be usable
if ($storagePath.Properties.status.provisioningStatus -ne "Succeeded") {
throw "Storage path '$($storagePath.Name)' has a creation provisioning status of '$($storagePath.Properties.status.provisioningStatus)'. Only storage paths with a successful creation can be used. Please select a different storage path or wait for provisioning to complete."
}

Comment thread
minhsuanlee marked this conversation as resolved.
# The latest operation (ProvisioningState) must also be Succeeded
$provisioningState = $storagePath.Properties.provisioningState
if ($provisioningState -eq "Failed") {
Comment thread
minhsuanlee marked this conversation as resolved.
Outdated
throw "Storage path '$($storagePath.Name)' has a failed provisioning state. The latest operation on this storage path did not succeed. Please resolve the issue and retry."
}
elseif ($provisioningState -ne "Succeeded") {
throw "Storage path '$($storagePath.Name)' has a provisioning state of '$provisioningState'. An operation is currently in progress. Please wait for it to complete and retry."
Comment thread
minhsuanlee marked this conversation as resolved.
Outdated
Comment thread
minhsuanlee marked this conversation as resolved.
Outdated
}
Comment thread
minhsuanlee marked this conversation as resolved.

$customProperties.StorageContainerId = $TargetStoragePathId
$customProperties.TargetArcClusterCustomLocationId = $arbArgResult.CustomLocation
$customProperties.TargetFabricAgentName = $targetDra.Name
Expand Down
6 changes: 6 additions & 0 deletions src/Migrate/Migrate/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
- Additional information about change #1
-->
## Upcoming Release
* Fixed bugs in `Initialize-AzMigrateLocalReplicationInfrastructure`
- Fixed caller identity resolution to correctly handle Service Principal and Managed Identity account types, with a null-guard for hostname lookup.
- Added cache storage account validations to reject unsupported SKU tiers and storage accounts with public network access disabled.
* Updated `New-AzMigrateLocalServerReplication`
- Removed redundant conditional guards on mandatory parameter ARM ID validation.
- Added storage path validation to verify existence, successful creation status, and latest operation provisioning state before initiating replication

## Version 2.11.0
* Updated DefaultCrashConsistentFrequencyInMinutes and DefaultAppConsistentFrequencyInMinutes to align with Azure Portal UX for Replication Policy
Expand Down
Loading