【GraphAPI】Graph PowerShell (3)：認証 (アプリケーション許可権限)

# はじめに
Graph PowerShell を利用すると、ユーザー委任権限、アプリケーション許可権限のどちらの方法でも Graph API を実行することができます。

この記事では、Graph PowerShell からアプリケーション許可権限で Graph API を実行する方法をまとめました。

※ Graph PowerShell のインストール方法については以下の記事にあります。

https://qiita.com/atsmin77/private/264d10bd9d7e9dfeaace

※ ユーザー委任権限、アプリケーション許可権限の違いについては以下の記事にあります。

https://qiita.com/atsmin77/items/31df2f5624840b45dda0

# アプリケーション許可権限で実行する方法

Graph PowerShell をアプリケーション許可権限で実行するためには、以下の手順が必要です。

1. Entra ID へのアプリケーションの登録
   Entra 管理センターからアプリケーションを登録し、必要なアクセス許可を追加します
1. Connect-MgGraph コマンドの実行
   登録したアプリケーションの情報を使って認証します

::: note
Entr ID 管理センターからのアプリケーションの登録方法とアクセス許可の追加方法については、以下のアプリケーション許可権限の記事で紹介しています。
:::

* Entra ID アプリケーションの登録方法
  (1.1 Entra ID へのアプリケーションの登録)
  
https://qiita.com/atsmin77/items/a31198368c4a570b801a#11-entra-id-%E3%81%B8%E3%81%AE%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E7%99%BB%E9%8C%B2

* Entra ID アプリケーションへのアクセス許可の追加方法
  (2.1.3 アクセス許可の追加方法)
  
https://qiita.com/atsmin77/items/b4ab7c00ce1bb8544f90#213-%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E8%A8%B1%E5%8F%AF%E3%81%AE%E8%BF%BD%E5%8A%A0%E6%96%B9%E6%B3%95

## 認証方法

アプリケーション許可権限で Connect-MgGraph コマンドを実行する際の認証方法として、以下のような方法を利用できます。

* クライアントシークレット
* 証明書
* マネージド ID

### クライアント シークレット

クライアント シークレットは Entra ID アプリケーションに登録したシークレット (パスワード) を使用して認証する方法です。

(参考：Use client secret credentials)
https://learn.microsoft.com/ja-jp/powershell/microsoftgraph/authentication-commands?view=graph-powershell-1.0#use-client-secret-credentials

