Azure MSSQL 無密碼 Python 連線

在 Azure 上使用 系統指派的受控識別 (System-Assigned Managed Identity) 是最推薦的做法,因為它完全不需要在程式碼裡寫死密碼,安全性最高,且生命週期與你的資源(如 VM 或 App Service)同步。

要實現這個功能,你需要完成三個步驟:啟用身份識別資料庫授權、以及修改 Python 連線字串


第一步:在 Azure 資源上啟用受控識別

在你運行 Python 程式碼的資源(例如 Azure VM, App Service 或 Function)的頁面:

  1. 點選左側選單的 「身分識別」(Identity)
  2. 「系統指派」 頁籤下,將狀態改為 「開啟」 並儲存。
  3. 系統會產生一個 物件識別碼 (Object ID),請記住這個資源的名稱。

上面的截圖來看,你目前是在 「Azure SQL Database」 的介面裡。

受控識別(Managed Identity)不是在資料庫(Database)上開啟的,而是在「跑 Python 程式碼的那個東西」上面開啟的。

請想像:

  • SQL Database 是「銀行」。
  • 你的 Python 程式碼(跑在 VM、App Service 或 Function 上)是「進去辦事的人」。

你現在需要在「辦事的人」身上發一張身分證(啟用受控識別),然後再去「銀行」告訴櫃檯(資料庫)說:「看到拿這張身分證的人就讓他進來」。

接下來你該怎麼做:

1. 找到「發身分證」的地方

請在 Azure Portal 最上方的搜尋框,搜尋你跑 Python 程式碼的資源名稱

  • 如果你是在 Azure VM 跑:去該 VM 的頁面,找左側選單的「身分識別」。
  • 如果你是在 App Service 跑:去該 App Service 的頁面,找左側選單的「身分識別」。
  • 如果你是在 Azure Function 跑:去該 Function 的頁面,找左側選單的「身分識別」。

進入後,你就會看到我上一個回覆說的「系統指派」狀態,將它改為 「開啟」

2. 在資料庫(也就是你截圖的這個地方)授權

當你在上面的資源開啟受控識別後,回到你截圖的這個 SQL Database 介面:

  1. 點擊左側選單的 「查詢編輯器 (預覽)」(Query editor)
  2. 用你的管理員帳號登入。
  3. 執行以下指令(假設你的 VM 或 App Service 名字叫 my-python-app):

SQL

CREATE USER [my-python-app] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [my-python-app];
ALTER ROLE db_datawriter ADD MEMBER [my-python-app];

💡 補充:截圖中的發現

從你的截圖中可以看到,在 「安全性」 區塊下,有一行:

  • 系統指派的身分識別:已啟用

這指的是 「SQL Server 自己」 的身分,通常是用來讓 SQL 去讀取 Key Vault 等其他資源用的。這不是給你的 Python 程式連線用的。 總結: 請去尋找你運行 Python 的那個 Azure 資源(VM / App Service),在那裡開啟身分識別。


了解更多: 利用客戶自控金鑰支援透明資料加密的受控識別
https://learn.microsoft.com/zh-tw/azure/azure-sql/database/transparent-data-encryption-byok-identity?view=azuresql&WT.mc_id=Portal-SqlAzureExtension

▋ 數據安全的大門與那把遺失的鑰匙

在數位世界裡,我們使用「透明資料加密」技術,這就像是在資料庫門口裝了一道自動感應門,資料進出時會自動上鎖或解鎖。傳統做法是讓雲端平台幫你管理這把鑰匙,這對大多數人來說很方便。然而,對於需要極高掌控權的管理者來說,這就像是把身家財產的進出權限完全交給外人,一旦發生資安爭議,你可能無法第一時間主動切換鎖頭。

▋ 拿回屬於你的主權鑰匙

為了解決這個信任缺口,現在有一種「客戶管理金鑰」的機制。簡單來說,這就是讓你自備鎖頭與鑰匙。當你的資料庫要啟動加密功能時,系統不再使用預設的鑰匙,而是去你的私密保險庫中讀取你指定的「身份證明」。這確保了只有經過你授權的特定身份,才能啟動那把開啟資料的鑰匙。

▋ 建立你的專屬數位防線

如果你希望全面掌控數據的安全命運,可以嘗試以下步驟:

  • 在雲端環境中建立一個專屬的「身份識別」,這就像是為你的資料庫申請一張專屬的身分證。
  • 設定嚴格的存取權限,確保只有這個「身份」能接觸到你的加密鑰匙。
  • 定期檢查這把鑰匙的使用紀錄,確保沒有任何未經授權的嘗試。

第二步:在 SQL Database 中授權

SQL Server 預設不認識這個受控識別,你必須進到資料庫裡幫它「開門」:

  1. 使用 Active Directory 管理員 帳號登入 SQL Database(可用 Azure Portal 的 Query Editor)。
  2. 執行以下 SQL 指令(將 <identity-name> 替換為你的 Azure 資源名稱):

SQL

-- 建立資料庫使用者
CREATE USER [<identity-name>] FROM EXTERNAL PROVIDER;

-- 授予讀取/寫入權限 (視需求調整角色)
ALTER ROLE db_datareader ADD MEMBER [<identity-name>];
ALTER ROLE db_datawriter ADD MEMBER [<identity-name>];

