Microsoft Graph PowerShell(Connect-MgGraph)で証明書サムプリントを指定して接続していたのに、ある日突然「Certificate with thumbprint … was not found in certificate store or has expired」と出て止まる。Azure(Microsoft Entra ID)のアプリ登録では証明書が有効に見えるのに…というときに、最短で原因を切り分けて復旧する手順をまとめます。
発生したエラーと状況
次のような条件で運用しているときに発生しやすいエラーです。
- Microsoft Entra ID(旧 Azure AD)でアプリ登録を作成し、証明書ベースでアプリケーション認証(App-Only)を実施している
- PowerShell で
Connect-MgGraph -ClientId -TenantId -CertificateThumbprintを使って Microsoft Graph に接続している - Azure ポータル上では証明書の有効期限が十分残っている(例:2022/2/16 作成、2032/2/16 まで有効と表示)
- しかし 2025/1/10 以降、次のエラーが発生するようになった
Certificate with thumbprint 'xxxxxxx' was not found in certificate store or has expired.
このメッセージは「期限切れ」だけでなく「見つからない」「秘密鍵が使えない」場合も同じ文言で出ることがあり、Azure 側の有効期限だけを見ていると原因特定が遅れがちです。
結論:原因は「Azure ではなく実行 PC の証明書ストア」にあることが多い
Connect-MgGraph -CertificateThumbprint が参照するのは、Azure ポータルの画面に表示されている“登録済み証明書そのもの”ではありません。実際に署名に使うのは、PowerShell を実行している Windows のローカル証明書ストアに存在する「秘密鍵付き証明書」です。
つまり、Azure 側に証明書(公開鍵)が残っていて有効に見えても、実行環境の証明書ストアから同じ証明書が消えていれば接続できません。典型例は次のとおりです。
- 証明書を作った PC を変更した/スクリプトを別 PC で回すようにした
- 実行ユーザーが変わった(手動実行は自分、タスクは別アカウント、など)
- Windows の再インストール、ユーザープロファイルの作り直し、証明書の整理でローカル証明書が削除された
- 証明書は残っているが秘密鍵がない(.cer だけを入れてしまった)/秘密鍵にアクセス権がない
Azure に登録する証明書と、ローカルに必要な証明書の違い
このエラーを理解するうえで、「Azure に登録されるもの」と「実行 PC に必要なもの」を分けて考えるのがコツです。
| 場所 | 置かれるもの | 役割 | ここがポイント |
|---|---|---|---|
| Microsoft Entra ID(アプリ登録) | 公開鍵(.cer 相当) | 「このアプリはこの公開鍵に対応する秘密鍵を持つはず」という照合情報 | ポータルに表示されるのは“公開鍵”であり、秘密鍵は持たない |
| Windows ローカル(証明書ストア) | 秘密鍵付き証明書(.pfx 相当、HasPrivateKey=True) | 実際にトークン要求へ署名するための鍵 | 秘密鍵がない/読めないと認証できない |
Azure 側は“公開鍵”しか持ちません。実行側(Windows)に“秘密鍵”が存在しない限り、アプリ認証は成立しません。
証明書ストアはどこを見る?CurrentUser と LocalMachine の使い分け
同じ Windows でも、証明書を入れる場所(ストア)を間違えると「手動は成功、タスクは失敗」のような状況になります。運用形態に合わせて選びましょう。
| ストア | パス | 向いている用途 | 注意点 |
|---|---|---|---|
| ユーザー(個人) | Cert:\CurrentUser\My | 自分でログオンして手動実行するスクリプト | 実行ユーザーが変わると見えない(タスク実行で失敗しやすい) |
| コンピューター(ローカル) | Cert:\LocalMachine\My | サーバーでの定期実行、複数ユーザーで共有する処理 | 秘密鍵のアクセス権設定が必要(実行アカウントに読み取り権限を付与) |
まずは「どのアカウントで PowerShell が動いているか」を固定し、そのアカウントから参照できるストアに証明書を入れるのが鉄則です。
切り分けチェック:まずは「本当に証明書があるか」を確認する
チェック項目の早見表
| チェック項目 | 確認方法 | OK の目安 | NG のとき |
|---|---|---|---|
| サムプリントは正しいか(余計な空白がないか) | 変数に入れる前に空白を除去する/目視確認 | 16進数の文字列が一致 | コピー時の空白・改行で不一致 |
| 証明書が実行ユーザーのストアにあるか | certmgr.msc →「個人」→「証明書」 | 同じサムプリントの証明書が存在 | 証明書がない/別ユーザーにしかない |
| 秘密鍵が付いているか | PowerShell で $cert.HasPrivateKey | True | False(.cer だけ) |
| 期限切れではないか | $cert.NotAfter | 現在日時より未来 | 本当に期限切れ |
GUI(certmgr.msc)での確認
- Windows の「ファイル名を指定して実行」で
certmgr.mscを実行します。 - 「個人」→「証明書」を開きます(PowerShell では
Cert:\CurrentUser\My)。 - Azure ポータルに表示されているサムプリントと一致する証明書があるか確認します。
PowerShell での確認コマンド(推奨)
GUI で見える/見えないは、実行アカウントが切り替わった瞬間に変わります。運用では PowerShell で機械的に確認できるようにしておくと安全です。
$thumbprint = "XXXXXXXXXXXX..." # 期待しているサムプリント
$thumbprint = ($thumbprint -replace "\s","").ToUpper()
# CurrentUser と LocalMachine の両方を検索して、どこにあるかを把握する
$stores = @("Cert:\CurrentUser\My","Cert:\LocalMachine\My")
foreach ($store in $stores) {
$cert = Get-ChildItem $store -ErrorAction SilentlyContinue |
Where-Object { $_.Thumbprint -eq $thumbprint } |
Select-Object -First 1
if ($cert) {
Write-Host "FOUND in $store"
$cert | Select-Object Subject, Thumbprint, NotBefore, NotAfter, HasPrivateKey
}
}
ここで何も表示されなければ、エラー原因はほぼ「この実行環境に証明書が存在しない」です。表示されるのに HasPrivateKey が False の場合は、秘密鍵なし(.cer のみ)を疑ってください。
解消手順:ローカル証明書の再作成→Entra ID へアップロード→スクリプト更新
「証明書がローカルにない(または秘密鍵がない)」ケースは、証明書を作り直してアプリ登録へ再登録するのが最短です。以下は自己署名証明書での例です。
新しい自己署名証明書の作成(PowerShell)
次の例は、実行ユーザーの「個人」ストアに秘密鍵付き証明書を作成し、Entra ID にアップロードするための .cer を出力します。運用に合わせてフォルダは変更してください。
$certName = "mygraphcert"
$outDir = "C:\mycerts"
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
# 10年有効の自己署名証明書を作成(例)
$cert = New-SelfSignedCertificate `
-Subject "CN=$certName" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyExportPolicy Exportable `
-KeySpec Signature `
-KeyLength 2048 `
-KeyAlgorithm RSA `
-HashAlgorithm SHA256 `
-NotAfter (Get-Date).AddYears(10)
# Azure(Entra ID)に登録する公開鍵(.cer)をエクスポート
$cerPath = Join-Path $outDir "$certName.cer"
Export-Certificate -Cert $cert -FilePath $cerPath | Out-Null
# 将来の移行・復旧用に秘密鍵付き(.pfx)も控えておくのがおすすめ
$pfxPath = Join-Path $outDir "$certName.pfx"
$pwd = Read-Host "PFX パスワードを入力" -AsSecureString
Export-PfxCertificate -Cert $cert -FilePath $pfxPath -Password $pwd | Out-Null
$cert | Select-Object Subject, Thumbprint, NotAfter, HasPrivateKey
Write-Host "CER: $cerPath"
Write-Host "PFX: $pfxPath"
ポイント:.cer は Entra ID に登録するため、.pfx は「実行環境を変えたときに同じ秘密鍵を持ち込むため」に保管します。.pfx は漏えいすると即アウトなので、アクセス制御された安全な場所に保管してください。
Microsoft Entra ID(アプリ登録)へ新証明書をアップロード
- Azure ポータルで「Microsoft Entra ID」→「アプリ登録」→対象アプリを開きます。
- 「証明書とシークレット」→「証明書」タブを開きます。
mygraphcert.cerをアップロードします。- 一覧に表示された新しい証明書のサムプリントを控えます。
古い証明書が残っている場合、動作確認が終わるまでは削除せず、新旧を併存させるのが安全です(切り替え後に旧証明書を撤去)。
PowerShell スクリプトを新しいサムプリントに更新して接続確認
控えたサムプリントを使って接続します。サムプリントはコピー時に空白が入りやすいので、スクリプト側で空白除去しておくと事故が減ります。
$tenantId = "<テナントID>"
$clientId = "<アプリケーション(クライアント)ID>"
$thumbprint = "<新しい証明書のサムプリント>"
$thumbprint = ($thumbprint -replace "\s","").ToUpper()
Connect-MgGraph -ClientId $clientId -TenantId $tenantId -CertificateThumbprint $thumbprint
# 接続確認(例:既定のカレンダー取得)
Get-MgUserDefaultCalendar -UserId "<対象ユーザーの UPN またはオブジェクトID>"
ここまでで接続できれば、原因はほぼ「ローカル証明書がなかった/秘密鍵がなかった」ことに確定します。
まだ直らないときに見るべき追加ポイント
証明書を作り直しても同じ文言が出る場合、次の “環境依存” が原因になりがちです。
実行ユーザーが違う(CurrentUser の罠)
自己署名証明書を Cert:\CurrentUser\My に作っている場合、その証明書が見えるのは「そのユーザーでログオンしているときだけ」です。よくあるパターンは次のとおりです。
- 手動実行:自分のアカウント → OK
- タスクスケジューラ:別のサービスアカウント → NG(証明書が見えない)
- 管理者として実行:管理者アカウントに切り替わっている → NG
対策は2つです。
- タスクやサービスも含め、同じユーザーコンテキストで実行する
- 証明書を
Cert:\LocalMachine\Myに配置し、実行アカウントに秘密鍵の読み取り権限を付与する
証明書はあるが秘密鍵がない/アクセス権がない
証明書が一覧に見えても、HasPrivateKey が False だったり、秘密鍵のアクセス権が不足していると失敗します。移行時に .cer だけインポートしてしまうと、見た目は“証明書がある”のに認証できません。
サムプリントの不一致(空白・大文字小文字・別証明書)
サムプリントは UI からコピーすると空白が混ざることがあります。また、古い証明書と新しい証明書が併存していると、想定と違う方を参照しているケースもあります。
| よくある症状 | 原因 | 対処 |
|---|---|---|
| 見つからないと言われるが certmgr では見える | サムプリントの空白/改行/別文字 | -replace "\s","" で空白除去し再確認 |
| 見つかるが接続に失敗する | 秘密鍵なし(.cer のみ) | .pfx をインポートする/作り直す |
| 手動は成功、タスクは失敗 | 実行ユーザーが違う | 同一アカウントで実行/LocalMachine へ移す |
| 突然失敗し始めた | 証明書の削除、プロファイル再作成、マシン変更 | 証明書の復元(.pfx)または再作成→再登録 |
運用で詰まらないためのベストプラクティス
証明書は「作ったら終わり」ではなく、バックアップと移行手順までセットにする
今回のようなトラブルは、証明書の有効期限よりも「実行環境が変わった」「ローカルの証明書が消えた」で起きることが多いです。次の運用を最初から組み込むと復旧が速くなります。
- 作成直後に .pfx(秘密鍵付き)を安全に保管する
- “どのサーバー/どのユーザーの証明書ストアに入っているか” を台帳化する
- 環境移行時は .pfx をインポートしてから接続確認する
有効期限の監視をスクリプト化しておく
期限切れは分かりやすいですが、運用だと意外に見落とされます。証明書の期限を定期的にチェックして、余裕をもって更新するだけでもトラブルが減ります。
$thumbprint = "<運用中のサムプリント>"
$thumbprint = ($thumbprint -replace "\s","").ToUpper()
$cert = Get-ChildItem Cert:\CurrentUser\My |
Where-Object { $_.Thumbprint -eq $thumbprint } |
Select-Object -First 1
if ($cert) {
$days = [int]((New-TimeSpan -Start (Get-Date) -End $cert.NotAfter).TotalDays)
$cert | Select-Object Subject, Thumbprint, NotAfter, HasPrivateKey
Write-Host "残り日数: $days 日"
} else {
Write-Host "証明書が見つかりません(ストア消失の可能性)。"
}
「期限切れ」だけでなく「見つからない」を検知できるのがポイントです。
証明書ローテーションは“併存→切り替え→撤去”が安全
いきなり古い証明書を消して新しいものに置き換えると、切り替えタイミングで障害が出やすくなります。おすすめは次の順番です。
- 新しい証明書を作成し、Entra ID のアプリ登録に追加(旧証明書は残す)
- スクリプトを新サムプリントに切り替えて動作確認
- 問題なければ旧証明書を撤去(撤去後も念のためログ監視)
本番運用では自己署名だけに頼らない選択肢も検討する
自己署名証明書は手軽ですが、漏えい対策・更新作業・保管場所の統制まで含めて設計する必要があります。組織の方針によっては、社内 CA 証明書を使う、鍵の保管を厳格化する(例:HSM や Key Vault など)といった選択肢も検討すると安心です。
まとめ
「Certificate with thumbprint … was not found in certificate store or has expired」は、Azure ポータル側の証明書が有効でも発生します。原因の多くは、Connect-MgGraph が参照するローカル証明書ストアに、同じサムプリントの“秘密鍵付き証明書”が存在しないことです。
まずは Cert:\CurrentUser\My(または実行環境に応じて LocalMachine\My)で証明書の有無と HasPrivateKey を確認し、見つからなければ「再作成→Entra ID へアップロード→サムプリント更新」で復旧できます。あわせて .pfx の安全な保管と、実行ユーザーの統一を徹底すると、同種のトラブルを予防できます。

コメント