﻿# 認識 Microsoft Entra ID 中的企業應用程式與服務主體物件

在 Microsoft Entra ID 中，有兩個很重要卻又有點抽象的物件，那就是「企業應用程式」(Enterprise applications) 與「服務主體」(Service Principal) 這兩項，這個應該有困擾我好幾年的時間，一直沒機會可以摸透這兩個物件的關聯性，這次終於在 Microsoft 的技術支援工程師的幫助下釐清了多年以來模糊的觀念，今天這篇文章我就來好好談談這個概念。

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a42dfaefa8428166ace6-ae3d21be-9244-4942-9a00-ad484d4e99b4.webp)

### 基本觀念

首先，你必須知道一件事，`Microsoft Entra ID` 的前身是 `Azure Active Directory`，它本質上是一個 `Identity and Access Management` (IAM) (身分識別與存取管理) 的解決方案，所以在這個目錄中，只要牽涉到「身分識別」(Identity)，就需要給他一個「主體」(Principal)，有了這個「主體」，你才能對這個「主體」進行「存取權管理」(Access Management)。換句話說，就是你在 Azure 進行 IAM 授權設定時，你必須選定一個「目標主體」才能授予權限給他們。

在 Microsoft Entra ID 稍微有點複雜，所有的「主體」物件大概分成以下幾種：

1.  一般主體物件 (Normal identity)
    
    -   使用者 (User) (也可以稱呼為 User Principal)
    -   群組 (Group)
    -   服務帳戶 (Service Principal)
2.  託管主體物件 (Managed identity)
    
    -   系統指派的託管主體 (System-assigned managed identity)
    -   使用者指派的託管主體 (User-assigned managed identity)

> 事先聲明：文章中若提到組織 (Organization)、目錄 (Directory)、租戶 (Tenant)，都是相同的意思，都代表一個 `Microsoft Entra ID` 目錄。

若要開發一個「單租戶」(`Single-teant`)或「多租戶」(`Multi-tenant`)的應用程式，首先你要先在你的 `Microsoft Entra ID` 中使用「應用程式註冊」中建立一個「應用程式」物件，這個應用程式物件為了要在整個目錄中擁有一個「身分識別」(Identity)，因此你在 Azure Portal 建立「應用程式」的時候，系統會自動幫你建立好一個相對應的「服務主體」(Service Principal)。如此一來你才能在 IAM 的管理介面中，找到這個「應用程式」來授權。

> 注意：你授權的對象其實是「服務主體」，而非「應用程式」。但是「服務主體」不能單獨存在，它一定要依附在一個「應用程式」上，所以你在 Azure Portal 上看到的「應用程式」其實就是「服務主體」的意思，因為你不能授權 IAM 給一個「應用程式」，而是「服務主體」。

看到上面的注意事項了嗎？你能說「服務主體」與「應用程式」這兩個東西一樣嗎？從概念上來說，他們是不一樣的！單單從 IAM 授權的角度上來看，他們兩者是分不開的，有依附關係存在。

有趣的地方就是，這兩個東西還真的是可以分開的。你可以利用 `PowerShell` 建立一個「應用程式」，但是不建立「服務主體」，這樣這個「應用程式」就只是一個「應用程式」而已，只能拿來透過 OAuth 2.0 與 Microsoft Entra ID 進行單一簽入與身分驗證，不能存取任何 Azure 資源，也不能進行任何 Azure 資源的 IAM 授權 (你會找不到這個應用程式來授權)。但是如果你建立了「服務主體」，這個「應用程式」就擁有了一個「服務主體」，這個「服務主體」就可以進行 IAM 的授權了。

> 注意：你透過 Azure Portal 建立「應用程式」的時候，系統會一定會自動幫你建立「服務主體」，但是透過 PowerShell 建立「應用程式」的時候，你必須額外建立「服務主體」才會有「服務主體」可以用。

以下是透過 PowerShell 建立「應用程式」與「服務主體」的範例：

```powershell
$app = New-AzADApplication -DisplayName "MyNewApplication"

$passwordCredential = New-Object -TypeName 'Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential'
$passwordCredential.DisplayName = 'Created from PowerShell'
$passwordCredential.EndDateTime = Get-Date "2090-12-31"

# 應用程式密鑰 (Client Secret) 只有當下查的到，之後是不能查的！
$appcred = $app | New-AzADAppCredential -PasswordCredentials $passwordCredential
$appcred.SecretText # 應用程式密鑰

Get-AzADApplication -DisplayName "MyNewApplication"
Get-AzADAppCredential -DisplayName "MyNewApplication"
```