[ユーザー委任権限の場合](https://qiita.com/atsmin77/private/09162cffd26618c8083f#%E4%BE%8B1-%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E7%99%BB%E9%8C%B2%E3%81%A8%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E8%A8%B1%E5%8F%AF%E3%81%AE%E8%BF%BD%E5%8A%A0) と同じように、以下の処理を Graph PowerShell で行うスクリプトの例を作ってみました。

1. Entra ID アプリケーションの登録
1. アプリケーションへのクライアント シークレットの追加
1. アクセス許可の追加
1. クライアントシークレットを使用して Connect-MgGraph で認証

::: note
Entra ID アプリケーションの登録は、**New-MgApplication** コマンドで行います。
アプリケーション許可権限のアクセス許可は **New-MgServicePrincipalAppRoleAssignment** コマンドに追加するアクセス許可の ID を指定します。アクセス許可の ID は **Find-MgGraphPermission** コマンドで取得することができます。
:::

```powershell
# 登録するアプリケーションの名前に置き換えてください
$AppName = "Graph PowerShell アプリケーション許可権限テスト"
# アプリケーションに追加するアクセス許可にカンマ区切りで置き換えてください
$Permissions = "User.Read.All", "Team.Create"

# サインインダイアログが表示されたら、管理者ユーザーでサインインしてください
Connect-MgGraph -Scopes "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All"
$TenantId = (Get-MgContext).TenantId

# クライアント シークレットを追加して、アプリケーションを作成します
$AppRegistration = New-MgApplication -DisplayName $AppName -PasswordCredentials @{DisplayName = "secret"}
$ClientId = $AppRegistration.AppId
$ClientSecret = $AppRegistration.PasswordCredentials.SecretText

# サービスプリンシパルを作成します
$ServicePrincipal = New-MgServicePrincipal -AppId $ClientId
$GraphResourceId = (Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'").Id
# アプリケーションに追加するアクセス許可の ID を Find-MgGraphPermission コマンドで取得します
$findPermissionId = {(Find-MgGraphPermission -PermissionType application -ExactMatch $_).Id}
# サービスプリンシパルにアクセス許可を追加します
$Permissions | % $findPermissionId | % {New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $GraphResourceId -PrincipalId $ServicePrincipal.Id -ResourceId $GraphResourceId -AppRoleId $_}

# 作成したアプリケーションの情報を表示します
echo @"
ClientId: $ClientId
TenantId: $TenantId
ClientSecret: $ClientSecret
"@

# Connect-MgGraph を実行して、クライアント シークレットで認証します
$SecureClientSecret = ConvertTo-SecureString -String $ClientSecret -AsPlainText -Force
$ClientSecretCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ClientId, $SecureClientSecret
Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $ClientSecretCredential

# アクセス許可が反映されるまで少し待ちます
while ((Get-MgContext).Scopes.Count -ne $Permissions.Count) {
	Disconnect-MgGraph | Out-Null
	Sleep 1
	Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $ClientSecretCredential | Out-Null
}
Get-MgContext
```

PowerShell へコピー & ペーストして実行すると、登録したアプリケーションの情報とクライアント シークレットを使用して、アプリケーション許可権限で認証されます。

※ 実行結果の例
```
ClientId               : 8d1c7020-9e7e-4666-b19f-a6e0ba03f3e6
TenantId               : ac2852a6-88d6-4ddc-9a63-93391e191144
Scopes                 : {Team.Create, User.Read.All}
AuthType               : AppOnly
TokenCredentialType    : ClientSecret
CertificateThumbprint  :
CertificateSubjectName :
SendCertificateChain   : False
Account                :
AppName                : Graph PowerShell アプリケーション許可権限テスト
ContextScope           : Process
Certificate            :
PSHostVersion          : 5.1.26100.4061
ManagedIdentityId      :
ClientSecret           : System.Security.SecureString
Environment            : Global
```

(参考：Using Find-MgGraphPermission cmdlet)
https://learn.microsoft.com/en-us/powershell/microsoftgraph/find-mg-graph-permission?view=graph-powershell-1.0

(参考：Use app-only authentication with the Microsoft Graph PowerShell SDK)
https://learn.microsoft.com/en-us/powershell/microsoftgraph/app-only?view=graph-powershell-1.0

(参考：Grant and revoke API permissions in Microsoft Entra ID)
https://learn.microsoft.com/en-us/powershell/microsoftgraph/how-to-grant-revoke-api-permissions?view=graph-powershell-1.0&pivots=grant-delegated-permissions

### 証明書

証明書を使用して認証することもできます。自己署名証明書も使用できます。

::: note
コマンドを実行する端末にインストールされた証明書によって認証を行います。
クライアントシークレットのような認証情報をスクリプト内に記載する必要が無いため、より安全に認証することができます。
:::

※ 自己署名証明書については @carol0226 さんの以下の記事で詳しく紹介されています。

https://qiita.com/carol0226/items/8663842e2c7422de7e55

(参考：自己署名公開証明書を作成してアプリケーションを認証する)
https://learn.microsoft.com/ja-jp/entra/identity-platform/howto-create-self-signed-certificate

以下は自己署名証明書を作成して、アプリケーションへの登録と認証を行うスクリプトの例です。

::: note
自己署名証明書の作成と端末へのインストールは **New-SelfSignedCertificate** コマンドで行います。
作成した証明書の情報を **New-MgApplication** コマンドの "-KeyCredentials" で指定してアプリケーションに追加しています。
:::

```powershell
# 登録するアプリケーションの名前に置き換えてください
$AppName = "Graph PowerShell アプリケーション許可権限テスト"
# アプリケーションに追加するアクセス許可にカンマ区切りで置き換えてください
$Permissions = "User.Read.All", "Team.Create"
# 認証で使用する自己署名証明書の名前に置き換えてください
$CertName = "テスト自己証明書" 

# 証明書を作成します
$Cert = New-SelfSignedCertificate -Subject "CN=$CertName" -CertStoreLocation "Cert:\CurrentUser\My" -KeyExportPolicy Exportable -KeySpec Signature -KeyLength 2048 -KeyAlgorithm RSA -HashAlgorithm SHA256

# サインインダイアログが表示されたら、管理者ユーザーでサインインしてください
Connect-MgGraph -Scopes "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All"
$TenantId = (Get-MgContext).TenantId 

# 証明書を追加して、アプリケーションを作成します
$AppRegistration = New-MgApplication -DisplayName $AppName -KeyCredentials @(@{ Type="AsymmetricX509Cert"; Usage="Verify"; Key=$Cert.RawData })
$ClientId = $AppRegistration.AppId

# サービスプリンシパルを作成します
$ServicePrincipal = New-MgServicePrincipal -AppId $ClientId
$GraphResourceId = (Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'").Id
# アプリケーションに追加するアクセス許可の ID を Find-MgGraphPermission コマンドで取得します
$findPermissionId = {(Find-MgGraphPermission -PermissionType application -ExactMatch $_).Id}
# サービスプリンシパルにアクセス許可を追加します
$Permissions | % $findPermissionId | % {New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $GraphResourceId -PrincipalId $ServicePrincipal.Id -ResourceId $GraphResourceId -AppRoleId $_}

# 作成したアプリケーションの情報と証明書の拇印を表示します
echo @"
ClientId: $ClientId
TenantId: $TenantId
CertificateThumbprint: $($Cert.Thumbprint)
"@

# Connect-MgGraph を実行して、証明書で認証します
Connect-MgGraph -ClientId $ClientId -TenantId $TenantId -CertificateThumbprint $Cert.Thumbprint

# アクセス許可が反映されるまで少し待ちます
while ((Get-MgContext).Scopes.Count -ne $Permissions.Count) {
	Disconnect-MgGraph | Out-Null
	Sleep 1
	Connect-MgGraph -ClientId $ClientId -TenantId $TenantId -CertificateThumbprint $Cert.Thumbprint | Out-Null
}
Get-MgContext
```

Connect-MgGraph コマンドでは、以下のように **"-CertificateThumbprint"** に認証で使用する証明書の拇印を指定します。

■ 書式

Connect-MgGraph -ClientId "<クライアント ID>" -TenantId "<テナント ID>" -CertificateThumbprint "<証明書の拇印>"

::: note
証明書の拇印は証明書の ID のようなものです。Entra 管理センターでアプリケーションの **[証明書とシークレット] - [証明書]** タブからも登録した証明書の拇印を確認することができます。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/13871f99-0a80-4f8c-8a42-b0e4f749b241.png)

