Posted in:

This follows on from my previous posts about how to upload blobs and how to copy blobs using the Azure V12 Blob Storage SDK.

In this post I want to share my findings about the BlobCopyFromUriOptions parameter that you can use with the StartCopyFromUriAsync method that I covered in my earlier post.

When you copy a blob, obviously you want a new blob with the same contents, but blobs also can have metadata and tags associated with them. They can also have other properties such as the content type header.

Setup

For my tests, I created a simple blob that had some metadata, tags and a custom content type.

var serviceClient = new BlobServiceClient(connectionString);
var containerClient = serviceClient.GetBlobContainerClient("my-container");
var sourceBlobClient = containerClient.GetBlobClient("sourceBlob.txt");
using var ms = new MemoryStream(Encoding.UTF8.GetBytes("Hello World"));
var blobUploadOptions = new BlobUploadOptions()
{
    Metadata = new Dictionary<string, string>() { { "Metadata1", "ABC" } },
    HttpHeaders = new BlobHttpHeaders() { ContentType = "application/abcdef" },
    Tags = new Dictionary<string, string>() { { "Tag1", "DEF" } },
};
await sourceBlobClient.UploadAsync(ms, blobUploadOptions);

Copying blob metadata

My initial assumption was that because BlobCopyFromUriOptions has a Metadata property, that I'd need to put the metadata from the source blob into this. However, leaving the Metadata property as null, or not using BlobCopyFromUriOptions at all, resulted in the metadata being copied across in all my test scenarios (I tried copying between storage accounts, and copying from and to SAS URIs).

var destBlobClient = destContainer.GetBlobClient("destBlob.txt");
// the metadata and content type will also get copied without the need to use BlobCopyFromUriOptions
await destBlobClient.StartCopyFromUriAsync(sourceClient.Uri).ConfigureAwait(false);

I was also pleased to see that the custom content type I set up on the source blob also got copied across successfully without the need to explicitly set it on the target blob.

Copying blob tags

Unfortunately with tags, things get a bit more complicated. There is a CopySourceTagsMode property on BlobCopyFromUriOptions that at first glance seems to do exactly what we want. If you set it to BlobCopySourceTagsMode.Copy the documentation claims that "the tags on the source blob will be copied to the destination blob".

Unfortunately, that isn't true. This property is ignored by StartCopyFromUriAsync. You can learn more about this issue here.

Here's the code that doesn't work:

var blobCopyOptions = new BlobCopyFromUriOptions()
{
    // this won't work - https://github.com/Azure/azure-sdk-for-net/issues/32684
    CopySourceTagsMode = BlobCopySourceTagsMode.Copy
};

So what can we do? Well, instead we need to read all the tags from the source blob, and set them on BlobCopyFromUriOptions like this:

var blobCopyOptions = new BlobCopyFromUriOptions()
{
    Tags = (await sourceClient.GetTagsAsync()).Value.Tags
};
// now copy, passing in the options
await destBlobClient.StartCopyFromUriAsync(sourceClient.Uri, blobCopyOptions).ConfigureAwait(false);

This works just fine, although there is now another problem. If the authenticated user or security principal performing the copy is in the Storage Blob Data Contributor role, which would normally be enough to perform blob copying, they will not be able to read or write index tags. They would either need to be granted the Storage Blob Data Owner role, or a custom role with the necessary permissions as described here.

To detect the situation where you don't have permission to fetch the tags, you can use the following code:

IDictionary<string, string> sourceTags = null;
try
{
    sourceTags = (await sourceClient.GetTagsAsync()).Value.Tags;
}
catch (RequestFailedException rfe) when (rfe.Status == 403 && rfe.ErrorCode == "AuthorizationPermissionMismatch")
{
    Console.WriteLine("WARNING: Tags cannot be retrieved from source");
}

Summary

Copying blobs along with metadata and other properties is nice and easy with the V12 SDKs. However, copying blob tags is slightly more complicated, so I hope the above notes help you to successfully copy blob tags.