次の方法で共有


サービス プリンシパルを使用したローカル開発時に Azure サービスに対して Python アプリを認証する

クラウド アプリケーションを構築する場合、開発者は多くの場合、アプリをローカルで実行してテストする必要があります。 ローカル開発中でも、アプリケーションは対話するすべての Azure サービスに対して認証を行う必要があります。 この記事では、ローカル開発時に専用のサービス プリンシパル ID を構成する方法について説明します。

ローカルで実行されているアプリで開発者が .envファイルからアプリケーション サービス プリンシパルを取得し、その ID を使用して Azure リソースに接続する方法を示す図。

ローカル開発専用のアプリケーション サービス プリンシパルでは、開発中にアプリに必要な Azure リソースのみにアクセスを制限することで、最小限の特権の原則がサポートされます。 専用アプリケーション サービス プリンシパルを使用すると、他のリソースへの意図しないアクセスのリスクが軽減され、運用に移行するときにアクセス許可に関連するバグを防ぐことができます。これにより、より広範なアクセス許可が問題につながる可能性があります。

Azure でローカル開発用にアプリケーションを登録する場合は、次の手順を実行することをお勧めします。

  • 開発者ごとに個別のアプリ登録を作成する: これにより、各開発者に独自のサービス プリンシパルが提供されるため、資格情報を共有する必要が回避され、より詳細なアクセス制御が可能になります。
  • アプリケーションごとに個別のアプリ登録を作成する: これにより、各アプリに必要なアクセス許可のみが付与され、潜在的な攻撃対象領域が減少します。

ローカル開発中に認証を有効にするには、アプリケーション サービス プリンシパルの資格情報を使用して環境変数を設定します。 Azure SDK for Python は、これらの変数を検出し、それらを使用して Azure サービスへの要求を認証します。

1 - Azure にアプリケーションを登録する

アプリケーション サービス プリンシパル オブジェクトは、Azure にアプリを登録するときに作成されます。 この登録は、Azure portal または Azure CLI を使用して実行できます。 登録プロセスでは、Microsoft Entra ID (旧称 Azure Active Directory) でアプリ登録が作成され、アプリのサービス プリンシパル オブジェクトが生成されます。 サービス プリンシパル オブジェクトは、Azure サービスに対してアプリを認証するために使用されます。 アプリ登録プロセスでは、アプリのクライアント シークレット (パスワード) も生成されます。 このシークレットは、Azure サービスに対してアプリを認証するために使用されます。 クライアント シークレットはソース管理ではなく、アプリケーション ディレクトリの .env ファイルに格納されます。 .env ファイルは、実行時にアプリケーションによって読み取り、Azure SDK for Python がアプリの認証に使用する環境変数を設定します。 次の手順では、Azure にアプリを登録し、アプリのサービス プリンシパルを作成する方法を示します。 手順は、Azure CLI と Azure portal の両方で示されています。

Azure CLI コマンドは、Azure Cloud Shell で、または Azure CLI がインストールされているワークステーション上で実行できます。

まず、az ad sp create-for-rbac コマンドを使用して、アプリの新しいサービス プリンシパルを作成します。 このコマンドは、アプリのアプリ登録も同時に作成します。

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

このコマンドの出力は次のようになります。 これらの値をメモするか、次の手順でこれらの値が必要になり、パスワード (クライアント シークレット) 値を再び表示できなくなるため、このウィンドウを開いたままにします。 ただし、必要に応じて、サービス プリンシパルや既存のパスワードを無効にせずに、後で新しいパスワードを追加できます。

{
  "appId": "00001111-aaaa-2222-bbbb-3333cccc4444",
  "displayName": "<service-principal-name>",
  "password": "Ee5Ff~6Gg7.-Hh8Ii9Jj0Kk1Ll2Mm3_Nn4Oo5Pp6",
  "tenant": "aaaabbbb-0000-cccc-1111-dddd2222eeee"
}

次に、 appID 値を取得し、変数に格納する必要があります。 この値は、Azure SDK for Python がサービス プリンシパルを使用して Azure に対して認証できるように、ローカル開発環境で環境変数を設定するために使用されます。

