Copying a blob snapshot to another blob

Recently I had a question in the comments on my blog post Backing up a Windows Azure Virtual Machine using PowerShell –

Can a snapshot blob be copied to another location instead of overwriting the original blob?

The short answer is yes, of course, as snapshots present themselves more or less like any other blob, but there is a subtle point around how to reference the snapshot so I thought it’s worth demonstrating using a quick console app, here are the key steps –

I created a console app and added the necessary references using

Install-Package WindowsAzure.Storage

I already had a few blobs I could play with, so I just added a couple of lines to connect to the account and create a snapshot of one of them 

string storageConnection = ConfigurationManager.AppSettings["StorageConnectionString"];
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnection);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

CloudBlobContainer container = blobClient.GetContainerReference("metars");
CloudBlockBlob blob = container.GetBlockBlobReference("EGLL.txt");

CloudBlockBlob snapshot = blob.CreateSnapshot();

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

As you can see, the CreateSnapshot returns a blob, but – even as demos go – it is unrealistic to expect to want to copy a snapshot immediately after its creation, so I drop this object and start from scratch.

To find the snapshot again I need to enumerate on all the blobs in the container, indicating to the platform I wish to see snapshots (and for that purpose – flattening the result as well).

As I’m only interested in this specific blob at this point I can add its name as the prefix –

string storageConnection = ConfigurationManager.AppSettings["StorageConnectionString"];
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnection);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

CloudBlobContainer container = blobClient.GetContainerReference("metars");

IEnumerable<IListBlobItem> blobAndSnapshots = container.ListBlobs( 
       prefix: "EGLL.txt",        
       useFlatBlobListing: true,         
       blobListingDetails: BlobListingDetails.Snapshots); 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Note: this will return the blob AND all its snapshots, so if wishing to enumerate on the snapshots only ignore any items with SnapshotTime value of null.

Once again – the result is a list of blobs, and so one can easily create a copy of the snapshot using the standard blob operations, a simple blob copy function could look something along the lines –

            //create a reference to targt blob
            ICloudBlob newCopy = container.GetBlockBlobReference("target.txt");
            //create a callback to note completion of copy
            AsyncCallback callBack = new AsyncCallback(CopyCompleted);
            //start copy
            newCopy.BeginStartCopyFromBlob(sourceBlob.Uri, callBack, null);

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Note: in ‘real world’ using the async based interface with its Start and End methods would be more useful, but this patterns is well documented.

But here’s the subtle point – whether the a blob is a snapshot or not, it’s Uri property will refer to the main blob and not to any snapshot and so using the copy code above with the sourceBlob referencing a snapshot will copy the main blob and not its references snapshot.

To copy the snapshot a specific Url needs to be constructed and used, here’s the modified code –

            //construct snapshot's uri
            string snapshotUrl = getSnapshotUrl(sourceBlob);
            //create a reference to targt blob
            ICloudBlob newCopy = container.GetBlockBlobReference("target.txt");
            //create a callback to note completion of copy
            AsyncCallback callBack = new AsyncCallback(CopyCompleted);
            //start copy
            newCopy.BeginStartCopyFromBlob(new Uri(snapshotUrl), callBack, null);

.
.
.
        private static string getSnapshotUrl(CloudBlockBlob snapshotBlob)
        {
            string encodedTime = System.Web.HttpUtility.UrlEncode(snapshotBlob.SnapshotTime.Value.ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ"));
            return string.Format("{0}?snapshot={1}", snapshotBlob.Uri, encodedTime);
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

As you can see- to copy the snapshot and not the main blob the source url needs to be changed to include the snapshot time offset. Doing so creates a new blob with the snapshot’s contents.

In this case I used the same container, but I could have used any other container or indeed another storage account.


Note: when copying a blob snapshot to another storage account one has to obtain, and provide, a shared signature signature token to the copy command (see example of such copy here). When doing so it is important that the token is the first query string parameter and the snapshot detail is provided after that (separated by an ampersand (‘&’), of course).

About Yossi Dahan
I work as a cloud solutions architect in the Azure team at Microsoft UK. I spend my days working with customers helping be successful in the cloud with Microsoft Azure.

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

%d bloggers like this: