Change the OS disk used by an Azure VM using PowerShell - azure

I'm trying to swapout the OS disk on an Azure VM. The disk is a managed disk, the disk that I'm replacing it with is a managed disk. I follow the instructions that I have found in several documents for doing this in PS.
https://azure.microsoft.com/en-us/blog/os-disk-swap-managed-disks/
I get below message that leaves me to believe that it completed successfully.
RequestId IsSuccessStatusCode StatusCode ReasonPhrase
--------- ------------------- ---------- ------------
True OK OK
Here is the PS code that I am using:
$vm = Get-AzureRmVM -ResourceGroupName rgname -Name vmname
Stop-AzureRmVM -ResourceGroupName rgname -Name $vm.Name -Force
$disk = Get-AzureRmDisk -ResourceGroupName rgname -Name newosdiskname
Set-AzureRmVMOSDisk -VM $vm -ManagedDiskId $disk.Id -Name $disk.Name
Update-AzureRmVM -ResourceGroupName rgname -VM $vm
So why is this not working?

As the description in the document, you can replace the OS disk of an existing VM with a managed OS disk. So the replacement disk must be an OS disk, and you also cannot swatch the OS type, for example, from Linux to Windows.
And the PowerShell code that you use works fine on my side and the screenshot of the result shows like this:
And I think this function is used to replace the backup OS disk. So it's better to use a backup managed disk of the OS disk to replace.

Couldnot get this to work using PS. I was able to easily do it via CLI.

Related

Copy Azure OSDisks V DataDisks and powershell set Lun at 0

I have a couple of questions
I'm copying some OS disks and some data disks, and I'm using the same process for both, which is create snapshot and then creating disks from the snapshots. I've read a bit online where there's two processes for what appears to be the same task. Is there any difference between the OS Disk and Data Disk when you take a snapshot and create a disk from the snapshot?
Also I'm trying to attach the aforementioned data disks and the LUN number always starts at 1, would anyone know how I can get it to start at 0. The code I have is
$dataDisks = Get-AzDisk | ? {$_.name -like "*$ddisk*"}
$lun = 0
foreach ($disk in $dataDisks){
$lun += 1
$vm = Add-AzVMDataDisk -CreateOption Attach -VM $vm -Lun $lun -ManagedDiskId $disk.Id
Update-AzVM -VM $vm -ResourceGroupName $VMRG -Verbose
}
Thanks in advance and apologies if this seems like a lot of questions in one post :)
OS disk and data disk snapshots should behave the same as the snapshot is taken of the VM in order to be able to roll-back the changes done on the disks until they're consolidated on the VM.
Regarding your other issue:
foreach ($disk in $dataDisks){
$vm = Add-AzVMDataDisk -CreateOption Attach -VM $vm -Lun $lun -ManagedDiskId $disk.Id
Update-AzVM -VM $vm -ResourceGroupName $VMRG -Verbose
$i++ # You were modifying the variable before it it had finished.
}
I have been doing a lot of subscription migrations and need to be able to move and attach data disks, pretty similar.
I always just start mine with this:
$lun = -1
## If multiple Data Disks it will prompt for the LUN, start at 0
foreach ($ddisk in $datadisks) {
$luns = ++$lun
$getddisk = Get-AzDisk -ResourceGroupName $destinationrg -DiskName $ddisk
Write-output $ddisk.id
$newvm = Add-AzVMDataDisk -vm $newvm -Name $ddisk -CreateOption Attach -
ManagedDiskId $getddisk.id -Caching Readonly -storageaccounttype
Premium_LRS -Lun $luns
}

Provisioning failed. OS Provisioning for VM 'VM Name' did not finish in the allotted time

