Moving a VHD (disk image) between Azure Storage accounts with x-plat CLI

I’ve discovered I’ve accidentally created a VHD in a Premium storage account, so I wanted to migrate it into a standard one. (Premium data are stored on SSDs and much more expensive.) I’ve found a way to do it using the Powershell commands, but I like the xplat CLI more. (It, too, is developed by Microsoft, built on Node.js and available via npm.) So I set to exploring.

Here is what’s worked for me; I can’t guarantee it being the best possible way to do it, but it works. :) Basically, first I find all the data I need to feed to the ‘blob copy start’ command, and set up an environment variable. Then I start the copy.

First you need to do a azure login, if you haven’t already. The rest of this text assumes you have.

The first thing one might try is to get a list of the blobs like this:

laci> azure storage blob list
info: Executing command storage blob list
error: Please set the storage account parameters or one of the following two environment variables to use the storage command.
1. AZURE_STORAGE_CONNECTION_STRING
2. AZURE_STORAGE_ACCOUNT and AZURE_STORAGE_ACCESS_KEY
error: Error information has been recorded to myUserFolder\.azure\azure.err
error: storage blob list command failed

… which fails, because the CLI doesn’t know which storage account should be used. You can set it into your environment variables in two ways: either a connection string, or account name + access key. Since the former is simpler, I’ll do that.

First, let’s see what storage accounts I have.

laci> azure storage account list
info: Executing command storage account list
+ Getting storage accounts
data: Name SKU Name SKU Tier Access Tier Kind Encrypted Service Location Resource Group
data: ------------ ------------ -------- ----------- ------- ----------------- ---------- 
data: targetStorageAccount Standard_LRS Standard Storage westeurope myResourceGroup
data: sourceStorageAccount Premium_LRS Premium Storage westeurope myResourceGroup
info: storage account list command OK

Both of the relevant accounts are listed. I want to operate on the sourceStorageAccount for now. Let’s get its conn string.

laci> azure storage account connectionstring show -g myResourceGroup sourceStorageAccount 
info: Executing command storage account connectionstring show
+ Getting storage account keys
data: connectionstring: DefaultEndpointsProtocol=https;AccountName=sourceStorageAccount;AccountKey=qR1MD3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnfiCRg+q18xraP8Sg==
info: storage account connectionstring show command OK

Now set it into an environment variable. In PS, you need to use the $env: variable. Its content will only last for the duration of this session (i.e. it won’t be persisted.)

$env:AZURE_STORAGE_CONNECTION_STRING = 'DefaultEndpointsProtocol=https;AccountName=sourceStorageAccount;AccountKey=qR1MD3bM8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxq18xraP8Sg=='

You now get the list of storage containers.

laci> azure storage container list
info: Executing command storage container list
+ Getting storage containers
data: Name Public Access Last Modified
data: -------------------------------------------------------------- ----
data: vhds      Off         Wed, 28 Sep 2016 11:28:05 GMT
info: storage container list command OK

And then the list of blobs, specifying the name of the container they're in. (In my case, the both the source and the target containers are called ‘vhds’; they’re in different storage accounts, though.)

laci> azure storage blob list --container vhds
info:    Executing command storage blob list
+ Getting blobs in container vhds
data:    Name                       Blob Type  Length        Content Type              Last Modified                  Snapshot Time
data:    -------------------------  ---------  ------------  --------------
data:    myVhdName.vhd  PageBlob   136367309312  application/octet-stream  Wed, 28 Sep 2016 12:10:25 GMT
info:    storage blob list command OK

You can get the conn string for the target storage account with

azure storage account connectionstring show -g myResourceGroup targetStorageAccount

Now we have all the data we need to start copying: the source blob and container names, the destination connection string and container name.

laci> azure storage blob copy start --source-blob myVhdName.vhd --source-container vhds --dest-connection-string 'DefaultEndpointsProtocol=https;AccountName=targetStorageAccount;AccountKey=qR1MD3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnfiCRg+q18xraP8Sg==' --dest-container vhds
info: Executing command storage blob copy start
Destination container name: vhds
+ Start copying blob https://sourceStorageAccount.blob.core.windows.net/vhds/myVhdName.vhd?se=2016-10-05T12%3A02%3A37Z&sp=r&sv=2015-04-05&sr=b&sig=exKOOxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxgM%3D
data: Copy ID Status
data: ------------------------------------ -------
data: 6bfdd6ff-f8c4-4dab-a6e2-ec8b391a827f pending
info: storage blob copy start command OK

We're informed the operation was enqueued successfully. To check on its progress, you can use ‘blob copy show’ with the container name.

laci> azure storage blob copy show --container vhds
info: Executing command storage blob copy show
Destination blob: myVhdName.vhd
+ Getting storage blob information
data: Copy ID Progress Status
data: ------------------------------------ ------------------------- -------
data: 90f29e4e-1aad-48f1-997f-590d475f61c9 136367309312/136367309312 success
info: storage blob copy show command OK

It’s already succeeded. Now I’ve switched my environment variable to point to the target storage account, and there my copied blob is.

laci> azure storage blob list --container vhds
info:    Executing command storage blob list
+ Getting blobs in container vhds
data:    Name                       Blob Type  Length        Content Type              Last Modified                  Snapshot Time
data:    -------------------------  ---------  ------------  ------------
data:    myVhdName.vhd  PageBlob   136367309312  application/octet-stream  Wed, 28 Sep 2016 12:10:25 GMT
info:    storage blob list command OK

So that’s it. To sum up, these were (roughly) the commands used:

azure storage account list
azure storage account connectionstring show -g myResourceGroup sourceStorageAccount 
$env:AZURE_STORAGE_CONNECTION_STRING = 'DefaultEndpointsProtocol=https;AccountName=sourceStorageAccount;AccountKey=qR1MD3bM8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxq18xraP8Sg=='
azure storage container list
azure storage blob list --container vhds
azure storage blob copy start --source-blob myVhdName.vhd --source-container vhds --dest-connection-string 'DefaultEndpointsProtocol=https;AccountName=targetStorageAccount;AccountKey=qR1MD3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnfiCRg+q18xraP8Sg==' --dest-container vhds
azure storage blob copy show --container vhds
azure storage blob list --container vhds