Exchange Online への PowerShell 接続を非対話的に行う方法

Exchange Online の基本認証無効化および Exchange Online PowerShell V2 Module のリリースに伴い、従来の接続方法からよりセキュアな方法へ見直しが必要になっています。特に夜間バッチなどの自動化システムにおいて、どのように非対話的に Exchange Online へ接続するかを検討する必要があります。

この記事では、非対話的に接続するための考えられる方法と、そのメリット・デメリットを紹介します。

考えられる方法:

  • 従来のまま New-PSSession に -Credential で資格情報を渡す
  • 従来の Exchange Online Remote PowerShell Module の Connect-EXOPSSession に -Credential で資格情報を渡す
  • スクリプト内に埋め込んだパスワードで PSCredential を作成し、Exchange Online PowerShell V2 Module の Connect-ExchangeOnline の -Credential に渡す
  • 事前に XML に保存した PSCredential を読み込んで、Exchange Online Remote PowerShell V2 Module の Connect-ExchangeOnline の -Credential に渡す
  • Exchange Online Remote PowerShell V2 Module の Connect-ExchangeOnline で証明書を使用する

以降ではそれぞれの方法について見ていきます。

従来のまま New-PSSession に -Credential で資格情報を渡す

どんな方法で -Credential に指定する PSCredential を作成しているとしても、基本認証が無効化されるためこの方法を使用し続けることはできません。

従来の Exchange Online Remote PowerShell Module の Connect-EXOPSSession に -Credential で資格情報を渡す

「従来の Exchange Online Remote PowerShell Module」とは、Exchange 管理センターの [ハイブリッド] – [Exchange Online PowerShell Module は、多要素認証をサポートします。Exchange Online をより安全に管理するには、このモジュールをダウンロードしてください。] からダウンロードできるモジュールです。この Exchange Online Remote PowerShell Module はスタート メニューから起動する必要があり、自動化に使用することはサポートされません。

スクリプト内に埋め込んだパスワードで PSCredential を作成し、Exchange Online PowerShell V2 Module の Connect-ExchangeOnline の -Credential に渡す

ここからは Exchange Online PowerShell V2 Module を使用する方法です。Exchange Online PowerShell V2 Module のインストール方法は公開情報を参照してください。

V2 Module になっても UPN とパスワードを基に PSCredential を作成して -Credential パラメータに指定することで、Exchange Online に接続することが可能です。例えば以下のようにします。

$Credential = New-Object PSCredential("admin@contoso.onmicrosoft.com", (ConvertTo-SecureString "pass" -AsPlainText -Force))
Connect-ExchangeOnline -Credential $Credential

この場合、UPN とパスワードだけで認証していますが基本認証ではなく OAuth (先進認証) が裏で使われています。New-PSSession でやっていたことと非常に似ているので、既存のスクリプトの改修は容易です。ただし以下のような問題があります。

  • UPN とパスワードを平文でスクリプトに記述しているため、スクリプトが流出するとアカウントが乗っ取られる
  • 自動化に使用するアカウントに多要素認証を設定できない

リスクを最小化するためには、以下のような方法が考えられます。

  • スクリプトの実行端末にアクセス可能なユーザーを限定して、スクリプト自体の流出を抑止する
  • アカウントをスクリプトの自動化以外の目的には一切使用しないようにし、乗っ取られた場合の被害を最小限にする
  • RBAC を使用して管理可能な内容を制限する

万が一アカウントが乗っ取られた場合は、アカウント自体を削除したり、パスワードを変更したり、多要素認証を設定したりして対処します。ただし、Exchange Online の管理が可能なアカウントが乗っ取られた状況であるため、他のアカウントに細工が行われていたり、もうすでに他のアカウントも乗っ取られている可能性があります。

事前に XML に保存した PSCredential を読み込んで、Exchange Online Remote PowerShell V2 Module の Connect-ExchangeOnline の -Credential に渡す

PSCredential は以下のように Export-CliXml と Import-CliXml コマンドを使用することでファイルへの保存と読み込みができます。保存したファイルは、同じ端末に同じユーザーでサインインしなければ、読み込むことはできません。

