Azure 托管 ID:如何将 azure 函数从连接字符串转换为托管 ID

Azure Managed ID: how to convert azure function from connection string to managed id

我有一个如下所示的 Azure 函数:

   [FunctionName("CreateWidgetWorkspace")]
    public async Task<IActionResult> CreateWidgetWorkspace(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "widget/workspaces")] HttpRequest req,
         [Queue("widgetworkspaces"), StorageAccount("WidgetStorageQueue")] ICollector<string> messageQueue,
        ILogger log)
    {           
       
        WorkspaceResponse response = new WorkspaceResponse();
        var content = await new StreamReader(req.Body).ReadToEndAsync();
        log.LogInformation($"Received following payload: {content}");

        var workspaceRequest = JsonConvert.DeserializeObject<Workspace>(content);
        if (workspaceRequest.name != null){     
                messageQueue.Add(JsonConvert.SerializeObject(workspaceRequest));                                                 
        } 
        else {
            response.status = "Error: Invalid Request";
            response.requestId=null;
        }
        return new OkObjectResult(JsonConvert.SerializeObject(response));  
    }

一切正常 - 连接字符串在我的 local.settings.json 文件中定义如下:

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "WidgetStorageQueue": 

"DefaultEndpointsProtocol=https;AccountName=账户名;AccountKey=asdf+asdf+AStRrziLg==" } }

但现在我已经创建了一个托管标识,并且已为资源组内的所有资源分配了“贡献者”角色。 所以我需要重构这段代码,不再使用 local.settings / 环境变量中的连接字符串。但是要使用托管 ID。 你能给我指出一篇能让我走上正确道路的文章或视频吗? 如果可能的话,我实际上不喜欢 Azure 密钥保管库。

谢谢。

编辑 1

我已经添加了答案中引用的 2 个包。这是我的 csproj 文件中的内容:

  <ItemGroup>
    <PackageReference Include="Azure.Data.Tables" Version="12.4.0" />
    <PackageReference Include="Azure.Storage.Queues" Version="12.10.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.Azure.Webjobs.Extensions.ServiceBus" Version="5.2.0" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage.Queues" Version="5.0.1" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.0.1" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </None>
  </ItemGroup>

这就是我的 local.settings.json 文件的样子:

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "dotnet","WidgetStorageQueue__queueServiceUri":"https://mystorageaccountname.queue.core.windows.net"
  },
  "ConnectionStrings": {}
}

但是我收到一个错误:

2022-05-05T19:30:00.774Z] Executed 'CreateWidgetWorkspace' (Failed, Id=asdf-a22b-asdf-asdf-asdf, Duration=6356ms)
[2022-05-05T19:30:00.777Z] System.Private.CoreLib: Exception while executing function: CreateWidgetWorkspace. Azure.Storage.Queues: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:asdf-8003-asdf-asdf-asdf
Time:2022-05-05T19:30:00.7494033Z
[2022-05-05T19:30:00.781Z] Status: 403 (Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.)
[2022-05-05T19:30:00.782Z] ErrorCode: AuthenticationFailed
[2022-05-05T19:30:00.784Z] 
[2022-05-05T19:30:00.785Z] Additional Information:
[2022-05-05T19:30:00.788Z] AuthenticationErrorDetail: Issuer validation failed. Issuer did not match.
[2022-05-05T19:30:00.790Z] 
[2022-05-05T19:30:00.791Z] Content:
[2022-05-05T19:30:00.793Z] <?xml version="1.0" encoding="utf-8"?><Error><Code>AuthenticationFailed</Code><Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:asdf-8003-asdf-asdf-asdf
Time:2022-05-05T19:30:00.7494033Z</Message><AuthenticationErrorDetail>Issuer validation failed. Issuer did not match.</AuthenticationErrorDetail></Error>      
[2022-05-05T19:30:00.795Z] 
[2022-05-05T19:30:00.796Z] Headers:
[2022-05-05T19:30:00.797Z] Server: Microsoft-HTTPAPI/2.0
[2022-05-05T19:30:00.801Z] x-ms-request-id: asdf-asdf-asdf-asdf-60671b000000
[2022-05-05T19:30:00.802Z] x-ms-error-code: AuthenticationFailed
[2022-05-05T19:30:00.809Z] Date: Thu, 05 May 2022 19:29:59 GMT
[2022-05-05T19:30:00.810Z] Content-Length: 422
[2022-05-05T19:30:00.811Z] Content-Type: application/xml
[2022-05-05T19:30:00.812Z] .

这是我的 Azure 资源组:

这是函数应用程序 - 您可以看到我已经为其分配了一个用户分配的托管标识:

下面是分配给我的托管身份的 RBAC 角色:

问题

根据我有限的知识/阅读,感觉我应该安装 Azure.Identity 并创建某种 DefaultAzureCredential? https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme#specifying-a-user-assigned-managed-identity-with-the-defaultazurecredential

编辑 2

答案中建议的更改基本上有效。澄清一下,local.setting.json 中实际有效的设置是这样的:

"[nameofConnectioninC#Method]__serviceUri":"https://[nameOfStorageAccount].queue.core.windows.net/"

本地调试时失败,但发布所有内容时,上游测试有效。

要将 Identity-based connections 与队列存储一起使用,您需要:

  1. 更新您的应用程序以使用这些 Nuget 包:Azure.Storage.Queues and Microsoft.Azure.WebJobs.Extensions.Storage.Queues

  2. 创建名为 <CONNECTION_NAME_PREFIX>__serviceUri 的应用设置:

    The data plane URI of the queue service to which you are connecting, using the HTTPS scheme.
    https://<storage_account_name>.queue.core.windows.net

    因此在您的情况下,您需要创建一个名为 WidgetStorageQueue__serviceUri

    的设置
  3. Grant permission to your function app identity to access queue storage. If you just need to send message to a queue, you could use the Storage Queue Data Message Sender角色。

我创建了一个小型功能应用程序来重现这个用例。

csproj 文件:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Azure.Storage.Queues" Version="12.9.0" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage.Queues" Version="5.0.0" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.0" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

函数文件

using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace FunctionApp1
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            [Queue("widgetworkspaces"), StorageAccount("WidgetStorageQueue")] ICollector<string> queueCollector,
            ILogger log)
        {
            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            queueCollector.Add(requestBody);
            return new OkObjectResult(requestBody);
        }
    }
}

设置文件

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "WidgetStorageQueue__queueServiceUri": "https://<storage_account_name>.queue.core.windows.net"
  }
}