APP_ID=$(az ad sp list \
  --all \
  --query "[?displayName=='$SERVICE_PRINCIPAL_NAME'].appId | [0]" \
  --output tsv)

2 - ローカル開発用の Microsoft Entra セキュリティ グループを作成する

通常、アプリケーションで作業する開発者は複数いるため、個々のサービス プリンシパル オブジェクトにロールを割り当てるのではなく、Microsoft Entra セキュリティ グループを作成して、アプリがローカル開発で必要とするロール (アクセス許可) をカプセル化することをお勧めします。 これには次のような利点があります。

  • ロールはグループ レベルで割り当てられるため、すべての開発者に同じロールが割り当てられることが保証されます。
  • アプリに新しいロールが必要な場合は、アプリの Microsoft Entra グループに追加するだけで済みます。
  • 新しい開発者がチームに参加する場合は、アプリで作業するための適切なアクセス許可が開発者に与えられるように、その開発者用に新しいアプリケーション サービス プリンシパルを作成してグループに追加します。

Microsoft Entra ID でセキュリティ グループを作成するには、az ad group create コマンドを使用します。 --display-name パラメーターと --main-nickname パラメーターは必須です。 グループに指定する名前は、アプリケーションの名前に基づく必要があります。 また、グループの名前に "local-dev" のような語句を含め、グループの目的も示すと便利です。

GROUP_DISPLAY_NAME="<group-name>"
GROUP_MAIL_NICKNAME="<group-mail-nickname>"
GROUP_DESCRIPTION="<group-description>"
az ad group create \
  --display-name $GROUP_DISPLAY_NAME \
  --mail-nickname $GROUP_MAIL_NICKNAME \
  --description $GROUP_DESCRIPTION

グループにメンバーを追加するには、アプリケーション サービス プリンシパルのオブジェクト ID が必要です。これは、アプリケーション ID とは異なります。 az ad sp list を使って、使用可能なサービス プリンシパルを一覧表示します。 --filter パラメーター コマンドは OData スタイル フィルターを受け取り、表示されているリストをフィルター処理するために使用できます。 --query パラメーターでは、必要な列のみに制限されます。

SP_OBJECT_ID=$(az ad sp list \
  --filter "startswith(displayName,'$GROUP_DISPLAY_NAME')" \
  --query "[0].id" \
  --output tsv)

その後、az ad group member add コマンドを使用して、グループにメンバーを追加できます。

az ad group member add \
    --group $GROUP_DISPLAY_NAME \
    --member-id $SP_OBJECT_ID

デフォルトでは、Microsoft Entraセキュリティグループの作成は、ディレクトリ内の特定の特権ロールに制限されます。 グループを作成できない場合は、ディレクトリの管理者に問い合わせてください。 既存のグループにメンバーを追加できない場合は、グループオーナーまたはディレクトリ管理者に問い合わせてください。 詳細については、「Microsoft Entraグループとグループ メンバシップの管理」を参照してください。

3 - アプリケーションにロールを割り当てる

次に、アプリで必要なリソースに対して必要なロール (アクセス許可) を決定し、それらのロールをアプリに割り当てる必要があります。 この例では、手順 2 で作成した Microsoft Entra グループにロールが割り当てられます。 役割は、リソース、リソースグループ、またはサブスクリプションの範囲で割り当てることができます。 次に、ほとんどのアプリケーションがすべてのAzureリソースを1つのリソースグループにグループ化するため、リソースグループスコープでロールを割り当てる例を示します。

azロール割り当て作成コマンドを使用して、Azureでユーザー、グループ、またはアプリケーションサービスのプリンシパルにロールが割り当てられます。 オブジェクトIDでグループを指定できます。 アプリケーション サービス プリンシパルを、appId で指定できます。

RESOURCE_GROUP_NAME=<resource-group-name>
SUBSCRIPTION_ID=$(az account show --query id --output tsv)
ROLE_NAME=<role-name>
az role assignment create \
  --assignee "$APP_ID" \
  --scope "./subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP_NAME" \
  --role "$ROLE_NAME"

![!注] Git Bash が /subscriptions/.. を扱わないようにするにはファイル パスとして、 scope パラメーターの文字列の先頭に ./ を付加し、文字列全体を二重引用符で囲みます。

