-
-
Save MarcelMeurer/aab2ef8f36d7c634beebea771f54bf30 to your computer and use it in GitHub Desktop.
| param( | |
| [ValidateNotNullOrEmpty()] | |
| [ValidateSet('Default', 'StartInternalTask-1', 'StartInternalTask-2', 'StartInternalTask-3', 'CheckInternalTask')] | |
| [string] $mode = "Default" | |
| ) | |
| $ErrorActionPreference = "Stop" | |
| #region Configuration | |
| $subscriptionId="xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxxxxx" | |
| # source VM (generation V1) | |
| $sourceVmName = "T-WVD-Basic-20" | |
| $sourceVmResourceGroup = "WVD_TEMPLATES" | |
| # target VM to be build (generation V2) - This wil be a copy of the source VM but as V2 generation | |
| $targetVmName = "T-WVD-Basic-30" # max. length is 15 | |
| $targetVmResourceGroup = "WVD_TEMPLATES" | |
| $enabledTrustedLaunch = $true | |
| $usePremium = $false # if $true, this speeds up the process but can only be done, if the VM size suppports premium disks | |
| $tempDiskSizeGb = 512 # must be larger to store the wim file of the original disk of the source | |
| #endregion | |
| #region InternalMethods | |
| $scriptPath = $PSCommandPath | |
| if (-not $PSCommandPath) {$scriptPath="C:\1Drive\OneDrive - sepago GmbH\Desktop\Convert-VmV1toV2.ps1"} | |
| $localPath = Split-Path $scriptPath -Resolve | |
| $logFileName = "$(Split-Path $scriptPath -Leaf).log" | |
| function LogWriter($message) { | |
| $message = "$(Get-Date ([datetime]::UtcNow) -Format "o") $message" | |
| write-host($message) | |
| if ([System.IO.Directory]::Exists($env:temp)) { try { write-output($message) | Out-File "$localPath\$logFileName" -Append } catch {} } | |
| } | |
| #endregion InternalMethods | |
| #region MainApp | |
| LogWriter ("Starting in mode: $mode") | |
| #region CreateTheAzureResources | |
| if ($mode -eq "Default") { | |
| if(!(Get-AzContext)) { | |
| # Connect to Azure if no connection exists | |
| Connect-AzAccount | |
| } | |
| # select subscription | |
| Get-AzSubscription -SubscriptionId $subscriptionId | Select-AzSubscription | |
| # check, if some of the new resources exist | |
| if (Get-AzDisk -ResourceGroupName $targetVmResourceGroup -DiskName "$($sourceVmName)-Disk-Copy" -ErrorAction SilentlyContinue) {LogWriter "The target disk $sourceVmName exist. Please delete it first."; break} | |
| if (Get-AzDisk -ResourceGroupName $targetVmResourceGroup -DiskName "$($targetVmName)-Disk-Converted" -ErrorAction SilentlyContinue) {LogWriter "The converted disk $($targetVmName)-Disk-Converted exist. Please delete it first."; break} | |
| if (Get-AzSnapshot -ResourceGroupName $targetVmResourceGroup -SnapshotName "$($sourceVmName)-Disk-Snap" -ErrorAction SilentlyContinue) {LogWriter "The snapshot $($sourceVmName)-Disk-Snap exist. Please delete it first."; break} | |
| if (Get-AzVm -ResourceGroupName $targetVmResourceGroup -Name $targetVmName -ErrorAction SilentlyContinue) {LogWriter "The target VM $targetVmName exist. Please delete it first."; break} | |
| # read data of the existing VM | |
| LogWriter ("Getting data of the source VM") | |
| $sourceVm=Get-AzVm -ResourceGroupName $sourceVmResourceGroup -Name $sourceVmName | |
| $sourceNic=Get-AzNetworkInterface -ResourceId $sourceVm.NetworkProfile.NetworkInterfaces[0].Id | |
| $location=$sourceNic.Location | |
| $subnetId=$sourceNic.IpConfigurations[0].Subnet.Id | |
| $sourceDisk = Get-AzDisk -ResourceGroupName $sourceVm.StorageProfile.OsDisk.ManagedDisk.Id.Split("/")[4] -DiskName $sourceVm.StorageProfile.OsDisk.ManagedDisk.Id.Split("/")[8] | |
| if ($sourceDisk.HyperVGeneration -like "V2") { | |
| Write-Host "Source VM is still a V2 VM" | |
| exit | |
| } | |
| # create target VM with a temporary Windows 11 to do the migration | |
| LogWriter ("Creating the target VM (V2)") | |
| $psc = New-Object System.Management.Automation.PSCredential("vmAdmin", (ConvertTo-SecureString "Sup+rTempS+cret123---" -AsPlainText -Force)) | |
| $nic = New-AzNetworkInterface -Name "nic-$($targetVmName)" -ResourceGroupName $targetVmResourceGroup -Location $location -SubnetId $subnetId -Force | |
| $vmConfig = New-AzVMConfig -VMName $targetVmName -VMSize $sourceVm.HardwareProfile.VmSize | |
| $vmConfig = Set-AzVMBootDiagnostic -VM $vmConfig -Enable | |
| $vmConfig = Set-AzVMSourceImage -VM $vmConfig -PublisherName "MicrosoftWindowsServer" -Offer "WindowsServer" -Skus "2022-Datacenter-g2" -Version "latest" # must be a V2 image | |
| $vmConfig = Set-AzVMOSDisk -VM $vmConfig -DiskSizeInGB $tempDiskSizeGb -CreateOption FromImage | |
| $vmConfig = Set-AzVMOperatingSystem -VM $vmConfig -ComputerName $targetVmName -Windows -EnableAutoUpdate -Credential $psc | |
| $vmConfig = Add-AzVMNetworkInterface -VM $vmConfig -Id $nic.Id | |
| if ($enabledTrustedLaunch) { | |
| $vmConfig = Set-AzVMSecurityProfile -VM $vmConfig -SecurityType TrustedLaunch | |
| $vmConfig = Set-AzVmUefi -VM $vmConfig -EnableVtpm $true -EnableSecureBoot $true | |
| } else { | |
| $vmConfig = Set-AzVMSecurityProfile -VM $vmConfig -SecurityType Standard | |
| } | |
| if ($sourceVm.LicenseType -eq $null) { | |
| New-AzVM -VM $vmConfig -ResourceGroupName $targetVmResourceGroup -Location $location | |
| } else { | |
| New-AzVM -VM $vmConfig -ResourceGroupName $targetVmResourceGroup -Location $location -LicenseType $sourceVm.LicenseType | |
| } | |
| $targetVm = Get-AzVM -ResourceGroupName $targetVmResourceGroup -Name $targetVmName | |
| $targetDiskOrg = Get-AzDisk -ResourceGroupName $targetVmResourceGroup -DiskName $targetVm.StorageProfile.OsDisk.Name | |
| # copy the source disk with a snapshot | |
| LogWriter ("Creating a copy of the source VM") | |
| $snapShotConfig = New-AzSnapshotConfig -SourceUri $sourceVm.StorageProfile.OsDisk.ManagedDisk.Id -Location $location -CreateOption copy | |
| $sourceDiskSnap = New-AzSnapshot -ResourceGroupName $targetVmResourceGroup -SnapshotName "$($sourceVmName)-Disk-Snap" -Snapshot $snapShotConfig # clean-up after use | |
| $diskConfig = New-AzDiskConfig -Location $location -SourceResourceId $sourceDiskSnap.Id -CreateOption Copy -SkuName $sourceDisk.Sku.Name | |
| if ($usePremium) {$diskConfig.Sku=[Microsoft.Azure.Management.Compute.Models.DiskSku]::new('Premium_LRS')} | |
| $sourceDiskCopy = New-AzDisk -Disk $diskConfig -ResourceGroupName $targetVmResourceGroup -DiskName "$($sourceVmName)-Disk-Copy" | |
| # create the new target disk to hold the data of the source disk (but as a V2 type) | |
| LogWriter ("Create an empty V2 disk as destion for the data") | |
| $diskConfig = New-AzDiskConfig -Location $location -SkuName $sourceDisk.Sku.Name -OsType Windows -HyperVGeneration V2 -DiskSizeGB $sourceDisk.DiskSizeGB -CreateOption "Empty" | |
| if ($enabledTrustedLaunch) {$diskConfig = Set-AzDiskSecurityProfile -Disk $diskConfig -SecurityType "TrustedLaunch"} | |
| if ($usePremium) {$diskConfig.Sku=[Microsoft.Azure.Management.Compute.Models.DiskSku]::new('Premium_LRS')} | |
| $targetDisk = New-AzDisk -Disk $diskConfig -ResourceGroupName $targetVmResourceGroup -DiskName "$($targetVmName)-Disk-Converted" | |
| # attach the copy of the source disk (V1) | |
| LogWriter ("Attaching the copied source disk to the target VM: Lun 6") | |
| $targetVm = Add-AzVMDataDisk -VM $targetVm -Name $sourceDiskCopy.Name -CreateOption Attach -ManagedDiskId $sourceDiskCopy.Id -Lun 6 -Caching ReadWrite | |
| Update-AzVM -VM $targetVm -ResourceGroupName $targetVmResourceGroup | |
| # attach the later target disk (V2) | |
| LogWriter ("Attaching the empty target disk to the target VM: Lun 7") | |
| $targetVm = Add-AzVMDataDisk -VM $targetVm -Name $targetDisk.Name -CreateOption Attach -ManagedDiskId $targetDisk.Id -Lun 7 -Caching ReadWrite | |
| Update-AzVM -VM $targetVm -ResourceGroupName $targetVmResourceGroup | |
| # target VM is ready with all attached disks | |
| # now we have to work with the partitions inside of the VM - Invoking this script with the parameter -mode StartInternalTask-1 | |
| LogWriter ("Run the first part of the converting process on the target VM - this can last hours") | |
| Invoke-AzVMRunCommand -ResourceGroupName $targetVmResourceGroup -Name $targetVmName -CommandId "RunPowerShellScript" -ScriptPath $scriptPath -Parameter @{"-mode" = "StartInternalTask-1"} | |
| # While the script is running internally, we have to wait for completion - looping every 2 minutes (the task can take seeral hours) | |
| $completed = $false | |
| $failed = $false | |
| do { | |
| Start-Sleep -Seconds 120 | |
| try { | |
| $rv = Invoke-AzVMRunCommand -ResourceGroupName $targetVmResourceGroup -Name $targetVmName -CommandId "RunPowerShellScript" -ScriptPath $scriptPath -Parameter @{"-mode" = "CheckInternalTask"} | |
| } catch { | |
| LogWriter ("The remote operation failed: $_") | |
| $failed = $true | |
| $completed = $true | |
| } | |
| LogWriter ("Waiting for the remote task - long running operation") | |
| if ($rv.Value[0].Message.Contains("###:COMPLETE:###")) { | |
| $completed = $true | |
| LogWriter ("The remote opertion completed") | |
| } | |
| } while (-not $completed) | |
| # 2nd step inside the VM: use dism to write the image to the new disk | |
| if (-not $failed) { | |
| # now we have to work with the partitions inside of the VM - Invoking this script with the parameter -mode StartInternalTask-1 | |
| LogWriter ("Run the second part of the converting process on the target VM - this can last hours") | |
| Invoke-AzVMRunCommand -ResourceGroupName $targetVmResourceGroup -Name $targetVmName -CommandId "RunPowerShellScript" -ScriptPath $scriptPath -Parameter @{"-mode" = "StartInternalTask-2"} | |
| # While the script is running internally, we have to wait for completion - looping every 2 minutes (the task can take seeral hours) | |
| $completed = $false | |
| $failed = $false | |
| do { | |
| Start-Sleep -Seconds 120 | |
| try { | |
| $rv = Invoke-AzVMRunCommand -ResourceGroupName $targetVmResourceGroup -Name $targetVmName -CommandId "RunPowerShellScript" -ScriptPath $scriptPath -Parameter @{"-mode" = "CheckInternalTask"} | |
| } catch { | |
| LogWriter ("The remote operation failed: $_") | |
| $failed = $true | |
| $completed = $true | |
| } | |
| LogWriter ("Waiting for the remote task - long running operation") | |
| if ($rv.Value[0].Message.Contains("###:COMPLETE:###")) { | |
| $completed = $true | |
| LogWriter ("The remote opertion completed") | |
| } | |
| } while (-not $completed) | |
| } | |
| # last step inside the VM: create UEFI partition | |
| if (-not $failed) { | |
| LogWriter ("Run the last part of the converting process on the target VM") | |
| Invoke-AzVMRunCommand -ResourceGroupName $targetVmResourceGroup -Name $targetVmName -CommandId "RunPowerShellScript" -ScriptPath $scriptPath -Parameter @{"-mode" = "StartInternalTask-3"} | |
| } | |
| if (-not $failed) { | |
| # ready with the internal work | |
| # stop the target VM | |
| LogWriter ("Deallocate the target VM") | |
| Stop-AzVM -ResourceGroupName $targetVmResourceGroup -Name $targetVmName -Force | |
| # detach the copy of the source disk (V1) | |
| LogWriter ("Detach the copied source disk") | |
| $targetVm = Remove-AzVMDataDisk -VM $targetVm -Name $sourceDiskCopy.Name | |
| Update-AzVM -VM $targetVm -ResourceGroupName $targetVmResourceGroup | |
| # detach the later target disk (V2) | |
| LogWriter ("Detacht the target disk") | |
| $targetVm = Remove-AzVMDataDisk -VM $targetVm -Name $targetDisk.Name | |
| Update-AzVM -VM $targetVm -ResourceGroupName $targetVmResourceGroup | |
| # swap os-disk | |
| LogWriter ("Swap OS disk to have the converted disk as the OS disk") | |
| $targetVm = Get-AzVM -ResourceGroupName $targetVmResourceGroup -Name $targetVmName | |
| $targetVm = Set-AzVMOSDisk -VM $targetVm -ManagedDiskId $targetDisk.Id -Name $targetDisk.Name | |
| Update-AzVM -VM $targetVm -ResourceGroupName $targetVmResourceGroup | |
| LogWriter ("Starting the converted target VM") | |
| Start-AzVM -ResourceGroupName $targetVmResourceGroup -Name $targetVmName | |
| # clean-up | |
| LogWriter ("Cleaning up") | |
| $sourceDiskSnap | Remove-AzSnapshot -Force | |
| $sourceDiskCopy | Remove-AzDisk -Force | |
| $targetDiskOrg | Remove-AzDisk -Force | |
| LogWriter ("We are ready. The new V2 VM $targetVmName is ready and a copy of the original VM (which could be removed if everything works as expected") | |
| } else { | |
| # something went wrong - stopping the process to let the admin doing some debugging | |
| LogWriter ("Error: Something went wrong - stopping the process to let the admin doing some debugging. Remember to clean-up manually (disks, snapshots, VM)") | |
| } | |
| } | |
| #endregion CreateTheAzureResources | |
| #region RunOnTheNewVmAndHandleDisks | |
| if ($mode -eq "StartInternalTask-1") { | |
| LogWriter("Preparing local VM") | |
| try {Set-MPPreference -ScanAvgCPULoadFactor 5} catch{} | |
| try { | |
| $defragSvc = Get-Service -Name defragsvc -ErrorAction SilentlyContinue | |
| Set-Service -Name defragsvc -StartupType Manual -ErrorAction SilentlyContinue | |
| $supportedSize = (Get-PartitionSupportedSize -DriveLetter "c" -ErrorAction Stop) | |
| if ((Get-Partition -DriveLetter "c").Size -lt $supportedSize.SizeMax) { | |
| LogWriter("Resize C: partition to fill up the disk") | |
| Resize-Partition -DriveLetter "c" -Size $supportedSize.SizeMax | |
| } | |
| Set-Service -Name defragsvc -StartupType $defragSvc.StartType -ErrorAction SilentlyContinue | |
| } | |
| catch { | |
| LogWriter("Resize C: partition failed: $_") | |
| } | |
| LogWriter ("Preparing the disks and partitions") | |
| $targetDiskNumber = (Get-Disk | Where-Object {$_.Path -like "*&000007#*"}).Number # lun=7 | |
| $sourceDiskNumber = (Get-Disk | Where-Object {$_.Path -like "*&000006#*"}).Number # lun=6 | |
| $diskPath=(Get-Disk -Number $targetDiskNumber).Path | |
| Get-Disk -Number $targetDiskNumber | Initialize-Disk -PartitionStyle GPT -ErrorAction SilentlyContinue | |
| # exclude defender | |
| LogWriter ("Set defender excludes to speed up the imaging and apply process") | |
| New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Exclusions" -Name "Paths" -Force -ErrorAction Ignore | |
| New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Exclusions\Paths" -Name "1" -Value "S:\" -force -ErrorAction Ignore | |
| New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Exclusions\Paths" -Name "2" -Value "T:\" -force -ErrorAction Ignore | |
| New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Exclusions" -Name "Paths" -Force -ErrorAction Ignore | |
| New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths" -Name "S:\" -Value 0 -force -ErrorAction Ignore | |
| New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths" -Name "T:\" -Value 0 -force -ErrorAction Ignore | |
| # delete all partion if needed | |
| Remove-Partition -DiskNumber $targetDiskNumber -PartitionNumber 2,3,4,5,6,7,8,9 -Confirm:$false -ErrorAction SilentlyContinue | |
| # Create UEFI partition | |
| LogWriter ("Create UEFI partition") | |
| $uefi=New-Partition -DiskNumber $targetDiskNumber -Size 100MB -GptType "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}" -IsHidden | |
| Format-Volume -FileSystem FAT32 -NewFileSystemLabel "SYSTEM" -Path $uefi.DiskPath | |
| # Create recovery partition | |
| LogWriter ("Create recovery partition") | |
| $recovery=New-Partition -DiskNumber $targetDiskNumber -Size 450MB -GptType "{de94bba4-06d1-4d40-a16a-bfd50179d6ac}" -IsHidden | |
| $null = @" | |
| select disk $targetDiskNumber | |
| select partition $($recovery.PartitionNumber) | |
| gpt attributes=0x8000000000000001 | |
| exit | |
| "@ | diskpart.exe | |
| # Create windows partition | |
| LogWriter ("Create Windows partition") | |
| $windows=New-Partition -DiskNumber $targetDiskNumber -UseMaximumSize | |
| # mount partitions | |
| LogWriter ("Mount partitions") | |
| Remove-PartitionAccessPath -DiskNumber $sourceDiskNumber -PartitionNumber 2 -AccessPath (Get-Partition -DiskNumber $sourceDiskNumber -PartitionNumber 2).AccessPaths[0] -ErrorAction SilentlyContinue | |
| Add-PartitionAccessPath -DiskNumber $sourceDiskNumber -PartitionNumber 2 -AccessPath "S:\" | |
| Add-PartitionAccessPath -DiskNumber $targetDiskNumber -PartitionNumber $uefi.PartitionNumber -AccessPath "U:\" | |
| Add-PartitionAccessPath -DiskNumber $targetDiskNumber -PartitionNumber $recovery.PartitionNumber -AccessPath "R:\" | |
| Add-PartitionAccessPath -DiskNumber $targetDiskNumber -PartitionNumber $windows.PartitionNumber -AccessPath "T:\" | |
| # Format drives | |
| LogWriter ("Format partitions") | |
| Format-Volume -FileSystem NTFS -NewFileSystemLabel "Windows Sytem Drive" -DriveLetter T:\ -ErrorAction SilentlyContinue | |
| Format-Volume -FileSystem NTFS -NewFileSystemLabel "SYSTEM" -DriveLetter R:\ -ErrorAction SilentlyContinue | |
| Format-Volume -FileSystem FAT32 -NewFileSystemLabel "SYSTEM" -DriveLetter U:\ -ErrorAction SilentlyContinue | |
| # Capture the source windows | |
| # Dism /Capture-Image /ImageFile:`"C:\Capture.wim`" /CaptureDir:S:\ /Name:Captured | |
| # Start dism but don't wait | |
| LogWriter ("Starting DISM to create an image") | |
| Start-Process -FilePath Dism.exe -ArgumentList "/Capture-Image /ImageFile:`"C:\Capture.wim`" /CaptureDir:S:\ /Name:Captured" | |
| Start-Sleep -Seconds 30 | |
| } | |
| if ($mode -eq "StartInternalTask-2") { | |
| # Rollout the image to the target disk | |
| # Dism /Apply-Image /ImageFile:"C:\Capture.wim" /ApplyDir:T:\ /Index:1 /CheckIntegrity | |
| LogWriter ("Starting DISM and apply image to the new Windows partition on the V2 disk") | |
| Start-Process -FilePath Dism.exe -ArgumentList "/Apply-Image /ImageFile:`"C:\Capture.wim`" /ApplyDir:T:\ /Index:1 /CheckIntegrity" | |
| Start-Sleep -Seconds 30 | |
| } | |
| if ($mode -eq "StartInternalTask-3") { | |
| # Create UEFI | |
| LogWriter ("Writing UEFI data to UEFI partition") | |
| Start-Process -Wait -FilePath "T:\Windows\System32\bcdboot.exe" -WorkingDirectory "T:\Windows\System32" -ArgumentList "T:\Windows /s U: /f UEFI" | |
| } | |
| #endregion RunOnTheNewVmAndHandleDisks | |
| #region RunOnTheNewVmAndCheckState | |
| if ($mode -eq "CheckInternalTask") { | |
| if (Get-Process -Name DISM -ErrorAction SilentlyContinue) { | |
| # DISM is still running | |
| LogWriter ("Starting DISM") | |
| } else { | |
| # DISM completed to the rest and terminate | |
| write-host "###:COMPLETE:###" | |
| } | |
| } | |
| #endregion RunOnTheNewVmAndCheckState | |
| #endregion MainApp | |
I think this needs to be changed? The #region InternalMethods script path
if (-not $PSCommandPath) {$scriptPath="C:\1Drive\OneDrive - sepago GmbH\Desktop\Convert-VmV1toV2.ps1"}
Hello,
i got the following error message:
New-AzVM : Cannot validate argument on parameter 'LicenseType'. The argument is null or empty. Provide an argument
that is not null or empty, and then try the command again.
At C:\temp\Scripts\Create-V2-From-V1-VM.ps1:96 char:107
- ... mResourceGroup -Location $location -LicenseType $sourceVm.LicenseType
-
~~~~~~~~~~~~~~~~~~~~~- CategoryInfo : InvalidData: (:) [New-AzVM], ParentContainsErrorRecordException
- FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.Azure.Commands.Compute.NewAzureVMCommand
Can you help me ?
Greetings Ronald
Thanks that worked. But next error
New-AzVM : The zone(s) 'Server' for resource 'Microsoft.Compute/virtualMachines/vmprtg001-v2' is not supported. The
supported zones for location 'westeurope' are '1,2,3'
ErrorCode: InvalidAvailabilityZone
ErrorMessage: The zone(s) 'Server' for resource 'Microsoft.Compute/virtualMachines/vmprtg001-v2' is not supported. The
supported zones for location 'westeurope' are '1,2,3'
ErrorTarget:
StatusCode: 400
ReasonPhrase: Bad Request
OperationID : 1e6b9f67-2f4f-4d89-a4dd-99bee270e7eb
At C:\temp\Scripts\Create-V2-From-V1-VM.ps1:96 char:9
-
New-AzVM -VM $vmConfig -ResourceGroupName $targetVmResourceGr ... -
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~- CategoryInfo : CloseError: (:) [New-AzVM], ComputeCloudException
- FullyQualifiedErrorId : Microsoft.Azure.Commands.Compute.NewAzureVMCommand
Hi @ronaldbok: should we check together via teams? Maybe you can sent me you mail address via linked in or by email.
Hi Marcel, thank you for your wonderful script. I do have a question. How do i reach you via MS teams?
Hi Marcel, thank you for your wonderful script. I do have a question. How do i reach you via MS teams?
Hi @zzubedi : You can reach me at marcel.meurer@itprocloud.com
@MarcelMeurer Would be great if you can implement that the VM Tags from the V1 VM are also set on the V2 VM 😃

My script runs for 3-4 hours, and every 2 minutes, I receive the following message:
'2024-05-03T13:37:41.6703738Z Waiting for the remote task - long running operation.'
I attempted to run it a second time, but I encountered the same issue.