教程:创建使用标识而不是机密连接到 Azure 服务的函数应用

本教程演示如何在可能的情况下使用 Microsoft Entra 标识而不是机密或连接字符串来配置函数应用。 使用标识有助于避免意外泄露敏感机密,并可以更好地了解数据的访问方法。 若要详细了解基于标识的连接,请参阅配置基于标识的连接

虽然显示的过程通常适用于所有语言,但本教程目前专用于支持 Windows 上的 C# 类库函数。

在本教程中,你将了解:

  • 使用 ARM 模板在 Azure 中创建函数应用
  • 在函数应用上同时启用系统分配的托管标识和用户分配的托管标识
  • 创建向其他资源授予权限的角色分配
  • 将不能替换为标识的机密移动到 Azure Key Vault
  • 将应用配置为使用其托管标识连接到默认主机存储

完成本教程后,应完成介绍如何将基于标识的连接(而非机密)与触发器和绑定配合使用的后续教程。

先决条件

为何使用标识?

管理机密和凭据是各种规模团队的共同挑战。 机密需要受到保护以防被盗或意外披露,并可能需要定期轮换。 许多 Azure 服务允许改用 Microsoft Entra ID 中的标识来对客户端进行身份验证以及检查可快速修改和撤销的权限。 这样做可以更好地控制应用程序安全性并降低操作开销。 标识可以是人类用户,例如应用程序的开发人员,也可以是 Azure 中具有托管标识的正在运行的应用程序。

由于某些服务不支持 Microsoft Entra 身份验证,因此在某些情况下,你的应用程序可能仍需要机密。 但是,这些机密可以存储在 Azure 密钥保管库中,这有助于简化机密的管理生命周期。 对密钥保管库的访问也通过标识进行控制。

了解如何在可行的情况下使用标识而不是机密,在无法使用标识情况下使用密钥保管库,这方面的知识能够降低风险,减少操作开销,并在总体上改善应用程序的安全态势。

创建一个使用 Key Vault 获取必要机密的函数应用

Azure 文件存储是尚不支持服务器消息块 (SMB) 文件共享的 Microsoft Entra 身份验证的服务示例。 Azure 文件存储是高级计划和消耗计划中用于 Windows 部署的默认文件系统。 虽然我们可以彻底删除 Azure 文件存储,但这样做会带来你可能不想要的限制。 相反,应将 Azure 文件存储连接字符串移到 Azure 密钥保管库中。 这样就可以集中对其进行管理,并通过标识控制对其的访问。

创建 Azure Key Vault

首先,需要一个密钥保管库来存储机密。 将此密钥保管库配置为使用 Azure 基于角色的访问控制 (RBAC) 来确定谁可以从保管库中读取机密。

  1. Azure 门户中,选择“创建资源(+)”。

  2. 在“创建资源”页上,选择“安全性”“密钥保管库”。

  3. 在“基本信息”页上,使用下表配置密钥保管库。

    选项 建议的值 描述
    订阅 你的订阅 要在其下创建此新函数应用的订阅。
    资源组 myResourceGroup 要在其中创建函数应用的新资源组的名称。
    密钥保管库名称 全局唯一名称 用于标识新密钥保管库的名称。 保管库名称只能包含字母数字字符和短划线,且不能以数字开头。
    定价层 Standard 计费选项。 “标准”对于本教程来说已足够。
    区域 首选区域 选择离你近或离函数访问的其他服务近的区域

    对“恢复选项”部分使用默认选择。

  4. 记下你使用的名称,供以后使用。

  5. 选择“下一步:访问策略”,前往“访问策略”选项卡

  6. 在“权限模型”下,选择“Azure 基于角色的访问控制”

  7. 选择“查看 + 创建”。 查看配置,然后选择“创建”。

为应用设置标识和权限