### 什麼是企業應用程式 (Enterprise applications)

先說結論：

> 企業應用程式 (Enterprise applications) 就是一個「服務主體」(Service Principal)！

我記得多年前，微軟就是這樣告訴我的，但是我當時是完全無法理解的。

企業應用程式這個概念是這樣的，當你要整合一個「第三方的應用程式」(例如 Salesforce、ServiceNow、Slack、GitHub、Jira、... 等等) 到你的 Microsoft Entra ID 目錄中，你就需要建立一個「企業應用程式」(Enterprise applications) 物件。事實上，你找不到可以直接建立「企業應用程式」的地方，因為「企業應用程式」這個物件其實是在使用這個「第三方的應用程式」時，在進行 OAuth 2.0 的驗證流程過程中，自動建立的。

你可以在 `Microsoft Entra ID` > `Enterprise applications` > `Consent and permissions` > `User consent settings` 找到如下圖的設定：

![Microsoft Entra ID > Enterprise applications > Consent and permissions > User consent settings](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/2b2b4e328b0f7e8562eb-1df95854-e01a-42b6-94dc-99b314caa4f6.jpg)

你有三個選項可以設定：

1.  `Do not allow user consent`
    
    不允許使用者同意建立企業應用程式，只有管理者可以建立
    
2.  `Allow user consent for apps from verified publishers, for selected permissions` (Recommended)
    
    只有「低度影響」的權限，且來自「已驗證的發行者」的應用程式，或是這個組織中註冊的應用程式，才能允許使用者同意。
    
3.  `Allow user consent for apps`
    
    允許所有使用者同意任何第三方應用程式註冊到 Microsoft Entra ID 之中。
    

你可以在 `Microsoft Entra ID` > `Enterprise applications` > `Consent and permissions` > `Admin consent settings` 找到如下圖的設定：

![Microsoft Entra ID > Enterprise applications > Consent and permissions > Admin consent settings](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/5b0091691fe247facd02-86e2b100-cdf3-4e4e-a7e8-dc549e4c6223.jpg)

建議將 `Admin consent requests` 的 `Users can request admin consent to apps they are unable to consent to` 選項開啟，讓組織內的使用者在在無法同意的應用程式時，可以向管理者請求同意。記得至少選取一個使用者、群組或角色來接收這些請求。