### マネージド ID

Azure の仮想マシンなどリソースから Graph API を実行する際には、マネージド ID による認証も利用できます。

Entra ID アプリケーションの場合は、クライアントシークレットや証明書の情報によって、接続元が Entra ID に登録されたアプリケーションであることを認証する必要があります。

仮想マシンや Logic Apps といった Azure 上のリソースの場合は、接続元のリソースを Azure の仕組みで認証することができます。

::: note
"認証" では接続元の身元 (ID: Identity) を検証します。マネージド ID は Azure 上で管理されている (マネージド) ID によって、接続元を検証することができる仕組みです。
認証は Azure 上で自動的に行われるので、シークレットや証明書といった認証情報が不要になります。
:::

※ システム割り当てマネージド ID を有効にしている Azure 仮想マシンの例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/a9eeb010-5af0-4969-a53d-f6e74499297b.png)

(参考：Azure リソースのマネージド ID とは)
https://learn.microsoft.com/ja-jp/entra/identity/managed-identities-azure-resources/overview

マネージド ID では、Entra ID アプリケーションではなく Azure 上の特定のリソースに対してアクセス許可を付与します。

以下はマネージ ID へのアクセス許可の追加と認証を行うスクリプトの例です。

::: note
マネージド ID による認証では認証情報が不要となるため、**Connect-MgGraph -Identity** とコマンドを実行するだけで認証できます。
:::

```powershell
# マネージド ID の名前に置き換えてください
# システム割り当ての場合はリソースの名前、ユーザー割り当ての場合はマネージド ID の名前になります
$ManagedIdName = "vm1"
# マネージド ID に追加するアクセス許可にカンマ区切りで置き換えてください
$Permissions = "User.Read.All", "Team.Create"

# サインインダイアログが表示されたら、管理者ユーザーでサインインしてください
Connect-MgGraph -Scopes "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All"

# マネージド ID のサービスプリンシパルを取得します
$ServicePrincipalId = $(Get-MgServicePrincipal -Filter "displayName eq '$ManagedIdName'").Id
$GraphResourceId = (Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'").Id
# アプリケーションに追加するアクセス許可の ID を Find-MgGraphPermission コマンドで取得します
$findPermissionId = {(Find-MgGraphPermission -PermissionType application -ExactMatch $_).Id}
# サービスプリンシパルにアクセス許可を追加します
$Permissions | % $findPermissionId | % {New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $GraphResourceId -PrincipalId $ServicePrincipalId -ResourceId $GraphResourceId -AppRoleId $_}

# 以下は Azure の仮想マシン上で実行します
# Connect-MgGraph を実行して、マネージド ID で認証します
Connect-MgGraph -Identity
# ユーザー割り当ての場合は、マネージド ID の ClientId を指定して実行してください
# Connect-MgGraph -Identity -ClientId "c1ff4887-5574-40ad-803d-4875d110e14e"
Get-MgContext
```