割り当て可能なロール名を取得するには、azロール定義リストコマンドを使用します。

az role definition list \
    --query "sort_by([].{roleName:roleName, description:description}, &roleName)" \
    --output table

たとえば、appId が 00001111-aaaa-2222-bbbb-3333cccc4444 であるアプリケーション サービス プリンシパルに、ID が であるサブスクリプションに含まれる aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e リソース グループのすべてのストレージ アカウントの Azure Storage blob コンテナとデータへの読み取り、書き込み、および削除のアクセスを許可するには、次のコマンドを使用して、アプリケーション サービス プリンシパルをストレージ BLOB データ共同作成者ロールに割り当てます。

az role assignment create --assignee 00001111-aaaa-2222-bbbb-3333cccc4444 \
    --scope "./subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-python-sdk-auth-example" \
    --role "Storage Blob Data Contributor"

Azure CLI を使用してリソースまたはサブスクリプション レベルでアクセス許可を割り当てる方法については、「Azure CLI を使用して Azure ロールを割り当てる」を参照してください。

4 - ローカル開発の環境変数を設定する

DefaultAzureCredential オブジェクトでは、実行時に一連の環境変数でサービス プリンシパル情報を検索します。 ほとんどの開発者は複数のアプリケーションで作業するため、python-dotenv などのパッケージを使用して、開発時にアプリケーションのディレクトリに格納されている .env ファイルから環境にアクセスすることをお勧めします。 このようにすると、Azure に対してアプリケーションを認証するために使用される環境変数のスコープが設定され、このアプリケーションのみで使用できます。

.env ファイルには Azure のアプリケーション シークレット キーが含まれているので、ソース管理にチェックインされることはありません。 Python 用の標準の .gitignore ファイルでは、チェックインから .env ファイルが自動的に除外されます。

python-dotenv パッケージを使用するには、まずアプリケーションにパッケージをインストールします。

pip install python-dotenv

次に、アプリケーションのルート ディレクトリに .env ファイルを作成します。 次のように、アプリ登録プロセスから取得した値を使用して環境変数の値を設定します。

  • AZURE_CLIENT_ID → アプリ ID の値です。
  • AZURE_TENANT_ID → テナント ID の値です。
  • AZURE_CLIENT_SECRET →アプリ用に生成されたパスワード/資格情報。
AZURE_CLIENT_ID=00001111-aaaa-2222-bbbb-3333cccc4444
AZURE_TENANT_ID=aaaabbbb-0000-cccc-1111-dddd2222eeee
AZURE_CLIENT_SECRET=Ee5Ff~6Gg7.-Hh8Ii9Jj0Kk1Ll2Mm3_Nn4Oo5Pp6

最後に、アプリケーションのスタートアップ コードで、python-dotenv ライブラリを使用して、起動時に .env ファイルから環境変数を読み取ります。

from dotenv import load_dotenv

if ( os.environ['ENVIRONMENT'] == 'development'):
    print("Loading environment variables from .env file")
    load_dotenv(".env")

5 - アプリケーションに DefaultAzureCredential を実装する

Azure SDK クライアント オブジェクトを Azure に対して認証するには、アプリケーションで DefaultAzureCredential パッケージから azure.identity クラスを使用する必要があります。 このシナリオでは、DefaultAzureCredential で環境変数 AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_CLIENT_SECRET が設定されていることを検出し、それらの変数を読み取り、Azure に接続するアプリケーション サービス プリンシパル情報を取得します。

まず、azure.identity パッケージを アプリケーションに追加します。

pip install azure-identity

次に、アプリで Azure SDK クライアント オブジェクトを作成する Python コードでは、次のことが必要になります。

  1. DefaultAzureCredential モジュールから azure.identity クラスをインポートします。
  2. DefaultAzureCredential オブジェクトを作成します。
  3. Azure SDK クライアント オブジェクト コンストラクターに DefaultAzureCredential オブジェクトを渡します。

この例を次のコード セグメントに示します。

from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient

# Acquire a credential object
token_credential = DefaultAzureCredential()

blob_service_client = BlobServiceClient(
        account_url="https://<my_account_name>.blob.core.windows.net",
        credential=token_credential)