$Credential = New-Object PSCredential("admin@contoso.onmicrosoft.com", (ConvertTo-SecureString "pass" -AsPlainText -Force))
$Credential | Export-CliXml c:\temp\credential.xml
$Credential = Import-CliXml c:\temp\credential.xml

これにより、パスワードをスクリプトに埋め込むよりも安全にパスワードを保管することができます。ただし以下のような問題があります。

  • 端末が乗っ取られれば、Exchange Online への接続アカウントも乗っ取られる
  • 自動化に使用するアカウントに多要素認証を設定できない

そのため、パスワードをスクリプトに埋め込むときと同じように少しでもリスクを最小化するように対策が必要です。

Exchange Online Remote PowerShell V2 Module の Connect-ExchangeOnline で証明書を使用する

Exchange Online PowerShell V2 Module のバージョン 2.0.3 では、ユーザー権限ではなくアプリケーションの権限を使用して Exchange Online へ接続できるようになりました。非対話的に Exchange Online へ接続するシナリオで最も推奨される方法がこの方法です。公開情報も用意されています。

この機能を利用するには、事前に Azure AD へのアプリの登録と、証明書の発行が必要です。証明書は自己署名証明書で問題ありません。ユーザー アカウントを使用する必要がなくなりますので、アカウントが乗っ取られる心配はありません。ユーザーに多要素認証をかけても問題ありません。ただし秘密鍵を含む証明書が流出した場合は「アプリケーションのパスワードが流出した」と考えることができ、第三者に Exchange Online に接続される可能性があります。

この方法を使用することのデメリットとしては、RBAC によるきめ細やかな実行コマンドの制御が困難になります。この接続方法では Exchange 管理者やヘルプデスク管理者など、Azure AD (Microsoft 365) のビルトインの管理者の役割をアプリに割り当てて使用する必要があります。

証明書を読み込む方法は現在は 3 つ提供されています。1 つ目が PFX ファイルを読み込む方法です。PFX ファイルを読み込む方法の場合、PFX ファイルの管理を厳重にする必要があります。PFX ファイル自体をパスワード保護することも可能ですが、パスワードをスクリプトに記述する必要が生じます。もしくはパスワードを SecureString にしてファイルに保存しておくなど、何らかのセキュアな方法を使用する必要があります。

2 つ目は証明書の拇印を指定して証明書ストアから読み込む方法です。この場合、証明書はユーザーのストアに保管されている必要があります。

3 つ目は .NET Framework の X509Certificate2 オブジェクトを指定する方法です。何らかの別の仕組みを経由して X509Certificate2 オブジェクトを取得できる場合はこの方法を使用します。

いずれの場合も Azure AD へのアプリケーションの登録がまずは必要です。以下に手順を紹介します。

  1. Azure Portal にサインインします。
  2. [Azure Active Directory] – [アプリの登録] – [新規登録] をクリックします。
  3. [名前] に任意のアプリの名前を入力します。(例 : App01)
  4. [サポートされているアカウントの種類] から [この組織ディレクトリのみに含まれるアカウント] を選択します。
  5. [リダイレクト URI] で [Web] を選択し、任意の URI を入力します。(例 : https://localhost/App01)
  6. [登録] をクリックします。
  7. 表示された [アプリケーション (クライアント) ID] の値を控えておきます。
  8. [管理] – [マニフェスト] をクリックします。
  9. マニフェスト内の “requiredResourceAccess” を探して、以下のようになるように内容を差し替えます。
	"requiredResourceAccess": [
        {
            "resourceAppId": "00000002-0000-0ff1-ce00-000000000000",
            "resourceAccess": [
                {
                    "id": "dc50a0fb-09a3-484d-be87-e023b12c6440",
                    "type": "Role"
                }
            ]
        }
	],
  1. [保存] をクリックします。
  2. [管理] – [API のアクセス許可] をクリックします。
  3. 一覧の中 [Exchange.ManageAsApp] が含まれていることを確認します。
  4. [<テナント名> に管理者の同意を与えます] をクリックします。
  5. 確認が表示されるので [はい] をクリックします。

次に証明書を発行します。

  1. Windows PowerShell を起動します。
  2. 以下のようにコマンドを実行します。証明書が発行され、cer ファイルと pfx ファイルが出力され、コンソール上に証明書の Thumbprint が表示されます。
$mycert = New-SelfSignedCertificate -Subject "<アプリの名前>" -CertStoreLocation "Cert:\CurrentUser\My" -NotAfter (Get-Date).AddYears(1) -KeySpec KeyExchange
$mycert | Export-Certificate -FilePath <エクスポート先のファイル名>
$mycert | Export-PfxCertificate -FilePath <エクスポート先のファイル名> -Password $(ConvertTo-SecureString -String "<エクスポートした PFX ファイルを保護する任意のパスワード>" -Force -AsPlainText)
$mycert | Select Thumbprint

実行例)