如果你是在 Azure App ServiceAzure VM 上啟用的受控識別,該「顯示名稱」通常就是你的 資源名稱

  • 錯誤做法CREATE USER [1234-1234-1234-1234-1234] ... (不可使用 ID)
  • 正確做法CREATE USER [你的AppService名稱] FROM EXTERNAL PROVIDER;

如果你不確定名稱,或者名稱有特殊字元,你可以直接使用 SID (這對應到你看到的物件識別碼) 來建立,這樣最準確:

-- 請將 '1234-1234-1234-1234-1234' 替換為你在身分識別頁面看到的 Object ID
CREATE USER [MyManagedIdentityName] 
WITH OBJECT_ID = '1234-1234-1234-1234-1234';

-- 接著再授權
ALTER ROLE db_datareader ADD MEMBER [MyManagedIdentityName];

第三步:修改 Python 連線字串

使用受控識別時,連線字串不需要 UIDPWD,而是改用 Authentication=ActiveDirectoryMsi

Python

import pyodbc

# 設定基礎參數
server = 'your-server.database.windows.net'
database = 'your-database'
driver = '{ODBC Driver 18 for SQL Server}'

# 系統指派受控識別的連線字串
connection_string = (
    f"Driver={driver};"
    f"Server={server};"
    f"Database={database};"
    "Authentication=ActiveDirectoryMsi;"  # 關鍵設定
    "Encrypt=yes;"
    "TrustServerCertificate=no;"
    "Connection Timeout=30;"
)

def connect_and_query():
    try:
        print(f"Connecting to {server} using Managed Identity...")
        conn = pyodbc.connect(connection_string)
        print("連線成功!")
        return conn
    except Exception as e:
        print(f"連線失敗: {e}")

# 執行連線
conn = connect_and_query()

💡 關鍵筆記:

  • 環境限制Authentication=ActiveDirectoryMsi 只能在 Azure 環境內 運行(例如在 Azure VM 上跑)。如果你在自己的電腦(在地端)跑這段程式碼會報錯,因為地端環境沒有 Managed Identity 憑證。
  • 驅動程式:請確保你的環境中安裝了 ODBC Driver 17 或 18,舊版驅動程式可能不支援 ActiveDirectoryMsi 參數。
  • 優點:即使你更換了密碼,或者這台機器被刪除了,都不需要去改程式碼,這就是「無密碼連線」的強大之處。

執行以下 SQL 指令時遇到 Error:

A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: TCP Provider, error: 0 – An existing connection was forcibly closed by the remote host.)

Failed to execute the query

這個錯誤「An existing connection was forcibly closed by the remote host」發生在 Pre-login handshake (預先登錄握手) 階段,通常不是程式碼邏輯問題,而是 網路層防火牆 的攔截。

這代表你的 Python 程式已經碰到了 SQL Server 的大門,但 SQL Server 因為某些安全規則直接把連線「踢掉」了。

請檢查以下三個最常見的原因:


1. Azure SQL 防火牆未允許你的 IP

這是最常見的原因。即便你使用受控識別,網路還是要通。

  • 如果你在 Azure 資源內執行:請到 SQL Server 的「網路 (Networking)」設定,確保 「允許 Azure 服務和資源存取此伺服器 (Allow Azure services and resources to access this server)」 有被勾選。
  • 如果你在公司內網執行:請確認你的公用 IP 已加入 SQL Server 的防火牆白名單。

2. 強制使用 TLS 1.2

Azure SQL 預設要求 TLS 1.2。如果你的 Python 環境或 ODBC 驅動程式版本太舊,握手時會因為加密協定不匹配而被中斷。

  • 解決方法:確保你的連線字串中有 Encrypt=yes;(你已經有了)。
  • 檢查驅動:確認你安裝的是 ODBC Driver 18 for SQL Server。注意:Driver 18 預設會強制驗證證書,如果你沒有設定憑證,請試著在連線字串加上 TrustServerCertificate=yes; 測試看看。

3. ODBC Driver 18 的「預設加密」改變

如果你是從 Driver 17 升級到 18,Driver 18 的預設行為變更了。請嘗試將連線字串稍微修改如下:

Python

connection_string = (
    f"DRIVER={driver};"
    f"SERVER={server};"
    f"DATABASE={database};"
    "Authentication=ActiveDirectoryMsi;"
    "Encrypt=yes;"
    "TrustServerCertificate=yes;"  # 先暫時改成 yes 測試是否為證書握手失敗
    "Connection Timeout=30;"
)

關鍵排除步驟(Debug Check)

  1. 測試工具連線:你在 Azure Portal 上的 「查詢編輯器 (Query Editor)」 現在能正常進去嗎?
    • 如果連 Query Editor 都進不去,那是資料庫本身的網路/防火牆設定問題。
  2. 確認受控識別是否已成功建立 User
    • 你剛才在嘗試 CREATE USER 時失敗了,這代表受控識別的身分還沒進到資料庫。
    • 重要:如果 CREATE USER 沒成功,Authentication=ActiveDirectoryMsi 就絕對連不上。

目前的卡點建議:

先解決 CREATE USER 的問題。請確認你是用 「Microsoft Entra 管理員」 的帳號登入 Query Editor。只有管理員有權限執行 FROM EXTERNAL PROVIDER 的指令。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *