ASP.NET Core を IIS で動かすと SQL Server Express だけ「IIS だと失敗・コマンドだと成功」という落差に悩まされがちです。原因はネットワークではなく“実行アカウントの違い”。本稿では GUI(SSMS)なし、sqlcmdだけでアプリケーション プール ID に必要権限を付与し、安定接続できるまでを一気通貫で解説します。
IIS から SQL Server Express に接続できない問題
質問の前提と典型的な現象
| 項目 | 状況 |
|---|---|
| OS / ミドルウェア | Windows Server 2022 / IIS / SQL Server Express |
| アプリ | ASP.NET Core 9.0 |
| CLI 実行 | dotnet MyP.dll では接続成功 |
| IIS 実行 | SQL 認証エラー(ログイン失敗、または DB へのマッピングなし) |
なぜ CLI は成功し、IIS は失敗するのか(原因の整理)
同じコードでも、実行主体(Windows アカウント)が異なれば SQL Server が認可する主体も変わります。
| 実行コンテキスト | Windows アカウント | SQL Server に見える主体 |
|---|---|---|
| コマンドライン(手動) | サインイン中の自分のアカウント | DOMAIN\YourName 等 |
| IIS(アプリケーション プール) | アプリケーション プールの ID | IIS APPPOOL\<AppPool名>(既定: ApplicationPoolIdentity) |
初期状態では IIS APPPOOL\<AppPool名> は SQL Server に登録されておらず、データベース ユーザーでもないため、ログインできない/DB に入れないのが失敗の本質です。
結論(先にやることの全体像)
- アプリプールの ID を SQL Server のサーバー ログインとして作成する。
- 対象データベースにユーザーを作成してログインと関連付ける。
- 最低限のロール(読み書き中心なら
db_datareaderとdb_datawriter)または用途に合うカスタム ロールを付与する。 - 接続文字列が Windows 認証(統合認証)であることを確認する。
sqlcmd だけで完結:権限付与の手順
前提の確認(全部コマンドでOK)
- アプリプール名の確認(GUIを使わず)
C:\Windows\System32\inetsrv\appcmd list apppool /text:"name,processModel.identityType"ApplicationPoolIdentityで動かしている場合、Windows 主体名はIIS APPPOOL\<AppPool名>になります。 - SQL Server インスタンス名(Express 既定は
.\SQLEXPRESS) - 実行アカウントの権限:これから実行する
sqlcmdの主体は SQL Server のsysadminである必要があります(インストール時に指定した管理者等)。確認は以下。sqlcmd -S .\SQLEXPRESS -E -Q "SELECT IS_SRVROLEMEMBER('sysadmin') AS is_sysadmin;"
ステップ 1:アプリプール ID をサーバー ログインとして登録
sqlcmd -S .\SQLEXPRESS -E -Q "CREATE LOGIN [IIS APPPOOL\<YourAppPoolName>] FROM WINDOWS;"
既に存在する場合はエラーになりますが無害です(後述の「よくあるエラーと対処」を参照)。
ステップ 2:データベース ユーザーを作成し、ログインと関連付け
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "CREATE USER [IIS APPPOOL\<YourAppPoolName>] FOR LOGIN [IIS APPPOOL\<YourAppPoolName>];"
ステップ 3:最低限のロールを付与(読み書き中心)
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "ALTER ROLE db_datareader ADD MEMBER [IIS APPPOOL\<YourAppPoolName>];"
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "ALTER ROLE db_datawriter ADD MEMBER [IIS APPPOOL\<YourAppPoolName>];"
ストアド実行が必要な場合は必要最小限で付与します。
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "GRANT EXECUTE TO [IIS APPPOOL\<YourAppPoolName>];"
ステップ 4:作成結果を確認
sqlcmd -S .\SQLEXPRESS -E -Q "SELECT name,type_desc FROM sys.server_principals WHERE name=N'IIS APPPOOL\<YourAppPoolName>';"
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "SELECT name,type_desc FROM sys.database_principals WHERE name=N'IIS APPPOOL\<YourAppPoolName>';"
ワンショットで安全に実行(冪等化スクリプト)
初回でも再実行でも安全に流せる T‑SQL を sqlcmd 経由で投下します。
set APPPOOL=<YourAppPoolName>
set DB=<YourDatabase>
sqlcmd -S .\SQLEXPRESS -E -Q "IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'IIS APPPOOL%APPPOOL%') CREATE LOGIN [IIS APPPOOL%APPPOOL%] FROM WINDOWS;"
sqlcmd -S .\SQLEXPRESS -E -d %DB% -Q "IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = N'IIS APPPOOL%APPPOOL%') CREATE USER [IIS APPPOOL%APPPOOL%] FOR LOGIN [IIS APPPOOL%APPPOOL%];"
sqlcmd -S .\SQLEXPRESS -E -d %DB% -Q "IF NOT EXISTS (SELECT 1 FROM sys.database_role_members m JOIN sys.database_principals r ON m.role_principal_id=r.principal_id JOIN sys.database_principals u ON m.member_principal_id=u.principal_id WHERE r.name='db_datareader' AND u.name=N'IIS APPPOOL%APPPOOL%') ALTER ROLE db_datareader ADD MEMBER [IIS APPPOOL%APPPOOL%];"
sqlcmd -S .\SQLEXPRESS -E -d %DB% -Q "IF NOT EXISTS (SELECT 1 FROM sys.database_role_members m JOIN sys.database_principals r ON m.role_principal_id=r.principal_id JOIN sys.database_principals u ON m.member_principal_id=u.principal_id WHERE r.name='db_datawriter' AND u.name=N'IIS APPPOOL%APPPOOL%') ALTER ROLE db_datawriter ADD MEMBER [IIS APPPOOL%APPPOOL%];"
より細かい最小権限(推奨パターン)
業務要件が明確なら、カスタム ロールにスコープを限定します。
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name='app_rw') CREATE ROLE app_rw;"
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO app_rw;"
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "GRANT EXECUTE ON SCHEMA::dbo TO app_rw;"
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "ALTER ROLE app_rw ADD MEMBER [IIS APPPOOL\<YourAppPoolName>];"
接続文字列の要点(ASP.NET Core)
Windows 認証(統合認証)を使う場合:
// appsettings.json
"ConnectionStrings": {
"Default": "Server=.\\SQLEXPRESS;Database=<YourDatabase>;Trusted_Connection=True;TrustServerCertificate=True;MultipleActiveResultSets=True;"
}
Trusted_Connection=TrueまたはIntegrated Security=Trueを指定。- 近年のドライバは既定で
Encrypt=Trueのため、自己署名や開発環境ではTrustServerCertificate=Trueを併記しておくと初期トラブルを避けやすい(本番は適切な証明書を構成)。
SQL ログインを使う場合(今回は不要だが比較のため):
"Server=.\\SQLEXPRESS;Database=<YourDatabase>;User ID=<SqlUser>;Password=<Secret>;TrustServerCertificate=True;"
アプリプール ID の確認・切替をコマンドで行う
| 目的 | コマンド | 備考 |
|---|---|---|
| ID を確認 | appcmd list apppool /text:"name,processModel.identityType" | 既定は ApplicationPoolIdentity |
| ApplicationPoolIdentity に設定 | appcmd set apppool /apppool.name:<AppPool> /processModel.identityType:ApplicationPoolIdentity | この記事の手順はこの前提で説明 |
| NetworkService に変更 | appcmd set apppool /apppool.name:<AppPool> /processModel.identityType:NetworkService | 主体名は NT AUTHORITY\NETWORK SERVICE になる |
| 特定ユーザーに変更 | appcmd set apppool /apppool.name:<AppPool> /processModel.identityType:SpecificUser /processModel.userName:<DOMAIN\User> /processModel.password:<Password> | 資格情報の保護とローテーションに注意 |
よくあるエラーと原因・対処
| エラーメッセージ例 | 原因 | 対処 |
|---|---|---|
| Login failed for user ‘IIS APPPOOL\…’(エラー 18456) | サーバー ログインがない/無効 | ステップ 1 の CREATE LOGIN を実行 |
| Cannot open database <DB> requested by the login. The login failed. | ログインはあるが DB ユーザーがない | ステップ 2 の CREATE USER … FOR LOGIN … |
| User, group, or role ‘<name>’ already exists in the current database. | 同名ユーザーが既にある | ALTER USER [<name>] WITH LOGIN = [<login>]; で再ひも付け |
| The server principal ‘<name>’ already exists. | ログインは既に存在 | 無視して次へ進むか、冪等スクリプトを使用 |
| Permission denied in database ‘<DB>’ | 必要ロール不足 | db_datareader/db_datawriter、必要に応じ GRANT EXECUTE を付与 |
| Login is from an untrusted domain… | クロスドメインで信頼関係なし | 同一ドメイン/信頼済みドメインのアカウントを使用 |
どの主体名を作ればいい?(早見表)
| IIS 側の設定 | SQL Server に作成するログイン名 | 備考 |
|---|---|---|
| ApplicationPoolIdentity(既定) | IIS APPPOOL\<AppPool名> | 本記事のメインパターン |
| NetworkService | NT AUTHORITY\NETWORK SERVICE | サーバー全体で共有されうる |
| LocalSystem | NT AUTHORITY\SYSTEM | 権限が強すぎるため通常は非推奨 |
| 特定ユーザー(ドメイン/ローカル) | DOMAIN\User または ServerName\User | サービス アカウントの管理が必要 |
実例(値を埋めたサンプル)
アプリプール名 MyWebPool、データベース MyAppDb の場合:
sqlcmd -S .\SQLEXPRESS -E -Q "CREATE LOGIN [IIS APPPOOL\MyWebPool] FROM WINDOWS;"
sqlcmd -S .\SQLEXPRESS -E -d MyAppDb -Q "CREATE USER [IIS APPPOOL\MyWebPool] FOR LOGIN [IIS APPPOOL\MyWebPool];"
sqlcmd -S .\SQLEXPRESS -E -d MyAppDb -Q "ALTER ROLE db_datareader ADD MEMBER [IIS APPPOOL\MyWebPool];"
sqlcmd -S .\SQLEXPRESS -E -d MyAppDb -Q "ALTER ROLE db_datawriter ADD MEMBER [IIS APPPOOL\MyWebPool];"
運用とセキュリティの注意点
- 最小権限:安易に
db_ownerを付けない。DDL が必要な初期化処理は移行ツールで行い、アプリ本番はSELECT/INSERT/UPDATE/DELETE/EXECUTEのみに抑制。 - 接続プール:権限追加後はアプリ再起動(リサイクル)で古いプールを解放。
iisresetまたはプールの停止→開始。 - 監査:主体をアプリプール単位に分けると監査ログで「どのサイトからの操作か」が追跡しやすい。
- 暗号化:開発環境では
TrustServerCertificate=Trueで回避可能だが、本番は適切なサーバー証明書を導入。 - バックアップ:SSMS がなくても
sqlcmdでバックアップは実行可(例:BACKUP DATABASE)。
チェックリスト(5分で原因切り分け)
- 主体の確認:アプリ内で
WindowsIdentity.GetCurrent().Nameをログ出力し、IIS 実行時の主体を把握。 - ログイン存在:
sys.server_principalsに主体があるか。 - DB ユーザー存在:
sys.database_principalsに主体があるか。 - ロール付与:必要ロールが付いているか。
- 接続文字列:統合認証になっているか、DB 名の誤りはないか。
トラブル応用編(スクリプト断片)
既存ユーザーのログイン再ひも付け:
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "ALTER USER [IIS APPPOOL\<YourAppPoolName>] WITH LOGIN = [IIS APPPOOL\<YourAppPoolName>];"
不要になったユーザー/ログインの削除:
sqlcmd -S .\SQLEXPRESS -E -d <YourDatabase> -Q "DROP USER IF EXISTS [IIS APPPOOL\<YourAppPoolName>];"
sqlcmd -S .\SQLEXPRESS -E -Q "DROP LOGIN IF EXISTS [IIS APPPOOL\<YourAppPoolName>];"
補足:ネットワークやファイアウォールは今回の主因ではない
同一マシン上の IIS → SQL Server Express で発生する「IIS のみ失敗」は、ほぼ必ず認証・権限設定の問題です。リモート接続(他サーバーからの接続)を計画している場合のみ、TCP 1433 の開放や SQL Server Browser/動的ポートの設計が論点になります。
補足:SQL Server Express の仕様を踏まえた設計
- 1 DB あたり容量上限(一般的に 10GB 程度)を意識。
- 同一サーバーに複数サイトが同居する場合は、アプリプールを分け、主体もプール単位で別々に作ると安全。
- メンテナンス作業(インデックス再構築や統計更新)はアプリの主体ではなく、専用の管理者ジョブで行う。
まとめ
- 鍵は「IIS が使う Windows アカウント(アプリケーション プール ID)を SQL Server に登録し、DB ユーザーとロールを正しく付与する」こと。
- SSMS なしでも
sqlcmdで完結:ログイン作成 → ユーザー作成 → ロール付与 → 確認の4手順。 - 最小権限と主体分離を徹底すれば、セキュアで扱いやすい運用になる。
コピペ用:最小構成スクリプト(完成版)
@echo off
setlocal
set S=.\SQLEXPRESS
set DB=<YourDatabase>
set APPPOOL=<YourAppPoolName>
rem 1) ログイン(サーバー主体)
sqlcmd -S %S% -E -Q "IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'IIS APPPOOL%APPPOOL%') CREATE LOGIN [IIS APPPOOL%APPPOOL%] FROM WINDOWS;"
rem 2) データベース ユーザー
sqlcmd -S %S% -E -d %DB% -Q "IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = N'IIS APPPOOL%APPPOOL%') CREATE USER [IIS APPPOOL%APPPOOL%] FOR LOGIN [IIS APPPOOL%APPPOOL%];"
rem 3) 最低限ロール
sqlcmd -S %S% -E -d %DB% -Q "IF NOT EXISTS (SELECT 1 FROM sys.database_role_members m JOIN sys.database_principals r ON m.role_principal_id=r.principal_id JOIN sys.database_principals u ON m.member_principal_id=u.principal_id WHERE r.name='db_datareader' AND u.name=N'IIS APPPOOL%APPPOOL%') ALTER ROLE db_datareader ADD MEMBER [IIS APPPOOL%APPPOOL%];"
sqlcmd -S %S% -E -d %DB% -Q "IF NOT EXISTS (SELECT 1 FROM sys.database_role_members m JOIN sys.database_principals r ON m.role_principal_id=r.principal_id JOIN sys.database_principals u ON m.member_principal_id=u.principal_id WHERE r.name='db_datawriter' AND u.name=N'IIS APPPOOL%APPPOOL%') ALTER ROLE db_datawriter ADD MEMBER [IIS APPPOOL%APPPOOL%];"
rem 4) 確認
sqlcmd -S %S% -E -Q "SELECT 'server_principal' AS scope, name, type_desc FROM sys.server_principals WHERE name=N'IIS APPPOOL%APPPOOL%';"
sqlcmd -S %S% -E -d %DB% -Q "SELECT 'database_principal' AS scope, name, type_desc FROM sys.database_principals WHERE name=N'IIS APPPOOL%APPPOOL%';"
endlocal
最後に:IIS 側で確認したいポイント
- 対象サイトが「正しいアプリケーション プール」を使用していること。
- そのプールの ID が意図通り(ApplicationPoolIdentity 等)であること。
- 権限付与後にプールを再起動していること。
FAQ(簡易)
Q. SSMS を入れずに運用し続けられますか?
A. 可能です。ユーザー/ロール/バックアップ/復元/DDL はすべて sqlcmd で実行できます。とはいえ、保守性と可視化の観点で GUI を併用できる状態にしておくと確認作業が速くなります。
Q. 接続がときどき失敗します。
A. ログイン・ユーザー・ロールが揃っていても、古い接続プールが残ると新権限が反映されないことがあります。プールのリサイクル、アプリケーションの再起動を行ってください。
Q. 別サーバーの SQL Server に接続します。
A. クロスサーバーでは SPN/Kerberos、ポート、ファイアウォール、信頼関係など追加論点が生じます。本稿の主因(主体未登録)に加え、TCP/暗号化/証明書の整備もあわせて確認してください。
この記事の要点(1枚表)
| やること | コマンド例 | 確認観点 |
|---|---|---|
| ログイン作成 | CREATE LOGIN [IIS APPPOOL\<Pool>] FROM WINDOWS; | sys.server_principals |
| DB ユーザー作成 | CREATE USER [IIS APPPOOL\<Pool>] FOR LOGIN […] | sys.database_principals |
| ロール付与 | ALTER ROLE db_datareader/db_datawriter ADD MEMBER […] | 権限不足エラーの有無 |
| 接続文字列 | Trusted_Connection=True;TrustServerCertificate=True; | 統合認証・暗号化の扱い |
以上で、IIS でもコマンド実行時と同じ主体で SQL Server に正しく認証・認可され、接続エラーは解消します。

コメント