示例:使用适用于 Python 的 Azure 库来访问 Azure 存储

本文介绍如何在 Python 应用程序代码中使用 Azure 客户端库将文件上传到 Azure Blob 存储容器。 本文假定您已经创建了“示例:创建 Azure 存储”中所示的资源。

除非另行说明,否则本文中的所有命令在 Linux/macOS bash 和 Windows 命令行程序中的工作方式相同。

1. 设置本地开发环境

如果尚未设置,则请设置一个可在其中运行此代码的环境。 以下是一些选项。

  • 使用 venv 或您选择的工具来配置 Python 虚拟环境。 若要开始使用虚拟环境,请务必激活它。 若要安装 python,请参阅 “安装 Python”。

    #!/bin/bash
    # Create a virtual environment
    python -m venv .venv
    # Activate the virtual environment
    source .venv/Scripts/activate # only required for Windows (Git Bash)
    
  • 使用 conda 环境。 若要安装 Conda,请参阅 “安装 Miniconda”。

  • Visual Studio CodeGitHub Codespaces 中使用开发容器

2. 安装库包

在您的 requirements.txt 文件中,添加需要的客户端库包的行,然后保存文件。

azure-storage-blob
azure-identity

然后,在终端或命令提示符中安装规定的内容。

pip install -r requirements.txt

3. 创建要上传的文件

创建名为 sample-source.txt 的源文件。 此文件名是代码所期望的。

Hello there, Azure Storage. I'm a friendly file ready to be stored in a blob.

4.通过应用代码使用 Blob 存储

本节展示了两种方法来访问在示例“创建 Azure 存储”中创建的 blob 容器中的数据。 要访问 blob 容器中的数据,应用必须能够与 Azure 进行身份验证,并获得授权才能访问容器中的数据。 本部分将介绍这两种方法:

  • 无密码(推荐)方法通过使用对应用进行身份验证。 DefaultAzureCredential 是一种链接凭据,可以使用一系列不同的凭据(包括开发人员工具凭据、应用程序服务主体和托管标识)对应用(或用户)进行身份验证。

  • 连接字符串方法使用连接字符串直接访问存储帐户

出于以下原因,我们建议尽可能使用无密码方法:

  • 连接字符串使用存储帐户(而不是该帐户中的单个资源)来验证连接代理。 因此,连接字符串授予的授权范围可能会超出所需的范围。 使用 DefaultAzureCredential,可以通过 Azure RBAC,向在其下运行应用的标识授予对存储资源更精细的最低特权权限。

  • 由于连接字符串包含纯文本形式的访问信息,因此如果未正确构造或保护该连接字符串,则可能存在潜在漏洞。 如果公开此类连接字符串,则可使用它来访问存储帐户下的各种资源。

  • 连接字符串通常存储在环境变量中,因此,如果攻击者获取了环境访问权限,就很容易对其进行破坏。 许多DefaultAzureCredential支持的凭据类型都不需要在环境中存储秘密。

DefaultAzureCredential 是一个有意见的预配置凭据链。 它旨在支持许多环境,以及最常见的身份验证流和开发人员工具。 一个 DefaultAzureCredential 实例,综合以下要素来确定要尝试为哪种凭据类型获取令牌;这些要素有:运行时环境、某些已知环境变量的值以及(可选)传递到其构造函数的参数。

在以下步骤中,将配置应用程序服务主体作为应用程序身份。 应用程序服务主体既适用于本地开发,也适用于内部托管的应用程序。 要配置 来使用应用程序服务主体,您需要设置以下环境变量:

请注意,已配置客户端机密。 这对于应用服务主体是必要的,但根据您的具体情况,您也可以将 DefaultAzureCredential 配置为使用不需要在环境变量中设置机密或密码的凭据。

