Move Azure (ARM) VM between Storage Accounts and beyond

Hi all,

One of the hardest operations which I’m actually encountering when working with Azure virtual machines is to move a Virtual Machine from a location to another.

Moving a VM from a location includes :

  • Change the VM’s Storage Account
  • Change the VM’s Storage Container
  • Change from  subscription
  • Change the VM location/Region
  • Change the Virtual Network

You may also want to :

  • Change the VM name
  • Change the VM’s availability set
  • Pass from single NIC VM to multiple NIC VM

or any combination of them.

Today, I’m publishing the first version of the ‘Move-ArmVM’ powershell script which is intended to provide a simple way to move/recreate an Azure Resource Manager VM (Or VMs), covering different move scenarios. The script will  create a copy of the Source VM. The Source VM will only be stopped, no change will affect it.

Move-ArmVM v1.0

Features

  • Move a VM to a different Storage Account
  • Move a VM to a different Storage Container
  • Move a VM to a different Virtual Network
  • Move a VM to another location
  • Move a VM to a different Subscription
  • Change the VM Name during the move
  • Change the VM’s resource Group during the move
  • Change the VM’s availability Set  during the move
  • Pass from single NIC VM to multiple NICs VM ( Annex 1- How to configure the parameter file to have  multiple VNICs on the target VM)

* This script supports moving one or more VMs (Annex  2- How to configure the parameter file to move multiple VMs)

Release Notes

This version does not support moving/creating the next items. You should create them manually after the move:

  • VNIC’s Public IP
  • Tags
  • Load Balancer Configurations
  • Anything not mentioned on the Features section

Download Link

Version 1.0 Preview : https://gallery.technet.microsoft.com/Move-Azure-ARM-VM-between-66adb292

How To use it ?

I highly recommend you to download this JSON editor. It’s free, simple and will help you visualize and edit  JSON files : JSONedit

1- Fill the parameter file

This script uses a Parameters file, you should first fill the required parameters. The parameter file name is hardcoded, do not change its naming and location (the same location than the script). A ‘Logs’ folder will be created on the working directory. The log files will be created under this folder.

 

Parameter

Content

Example

Source    

Subscription

The source Subscription Name SamirSub

VmName

The source VM Name (The name of the VM to be moved) ADFS01

VmRG

The source VM Resource Group ADFSRG
Destination    

Subscription

The destination Subscription Name
  • BuildSub
Compute    

AvailabilitySet

  • Set this value to 0 if you don’t want to place the VM in a availability Set
  • Type the name of the target availability set. If the AS does not exist, it will be created
  • 0
  • AdfsAS

VmName

  • Set this value to 1 if you want to use the same source VM Name
  • Type another Name if you want that the moved VM have another name
  • 1
  • ADFSVM01

VmRG

  • Type a name of the resource group when to place the target VM. If the Resource Group does not exist, it will be created under the same region where the VM will be created
  • ADFSVMRG

VmSize*

  • Set this value to 1 if you want to use the same source VmSize
  • Type a new VM Size (Standard_A1, Standard_A2, Standard_D1…)
  • 1
  • Standard_D1
Storage    

StorageAccountName

  • Type the name of the target Storage Account. This Storage Account must already exist
  • vhdsa

Container

  • Type the name of the target storage container, if the container does not exist, it will be created
  • vhd
VNICs**    

NsgName*

  • Type 0 if you don’t want to attach an NSG to this NIC
  • Type the name of destination Network Security Group to attach to this VNIC
  • 0
  • ADFSnsg

NsgRG*

  • Type the NSG’s resource Group. If the NSG is set to 0, this parameter will be ignored
  • ADFSNsgRG

VnetName*

  • Type the Virtual Network name for this VNIC
 

VnetRG*

  • Type the Virtual Network Resource Group
 

SubnetName*

  • Type the Subnet name for this VNIC
 

IP*

  • Type the IP address if this VNIC
  • 192.168.1.77

* The value of this parameter is not monitored, if the value is wrong (Inexistent Vnet, erroned IP…), the script will fail. The error can be checked  on the log file

** You can choose to have multiple VNICs on the target VM. Check the Annex for the how to. The VM size must support the VNICs count