若要使用 Azure 密钥保管库,应用需有一个标识,并且可向该标识授予机密读取权限。 此应用将使用用户分配的标识,因此甚至可以在创建该应用之前设置权限。 有关 Azure Functions 的托管标识的详细信息,请参阅“如何在 Azure Functions 中使用托管标识”。

  1. Azure 门户中,选择“创建资源(+)”。

  2. 在“创建资源”页上,选择“标识”“用户分配的托管标识”。

  3. 在“基本信息”页上,使用下表配置标识。

    选项 建议的值 描述
    订阅 你的订阅 要在其下创建此新函数应用的订阅。
    资源组 myResourceGroup 要在其中创建函数应用的新资源组的名称。
    区域 首选区域 选择离你近或离函数访问的其他服务近的区域
    名称 全局唯一名称 用于标识新的用户分配标识的名称。
  4. 选择“查看 + 创建”。 查看配置,然后选择“创建”。

  5. 创建标识后,在门户中导航到该标识。 选择“属性”,并记下资源 ID 以备后用。

  6. 选择“Azure 角色分配”,然后选择“添加角色分配(预览版)”

  7. 在“添加角色分配(预览版)”页面中,使用下表中所示的选项

    选项 建议的值 说明
    范围 密钥保管库 范围是角色分配应用到的资源集。 范围具有在较低级别继承的级别。 例如,如果选择订阅范围,则角色分配将应用于订阅中的所有资源组和资源。
    订阅 你的订阅 要在其下创建此新函数应用的订阅。
    资源 你的密钥保管库 之前创建的密钥保管库。
    角色 Key Vault 机密用户 角色是被授予的权限的集合。 Key Vault 机密用户授予标识从保管库读取机密值的权限。
  8. 选择“保存”。 刷新标识的角色分配列表时,可能需要一两分钟时间,角色才会显示出来。

该标识现在能够读取密钥保管库中存储的机密。 在本教程的稍后部分,你会添加用于不同目的的其他角色分配。

生成用于创建函数应用的模板

由于用于创建函数应用的门户体验不会与 Azure 密钥保管库交互,你需要生成并编辑一个 Azure 资源管理器模板。 然后,可以使用此模板来创建函数应用,以引用密钥保管库中的 Azure 文件存储连接字符串。

重要

编辑 ARM 模板之前,请勿创建函数应用。 Azure 文件存储配置需要在应用创建时进行设置。

  1. Azure 门户中,选择“创建资源(+)”。

  2. 在“创建资源”页上,选择“计算”“函数应用”。

  3. 在“基本信息”页上,使用下表配置函数应用。

    选项 建议的值 描述
    订阅 你的订阅 要在其下创建此新函数应用的订阅。
    资源组 myResourceGroup 要在其中创建函数应用的新资源组的名称。
    函数应用名称 全局唯一名称 用于标识新 Function App 的名称。 有效字符为 a-z(不区分大小写)、0-9-
    发布 代码 选择发布代码文件或 Docker 容器。
    运行时堆栈 .NET 本教程使用 .NET。
    区域 首选区域 选择离你近或离函数访问的其他服务近的区域
  4. 选择“查看 + 创建”。 应用使用“托管”和“监视”页面上的默认值 。 查看默认选项,这些选项包含在生成的 ARM 模板中。

  5. 不要在此处创建函数应用,而是选择“下载自动化模板”,该选项位于“下一步”按钮的右侧 。

  6. 在模板页面中,选择“部署”,然后在“自定义部署”页面中,选择“编辑模板” 。

    显示“模板”页面顶部的“部署”按钮的屏幕截图。

编辑模板

现在编辑模板,以将 Azure 文件存储连接字符串存储在密钥保管库中,并允许函数应用引用该字符串。 在继续操作之前,请确保具有前面部分中的以下值:

  • 用户分配的标识的资源 ID
  • 密钥保管库的名称

注意

如果要创建一个完整的自动化模板,需要包含标识和角色分配资源的定义,以及适当的 dependsOn 子句。 这将取代之前使用门户的步骤。 请参阅 Azure 资源管理器指南和每个服务的文档。

  1. 在编辑器中,找到 resources 数组的开始位置。 在函数应用定义的前面添加以下节,以将 Azure 文件存储连接字符串放到密钥保管库中。 将“VAULT_NAME”替换为密钥保管库的名称。

    {
        "type": "Microsoft.KeyVault/vaults/secrets",
        "apiVersion": "2016-10-01",
        "name": "VAULT_NAME/azurefilesconnectionstring",
        "properties": {
            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2019-06-01').keys[0].value,';EndpointSuffix=','core.chinacloudapi.cn')]"
        },
        "dependsOn": [
            "[concat('Microsoft.Storage/storageAccounts/', parameters('storageAccountName'))]"
        ]
    },
    
  2. 在函数应用资源(将 type 设置为 Microsoft.Web/sites)的定义中,将 Microsoft.KeyVault/vaults/VAULT_NAME/secrets/azurefilesconnectionstring 添加到 dependsOn 数组。 再次将“VAULT_NAME”替换为你的密钥保管库的名称。 这样做可以防止在定义机密之前创建应用。 dependsOn 数组应类似于以下示例:

        {
            "type": "Microsoft.Web/sites",
            "apiVersion": "2018-11-01",
            "name": "[parameters('name')]",
            "location": "[parameters('location')]",
            "tags": null,
            "dependsOn": [
                "microsoft.insights/components/idcxntut",
                "Microsoft.KeyVault/vaults/VAULT_NAME/secrets/azurefilesconnectionstring",
                "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
                "[concat('Microsoft.Storage/storageAccounts/', parameters('storageAccountName'))]"
            ],
            // ...
        }
    
  3. 将以下示例中的 identity 块添加到函数应用资源的定义中。 将“IDENTITY_RESOURCE_ID”替换为用户分配标识的资源 ID。

    {
        "apiVersion": "2018-11-01",
        "name": "[parameters('name')]",
        "type": "Microsoft.Web/sites",
        "kind": "functionapp",
        "location": "[parameters('location')]",
        "identity": {
            "type": "SystemAssigned,UserAssigned",
            "userAssignedIdentities": {
                "IDENTITY_RESOURCE_ID": {}
            }
        },
        "tags": null,
        // ...
    }
    

    identity 块还会设置系统分配的标识,你会在本教程稍后的部分使用该标识。

  4. keyVaultReferenceIdentity 属性添加到函数应用的 properties 对象,如以下示例中所示。 将“IDENTITY_RESOURCE_ID”替换为用户分配标识的资源 ID。

    {
        // ...
         "properties": {
                "name": "[parameters('name')]",
                "keyVaultReferenceIdentity": "IDENTITY_RESOURCE_ID",
                // ...
         }
    }
    

    你需要此配置,因为应用可能配置了多个用户分配的标识。 每当要使用用户分配的标识时,都必须使用 ID 予以指定。 不需要以这种方式指定系统分配的标识,因为一个应用只能有一个标识。 许多使用托管标识的功能假定它们默认使用系统分配的标识。

  5. 找到用于定义 WEBSITE_CONTENTAZUREFILECONNECTIONSTRING 应用程序设置的如以下示例所示的 JSON 对象:

    {
        "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
        "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2019-06-01').keys[0].value,';EndpointSuffix=','core.chinacloudapi.cn')]"
    },
    
  6. value 字段替换为对机密的引用,如下例所示。 将“VAULT_NAME”替换为密钥保管库的名称。

    {
        "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
        "value": "[concat('@Microsoft.KeyVault(SecretUri=', reference(resourceId('Microsoft.KeyVault/vaults/secrets', 'VAULT_NAME', 'azurefilesconnectionstring')).secretUri, ')')]"
    },
    
  7. 选择“保存”以保存更新后的 ARM 模板。

部署修改后的模板

  1. 确保创建选项(包括“资源组”)仍然正确,然后选择“查看 + 创建” 。

  2. 在模板验证后,记下“存储帐户名称”,因为稍后将会用到。 最后,选择“创建”以创建 Azure 资源,将代码部署到函数应用。

  3. 部署完成后,选择“转到资源组”,然后选择新的函数应用。

祝贺你! 你已成功创建函数应用以从 Azure Key Vault 引用 Azure 文件存储连接字符串。

每当应用需要添加对机密的引用时,只需定义一个新的应用程序设置,该设置指向存储在 Key Vault 中的值。 有关详细信息,请参阅 Azure Functions 的密钥保管库参考

提示

Application Insights 连接字符串及其包含的检测密钥不被视为机密,可以使用读取者权限从 App Insights 中对其进行检索。 尽管可以,但不需要将它们移动到 Key Vault 中。

为 AzureWebJobsStorage 使用托管标识

接下来,为 AzureWebJobsStorage 连接使用在前面步骤中配置的系统分配的标识。 AzureWebJobsStorage 由 Functions 运行时以及多个触发器和绑定用于在多个运行实例之间进行协调。 这是函数应用程序运作的必要项(与 Azure 文件存储一样)。默认情况下,它是在新建函数应用时使用连接字符串配置的。

向系统分配的标识授予对存储帐户的访问权限

与之前对用户获配的标识和密钥保管库执行的步骤类似,现在请创建一个角色分配,用于向系统分配的标识授予对存储帐户的访问权限。

  1. Azure 门户中,导航到之前使用函数应用创建的存储帐户。

  2. 选择“访问控制 (IAM)”。 可在此页面查看和配置谁有权访问资源。

  3. 选择“+ 添加”,然后选择“添加角色分配”

  4. 搜索“存储 Blob 数据所有者”,选择该角色,然后选择“下一步”

  5. 在“成员”选项卡上的“将访问权限分配给”下,选择“托管标识”

  6. 选择“选择成员”,打开“选择托管标识”面板

  7. 确认“订阅”是之前在其中创建资源的那个订阅。

  8. 在“托管标识”选择器中,从“系统分配的托管标识”类别中选择“函数应用” 。 “函数应用”标签旁边的括号中可能包含一个数字,表示订阅中具有系统分配标识的应用的数量。

  9. 你的应用程序应显示在输入字段下的列表中。 如果没有看到它,可以使用“选择”框筛选具有你的应用名称的结果。

  10. 选择自己的应用程序。 它应该向下移动到“所选成员”部分。 选择“选择”。

  11. 在“添加角色分配”屏幕上,选择“查看 + 分配”。 查看配置,然后选择“查看 + 分配”

提示

如果打算将函数应用用于 Blob 触发的函数,则将需要对 AzureWebJobsStorage 使用的帐户上的“存储帐户参与者”和“存储队列数据参与者”角色重复这些步骤。 有关详细信息,请参阅 Blob 触发器基于标识的连接

编辑 AzureWebJobsStorage 配置

接下来更新函数应用,以便在它使用 Blob 服务作为主机存储时使用其系统分配的标识。

重要

某些触发器和绑定使用 AzureWebJobsStorage 配置,这些扩展还必须能够使用基于标识的连接。 使用 blob 触发器或事件中心触发器的应用可能需要更新这些扩展。 由于尚未为此应用定义任何函数,因此尚无问题。 若要了解有关此要求的更多信息,请参阅使用标识连接到主机存储

同样,在 Linux 消耗计划中使用服务器端生成时,AzureWebJobsStorage 用于部署工件。 在 Linux 消耗计划中为 AzureWebJobsStorage 启用基于标识的连接时,需要通过外部部署包进行部署。

  1. Azure 门户中,导航到你的函数应用。

  2. 在函数应用中,展开“设置”,然后选择“环境变量”

  3. 在“应用设置”选项卡中,选择 AzureWebJobsStorage 应用设置,并根据下表进行编辑:

    选项 建议的值 说明
    名称 AzureWebJobsStorage__accountName 将名称从 AzureWebJobsStorage 更改为确切名称 AzureWebJobsStorage__accountName。 此设置指示主机使用标识,而不是搜索存储的机密。 新设置使用双下划线 (__),这是应用程序设置中的特殊字符。
    你的帐户名称 将连接字符串中的名称更新为你的 StorageAccountName。

    此配置告知系统使用标识连接到资源。

  4. 选择“应用”,然后选择“应用并确认”以保存更改并重新启动应用程序功能。

将应用配置为使用托管标识连接到 Blob 后,现在便不再需要遵守 AzureWebJobsStorage 的存储连接字符串要求。

注意

__accountName 语法是 AzureWebJobsStorage 连接独有的,且不能用于其他存储连接。 要了解如何定义其他连接,请参阅应用使用的每个触发器和绑定的参考。

后续步骤

本教程演示了如何创建函数应用,而无需在其配置中存储机密。

继续学习下一个教程,了解如何在触发器和绑定连接中使用标识。