※ 実行結果の例

```
ClientId               : c8f20ea9-30aa-421d-9928-be04bafd2ec1
TenantId               : ac2852a6-88d6-4ddc-9a63-93391e191144
Scopes                 : {Team.Create, User.Read.All}
AuthType               : ManagedIdentity
TokenCredentialType    : ManagedIdentity
CertificateThumbprint  :
CertificateSubjectName :
SendCertificateChain   : False
Account                :
AppName                : vm1
ContextScope           : Process
Certificate            :
PSHostVersion          : 5.1.17763.7309
ManagedIdentityId      : MSI@50342
ClientSecret           :
Environment            : Global
```

::: note warn
マネージ ID へアクセス許可を付与した後、反映されるまでにはしばらく時間が掛かるようです。試してみたところコマンドを実行した翌朝には反映されました。
:::

(参考：Use managed identity)
https://learn.microsoft.com/ja-jp/powershell/microsoftgraph/authentication-commands?view=graph-powershell-1.0#use-managed-identity


## 補足

### サービス プリンシパルについて

Entra ID アプリケーションやマネージド ID のアクセス許可は、対応するサービス プリンシパルで制御されます。この記事のスクリプトの例でも、サービス プリンシパルを作成してアクセス許可を追加しています。

::: note
Entra 管理センターからアプリケーションを登録すると、自動的にサービス プリンシパルも作成されます。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/dccf53be-7ab3-468c-8b5b-e3a74bfe1bcc.png)

Entra 管理センターから、以下の手順でサービス プリンシパルに付与されているアクセス許可を確認することができます。

1. Entra 管理センターで **[アプリケーション] - [エンタープライズアプリケーション] - [すべてのアプリケーション]** をクリックします
   **[アプリケーションの種類]** のフィルタを **"×"** でクリアします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/586a8540-f95c-4aed-84ff-ebae423ebd75.png)
1. 検索ボックスにアプリケーションの名前やマネージドIDの場合は Azure のリソース名を入力します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/02e9bc11-673b-4dd9-b112-dbf2170c37a4.png)
1. **[アクセス許可]** をクリックすると、サービスプリンシパルに付与されているアクセス許可を確認できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/00c3d22d-adde-4c85-80c3-b4441290d516.png)

(参考：Microsoft Entra ID のアプリケーションとサービス プリンシパル オブジェクト)
https://learn.microsoft.com/ja-jp/entra/identity-platform/app-objects-and-service-principals?tabs=browser

### アプリケーションの一括削除

この記事のスクリプトを実行すると、その度に同じ名前のアプリケーションが作成されます。作成されたアプリケーションは以下のコマンドで一括削除することができます。

```powershell
# 一括削除するアプリケーションの名前を指定してください
$ApplicationName = "Graph PowerShell アプリケーション許可権限テスト"

# サインインダイアログが表示されたら、管理者ユーザーでサインインしてください
Connect-MgGraph -Scopes "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All"
# 削除対象のアプリケーションの情報を取得します
Get-MgApplication -Filter "DisplayName eq '$ApplicationName'"  | tee -Variable apps
# アプリケーションを一括削除します
$apps | % { Remove-MgApplication -ApplicationId $_.Id }
# アプリケーションが削除されたことを確認します
Get-MgApplication -Filter "DisplayName eq '$ApplicationName'" 
# アプリケーションを削除するとサービスプリンシパルも削除されます
Get-MgServicePrincipal -Filter "DisplayName eq '$ApplicationName'" 
```
::: note
開発用のテナントで Graph API を検証する時は、この記事のスクリプトのようにその都度アプリケーションを作成して認証まで行うと、クライアント ID 等の認証情報を確認する必要が無いので便利です。
:::

# 関連記事

Connect-MgGraph コマンドによる認証が完了すると、Graph PowerShell から Graph API を実行することができます。

Graph API の実行方法については以下の記事で紹介しています。

https://qiita.com/atsmin77/private/933b9a08791aed639d38

ユーザー委任権限で認証する方法については以下の記事で紹介しています。

https://qiita.com/atsmin77/private/09162cffd26618c8083f