$mycert = New-SelfSignedCertificate -Subject "App01" -CertStoreLocation "Cert:\CurrentUser\My" -NotAfter (Get-Date).AddYears(1) -KeySpec KeyExchange
$mycert | Export-Certificate -FilePath c:\temp\mycert.cer
$mycert | Export-PfxCertificate -FilePath c:\temp\mycert.pfx -Password $(ConvertTo-SecureString -String "1234" -Force -AsPlainText)
$mycert | Select Thumbprint
  1. Azure Portal で先ほど登録したアプリのページに戻ります。
  2. [管理] – [証明書とシークレット] をクリックします。
  3. [証明書のアップロード] をクリックします。
  4. 先ほど出力された CERファイルを選択して [追加] をクリックします。
  5. Azure Portal で [Active Directory] – [管理] – [ロールと管理者] をクリックします。
  6. [Exchange 管理者] をクリックします。
  7. [割り当ての追加] をクリックします。
  8. (PIM を使用していない環境の場合) 登録したアプリの名前で検索して選択し、[追加] をクリックします。
  9. (PIM を使用している環境の場合) [メンバーの選択] – [メンバーが選択されていない] (もしくは [xxx 選択済み]) をクリックします。
  10. (PIM を使用している環境の場合) 登録したアプリの名前で検索して選択し、[選択] をクリックします。
  11. (PIM を使用している環境の場合) [次へ] をクリックします。
  12. (PIM を使用している環境の場合) [割り当ての種類] から [アクティブ] を選択し、必要に応じて期間や割り当ての理由を入力して [割り当て] をクリックします。

以上で準備は完了です。

PFX ファイルを読み込んで接続するには以下のようにコマンドを実行します。

Connect-ExchangeOnline -CertificateFilePath "<PFX ファイルのパス>" -CertificatePassword (ConvertTo-SecureString -String "<PFX ファイルのパスワード>" -AsPlainText -Force) -AppID "<登録したアプリのアプリケーション ID>" -Organization "<onmicrosoft ドメイン名>"

実行例)

Connect-ExchangeOnline -CertificateFilePath "c:\temp\cert.pfx" -CertificatePassword (ConvertTo-SecureString -String "pass" -AsPlainText -Force) -AppID "3dc79906-a7ff-4493-b556-407945d80662" -Organization "contoso.onmicrosoft.com"

証明書の拇印を指定して証明書ストアから読み込んで接続するには以下のようにコマンドを実行します。

Connect-ExchangeOnline -CertificateThumbPrint "<証明書の拇印>" -AppID "<登録したアプリのアプリケーション ID>" -Organization "<onmicrosoft ドメイン名>"

実行例)

Connect-ExchangeOnline -CertificateThumbPrint "1844DDF33847CB699C6CAEF5DE9DF382380E127D" -AppID "3dc79906-a7ff-4493-b556-407945d80662" -Organization "contoso.onmicrosoft.com"

X509Certificate2 オブジェクトを指定して接続するには以下のようにコマンドを実行します。この例では証明書ストアから証明書を読み込んでいます。

Connect-ExchangeOnline -Certificate:(Get-ChildItem -Path Cert:\CurrentUser\My\<証明書の拇印>) -AppID "<発行したアプリのアプリケーション ID>" -Organization "<onmicrosoft ドメイン名>"

実行例)

Connect-ExchangeOnline -Certificate:(Get-ChildItem -Path Cert:\CurrentUser\My\1844DDF33847CB699C6CAEF5DE9DF382380E127D) -AppID "3dc79906-a7ff-4493-b556-407945d80662" -Organization "contoso.onmicrosoft.com"