Search code examples
c#.netazureazure-blob-storageazurite

unable to StartCopyFromUriAsync a blob between 2 storage accounts


followup question for this
I have 2 storage accounts setup locally. I tried to StartCopyFromUriAsync blob from 1 storage account to another. before this I tried to StartCopyFromUriAsync same account+same container and same account+different container and both of them worked in my approach. But when I try between 2 storage accounts It gives Azure.RequestFailedException: 'Server failed to authenticate the request. Make sure the value of the Authorization header is formed correctly including the signature in the line StartCopyFromUriAsync

I tried like this

var sourceAccountName = "devstoreaccount1";
var sourceAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
var sourceBlobEndpoint = $"http://127.0.0.1:10000/{sourceAccountName}";

var destinationAccountName = "devstoreaccount2";
var destinationAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
var destinationBlobEndpoint = $"http://127.0.0.1:10000/{destinationAccountName}";

var sourceConnectionString = $"DefaultEndpointsProtocol=http;AccountName={sourceAccountName};AccountKey={sourceAccountKey};BlobEndpoint={sourceBlobEndpoint}";
var destinationConnectionString = $"DefaultEndpointsProtocol=http;AccountName={destinationAccountName};AccountKey={destinationAccountKey};BlobEndpoint={destinationBlobEndpoint}";

var sourceContainerName = "srccontainer";
var destinationContainerName = "destcontainer";

var sourceBlobName = "myblob";  
var destinationBlobName = "destblob";

var sourceBlobServiceClient = new BlobServiceClient(sourceConnectionString);
var destinationBlobServiceClient = new BlobServiceClient(destinationConnectionString);

var sourceContainerClient = sourceBlobServiceClient.GetBlobContainerClient(sourceContainerName);
var sourceBlobClient = sourceContainerClient.GetBlobClient(sourceBlobName);

var destinationContainerClient = destinationBlobServiceClient.GetBlobContainerClient(destinationContainerName);
var destinationBlobClient = destinationContainerClient.GetBlobClient(destinationBlobName);

await destinationContainerClient.CreateIfNotExistsAsync();

var sourceBlobUri = sourceBlobClient.Uri;

Console.WriteLine($"Starting copy from {sourceBlobUri} to {destinationBlobClient.Uri}");

var copyOperation = await destinationBlobClient.StartCopyFromUriAsync(sourceBlobUri);
await copyOperation.WaitForCompletionAsync();

Console.WriteLine("Copy completed");

whats going on here. any idea why?


Solution

  • The error already hints at the problem.

    You will need to authenticate against the source blob. Unless the source blob is public available you will need to use a SAS token for example to add authentication information. Add a method like

    public static async Task<Uri> CreateServiceSASBlob(
        BlobClient blobClient,
        string storedPolicyName = null)
    {
        // Check if BlobContainerClient object has been authorized with Shared Key
        if (blobClient.CanGenerateSasUri)
        {
            // Create a SAS token that's valid for one day
            BlobSasBuilder sasBuilder = new BlobSasBuilder()
            {
                BlobContainerName = blobClient.GetParentBlobContainerClient().Name,
                BlobName = blobClient.Name,
                Resource = "b"
            };
    
            if (storedPolicyName == null)
            {
                sasBuilder.ExpiresOn = DateTimeOffset.UtcNow.AddDays(1);
                sasBuilder.SetPermissions(BlobContainerSasPermissions.Read);
            }
            else
            {
                sasBuilder.Identifier = storedPolicyName;
            }
    
            Uri sasURI = blobClient.GenerateSasUri(sasBuilder);
    
            return sasURI;
        }
        else
        {
            // Client object is not authorized via Shared Key
            return null;
        }
    }
    

    And modify your code to

        var sourceAccountName = "devstoreaccount1";
        var sourceAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
        var sourceBlobEndpoint = $"http://127.0.0.1:10000/{sourceAccountName}";
    
        var destinationAccountName = "devstoreaccount2";
        var destinationAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
        var destinationBlobEndpoint = $"http://127.0.0.1:10000/{destinationAccountName}";
    
        var sourceConnectionString = $"DefaultEndpointsProtocol=http;AccountName={sourceAccountName};AccountKey={sourceAccountKey};BlobEndpoint={sourceBlobEndpoint}";
        var destinationConnectionString = $"DefaultEndpointsProtocol=http;AccountName={destinationAccountName};AccountKey={destinationAccountKey};BlobEndpoint={destinationBlobEndpoint}";
    
        var sourceContainerName = "srccontainer";
        var destinationContainerName = "destcontainer";
    
        var sourceBlobName = "myblob";
        var destinationBlobName = "destblob";
    
        var sourceBlobServiceClient = new BlobServiceClient(sourceConnectionString);
        var destinationBlobServiceClient = new BlobServiceClient(destinationConnectionString);
    
        var sourceContainerClient = sourceBlobServiceClient.GetBlobContainerClient(sourceContainerName);
        var sourceBlobClient = sourceContainerClient.GetBlobClient(sourceBlobName);
        Uri blobSASURI = await CreateServiceSASBlob(sourceBlobClient);
    
        var destinationContainerClient = destinationBlobServiceClient.GetBlobContainerClient(destinationContainerName);
        var destinationBlobClient = destinationContainerClient.GetBlobClient(destinationBlobName);
    
        await destinationContainerClient.CreateIfNotExistsAsync();
    
        Console.WriteLine($"Starting copy from {blobSASURI} to {destinationBlobClient.Uri}");
    
        var copyOperation = await destinationBlobClient.StartCopyFromUriAsync(blobSASURI);
        await copyOperation.WaitForCompletionAsync();
    
        Console.WriteLine("Copy completed");
    

    More background, including on how to generate a SAS token for a container instead of a blob can be found here