2- Run the script

After the configuration of the parameter file, run the script file. You will be prompted for your Azure credentials. The Parameter file name and location are hardcoded and can’t be changed. The parameter file have to be located on the location than the script file

Annex
1- How to configure the parameter file to have  multiple VNICs on the target VM

  • Open the parameter file on a text editor
  • Copy the Section between the two brackets, on the VNICs section

0791

  • Paste it just after the closing bracket of the first VNIC (paste it too many times than the VNICs count). On the example, I will paste it two times because  I want to have 3 VNICs on the target VM

0792

  • Add a comma (,) after all the VNICs closing brackets except the last one

0793

2- How to configure the parameter file to move multiple VMs

  • Open the parameter file on a text editor
  • Copy the Section between the two brackets, on the Virtual Machines section

0794

  • Paste it just after the closing bracket of the previous Virtual Machine (paste it too many times than the VMs count). On the example, I will paste it only one time because  I want to move 2 VMs. Add a comma (,) after all the VMs closing brackets except the last one

0795

Advertisements

43 thoughts on “Move Azure (ARM) VM between Storage Accounts and beyond

  1. Hello Samir. Thanks for the great content on your site. I tried the Move-ArmVMv1.0preview but I keep getting an error “Cannot validate argument on parameter ‘VMName'” when it attempts to start creating the destination VM. The error I keep getting is:

    [13-04-2016 11:36:28] Creating the Destination VM Object : MyNewVM …
    [13-04-2016 11:36:28] Error Cannot validate argument on parameter ‘VMName’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
    [13-04-2016 11:36:28] [Failed] Creating the Destination VM Object : MyNewVM …
    [13-04-2016 11:36:28] Exiting the script, please solve the problem then try again

    I got the error when I had “VmName” : “1”, in my param file. I also get it even when I change the name to something else such as “MyNewVM2”

    Any help? Thank you so much.

      • Thanks!!
        It worked but I found another typo in line 652:
        $Command = {New-AzureRmVMConfig -VMName $RDestVmName -VMSize VmSize}. I changed that to:
        $Command = {New-AzureRmVMConfig -VMName $RDestVmName -VMSize $RDestVmSize} and it worked like charm!!

        Merci beaucoup!!

      • Thank you rant the new version 1.2
        I’m now getting the following error.

        [09-06-2016 18:16:31] [Completed] Get the destination Storage Account : barrstorage …
        [09-06-2016 18:16:31] Regulate the Destination Container : vhd …
        [09-06-2016 18:16:32] Error Cannot validate argument on parameter ‘StorageAccountKey’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
        [09-06-2016 18:16:32] [Failed] Regulate the Destination Container : vhd …
        [09-06-2016 18:16:32] Exiting the script, please solve the problem then try again

  2. I’m getting:

    “Error The term ‘Import-AzureRM’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.”

    Should this be “Import-AzureVM” (this is a valid cmdlet)? Although that’s an ASM cmdlet so doubt it should be that….

    I’m using v1.3.2 of Azure PS.

  3. I figured out that, with this version of Azure PS, that whole section is not really required….commented it out and the script worked fine after that. Many thanks for an awesome script.

    • Indeed, the import command is not required since the modules are loaded by default. But i explicitly added it to avoid any issue if the modules were not loaded with the session.

  4. Hi,

    Script looks great. i am hitting issues when moving between subscriptions. tried modifying the script for subscription ID’s instead of names but a similar problem.
    are there any limitation on non Azure ad accounts (i.e. live ID / microsoft accounts)

    errors are:
    Selecting the Subscription [**my source subscription**]
    Error Provided subscription **my source subscription** does not exist
    [Failed] Selecting the Subscription [**my source subscription**] …

    Any thoughts?

    I have ruled out basic issues, like i have access to teh subscriptsions, typos, and i can select the subscription using the select-azurermsubscription.

    Thanks,
    Ciaran

  5. sorry should also say that i am using version 1.4 of Azure powershell:
    Version : 1.4.0
    Name : Azure
    Author : Microsoft Corporation
    PowerShellVersion : 3.0

  6. Hey Samir,

    Great script, however when I run it I get the error.

    [12-05-2016 18:14:36] Error Cannot validate argument on parameter ‘VMSize’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
    [12-05-2016 18:14:36] [Failed] Creating the Destination VM Object : TestVM …
    [12-05-2016 18:14:36] Exiting the script, please solve the problem then try again

    Does not matter if you state a zero or the actual size i.e “Standard_A3”

    Any ideas

  7. Dear Samir,

    Great script, however when I run it I get the error

    05-2016 18:54:49] Validating the VM [Pay-As-You-Go / TestVM / TestVM] …
    [12-05-2016 18:54:50] Error Run Login-AzureRmAccount to login.
    [12-05-2016 18:54:50] [Failed] Validating the VM [Pay-As-You-Go / TestVM / TestVM] …
    [12-05-2016 18:54:50] Exiting the script, please solve the problem then try again

  8. Hi Samir,
    Greetings!! Suddenly I ran into this issue: “[13-05-2016 14:05:49] Error Cannot validate argument on parameter ‘StorageAccountKey’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
    [13-05-2016 14:05:49] [Failed] Regulate the Destination Container: vm1container …
    [13-05-2016 14:05:49] Exiting the script, please solve the problem then try again

    That’s the fist time I’m seeing this. It looks like something changed in the backend? Any pointers would be greatly appreciated as always.

    Regards.

    • can you manually run the following commands :

      #Load function
      Function GetSARG ($SAName)
      {

      $SA = Get-AzureRmStorageAccount | where {$_.StorageAccountName -eq $SAName}
      $SARG = $SA.ResourceGroupName
      return $SARG

      }

      #Initialise variables
      $SAName =

      $SARG = GetSARG -SAName $SAName
      $SAKeys = Get-AzureRMStorageAccountKey -ResourceGroupName $SARG -Name $SAName
      $SAContext = New-AzureStorageContext -StorageAccountName $SAName -StorageAccountKey $SAKeys.Key1

      • I am having the same problem – when I run the commands above, I get the following:

        Get-AzureRmStorageAccountKey : Cannot validate argument on parameter ‘ResourceGroupName’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
        At line:15 char:59
        + … AKeys = Get-AzureRMStorageAccountKey -ResourceGroupName $SARG -Name $ …
        + ~~~~~
        + CategoryInfo : InvalidData: (:) [Get-AzureRmStorageAccountKey], ParentContainsErrorRecordException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.Azure.Commands.Management.Storage.GetAzureStorageAccountKeyCommand

      • Hi Samir,

        When i run this command manually i get the error “Get-AzureRmStorageAccountKey : Cannot validate argument on parameter ‘ResourceGroupName’. The argument is null or empty. Provide an argument that is not null or empty, and then try the comman
        d again.”

      • I get the following error when I urn these steps.

        PS C:\Move-ArmVMv1.2> #Load function
        Function GetSARG ($SAName)
        {

        $SA = Get-AzureRmStorageAccount | where {$_.StorageAccountName -eq $SAName}
        $SARG = $SA.ResourceGroupName
        return $SARG

        }

        #Initialise variables
        $SAName =

        $SARG = GetSARG -SAName $SAName
        $SAKeys = Get-AzureRMStorageAccountKey -ResourceGroupName $SARG -Name $SAName
        $SAContext = New-AzureStorageContext -StorageAccountName $SAName -StorageAccountKey $SAKeys.Key1
        Get-AzureRmStorageAccountKey : Cannot validate argument on parameter ‘ResourceGroupName’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
        At line:15 char:59
        + $SAKeys = Get-AzureRMStorageAccountKey -ResourceGroupName $SARG -Name $SAName
        + ~~~~~
        + CategoryInfo : InvalidData: (:) [Get-AzureRmStorageAccountKey], ParentContainsErrorRecordException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.Azure.Commands.Management.Storage.GetAzureStorageAccountKeyCommand

        PS C:\Move-ArmVMv1.2>

    • Hi Ativoj, Samir,

      even I am getting the same error message. did you manage to get a fix for this.

      error message:

      [14-06-2016 17:42:31] Regulate the Destination Container : dn1vhd …
      [14-06-2016 17:43:35] Error Cannot validate argument on parameter ‘StorageAccountKey’. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.
      [14-06-2016 17:43:35] [Failed] Regulate the Destination Container : dn1vhd …
      [14-06-2016 17:43:35] Exiting the script, please solve the problem then try again

  9. hi

    can u help on this

    [27-05-2016 20:50:03] [Completed] Copying the VHDs to the destination storage location …
    [27-05-2016 20:50:03] Creating the Destination VM: azurevm1 …
    [27-05-2016 20:50:06] Error Changing property ‘osDisk.vhd.uri’ is not allowed.
    StatusCode: 409
    ReasonPhrase: Conflict
    OperationID : ’25faa9c9-f551-44ab-b00f-ddec7f75de3f’
    [27-05-2016 20:50:06] [Failed] Creating the Destination VM: azurevm1 …
    [27-05-2016 20:50:06] Exiting the script, please solve the problem then try again

  10. I am able to get around the Storage Account Key issue reported by ativoj by hard-coding the proper storage key into the script, which allows the script to proceed.

    However, the script fails with the following error immediately after creating the Destination Storage Account Container and attempting to copy the VHD:

    “Error The remote server returned an error: (403) Forbidden. HTTP Status Code: 403 – HTTP Error Message: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.”

    I have spent a significant amount of time on this, and Azure Support has not been able to help either. It seems this solution no longer works. Can you please provide some advisement?

      • Hi Samir, I just wanted to mention I got over this problem in the end. My fix was to split the function GetStorageContext into two functions, one for sourcestoragecontext and one for destinationstoragecontext. I hard coded the relevant storage key into each function which seemed to fix the problem for me, Thanks again for a great script, Tom.

  11. Hi Samir.

    Can you make the script support moving VM’s between different accounts (move from my MSDN Azure account to my customers Azure account)

    Kind regards,

    Morten

  12. I’m getting the following error any help would be appreciated.

    Info : Please, Enter the credentials of an Admin account of Azure
    [ 1 ] VM(s) will be moved
    Hit any key to continue or Ctrl^c to Cancel:

    Starting the move of [smserver1]
    Validating the Subscription [Free Trial] …
    Selecting the Subscription [Free Trial] …
    Validating the user role of [siobhancm@gmail.com] …
    Validating the VM [Free Trial / barrsmrg / smserver1] …
    Validating the Subscription [Free Trial] …
    Selecting the Subscription [Free Trial] …
    Validating the user role of [siobhancm@gmail.com] …
    Validating the SA [Free Trial / barrsmnewstorage] …
    Selecting the Source Subscription : Free Trial …
    Stopping the VM : smserver1 …
    Getting the Source VM configuration : smserver1 …
    Getting the Source VM Disks : smserver1 …
    Selecting the Destination Subscription : Free Trial …
    Cheking the Destination Resource Group : barrsmnewrg …
    WARNING: The usability of Tag parameter in this cmdlet will be modified in a future release. This will impact creating, updating and appending tags for
    Azure resources. For more details about the change, please visit https://github.com/Azure/azure-powershell/issues/726#issuecomment-213545494
    Creating the Destination VM Object : smserver1 …
    ERROR : Exiting the Script. Errors are logged on the log file : C:\Move-ArmVM-V3\Logs\2016-06-09-13-21.log

  13. Hi all,

    I found a working solution for this error: “Error Cannot validate argument on parameter ‘StorageAccountKey'”

    Downloaded script: Move-ArmVMv1.2preview.rar
    Function GetStorageContext ($SAName)

    Change this line: (line 177)
    $SAContext = New-AzureStorageContext -StorageAccountName $SAName -StorageAccountKey $SAKeys.Key1

    Into this:
    $SAContext = New-AzureStorageContext -StorageAccountName $SAName -StorageAccountKey $($sakeys.Item(0).value)

    That fixed the error for me.

    Also I found two more issues the URI of the OS disks and datadisks didn’t work in my situation. It gave me the error “Error This operation is not supported for a relative URI.”

    If fixed that by changing the lines: 722 and 739

    Original (line 722):
    $RDestOsDiskUri = $RDestStorageAccount.PrimaryEndpoints.Blob.AbsoluteUri + $RDestContainer +’/’+$RDestOSDiskBlob

    Changed:
    $RDestOsDiskUri = $RDestStorageAccount.PrimaryEndpoints.Blob + $RDestContainer +’/’+$RDestOSDiskBlob

    Original: (line 739)
    $DataDiskBlobUri = $RDestStorageAccount.PrimaryEndpoints.Blob.AbsoluteUri + $RDestContainer +’/’+$DataDiskBlob

    Changed:
    $DataDiskBlobUri = $RDestStorageAccount.PrimaryEndpoints.Blob + $RDestContainer +’/’+$DataDiskBlob

    After these changes the script succesfully copied a VM in my situation.

    With kind regards
    Stefan Peters

  14. Hi all,

    I found a working solution for this error:
    Error Cannot validate argument on parameter ‘StorageAccountKey’

    Downloaded script: Move-ArmVMv1.2preview.rar
    Function GetStorageContext ($SAName)

    Change this line: (line 177)
    $SAContext = New-AzureStorageContext -StorageAccountName $SAName -StorageAccountKey $SAKeys.Key1

    Into this:
    $SAContext = New-AzureStorageContext -StorageAccountName $SAName -StorageAccountKey $($sakeys.Item(0).value)

    That fixed the error for me.

    Also I found two more issues the URI of the OS disks and datadisks didn’t work in my situation. It gave me the error “Error This operation is not supported for a relative URI.”

    If fixed that by changing the lines: 722 and 739

    Original (line 722):
    $RDestOsDiskUri = $RDestStorageAccount.PrimaryEndpoints.Blob.AbsoluteUri + $RDestContainer +’/’+$RDestOSDiskBlob

    Changed:
    $RDestOsDiskUri = $RDestStorageAccount.PrimaryEndpoints.Blob + $RDestContainer +’/’+$RDestOSDiskBlob

    Original: (line 739)
    $DataDiskBlobUri = $RDestStorageAccount.PrimaryEndpoints.Blob.AbsoluteUri + $RDestContainer +’/’+$DataDiskBlob

    Changed:
    $DataDiskBlobUri = $RDestStorageAccount.PrimaryEndpoints.Blob + $RDestContainer +’/’+$DataDiskBlob

    After these changes it succesfully copied a VM in my situation.

    With kind regards
    Stefan Peters

  15. Getting an error when it tries to create the VMNIC. I’m trying to consolidate storage accounts, so all I’m trying to do is move the VHDs to a new storage account.

    [16-12-2016 14:24:31] Creating the VNIC : VNIC-az-drproxy8-01 …
    [16-12-2016 14:24:32] Error IP configuration /resourceGroups/Infrastructure/providers/Microsoft.Network/networkInterfaces/VNIC-az-drproxy8-01/ipConfigurations/ipconfig1 is using the private IP address 10.55.1.16 which is already allocated to resource /resourceGroups/InfrastructureV2/providers/Microsoft.Network/networkInterfaces/az-drproxy8_nic.
    StatusCode: 400
    ReasonPhrase: Bad Request
    OperationID : ‘5b0a291f-1933-4908-959e-847f1e34a7a8’
    [16-12-2016 14:24:32] [Failed] Creating the VNIC : VNIC-az-drproxy8-01

  16. I was also struggling with this and made a few changes that fixed the errors I encountered.

    function Get-VMSize – modified:
    $VMObject.HardwareProfile.VirtualMachineSize to $VMObject.HardwareProfile.VmSize

    function GetStorageContext – modified:
    $SAKeys.Key1 to $SAKeys[0].Value (similar to Stephan’s)

    Prepare the destination VM Object section – modified:
    -VMSize $VMsize to -VMSize $RDestVMsize

    Set the VM’s OS Disk section – modified:
    $RDestStorageAccount.PrimaryEndpoints.Blob.AbsoluteUri to $RDestStorageAccount.PrimaryEndpoints.Blob

    I was also getting the import module error and the select subscription func would not work unless I explicitly declared the subscription name and tenantId. But those were easy fixes and likely due to a token issue.

    Great work all around though! Thanks!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s