Saturday, October 17, 2015

Move Azure blobs between containers

I finally got some time to tidy up my Azure account. The first thing I wanted to do was to bring all my vms in a single storage, gathering them from various subscriptions. In order to do that I had to copy the vhds (located in the vhds container of each storage account) to their final destination.
This is how I ended up creating the following simple program. It schedules the transfer of the blob and then monitors the status of the blobs in order to get an indication of when the process is done.
If you are more into powershell, you can follow this tutorial which also mentions how to mark the moved vhd as a VM image (Virtual Machines->Disks->Create).
using System;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
/// <summary>
/// Moves blobs from one container to another
/// Install-Package WindowsAzure.Storage
/// </summary>
namespace MoveBlobs
{
class Program
{
// Get the following settings from your azure portal
private const string FromAccountConnectionString = "DefaultEndpointsProtocol=https;AccountName=;AccountKey=";
private const string ToAccountConnectionString = "DefaultEndpointsProtocol=https;AccountName=;AccountKey=";
static void Main(string[] args)
{
var fromAccount = CloudStorageAccount.Parse(FromAccountConnectionString);
var fromContainer = fromAccount.CreateCloudBlobClient().GetContainerReference("vhds");
var toAccount = CloudStorageAccount.Parse(ToAccountConnectionString);
var toContainer = toAccount.CreateCloudBlobClient().GetContainerReference("vhds");
// Copy all blobs from the vhds container
CopyBlobs(fromContainer, toContainer);
MonitorCopy(toContainer);
Console.WriteLine("Finished copying. Press any key to exit.");
Console.ReadKey();
}
/// <summary>
/// Reports the copy status and retries failed copies
/// </summary>
/// <param name="destContainer">The container that has the placeholders for the incoming blobs</param>
public static void MonitorCopy(CloudBlobContainer destContainer)
{
bool pendingCopy = true;
while (pendingCopy)
{
Console.Clear();
Console.WriteLine("Status update {0:dd/MM/yyy HH:mm:ss}", DateTime.Now);
pendingCopy = false;
var destBlobList = destContainer.ListBlobs("",true, BlobListingDetails.Copy);
foreach (var dest in destBlobList)
{
var destBlob = dest as CloudBlob;
// Skip blob if no copy state
if (destBlob.CopyState == null)
{
continue;
}
// If failed or aborted restart
if (destBlob.CopyState.Status == CopyStatus.Aborted ||
destBlob.CopyState.Status == CopyStatus.Failed)
{
pendingCopy = true;
Console.WriteLine("Restarting copy for {0}", destBlob.Name);
destBlob.StartCopy(destBlob.CopyState.Source);
}
// If it's pending, we will wait
else if (destBlob.CopyState.Status == CopyStatus.Pending)
{
pendingCopy = true;
}
Console.WriteLine("{0}:{1}",destBlob.Name, destBlob.CopyState.Status);
}
// Wait for 100 secs
System.Threading.Thread.Sleep(100000);
};
}
/// <summary>
/// Retrieves the security token for one day read access to the blob
/// </summary>
/// <param name="container">The container that has the blobs</param>
/// <returns></returns>
public static string GetOneDayReadToken(CloudBlobContainer container)
{
//Set the expiry time and permissions for the container.
//In this case no start time is specified, so the shared access signature becomes valid immediately.
SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy();
sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24);
// We need Read permission only
sasConstraints.Permissions = SharedAccessBlobPermissions.Read;
//Generate the shared access signature on the container, setting the constraints directly on the signature.
return container.GetSharedAccessSignature(sasConstraints);
}
/// <summary>
/// Schedule copy for all blobs from srcContainer to destContainer
/// </summary>
/// <param name="srcContainer">From which container</param>
/// <param name="destContainer">To which container</param>
public static void CopyBlobs(CloudBlobContainer srcContainer,CloudBlobContainer destContainer)
{
// get the SAS token to use for all blobs
string blobToken = GetOneDayReadToken(srcContainer);
var srcBlobList = srcContainer.ListBlobs("", true, BlobListingDetails.None);
foreach (var src in srcBlobList)
{
var srcBlob = src as CloudBlob;
// Create appropriate destination blob type to match the source blob
CloudBlob destBlob;
if (srcBlob.Properties.BlobType == BlobType.BlockBlob)
{
destBlob = destContainer.GetBlockBlobReference(srcBlob.Name);
}
else
{
destBlob = destContainer.GetPageBlobReference(srcBlob.Name);
}
// copy using src blob as SAS
destBlob.StartCopy(new Uri(srcBlob.Uri.AbsoluteUri + blobToken));
}
}
}
}
view raw Program.cs hosted with ❤ by GitHub
/// <summary>
/// Rename a blob
/// </summary>
/// <param name="container">The container that has the blob</param>
/// <param name="oldName">The old name</param>
/// <param name="newName">The new name</param>
public static void RenamePageBlob(CloudBlobContainer container, string oldName, string newName)
{
var source = container.GetBlobReferenceFromServer(oldName);
var target = container.GetPageBlobReference(newName);
target.StartCopy(source.Uri);
while (target.CopyState.Status == CopyStatus.Pending)
{
Console.WriteLine("{0:HH:mm:ss} Waiting", DateTime.Now);
System.Threading.Thread.Sleep(1000);
}
if (target.CopyState.Status != CopyStatus.Success)
throw new ApplicationException("Rename failed: " + target.CopyState.Status);
source.Delete();
}
view raw Rename.cs hosted with ❤ by GitHub

No comments: