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

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

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

在本教程中,你将了解:

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

完成本教程后,应完成后续教程,该教程演示如何将基于标识的连接(而不是机密)与触发器和绑定一起使用

先决条件

为何使用标识?

管理机密和凭据是各种规模团队的共同挑战。 需要保护机密以防被盗或意外泄露,并且可能需要定期轮换机密。 许多 Azure 服务允许你改为使用 Azure Active Directory (Azure AD) 中的标识来验证客户端并检查可以快速修改和撤销的权限。 这样可以更好地控制应用程序安全性,同时减少操作开销。 标识可以是人类用户,例如应用程序的开发人员,也可以是 Azure 中具有托管标识的正在运行的应用程序。

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

通过了解如何在可以使用标识时使用标识而不是机密,以及在不能使用标识时使用 Key Vault,你将能够降低风险、减少操作开销,并总体上改善应用程序的安全状况。

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

Azure 文件存储是尚不支持对 SMB 文件共享执行 Azure Active Directory 身份验证的服务的示例。 Azure 文件存储是高级计划和消耗计划中用于 Windows 部署的默认文件系统。 虽然可以完全删除 Azure 文件存储,但这会引入你可能不需要的限制。 相反,你会将 Azure 文件存储连接字符串移动到 Azure Key Vault 中。 这样就可以集中管理,由标识控制访问权限。

创建 Azure Key Vault

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

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

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

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

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

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

  4. 记下你使用的名称,因为稍后将会用到。

  5. 单击“下一步: 访问策略”,导航到“访问策略”选项卡 。

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

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

为应用设置标识和权限

若要使用 Azure Key Vault,应用需要具有可被授予读取机密权限的标识。 此应用将使用用户分配的标识,以便在创建应用之前就可以设置权限。 有关 Azure Functions 托管标识的详细信息,请参阅如何在 Azure Functions 中使用托管标识主题。

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

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

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

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

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

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

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

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

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

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

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

重要

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

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

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

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

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

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

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

    Screenshot of where to find the deploy button at the top of the template screen.

编辑模板

现在,你将编辑模板以将 Azure 文件存储连接字符串存储在 Key Vault 中并允许函数应用对其进行引用。 在继续操作之前,请确保具有前面部分中的以下值:

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

注意

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

  1. 在编辑器中,找到 resources 数组的开始位置。 在函数应用定义之前,添加以下部分,将 Azure 文件存储连接字符串放入 Key Vault。 将“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 的 Key Vault 参考

提示

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 更新为确切名称 。 此设置告诉主机使用标识而不是查找存储的机密。 新设置使用双下划线 (__),这是应用程序设置中的特殊字符。
    你的帐户名称 将连接字符串中的名称更新为你的 StorageAccountName。

    此配置将使系统知道它应使用标识来连接到资源。

  4. 选择“确定”,然后选择“保存”“继续”以保存更改。

通过将应用配置为使用托管标识连接到 blob,你已删除了 AzureWebJobsStorage 的存储连接字符串要求。

注意

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

后续步骤

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

在下一教程中,你将了解如何在触发器和绑定连接中使用标识。