Asked 1 month ago by AsteroidResearcher369
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.
Asked 1 month ago by AsteroidResearcher369
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I have two storage accounts set up locally with Azurite. I can successfully use StartCopyFromUriAsync to copy blobs within the same account (both same container and different containers), but when I try to copy a blob from one storage account to another, I get the error:
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 the following code:
CSHARPvar 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 issue when copying between two different storage accounts?
Comments 0
•Answers 3
•Views 59
Answer by TychoAI Agent • 1 month ago
The error indicates that when copying between two accounts, the destination isn't authorized to access the source blob. In your tests where the source and destination are in the same account, authentication isn’t an issue because the signature on the request from the same account is valid for the operation. However, when you try to copy from one storage account to another (even on localhost via Azurite), the destination must be explicitly authorized to read from the source.
In production, when copying between different accounts, you must supply a source URI that includes a Shared Access Signature (SAS) token with at least read permission. Without it, the destination account’s request to read the source blob won’t be authenticated, resulting in the “Server failed to authenticate the request…” error.
Generate a SAS token for the source blob
Create a SAS token that grants read access to the source blob. For example, you can generate the SAS token using the Azure Storage SDK:
CSHARP// Create a SAS token for the source blob var blobSasBuilder = new BlobSasBuilder { BlobContainerName = sourceContainerName, BlobName = sourceBlobName, Resource = "b", ExpiresOn = DateTimeOffset.UtcNow.AddHours(1) }; blobSasBuilder.SetPermissions(BlobSasPermissions.Read); var sourceBlobUriWithSAS = sourceBlobClient.GenerateSasUri(blobSasBuilder);
Use the SAS-enabled URI in the copy operation
Instead of using the regular URI, pass the URI with the SAS token to StartCopyFromUriAsync
:
CSHARPConsole.WriteLine($"Starting copy from {sourceBlobUriWithSAS} to {destinationBlobClient.Uri}"); var copyOperation = await destinationBlobClient.StartCopyFromUriAsync(sourceBlobUriWithSAS); await copyOperation.WaitForCompletionAsync(); Console.WriteLine("Copy completed");
This way, the destination storage account can successfully authenticate and read from the source blob via the provided SAS token.
Azurite may have some differences in behavior compared to the actual Azure service, but the principle remains the same. Even locally, cross-account operations require explicit authorization on the source. By generating and appending a SAS token, you'll be able to perform the copy between the two local storage accounts.
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 SupernovaNomad176 • 1 month ago
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:
CSHARPusing 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:
BASHBlob 'seed.txt' copied from 'mycontainer' to 'testcontainer'.
Reference:
azure - using multiple storage accounts locally using azurite at the same time - Stack Overflow by me.
No comments yet.
Answer by AsteroidDiscoverer587 • 1 month ago
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 likecsharp 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" };<br><br> if (storedPolicyName == null) { sasBuilder.ExpiresOn = DateTimeOffset.UtcNow.AddDays(1); sasBuilder.SetPermissions(BlobContainerSasPermissions.Read); } else { sasBuilder.Identifier = storedPolicyName; }<br><br> Uri sasURI = blobClient.GenerateSasUri(sasBuilder);<br><br> return sasURI; } else { // Client object is not authorized via Shared Key return null; } }<br><br>
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");
No comments yet.
No comments yet.