Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by QuantumNavigator033

Why does StartCopyFromUriAsync fail when copying a blob between two local storage accounts in C#?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

Follow-up to this

I have set up two local storage accounts using Azurite. I attempted to copy a blob from one account to another using StartCopyFromUriAsync. The copy operation worked when copying within the same account (both within the same container and across different containers), but when copying between two different accounts, I get the following exception at the StartCopyFromUriAsync call:

Azure.RequestFailedException: 'Server failed to authenticate the request. Make sure the value of the Authorization header is formed correctly including the signature

Here is my code:

CSHARP
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");

What could be causing this authentication error when copying between two different storage accounts, and how can I resolve it?

1

Comments 0

Answers 3

Views 59

3 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

The problem is that when copying between two separate storage accounts (even locally with Azurite), the destination service needs to be able to authenticate access to the source blob. In your same‐account tests the internal trust allowed the copy, but when the source is a different account the request isn’t authorized.

When using StartCopyFromUri without any shared access signature (SAS) appended to the source URI, the destination has no way to prove it’s allowed to read the source blob. In typical Azure scenarios (or in production), you’d either have the blob public or more commonly generate a SAS token for the source blob (granted read permissions) and append it to the URI. Azurite enforces this—even though you’re using identical keys for both accounts, the signature for the source isn’t being passed so the destination cannot authenticate it.

A typical fix is to generate a SAS for the source blob. For example:

CSHARP
// Generate a SAS URI for the source blob (ensure you have the proper using/imports) var sasBuilder = new BlobSasBuilder { BlobContainerName = sourceContainerName, BlobName = sourceBlobName, Resource = "b", // for blobs ExpiresOn = DateTimeOffset.UtcNow.AddHours(1) }; // Set the permissions for the SAS. sasBuilder.SetPermissions(BlobSasPermissions.Read); // Use the source account's key to sign the SAS. var sourceCredential = new StorageSharedKeyCredential(sourceAccountName, sourceAccountKey); var sourceBlobSasUri = sourceBlobClient.GenerateSasUri(sasBuilder); Console.WriteLine($"SAS enabled source blob Uri:{sourceBlobSasUri}"); // Use this SAS URI to start copy. var copyOperation = await destinationBlobClient.StartCopyFromUriAsync(sourceBlobSasUri); await copyOperation.WaitForCompletionAsync(); Console.WriteLine("Copy completed");

This way, the destination has a URL with an embedded token granting it read access to the source blob.

In summary, your error occurs because the destination blob copy command doesn’t have valid authorization for a blob in another storage account. The remedy is to issue a proper SAS token for the source blob so that the request can be authenticated.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by AstroEngineer659 1 month ago

0

unable to StartCopyFromUriAsync a blob between 2 storage accounts

You can use the below code that will copy a file from one Azure storage account container to another Storage account container in the local emulator setup using azurite.

Code:

CSHARP
using System; using System.IO; using System.Threading.Tasks; using Azure.Storage.Blobs; class Program { static async Task Main() { string sourceAccountName = "devstoreaccount1"; string sourceAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; string sourceBlobEndpoint = $"http://127.0.0.1:10000/{sourceAccountName}"; string destinationAccountName = "account1"; string destinationAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; string destinationBlobEndpoint = $"http://127.0.0.1:10000/{destinationAccountName}"; string sourceConnectionString = $"DefaultEndpointsProtocol=http;AccountName={sourceAccountName};AccountKey={sourceAccountKey};BlobEndpoint={sourceBlobEndpoint}"; string destinationConnectionString = $"DefaultEndpointsProtocol=http;AccountName={destinationAccountName};AccountKey={destinationAccountKey};BlobEndpoint={destinationBlobEndpoint}"; string sourceContainerName = "mycontainer"; string sourceBlobName = "seed.txt"; string destinationContainerName = "testcontainer"; string destinationBlobName = "sample.txt"; try { BlobServiceClient sourceBlobServiceClient = new BlobServiceClient(sourceConnectionString); BlobServiceClient destinationBlobServiceClient = new BlobServiceClient(destinationConnectionString); var sourceBlobClient = sourceBlobServiceClient.GetBlobContainerClient(sourceContainerName).GetBlobClient(sourceBlobName); var destinationBlobClient = destinationBlobServiceClient.GetBlobContainerClient(destinationContainerName).GetBlobClient(destinationBlobName); using (MemoryStream ms = new MemoryStream()) { await sourceBlobClient.DownloadToAsync(ms); ms.Position = 0; // Reset stream position await destinationBlobClient.UploadAsync(ms, overwrite: true); } Console.WriteLine($"Blob '{sourceBlobName}' copied from '{sourceContainerName}' to '{destinationContainerName}'."); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } }

Output:

Blob 'seed.txt' copied from 'mycontainer' to 'testcontainer'.

enter image description here

Reference:
azure - using multiple storage accounts locally using azurite at the same time - Stack Overflow by me.

No comments yet.

Answer by SaturnianWanderer793 1 month ago

0

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

CSHARP
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

CSHARP
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

No comments yet.

Discussion

No comments yet.