我以 [Microsoft Graph Explorer](https://developer.microsoft.com/en-us/graph/graph-explorer) 為例，這是一個「多租戶」的應用程式，而且是微軟驗證的發行者，所以在 `Allow user consent for apps from verified publishers, for selected permissions` 的設定下，當使用者第一次使用這個應用程式時，會看到如下圖的畫面，你必須填寫一個理由給管理者，說服他們同意放行這個「第三方應用程式」或「企業應用程式」或「服務主體」：

![Approval required](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/3755b96b0279b6caef71-acab1004-6b6a-42ba-8944-fa760e29b5a7.jpg)

管理者則會收到以下郵件：

![Please review the admin consent request for Graph Explorer](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/1759ec21fe5161c2eb29-2eefe3b2-911c-4bd8-9d4b-2b4e0ac3784f.jpg)

然你你可以點擊郵件中的 `Review request` 按鈕，進入 `Admin consent requests` 頁面，這時你可以看到這個請求的詳細資訊，包括請求的應用程式、請求的權限、請求的理由、請求的使用者等等：

![Admin consent requests](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/7d9941e071867ea7168a-e1016147-f790-4954-b356-24b4b01a118e.jpg)

點進去之後，就可以點擊 `Review permission and consent` 進行同意，同意之後就會在 `Enterprise applications` 中看到這個物件了！

![Review permission and consent](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/2550e428e25b97d476c1-12fe22a3-5cd0-4b42-93e4-1edc0d5a4fe1.jpg)

如果你在上述 `User consent settings` 的設定中選擇了 `Allow user consent for apps`，那麼組織內任意「使用者」都可以同意這個「企業應用程式」在使用者所屬的 Microsoft Entra ID 中建立，並且你將會在 `Enterprise applications` 中看到這個物件。然而，這個物件其實並不是一個「應用程式」物件，而是一個「服務主體」物件而已。

我在本文稍早有提到「服務主體」物件是不能單獨存在的，他必須「依附」於某一個「應用程式」物件。沒錯，在「多租戶」的應用程式中，本身他就是一個「應用程式」物件，只是他只會建立在該應用程式所屬的那個目錄下，當你在你想使用這個「多租戶」應用程式時，這個「應用程式」物件就會自動在你所屬的目錄中建立一個「服務主體」物件，這個「服務主體」物件就是你在 `Enterprise applications` 中看到的那個物件。

這時我們回到本小節一開始的結論：

> 企業應用程式 (Enterprise applications) 就是一個「服務主體」(Service Principal)！

沒錯，你就算建立的是「單租戶」的應用程式，你在你的 Microsoft Entra ID 目錄中也可以看到同名的「企業應用程式」物件，但這其實就是一個「服務主體」物件而已。

所以我要再次強調：

> 企業應用程式 (Enterprise applications) 就是一個「服務主體」(Service Principal)！

「企業應用程式」與「服務主體」的關係是這樣的：

-   你看他是一個「應用程式」的時候，他就是一個「企業應用程式」
-   你看他是一個「身分識別」的時候，他就是一個「服務主體」

因為「企業應用程式」來自於外部，另一個 Microsoft Entra ID 目錄下的「應用程式」，而該應用程式會需要讀取到我們這個 Microsoft Entra ID 目錄下的資料，因此會有一些安全議題需要考量。你要是身為一個 Microsoft Entra ID 管理員，你應該會想要保護組織的資料安全，不能讓使用者任意同意這些「企業應用程式」的存取權限。也因此你應該要在 `User consent settings` 中設定 `Do not allow user consent` 或 `Allow user consent for apps from verified publishers, for selected permissions` 才對，這樣就可以避免使用者自行同意這些「企業應用程式」的存取權限。

當這個「企業應用程式」來存取我們 Microsoft Entra ID 目錄下的資料時，到底是誰存取的？那就需要一個「身分」了是吧！此時你就可以稱他為一個「服務主體」了。

換句話說，對「使用者」來講，他們用的是一個「企業應用程式」，這個程式抓了我什麼資料我不管，我只管這個第三方應用程式可以正常執行就好。但是對「管理者」來說，他們看到的是「服務主體」來我的目錄索取資料，這個「企業應用程式」可以信任嗎？不能信任的話，我可以直接從 Enterprise applications 直接將「服務主體」刪除，我的使用者就安全了，因為這個「企業應用程式」再也不能持續向 Microsoft Entra ID 拿資料了。

這樣你明白了嗎？這是我能想到最淺顯的解釋了！這個觀念這麼繞，真的是卡了我數年時間，直到今日我才徹底理解這之間的關係！我真是佩服微軟可以想出這麼複雜的概念，但是這樣的設計確實有其意義，只是很少人可以講清楚！

### 總結

今天這篇文章我們可以很好的理解到三個不同的物件：

1.  應用程式 (Application)
    
    如果你要自行開發應用程式，而且需要跟 Microsoft Entra 進行整合 (單一簽入)，就可以建立一個新的應用程式註冊。
    
    建立好的「應用程式註冊」可以輕鬆透過 OAuth 2.0 與 OIDC 實現單一簽入。
    
    當你想用 `App registraions` > `Expose an API` 建立「應用程式」的「授權範圍」(Scope)，就必須要設定 `Application ID URI` 參數，這個參數就是你的 API 的唯一識別碼，在 `Single-tenant applications` (單租戶應用程式) 通常會設定成 `api://myexample` 格式，只要不跟其他人重複就好，你可以把 `myexample` 改成 GUID 以避免重複，範例如下：
    
    ```powershell
    $app = New-AzADApplication -DisplayName "MyNewApplication" -IdentifierUri "api://myexample"
    ```
    
    > 這裡的 `-IdentifierUri` 參數，隨時都可以在 `Microsoft Entra ID` 的 `App registrations` 中修改。
    
    如果你想要開發 `Multi-tenant applications` (多租戶應用程式) 的話，在建立時 `-IdentifierUri` 參數需要設定為 `https://abc.domain.com`，這樣才能讓其他租戶的使用者進行授權，不過這個域名必須先驗證過，否則會出現以下錯誤訊息：
    
    ```
    Values of identifierUris property must use a verified domain of the organization or its subdomain: 'http://www.example.com'
    ```
    
    這個 Client Secret 金鑰是一個密碼，所以要妥善保管
    
    這個 Client Secret 金鑰是有時效性的，通常是 6 個月，透過 Azure Portal 建立最長是 2 年，透過 PowerShell 建立最長則無限制！
    
    這個 Client Secret 金鑰過期後，就需要重新建立一個新的 Client Secret 金鑰
    
2.  服務主體 (Service Principal)
    
    在 Azure 上面的所有資源，如果要授權一個「服務」來存取資源，就需要一個 Service Principal (服務主體)
    
    Service Principal (服務主體) 是一個安全的身份 (Securable)，可以用來存取資源。就跟 User 或 Group 一樣，都是一個安全的身份。
    
3.  企業應用程式 (Enterprise applications)
    
    針對「多租戶」的第三方應用程式，你需要建立一個「企業應用程式」物件，這個物件其實就是一個「服務主體」物件。
    
    任何想要存取 Microsoft Entra ID 目錄中的資料，都需要一個「服務主體」的身分！
    
    透過 Azure Portal 建立應用程式後，預設會自動建立一個「服務主體」，這個「服務主體」就是你在 `Enterprise applications` 中看到的那個物件。
    

這三個物件之間的關係是這樣的：

-   一個「應用程式」可以建立多組「應用程式密鑰」(Client Secret)，這個密鑰是用來進行 OAuth 2.0 的驗證流程的。
    
    ```powershell
    $app = New-AzADApplication -DisplayName "MyNewApplication"
    
    $passwordCredential = New-Object -TypeName 'Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential'
    $passwordCredential.DisplayName = 'Created from PowerShell'
    $passwordCredential.EndDateTime = Get-Date "2090-12-31"
    
    # 應用程式密鑰 (Client Secret) 只有當下查的到，之後是不能查的！
    $appcred = $app | New-AzADAppCredential -PasswordCredentials $passwordCredential
    $appcred.SecretText # 應用程式密鑰
    
    Get-AzADApplication -DisplayName "MyNewApplication"
    Get-AzADAppCredential -DisplayName "MyNewApplication"
    
    # 取得應用程式與密鑰
    $ap = Get-AzADApplication -DisplayName "MyNewApplication"
    $apCredential = Get-AzADAppCredential -DisplayName "MyNewApplication"
    ```
    
    你可以用 `az login --service-principal` 登入為「服務主體」身分，登入密碼可以是「應用程式註冊」或「服務主體」的任何一組 `Client secrets` 金鑰。
    
    ```bash
    az login --service-principal -u "api://myexample" -p "1Zu8Q~38PLY2eMvldtR544_nZ4NmLLFhCNnoYciD" --tenant "miniasp.onmicrosoft.com"
    ```
    
-   一個「服務主體」可以建立多組「應用程式密鑰」(Client Secret)，但是幾乎沒有人會這麼用，連微軟的人也不知道為什麼有這種設計，他們從來沒看過有人這樣用。
    
    你只能透過 PowerShell 建立「服務主體」的「應用程式密鑰」(Client Secret)，而且在 Azure Portal 是完全看不到的。
    
    ```powershell
    $app = New-AzADApplication -DisplayName "MyNewApplication"
    
    $sp = New-AzADServicePrincipal -AppId $app.AppId
    
    $passwordCredential = New-Object -TypeName 'Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential'
    $passwordCredential.DisplayName = 'Created from PowerShell'
    $passwordCredential.EndDateTime = Get-Date "2090-12-31"
    
    # 密碼只有當下查的到，之後是不能查的！
    $credential = New-AzADServicePrincipalCredential -ObjectId $sp.Id -PasswordCredentials $passwordCredential
    Write-Output $credential
    
    Get-AzADServicePrincipalCredential -DisplayName "MyNewApplication"
    
    # 取得服務主體(企業應用程式)與密鑰
    $sp = Get-AzADServicePrincipal -DisplayName "MyNewApplication"
    $spCredential = Get-AzADServicePrincipalCredential -DisplayName "MyNewApplication"
    ```
    
-   一個「企業應用程式」就是一個「服務主體」
    
    刪除「企業應用程式」的意思就是刪除「服務主體」，此舉會讓組織內所有人都無法再使用該「企業應用程式」，因為失去了「身分識別」將無法存取組織內的任何資料。
    
-   一個「企業應用程式」來自於「應用程式」
    
    對「單租戶」應用程式而言，「應用程式」只會存在於他所屬的目錄下，其目錄也會自動建立一個「企業應用程式」
    
    對「多租戶」應用程式而言，「應用程式」只會存在於他所屬的目錄下，其他目錄要用就會在其目錄中建立一個「企業應用程式」
    
-   一個「應用程式」可以有一個「服務主體」，也可以完全沒有「服務主體」，沒有服務主體的狀況比較少見！
    
    建立「應用程式」不一定要建立「服務主體」
    
    建立「服務主體」就一定要建立「應用程式」
    
    ```powershell
    # 列出應用程式
    Get-AzADApplication -DisplayName 'MyNewApplication'
    
    # 列出企業應用程式或服務主體
    Get-AzADServicePrincipal -DisplayName 'MyNewApplication'
    ```
    

### 詭異的問題整理

我在觀念不清晰的時候，寫了好幾個問題，現在看起來會覺得有點蠢，但是我還是想把這些問題列出來，當作是一個學習的過程，也許有人會有同樣的疑問，我希望這些問題能幫助你理解這個觀念！

Q: 何謂 Enterprise applications？如何使用？

A: 企業應用程式 (Enterprise applications) 是一個「服務主體」(Service Principal)，用來存取自己 Microsoft Entra ID 目錄下的資料。你可以透過使用企業應用程式的過程中，使用 OAuth 2.0 登入並授權，第一次使用的時候需要使用者或管理者同意才能開始用 (看你權限如何設定)，同意後，會在 `Enterprise applications` 中看到這個物件。

Q: Enterprise applications 與 Service principal 到底有何關聯性？

A: 企業應用程式 (Enterprise applications) 就是一個「服務主體」(Service Principal)，全看你用什麼角度看這個東西！基本上在 PowerShell 找不到可以管理「企業應用程式」的 Cmdlet，因為本質上他就是 `Service Principal` 而已。

Q: 建立 Enterprise applications 是否一定要有 Service Principal？

A: 使用者在使用「應用程式」時，就需要有個「服務主體」才能存取資料，這個「服務主體」就是「企業應用程式」。

Q: 整合第三方應用程式的 Enterprise applications 也會自動建立一個 Service Principal 嗎？

A: 是的，一定會建立。但是管理者可以設定權限，可以不讓使用者自行同意這些「企業應用程式」的存取權限。

Q: 如何透過 Azure PowerShell 建立一個 Enterprise applications？好像沒有找到任何 Cmdlet 可用？

A: 沒辦法，因為企業應用程式 (Enterprise applications) 就是一個「服務主體」(Service Principal)，你只能透過建立「應用程式」來建立「服務主體」。如果是多租戶應用程式(第三方應用程式)，第一次使用者會顯示使用者同意頁面，同意後就會自動在你的目錄下建立一個「企業應用程式」。

### 相關連結

-   [Application and service principal objects in Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser&WT.mc_id=DT-MVP-4015686)
-   Azure PowerShell
    -   Application
        -   [Get-AzADApplication](https://learn.microsoft.com/en-us/powershell/module/az.resources/get-azadapplication?view=azps-11.3.0&WT.mc_id=DT-MVP-4015686)
        -   [Get-AzADAppCredential](https://learn.microsoft.com/en-us/powershell/module/az.resources/get-azadappcredential?view=azps-11.3.0&WT.mc_id=DT-MVP-4015686)
        -   [New-AzADApplication](https://learn.microsoft.com/en-us/powershell/module/az.resources/new-azadapplication?view=azps-11.3.0&WT.mc_id=DT-MVP-4015686)
        -   [New-AzADAppCredential](https://learn.microsoft.com/en-us/powershell/module/az.resources/new-azadappcredential?view=azps-11.3.0&WT.mc_id=DT-MVP-4015686)
    -   Service Principal
        -   [Get-AzADServicePrincipal](https://learn.microsoft.com/en-us/powershell/module/az.resources/get-azadserviceprincipal?view=azps-11.3.0&WT.mc_id=DT-MVP-4015686)
        -   [Get-AzADSpCredential](https://learn.microsoft.com/en-us/powershell/module/az.resources/get-azadspcredential?view=azps-11.3.0&WT.mc_id=DT-MVP-4015686)
        -   [New-AzADServicePrincipal](https://learn.microsoft.com/en-us/powershell/module/az.resources/new-azadserviceprincipal?view=azps-11.3.0&WT.mc_id=DT-MVP-4015686)
        -   [New-AzADSpCredential](https://learn.microsoft.com/en-us/powershell/module/az.resources/new-azadspcredential?view=azps-11.3.0&WT.mc_id=DT-MVP-4015686)