例如,在本地开发中,如果 DefaultAzureCredential 无法使用配置的环境变量来获取令牌,它就会尝试使用(已经)登录到 Azure CLI 等开发工具的用户来获取令牌;对于托管在 Azure 中的应用,DefaultAzureCredential 可以配置为使用托管身份。 在所有情况下,应用中的代码都保持不变,只是配置和/或运行环境发生了变化。

  1. 使用以下代码创建一个名为use_blob_auth.py的文件。 注释对步骤进行了说明。

    import os
    import uuid
    
    from azure.identity import DefaultAzureCredential
    
    # Import the client object from the SDK library
    from azure.storage.blob import BlobClient
    
    credential = DefaultAzureCredential()
    
    # Retrieve the storage blob service URL, which is of the form
    # https://<your-storage-account-name>.blob.core.windows.net/
    storage_url = os.environ["AZURE_STORAGE_BLOB_URL"]
    
    # Create the client object using the storage URL and the credential
    blob_client = BlobClient(
        storage_url,
        container_name="blob-container-01",
        blob_name=f"sample-blob-{str(uuid.uuid4())[0:5]}.txt",
        credential=credential,
    )
    
    # Open a local file and upload its contents to Blob Storage
    with open("./sample-source.txt", "rb") as data:
        blob_client.upload_blob(data)
        print(f"Uploaded sample-source.txt to {blob_client.url}")
    

    参考链接:

  2. 创建名为 AZURE_STORAGE_BLOB_URL 的环境变量:

    set AZURE_STORAGE_BLOB_URL=https://pythonazurestorage12345.blob.core.windows.net
    

    将“pythonazurestorage12345”替换为存储帐户的名称。

    AZURE_STORAGE_BLOB_URL 环境变量仅由此示例使用。 Azure 库不会使用它。

  3. 使用 az ad sp create-for-rbac 命令为应用程序创建一个新服务主体。 该命令会同时为应用创建应用注册。 为服务主体指定一个您选择的名称。

    az ad sp create-for-rbac --name <service-principal-name>
    

    此命令的输出如下所示。 记下这些值或使此窗口保持打开状态,因为在下一步中需使用这些值,且无法再次查看密码(客户端密码)值。 但是,如果需要,可稍后添加新密码,而不会使服务主体或现有密码失效。

    {
      "appId": "00001111-aaaa-2222-bbbb-3333cccc4444",
      "displayName": "<service-principal-name>",
      "password": "Aa1Bb~2Cc3.-Dd4Ee5Ff6Gg7Hh8Ii9_Jj0Kk1Ll2",
      "tenant": "aaaabbbb-0000-cccc-1111-dddd2222eeee"
    }
    

    Azure CLI 命令可以在 Azure Cloud Shell 中或是安装了 Azure CLI 的工作站上运行。

  4. 为应用程序服务主体创建环境变量:

    使用上一命令输出中的值创建以下环境变量。 这些变量会指示 DefaultAzureCredential 使用应用程序服务主体。

    • AZURE_CLIENT_ID →应用 ID 值。
    • AZURE_TENANT_ID →租户 ID 值。
    • AZURE_CLIENT_SECRET →为应用生成的密码/凭据。
    set AZURE_CLIENT_ID=00001111-aaaa-2222-bbbb-3333cccc4444
    set AZURE_TENANT_ID=aaaabbbb-0000-cccc-1111-dddd2222eeee
    set AZURE_CLIENT_SECRET=Aa1Bb~2Cc3.-Dd4Ee5Ff6Gg7Hh8Ii9_Jj0Kk1Ll2
    
  5. 尝试运行代码(故意使其失败):

    python use_blob_auth.py
    
  6. 请注意错误“此请求无权使用此权限执行该操作”。由于正在使用的本地服务主体尚无权访问 Blob 容器,因此会出现此错误。

  7. 使用 az role assignment create Azure CLI 命令,向服务主体授予对 Blob 容器的存储 Blob 数据参与者权限:

    az role assignment create --assignee <AZURE_CLIENT_ID> \
        --role "Storage Blob Data Contributor" \
        --scope "/subscriptions/<AZURE_SUBSCRIPTION_ID>/resourceGroups/PythonAzureExample-Storage-rg/providers/Microsoft.Storage/storageAccounts/pythonazurestorage12345/blobServices/default/containers/blob-container-01"
    

    --assignee 参数标识了服务主体。 将 AZURE_CLIENT_ID 占位符替换为服务主体的应用 ID<>。

    --scope参数标识此角色分配适用的位置。 在此示例中,将“存储 Blob 数据参与者”角色授予服务主体,用于名为“blob-container-01”的容器。

    • PythonAzureExample-Storage-rgpythonazurestorage12345 替换为包含您的存储帐户的资源组以及存储帐户的确切名称。 此外,如有必要,请调整 Blob 容器的名称。 如果使用错误名称,会发现错误“无法对嵌套资源执行请求的操作。 找不到父资源‘pythonazurestorage12345’。

    • 将 AZURE_SUBSCRIPTION_ID 占位符替换为你的 Azure 订阅 ID<>。 (可以运行 az account show 命令并从输出中的 id 属性获取订阅 ID。)

    窍门

    如果使用 bash shell 时,角色分配命令返回错误“找不到连接适配器”,请尝试设置 export MSYS_NO_PATHCONV=1 以避免路径转换。 有关详细信息,请参阅此问题

  8. 花一两分钟时间等待权限传播,然后再次运行该代码,验证它现在是否正常工作。 如果再次看到权限错误,请等待更长的时间,然后重试代码。

