无法 upload/download 从本地环境到容器的 blob 存储

Unable to upload/download blob storage to container from local environment

我有一个现有的功能应用程序,它具有从 sftp 下载文件、处理文件并将文件上传到 blob 存储的功能。此功能应用程序已在 azure 中部署并正常运行。但是,当我 运行 来自 visual studio (本地)的相同内容时,我在将文件上传到 blob 存储时出错。

Microsoft.Azure.Storage.StorageException: 此请求无权使用此权限执行此操作。

我确保此应用在存储帐户中分配了必要的贡献者角色(这就是它在 Azure 中运行的原因)。我是否需要在本地设置或项目设置中配置任何其他设置才能使这些功能像部署到 Azure 时一样工作?或者当 运行 来自 dev 但 运行 仅来自 azure 时,存储帐户中是否可以覆盖执行上传操作的任何设置?

我确保我使用的本地设置都从具有存储帐户名称、blob 存储的连接字符串、sftp url 和具有 sft 站点的 credetails 等的密钥保管库名称的应用程序配置中复制。 ,

更新: 忘记提及来自我机器命令行的 az storage container upload 命令已成功上传文件。

更新2: 按照 tutorial 的建议,我成功地创建了容器、上传 blob 并没有错误地进行清理。 然后我合并代码以模拟简单的下载 blob 到内存流中,这也会引发错误

This request is not authorized to perform this operation using this permission (see full log in the bottom)

下载 blob 的代码:

static async Task TokenCredentialsSample()
{
    var tenantId = "xxxxx-xxxx-xxxx-xxxx-xxxxxxxx";
    var tokenProvider = new AzureServiceTokenProvider().GetAccessTokenAsync("https://storage.azure.com/",tenantId);
    var tokenCredential = new TokenCredential(tokenProvider.Result);
    var storageCredentials = new StorageCredentials(tokenCredential);
    var uri = new Uri("https://mystorageaccount.blob.core.windows.net/mycontainer/inbound/myfile.csv");

    var cloudBlockBlob = new CloudBlockBlob(uri, storageCredentials);

    var memoryStream = new MemoryStream();

    cloudBlockBlob.DownloadToStream(memoryStream); // Error here
    memoryStream.Position = 0;
    memoryStream.Close();

}

我还确保我从命令行输入 az login 以切换到 select 正确的订阅,并且该帐户在 visual studio 选项中 selected。

错误日志:

Microsoft.Azure.Storage.StorageException HResult=0x80131500 Message=此请求无权使用此权限执行此操作。 来源=Microsoft.Azure.Storage.Common 堆栈跟踪: 在 Microsoft.Azure.Storage.Core.Executor.Executor.d__11.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() 在 Microsoft.Azure.Storage.Core.Executor.Executor.<>c__DisplayClass0_01.<ExecuteSync>b__0() at Microsoft.Azure.Storage.Core.Util.CommonUtility.RunWithoutSynchronizationContext[T](Func1 actionToRun) 在 Microsoft.Azure.Storage.Core.Executor.Executor.ExecuteSync[T](RESTCommand1 cmd, IRetryPolicy policy, OperationContext operationContext) at Microsoft.Azure.Storage.Blob.CloudBlob.DownloadRangeToStream(Stream target, Nullable1 偏移量,Nullable`1 长度,AccessCondition accessCondition,BlobRequestOptions 选项,OperationContext operationContext) 在 Microsoft.Azure.Storage.Blob.CloudBlob.DownloadToStream(流目标、AccessCondition 访问条件、BlobRequestOptions 选项、OperationContext operationContext) 在 BlobStorage.Program.d__2.MoveNext() 在 c:...\source\repos\BlobStorage\Program.cs:line 111 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter.GetResult() 在 BlobStorage.Program.d__0.MoveNext() 在 c:...\source\repos\BlobStorage\Program.cs:line 19

这个异常最初是在这个调用栈中抛出的: Microsoft.Azure.Storage.Core.Executor.Executor.ExecuteAsync(Microsoft.Azure.Storage.Core.Executor.RESTCommand, Microsoft.Azure.Storage.RetryPolicies.IRetryPolicy, Microsoft.Azure.Storage.OperationContext, System.Threading.CancellationToken) System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task) System.Runtime.CompilerServices.TaskAwaiter.GetResult() Microsoft.Azure.Storage.Core.Executor.Executor.ExecuteSync.AnonymousMethod__0() Microsoft.Azure.Storage.Core.Util.CommonUtility.RunWithoutSynchronizationContext(System.Func) Microsoft.Azure.Storage.Core.Executor.Executor.ExecuteSync(Microsoft.Azure.Storage.Core.Executor.RESTCommand, Microsoft.Azure.Storage.RetryPolicies.IRetryPolicy, Microsoft.Azure.Storage.OperationContext) Microsoft.Azure.Storage.Blob.CloudBlob.DownloadRangeToStream(System.IO.Stream,长?,长?,Microsoft.Azure.Storage.AccessCondition,Microsoft.Azure.Storage.Blob.BlobRequestOptions,Microsoft.Azure.Storage.OperationContext) Microsoft.Azure.Storage.Blob.CloudBlob.DownloadToStream(System.IO.Stream, Microsoft.Azure.Storage.AccessCondition, Microsoft.Azure.Storage.Blob.BlobRequestOptions, Microsoft.Azure.Storage.OperationContext) ... [调用堆栈 T运行已分类]

1) 我看到你从你的方法中返回了 Task,但没有 awaiting 任何东西。
将'async'关键字添加到方法签名并更改以下代码是一个很好的做法:

var tokenProvider = new AzureServiceTokenProvider().GetAccessTokenAsync("https://storage.azure.com/",tenantId);

类似于:

var tokenProvider = new AzureServiceTokenProvider();
var token = await tokenProvider.GetAccessTokenAsync("https://storage.azure.com/",tenantId);
var tokenCredential = new TokenCredential(token );

现在,获取您的访问令牌,您可以使用 http://jwt.io 之类的工具打开它并检查声明。

2) 尝试从以下位置更改访问令牌:

GetAccessTokenAsync("https://storage.azure.com/",tenantId);

至:

GetAccessTokenAsync("https://[youraccount].blob.core.windows.net",tenantId);

3) AzureServiceTokenProvider 将在一组预定义的位置查找凭据。

AzureServiceTokenProvider documentation值得一读,有助于解决问题。
您是否尝试过使用第一个选项将 RunAs=Developer; DeveloperTool=AzureCli 作为连接字符串传递,以便它明确知道要查找的位置?

因此,从您执行 azure cli 并执行类似 azure login 的相同命令 window,您是否尝试从那里 运行 控制台程序?

您登录的是哪个订阅? az account list
您有很多订阅吗?
您的默认订阅可能不是您尝试从中访问 blob 的订阅。

好的。我找到了解决方案。

我发现 this article which clearly stated in the note that Data Reader or Data Contributor role is a must! None of the MS documents such as this 没有像文章那样强调额外角色(数据 Reader/Data 贡献者)的重要性。

Note that it is not enough that your user is an Owner/Contributor on the subscription/resource group/Storage account. The user must be assigned to a Data Reader or Data Contributor role to get access to the data using Azure AD authentication.

我们假设贡献者已经足够了,但事实并非如此。

此外,数据 Reader/Contributor 角色必须在存储帐户资源本身...它不会在订阅资源级别工作。

老实说,这感觉像是一个错误。 应允许在订阅级别具有所有者或贡献者角色的 user/service 原则在没有补充角色的情况下通过 cli 上传。 如果我可以从 UI 做到这一点,我应该也可以通过 cli 做到这一点。