I'm new to Azure. I'm trying to create resources in Azure using powershell.
My requirement is to create an image from a VM. I have followed to ways to do it :
Process 1: Do it manually
Generalize the VM : Login to VM -> Open command prompt -> cd %windir%\system32\sysprep --> run sysprep.exe --> Check generalize button--> Shutdown.
Create snapshot : Go to Azure portal-> Go to the VM which is generalized --> Click on Capture button --> Give image name and mention resource group and click on Create.
This will create an Image.
Process 2: Do it with powershell
# create session of the VM
$UserName = "$IPAddress\$adminUsername"
$Password = ConvertTo-SecureString $adminPassword -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($UserName, $Password)
$s = New-PSSession -ComputerName $IPAddress -Credential $psCred
# Run SysPrep for generalizing the VM
$sysprep = 'C:\Windows\System32\Sysprep\Sysprep.exe'
$arg = '/generalize /oobe /shutdown /quiet'
Invoke-Command -Session $s -ScriptBlock {param($sysprep,$arg)Start-Process -FilePath $sysprep -ArgumentList $arg} -ArgumentList $sysprep,$arg
#Stop the VM
Stop-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $virtualMachineName -Force
# Generalize the VM
Set-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $virtualMachineName -Generalized
# Create the Image
$vm = Get-AzureRmVM -Name $virtualMachineName -ResourceGroupName $ResourceGroupName
$image = New-AzureRmImageConfig -Location $location -SourceVirtualMachineId $vm.ID
New-AzureRmImage -Image $image -ImageName $ImageName -ResourceGroupName $ResourceGroupName
Both the processes will create a Image. But the problem I'm facing here is when I spin VM from the image created from Process 1 , it is created successfully without any issue.
But when I spin VM from image created from Process2 , it is getting created but with below error message :
Provisioning failed. OS Provisioning for VM 'VM Name' did not finish
in the allotted time. However, the VM guest agent was detected
running. This suggests the guest OS has not been properly prepared to
be used as a VM image (with CreateOption=FromImage).
Can anyone tell me what it is I'm doing wrong with powershell script, that I'm getting this error.
Time seems to be the issue.
Sysprep normally takes 10 - 15 minutes, you have no sleep time. You are shutting down VM as soon as the sysprep script is sent, no time to actually sysprep system.
You can either put a sleep time or a loop to check when VM is in a Stopped state.
There are quite a few docs on papering and creating VMs from images in Azure. As the error suggests, you likely missed a step.
If you are uploading a VHD from on prem to use in Azure, start with these docs
https://learn.microsoft.com/en-us/azure/virtual-machines/windows/prepare-for-upload-vhd-image
https://learn.microsoft.com/en-us/azure/virtual-machines/windows/upload-generalized-managed
If the VM you are capturing an image of is already in Azure and working, then start with these
https://learn.microsoft.com/en-us/azure/virtual-machines/windows/capture-image-resource
https://learn.microsoft.com/en-us/azure/virtual-machines/windows/create-vm-generalized-managed?toc=%2fazure%2fvirtual-machines%2fwindows%2ftoc.json
The last link shows you how to create the VM from that image via the portal or a simple PS command
New-AzVm `
-ResourceGroupName "myResourceGroup" `
-Name "myVMfromImage" `
-ImageName "myImage" `
-Location "East US" `
-VirtualNetworkName "myImageVnet" `
-SubnetName "myImageSubnet" `
-SecurityGroupName "myImageNSG" `
-PublicIpAddressName "myImagePIP" `
-OpenPorts 3389

VM Created from Image with Azure Powershell does not set ComputerName

I have an Azure Image, which when I use Azure Powershell to create a VM from, despite me setting the ComputerName in the script, the VM is created without setting the ComputerName to the provided value.
Script:
$ImageName = 'MyImage'
$RsgName = 'MyRsg'
$VmName = 'MyNewVM'
$DiagnosticStorageName = 'diagnosticsstore5048'
$cred = Get-Credential -Credential 'TheAdmin'
$Image = Get-AzureRmImage -ImageName $ImageName -ResourceGroupName $RsgName
# Get NIC
$nic = Get-AzureRmNetworkInterface -ResourceGroupName $RsgName
# Configure the new VM
$Vm = New-AzureRmVMConfig -VMName $VmName -VMSize 'Standard_A2_v2'
$Vm = Set-AzureRmVMSourceImage -VM $Vm -Id $Image.Id
$Vm = Set-AzureRmVMOSDisk -VM $Vm -Name $VmName'-disk' -StorageAccountType 'StandardLRS' -DiskSizeInGB '128' -CreateOption FromImage -Caching ReadWrite
$Vm = Add-AzureRmVMNetworkInterface -VM $Vm -Id $nic.Id
$Vm = Set-AzureRmVMBootDiagnostics -VM $Vm -Enable -ResourceGroupName $RsgName -StorageAccountName $DiagnosticStorageName
$Vm = Set-AzureRmVMOperatingSystem -VM $Vm -Windows -ComputerName 'dor' -Credential $Cred -ProvisionVMAgent
New-AzureRmVM -VM $Vm -ResourceGroupName $RsgName -Location 'West Europe' -DisableBginfoExtension
Last time I run the script to create the VM, it left the new VM with a computer name of 'WIN-I80O6J22ENS'
The Image was created as per the process here: https://learn.microsoft.com/en-gb/azure/virtual-machines/windows/capture-image-resource?toc=%2Fazure%2Fvirtual-machines%2Fwindows%2Fclassic%2Ftoc.json
UPDATE
Alot of people think that I am not Generalizing the image correctly, so I wanted to add here how I am doing it.
Inside the VM I run:
Start-Process -FilePath $env:windir\System32\Sysprep\Sysprep.exe -ArgumentList "/generalize /oobe /shutdown /unattend:$env:windir\System32\Sysprep\unattend.xml"
Unattend.xml only has one setting in it which is to step the TimeZone.
Once this is completed and the VM OS has shut down, I run the following to get the VM, stop it, and set it to Generalized:
# Shutdown Source VM & Generalize
$SourceVM = Get-AzureRmVM -ResourceGroupName $SourceRsg -Name $SourceVMName
$Null = Stop-AzureRMVM -ResourceGroupName $SourceRsg -Name $SourceVMName -Force
Write-Host 'Stopped Source VM'
Set-AzureRmVm -ResourceGroupName $SourceRsg -Name $SourceVMName -Generalized
Write-Host 'Set Source VM to Generalized'
I have noticed that when the last command is ran, the output is:
OperationId :
Status :
StartTime :
EndTime :
Error :
It doesn't actually say if it was successful or not?
After this I create the Image from the VM disk.
It might be caused by the image not being "Generalized".
Take a look at the Create a managed image of a generalized VM in Azure guide.
Hope it helps!
If you have a specialized image, the Computer name will be missing because the old computer name and the new computer name share the same RID and SID number and on a ideal situation each virtual machine deployment has different RID and SID number. The Computer name can be displayed if the image is a generalized.
Check this article for more information:
https://learn.microsoft.com/en-us/azure/virtual-machines/windows/create-vm-specialized
https://learn.microsoft.com/en-us/azure/virtual-machines/windows/capture-image-resource
The point everyone is trying to make, from your description of the issue, it seems the Image was not generalized correctly or aspect of the generalization failed.
The script work fine on my end, so the only issue is with the image. Screenshot
Try another deployment from that image an see is you get the same computer name (WIN-I80O6J22ENS).
If you get the same name that is a sure confirmation of issue with generalization.
If the name changes it might mean that the machine you imaged has a policy that is not accepting the name 'dor' so the system is generating default name.
Either way issue is not with the Powershell or Azure. Hope this helps.

NewAzureRmVM: Required parameter 'networkProfile' is missing (null)

So I have been trying to create a VM from Linux VHD and getting this error.
Following this
https://pebkac.io/2016/10/mikrotik-chr-in-azure-part-two/
Dinesh has resolved the problem, per comments on this.
It seems that the NIC creation may have been long running.
According to your error, please ensure NIC Interface is created successful.
$InterfaceName = "nic-chr-test"
$Interface = Get-AzureRmNetworkInterface -Name $InterfaceName -ResourceGroupName
You could the result of $Interface, if this is NULL, please create NIC firstly.
Also, you could try to use this template or scripts in this
template.
[Worked for me , needed to add the network interface to vm. Here is the cmdlet
Assuming you already created $nic - network interface earlier
PS C:\azure> $vm = Add-azurermvmnetworkinterface -vm $vm -id $nic.id
PS C:\azure> new-azurermvm -resourcegroupname $rgname -location $location -vm $vm
Add the NW interface for the VM
$vm = Add-AzureRmVMNetworkInterface -VM $vm -Id $nic.id

Reducing size of Azure VM OS Disk for download/export

I would like to create an Azure VM with a smaller OS disc than the default 127gb. I've been unable to find such an option in the Azure Portal, so I have attempted to shrink the disk. I have not been successful.
I understand I can trim (using the defragmentation tool) and shrink the volume (with Disk Management) but this won't change the "physical" size of the hard disk. That is, if I shrink the disk to 40gb, there will just be 87gb unallocated and the blob will still report 127gb.
What I am attempting to achieve is to shrink the blob to match the allocated space facilitating smaller downloads/exports of the VM image (e.g. 40 vs 127gb).
Any and all help is appreciated.
I have written a blog post detailing this answer in full. But the main issue here was being able to reduce the size of the Azure VM which defaults to 127gb in order to allow for fasted export/download. The way I have achieved this is by trimming the hard drive and then using Disk2VHD to create a VHD file of the running VM. Disk2VHD will create an expandable disk that is only as large as the current data on the disc, not the entire available disk. In my case 40gb vs 127gb. If one saves this VHD file to an attached disk (read: blob storage) it can be easily downloaded via HTTP by your entire team. Thus, the download is now 40gbs instead of 127gbs. For more, please read my detailed blog post:
http://www.kevinmcloutier.com/?p=263
Original link no longer works:
https://web.archive.org/web/20161027213258/http://kevinmcloutier.com/post/4
In Azure if you create a VM it will create with some default configuration. At present it is not supported to reduce/shrink the OS disk (managed or unmanaged) size of an Azure VM from the Azure Portal (say from 128Gb to 32Gb for example), using the following process we can archive that, and cut down the disk cost.
Step 1. Open your VM and go to the Disk Management.
Step 2. Open PowerShell and execute the following command.
After successful execution you can find the following
Step 3. Now deallocate the VM from the Azure portal
Step 4. Now Go the Properties blade of the disk and copy the Resource ID
Step 5. Now execute the following PowerShell script from your local system. Must change $DiskID, $VMName, $AzSubscription with your value
# Variables
$DiskID = ""# eg. "/subscriptions/203bdbf0-69bd-1a12-a894-a826cf0a34c8/resourcegroups/rg-server1-prod-1/providers/Microsoft.Compute/disks/Server1-Server1"
$VMName = "VM-Server1"
$DiskSizeGB = 32
$AzSubscription = "Prod Subscription"
# Script
# Provide your Azure admin credentials
Connect-AzAccount
#Provide the subscription Id of the subscription where snapshot is created
Select-AzSubscription -Subscription $AzSubscription
# VM to resize disk of
$VM = Get-AzVm | ? Name -eq $VMName
#Provide the name of your resource group where snapshot is created
$resourceGroupName = $VM.ResourceGroupName
# Get Disk from ID
$Disk = Get-AzDisk | ? Id -eq $DiskID
# Get VM/Disk generation from Disk
$HyperVGen = $Disk.HyperVGeneration
# Get Disk Name from Disk
$DiskName = $Disk.Name
# Get SAS URI for the Managed disk
$SAS = Grant-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $DiskName -Access 'Read' -DurationInSecond 600000;
#Provide the managed disk name
#$managedDiskName = "yourManagedDiskName"
#Provide Shared Access Signature (SAS) expiry duration in seconds e.g. 3600.
#$sasExpiryDuration = "3600"
#Provide storage account name where you want to copy the snapshot - the script will create a new one temporarily
$storageAccountName = "shrink" + [system.guid]::NewGuid().tostring().replace('-','').substring(1,18)
#Name of the storage container where the downloaded snapshot will be stored
$storageContainerName = $storageAccountName
#Provide the key of the storage account where you want to copy snapshot.
#$storageAccountKey = "yourStorageAccountKey"
#Provide the name of the VHD file to which snapshot will be copied.
$destinationVHDFileName = "$($VM.StorageProfile.OsDisk.Name).vhd"
#Generate the SAS for the managed disk
#$sas = Grant-AzureRmDiskAccess -ResourceGroupName $resourceGroupName -DiskName $managedDiskName -Access Read -DurationInSecond $sasExpiryDuration
#Create the context for the storage account which will be used to copy snapshot to the storage account
$StorageAccount = New-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName -SkuName Standard_LRS -Location $VM.Location
$destinationContext = $StorageAccount.Context
$container = New-AzStorageContainer -Name $storageContainerName -Permission Off -Context $destinationContext
#Copy the snapshot to the storage account and wait for it to complete
Start-AzStorageBlobCopy -AbsoluteUri $SAS.AccessSAS -DestContainer $storageContainerName -DestBlob $destinationVHDFileName -DestContext $destinationContext
while(($state = Get-AzStorageBlobCopyState -Context $destinationContext -Blob $destinationVHDFileName -Container $storageContainerName).Status -ne "Success") { $state; Start-Sleep -Seconds 20 }
$state
# Revoke SAS token
Revoke-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $DiskName
# Emtpy disk to get footer from
$emptydiskforfootername = "$($VM.StorageProfile.OsDisk.Name)-empty.vhd"
# Empty disk URI
#$EmptyDiskURI = $container.CloudBlobContainer.Uri.AbsoluteUri + "/" + $emptydiskforfooter
$diskConfig = New-AzDiskConfig `
-Location $VM.Location `
-CreateOption Empty `
-DiskSizeGB $DiskSizeGB `
-HyperVGeneration $HyperVGen
$dataDisk = New-AzDisk `
-ResourceGroupName $resourceGroupName `
-DiskName $emptydiskforfootername `
-Disk $diskConfig
$VM = Add-AzVMDataDisk `
-VM $VM `
-Name $emptydiskforfootername `
-CreateOption Attach `
-ManagedDiskId $dataDisk.Id `
-Lun 63
Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM
$VM | Stop-AzVM -Force
# Get SAS token for the empty disk
$SAS = Grant-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername -Access 'Read' -DurationInSecond 600000;
# Copy the empty disk to blob storage
Start-AzStorageBlobCopy -AbsoluteUri $SAS.AccessSAS -DestContainer $storageContainerName -DestBlob $emptydiskforfootername -DestContext $destinationContext
while(($state = Get-AzStorageBlobCopyState -Context $destinationContext -Blob $emptydiskforfootername -Container $storageContainerName).Status -ne "Success") { $state; Start-Sleep -Seconds 20 }
$state
# Revoke SAS token
Revoke-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername
# Remove temp empty disk
Remove-AzVMDataDisk -VM $VM -DataDiskNames $emptydiskforfootername
Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM
# Delete temp disk
Remove-AzDisk -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername -Force;
# Get the blobs
$emptyDiskblob = Get-AzStorageBlob -Context $destinationContext -Container $storageContainerName -Blob $emptydiskforfootername
$osdisk = Get-AzStorageBlob -Context $destinationContext -Container $storageContainerName -Blob $destinationVHDFileName
$footer = New-Object -TypeName byte[] -ArgumentList 512
write-output "Get footer of empty disk"
$downloaded = $emptyDiskblob.ICloudBlob.DownloadRangeToByteArray($footer, 0, $emptyDiskblob.Length - 512, 512)
$osDisk.ICloudBlob.Resize($emptyDiskblob.Length)
$footerStream = New-Object -TypeName System.IO.MemoryStream -ArgumentList (,$footer)
write-output "Write footer of empty disk to OSDisk"
$osDisk.ICloudBlob.WritePages($footerStream, $emptyDiskblob.Length - 512)
Write-Output -InputObject "Removing empty disk blobs"
$emptyDiskblob | Remove-AzStorageBlob -Force
#Provide the name of the Managed Disk
$NewDiskName = "$DiskName" + "-new"
#Create the new disk with the same SKU as the current one
$accountType = $Disk.Sku.Name
# Get the new disk URI
$vhdUri = $osdisk.ICloudBlob.Uri.AbsoluteUri
# Specify the disk options
$diskConfig = New-AzDiskConfig -AccountType $accountType -Location $VM.location -DiskSizeGB $DiskSizeGB -SourceUri $vhdUri -CreateOption Import -StorageAccountId $StorageAccount.Id -HyperVGeneration $HyperVGen
#Create Managed disk
$NewManagedDisk = New-AzDisk -DiskName $NewDiskName -Disk $diskConfig -ResourceGroupName $resourceGroupName
$VM | Stop-AzVM -Force
# Set the VM configuration to point to the new disk
Set-AzVMOSDisk -VM $VM -ManagedDiskId $NewManagedDisk.Id -Name $NewManagedDisk.Name
# Update the VM with the new OS disk
Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM
$VM | Start-AzVM
start-sleep 180
# Please check the VM is running before proceeding with the below tidy-up steps
# Delete old Managed Disk
Remove-AzDisk -ResourceGroupName $resourceGroupName -DiskName $DiskName -Force;
# Delete old blob storage
$osdisk | Remove-AzStorageBlob -Force
# Delete temp storage account
$StorageAccount | Remove-AzStorageAccount -Force
You would have to create your own VM image and then deploy using that. This template shows you how to deploy using your own image.
https://github.com/Azure/azure-quickstart-templates/tree/master/101-vm-from-user-image
Currently, the images in the gallery are all 127gb. Since Azure VMs only used fixed size discs, you can't just select the size.
Sapnandu's solution works. Many thanks!
I'm still not 100% sure what was the magic criteria to make it work, but finally I have a vm running with the reduced disk. Making it Gen1 definitely was one.
I tried many similar things and they all got stuck at boot time.
My way was:
Create the vm from the gallery images. You'll have a 128Gb OS disk.
Tweak the vm according to your needs. Here you can run the resize script. So, you'll end up with a ~90Gb unallocated space.
After a few failed attempts I started to value the configured vm and I really didn't want to repeat the configuration again.
Make a snapshot of this disk then make a disk from the snapshot. These options will be presented to you by the snapshot and by the disk.
Then make a new vm from the disk. Check if it boots. Boot diagnostics shows it rather quickly.
After stopping the new vm I looked up the 3 (subscriptionID,diskID,vmName) required parameters for Sapnandu's script (Step 5. in his post) and executed the script in azure cloud shell. (the icon in header)
It takes time, about 10 minutes or so.
When the script finished the new vm was running with the smaller disk.
So, there is a vm setup where the script works perfectly.

Resources