有关角色分配的详细信息,请参阅如何使用 Azure CLI 分配角色权限

重要

在前面的步骤中,你的应用是在应用程序服务主体下运行的。 应用程序服务主体在配置中需要一个客户机密。 但是,可以使用相同的代码在不同的凭据类型下运行应用程序,而无需在环境中显式配置密码或机密。 例如,在开发过程中,DefaultAzureCredential 可以使用开发人员工具凭据,比如用于通过 Azure CLI 登录的凭据;或者,对于在 Azure 中托管的应用,可以使用托管身份。 要了解更多信息,请参阅“使用 Azure SDK for Python 认证 Python 应用以访问 Azure 服务”。

5:验证 Blob 的创建

运行任一方法的代码后,请转到 Azure 门户,导航到 blob 容器,以验证是否存在名为“sample-blob-{random}.txt”的新 blob,并使用与 sample-source.txt 文件相同的内容:

Azure portal page for the blob container, showing the uploaded fileblob 容器的 Azure 门户页面,显示已上传的文件

如果创建了名为 AZURE_STORAGE_CONNECTION_STRING 的环境变量,还可以使用 Azure CLI 通过 az storage blob list 命令验证 Blob 是否存在:

az storage blob list --container-name blob-container-01

如果按照说明使用了无密码身份验证,则可以在前面的命令中添加--connection-string参数,并在连接字符串中输入存储帐户的信息。 要获取连接字符串,请使用 `az storage account show-connection-string` 命令。

az storage account show-connection-string --resource-group PythonAzureExample-Storage-rg --name pythonazurestorage12345 --output tsv

使用整个连接字符串作为 --connection-string 参数的值。

注意

如果 Azure 用户帐户在容器上具有“存储 Blob 数据参与者”角色,则可以使用以下命令列出容器中的 Blob:

az storage blob list --container-name blob-container-01 --account-name pythonazurestorage12345 --auth-mode login

6.清理资源

如果无需保留在此示例中使用的资源组和存储资源,则请运行 az group delete 命令。 资源组不会在订阅中产生任何持续费用,但资源组中的资源(如存储帐户)则可能会继续产生费用。 清理不经常使用的组是一种很好的做法。 --no-wait 参数允许命令立即返回,而不是等到操作完成再返回。

az group delete -n PythonAzureExample-Storage-rg  --no-wait

你还可以使用 ResourceManagementClient.resource_groups.begin_delete 方法从代码中删除资源组。 “示例:创建资源组”中的代码演示了用法。

如果按照说明使用了无密码身份验证,则最好删除已创建的应用程序服务主体。 您可以使用 az ad app delete 命令。 将 AZURE_CLIENT_ID 占位符替换为服务主体的应用 ID<>。

az ad app delete --id <AZURE_CLIENT_ID>

另请参阅