Azure Functions上で動作するPythonアプリケーションにおいて、マネージドIDを利用すると、データベースに接続するためのパスワードを使わずに、SQL Databaseに接続することができる。
今回は、マネージドIDを利用して、Azure Functions上で動作するPythonアプリケーションでSQL Databaseに接続してみたので、その手順を共有する。
前提条件
下記サイトの手順に従って、Azure Functions上で動作するPythonアプリケーションの作成が完了していること。

また、下記サイトの手順に従って、SQL Databaseを作成済であること。

やってみたこと
- Azure FunctionsのマネージドIDを有効化
- SQL ServerでEntra ID管理者を設定
- Azure Functionsの権限をDB上で設定
- Azure FunctionsからSQL Databaseへの接続確認
Azure FunctionsのマネージドIDを有効化
Azure FunctionsのマネージドIDを有効化するのは、Azure Portal上で実施できる。その手順は、以下の通り。
1) Azure Portalにログインし、マネージドIDを有効化するAzure Functionsを選択し、「設定」メニューから「ID」を選択する。
2)「システム割り当て済み」タブの状態を「オフ」から「オン」に変更し、「保存」ボタンを押下する。

3) ダイアログ「システム割り当てマネージドIDを有効化する」が表示されるため、「はい」ボタンを押下する。
4) システム割り当てマネージドIDが有効化されると、以下のように、オブジェクト(プリンシパル)IDの設定が確認できる。
SQL ServerでEntra ID管理者を設定
SQL Database上で、先ほどマネージドIDを有効化したAzure Functionsの権限設定を行うため、SQL ServerのEntra ID管理者を設定する。その手順は、以下の通り。
1) Azure Portal上で「SQL Server」を選択する。
2) Azure Functionsから接続予定のデータベースのリンクを押下する。
3)「Microsoft Entra 管理者」のメニューを選択する。
5)「ユーザー」タブで自分自身を選択し、「選択」ボタンを押下する。
6)「Microsoft Entra 管理者」の設定を確認し、「保存」ボタンを押下する。
7) Entra IDの登録が完了すると、以下のように、完了メッセージの表示が確認できる。
8) 概要メニューを確認すると、以下のように、Microsoft Entra 管理者の設定がされていることを確認できる。

Azure Functionsの権限をDB上で設定
SQL Database上に、Entra認証でログイン後、マネージドIDを有効化したAzure Functionsの権限設定を行う。その手順は、以下の通り。
1) Azure Portal上で「SQL データベース」を選択する。
2) Azure Functionsから接続予定のデータベースのリンクを押下する。
3)「クエリ エディター(プレビュー)」メニューを選択後、Microsoft Entra の認証のボタンを押下する。
4) マネージドIDを有効化したAzure FunctionsのFunction名を利用し、クエリ内で「CREATE USER (Function名) FROM EXTERNAL PROVIDER」を実行する。
5) マネージドIDを有効化したAzure Functionsに対し、DBの読み取り権限を設定するため、クエリ内で「ALTER ROLE db_datareader ADD MEMBER (Function名)」を実行する。
6) 作成したスキーマの確認は、「SELECT * FROM sys.database_principals WHERE name = ‘(Function名)’」を実行することで確認できる。
7) 作成したスキーマの権限は、「IS_ROLEMEMBER ( ‘(権限名)’ , ‘(Function名)’ ) 」の設定値で、以下のように確認できる。この値が1の場合は権限あり、0の場合は権限なしとなる。

Azure FunctionsからSQL Databaseへの接続確認
Azure FunctionsからSQL Databaseへの接続確認は、以下の記事の「ローカル環境でのAzure Functions動作検証」に記載のソースコードを修正することで確認できる。

修正後のrequirements.txtの内容は以下の通りで、pyodbcを追加している。
# Do not include azure-functions-worker in this file # The Python Worker is managed by the Azure Functions platform # Manually managing azure-functions-worker may cause unexpected issues azure-functions pyodbc==5.2.0
また、修正後のfunction_app.pyの内容は以下の通りで、SQL DatabaseにマネージドIDを利用して接続し、取得した値をログ出力している。
import azure.functions as func import datetime import json import logging import pyodbc app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) @app.route(route="HttpExample") def HttpExample(req: func.HttpRequest) -> func.HttpResponse: logging.info('Python HTTP trigger function processed a request.') # SQL Database接続情報 DRIVER_NAME = '{ODBC Driver 18 for SQL Server}' SERVER_NAME = 'azure-db-purinit.database.windows.net' DATABASE_NAME = 'azureSqlDatabase' AUTH = 'ActiveDirectoryMsi' # SQL Databaseに接続し、データを取得し表示 conn = pyodbc.connect('DRIVER=' + DRIVER_NAME + ';SERVER=' + SERVER_NAME + ';DATABASE=' + DATABASE_NAME + ';Authentication=' + AUTH + ';Encrypt=yes;') cursor = conn.cursor() cursor.execute('SELECT name, sex FROM dbo.USER_DATA WHERE id = 1') for row in cursor: logging.info(row[0]) logging.info(row[1]) # SQL Database接続後に処理を終了する cursor.close() conn.close() name = req.params.get('name') if not name: try: req_body = req.get_json() except ValueError: pass else: name = req_body.get('name') if name: return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.") else: return func.HttpResponse( "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.", status_code=200 )
さらに、この修正後ソースコードをAzure上にAzure Functionsアプリをデプロイした結果は、以下の通り。
1) ブラウザ上で「https://azurefuncpython.azurewebsites.net/api/httpexample」にアクセスすると、以下のように、function_app.pyで定義していたHttpResponseが返却されることが確認できる。
2) このときのアクセスログを確認すると、以下の赤枠部分に、USER_DATAテーブルから取得した値の表示が確認できる。

なお、Azure Functions動作検証手順は、以下の記事の「Azure上でのAzure Functions動作検証」を参照のこと。

要点まとめ
- Azure Functions上で動作するPythonアプリケーションにおいて、マネージドIDを利用すると、データベースに接続するためのパスワードを使わずに、SQL Databaseに接続することができる。