Windows Server 2022 のタスク スケジューラで「ユーザーがログオンしていなくても実行」を選ぶと、バッチが成功と表示されるのにファイルがコピーされない――現場で非常によく出会う落とし穴です。本記事はその再現条件と原因の本質、確実に動かすための設計と運用のベストプラクティス、そしてすぐ使える堅牢なサンプル スクリプトまで一気通貫で解説します。
質問概要
- 環境: Microsoft Windows Server 2022 Datacenter
- 目的: バッチ ファイルで Azure File Share 上のフォルダー → サーバー上の別フォルダーへファイルをコピー
- 状況:
- 手動実行 & 「ユーザーがログオン中のみ実行」では成功
- 「ユーザーがログオンしているかどうかに関係なく実行」にすると、タスクは「成功」だが実体はコピーされない
- 求めているもの: 失敗原因の特定とベストプラクティス
なぜ起きるのか ― 非対話セッションの3つの壁
タスク スケジューラで「ユーザーがログオンしていなくても実行」を選ぶと、処理は 非対話セッション(バックグラウンド ログオン) で走ります。このモードには次の制約があり、コピー失敗の主因になります。
- ドライブ レターの不可視:Z: などのネットワーク ドライブは対話セッションに紐づくため、非対話セッションでは見えません。
→ UNC パス(\\server\share)に置き換えるのが定石。 - 資格情報・プロファイルが読み込まれない:ユーザー プロファイル依存の資格情報や環境変数が未ロード。
→ タスクの「パスワードを保存しない(このタスクはローカル コンピューターのリソースのみ)」にチェックがあるとネットワークへ出られません。 - トークン権限の不足:同じ管理者アカウントでも、昇格トークンなしでは共有資源にアクセスできないケース。
→ 最上位の特権で実行を必ず有効化。
まずはここを直す(即効性のある設定)
| 項目 | 推奨値 | 理由 |
|---|---|---|
| パスの指定 | \\storageaccount.file.core.windows.net\sharename(UNC) | 非対話セッションでドライブ レターが見えないため |
| 最上位の特権で実行 | チェック ON | 昇格トークンで SMB アクセスの失敗を避ける |
| パスワードを保存しない | チェック OFF | OFF でないとネットワーク資源に出られない |
| 実行ユーザー | ドメインのサービス アカウント | Azure File Share / コピー先 NTFS に明示権限付与しやすい |
| 開始(作業フォルダー) | スクリプト配置ディレクトリ(例: D:\Jobs\Scripts) | 相対パスやログ出力の失敗を防ぐ |
| ネットワーク条件 | 「ネットワーク接続がある場合のみ開始」ON(任意) | 起動直後などのネットワーク未確立時の失敗を回避 |
| 構成 | Windows Server 2022 | トークン/ポリシー差異での挙動違いを防ぐ |
権限とアクセス制御の整え方
Azure File Share の認証方式を整理する
| 方式 | 認証 | 実務ポイント | 代表コマンド |
|---|---|---|---|
| アカウント キー(従来) | ユーザー: Azure\<StorageAccountName>パスワード: ストレージ アカウント キー | シンプル。機微情報の保管に注意 | net use \\<account>.file.core.windows.net\<share> /user:Azure\<account> <key> |
| AD DS/Entra ID ベース(ID ベース) | ドメイン アカウント(RBAC/ACL) | キーレスで運用可。権限設計が肝 | 通常の net use \\...\(ユーザー/権限は AD 側で管理) |
| SAS + AzCopy(HTTPS) | SAS トークン | SMB/445 を避けられる。自動化に強い | azcopy copy "https://.../share?...SAS" "D:\Dest" --recursive |
共有/NTFS の二段階で付与
- Azure File Share 側:対象ユーザー(サービス アカウント)に 読み取り/書き込み 権限(ID ベースの場合は適切なロール/ACL)。
- コピー先ローカル フォルダー:NTFS の 変更 以上(必要なら 作成/削除)。
ローカル セキュリティ ポリシー
- バッチ ジョブとしてログオン(SeBatchLogonRight):実行アカウントを付与。
- ネットワーク経由でこのコンピューターへアクセス(SeNetworkLogonRight):必要に応じて確認。
- 逆に ネットワーク経由でのアクセスを拒否 に入っていないかもチェック。
ファイアウォール/ネットワーク前提
- Azure File Share(SMB)は TCP 445 を使用。サーバーから
*.file.core.windows.netへのアウトバウンド 445 が許可されていること。 - SMB 署名/暗号化ポリシーの厳格化により接続拒否されることがあるため、ドメイン/ローカルのセキュリティ ポリシーでの強制設定との整合性を確認。
- プロキシ配下では SMB は通りません。必要に応じて AzCopy(HTTPS 443)への切り替えを検討。
堅牢なスクリプト例(バッチ / robocopy 版)
成功・失敗をタスク スケジューラが正しく判定できるよう、エラー処理とログをきちんと入れます。UNC で接続し、資格情報はコマンド内で一時マップします。
@echo off
setlocal enabledelayedexpansion
rem ===== 設定 =====
set "SRC=\.file.core.windows.net<sharename>\input"
set "DST=D:\Data\Inbound"
set "LOG=D:\Jobs\Logs\copy-%%DATE:~0,4%%%%DATE:~5,2%%%%DATE:~8,2%%.log"
set "AZUSER=Azure<storageaccount>"
set "AZPASS="
rem ===== ログ開始 =====
echo [START] %DATE% %TIME% >> "%LOG%"
rem ===== 一時接続 =====
net use "%SRC%" "%AZPASS%" /USER:%AZUSER% >nul 2>&1
if errorlevel 1 (
echo [ERROR] NET USE failed (%ERRORLEVEL%) >> "%LOG%"
exit /b 32
)
rem ===== コピー(robocopy)=====
robocopy "%SRC%" "%DST%" *.* /S /FFT /Z /R:3 /W:5 /NFL /NDL /NP /LOG+:"%LOG%"
set "RC=%ERRORLEVEL%"
rem robocopy の戻り値: 0/1/2/3/4/5/6/7 = 成功/警告, 8+ = 失敗
if %RC% GEQ 8 (
echo [ERROR] ROBOCOPY failed (RC=%RC%) >> "%LOG%"
set "RET=64"
) else (
echo [OK] ROBOCOPY success (RC=%RC%) >> "%LOG%"
set "RET=0"
)
rem ===== 切断 =====
net use "%SRC%" /DELETE >nul 2>&1
echo [END] %DATE% %TIME% RET=%RET% >> "%LOG%"
exit /b %RET%
ポイント:robocopy の戻り値は 8 以上がエラーです。タスクの「結果 0x0」(成功)だけを鵜呑みにせず、スクリプトが適切な終了コードを返すようにします。
PowerShell 版(セキュアな資格情報の分離)
DPAPI で暗号化したパスワードをファイル保存し、タスクは同一ユーザーで復号して利用する方法です(サーバー単位で有効)。
初期化(手動・一度だけ)
# 実行ユーザーのプロファイル配下に保存
$SecurePath = 'C:\ProgramData\Jobs\azfiles.cred'
$Plain = Read-Host -AsSecureString 'Enter storage key or password'
$Plain | ConvertFrom-SecureString | Set-Content -Path $SecurePath -Encoding ascii
スクリプト本体(PowerShell 7 推奨)
param(
[string]$Src = "\\<account>.file.core.windows.net\<share>\input",
[string]$Dst = "D:\Data\Inbound",
[string]$User = "Azure\<account>",
[string]$CredPath = "C:\ProgramData\Jobs\azfiles.cred",
[string]$Log = "D:\Jobs\Logs\copy-$((Get-Date).ToString('yyyyMMdd-HHmmss')).log"
)
$ErrorActionPreference = 'Stop'
Start-Transcript -Path $Log -Append
try {
$secure = Get-Content -Path $CredPath -Encoding ascii | ConvertTo-SecureString
$cred = New-Object System.Management.Automation.PSCredential($User, $secure)
# 一時接続(UNC 推奨。ドライブ レターは使わない)
$null = cmd /c "net use `"$Src`" `"$($cred.GetNetworkCredential().Password)`" /USER:$User"
# コピー(robocopy を使うと大容量でも堅牢)
$robolog = $Log
$robocmd = "robocopy `"$Src`" `"$Dst`" *.* /S /FFT /Z /R:3 /W:5 /NFL /NDL /NP /LOG+:`"$robolog`""
$proc = Start-Process -FilePath "cmd.exe" -ArgumentList "/c $robocmd" -PassThru -Wait
$rc = $proc.ExitCode
if ($rc -ge 8) { throw "ROBOCOPY failed. RC=$rc" }
Write-Host "Copy completed. RC=$rc"
exit 0
}
catch {
Write-Error $_
exit 64
}
finally {
cmd /c "net use `"$Src`" /DELETE" | Out-Null
Stop-Transcript
} </code></pre>
<h3>タスク設定例(PowerShell を直接起動)</h3>
<ul>
<li><strong>プログラム/スクリプト</strong>:<code>C:\Program Files\PowerShell\7\pwsh.exe</code></li>
<li><strong>引数の追加</strong>:<code>-NoLogo -NoProfile -ExecutionPolicy Bypass -File "D:\Jobs\Scripts\Copy-AzFiles.ps1"</code></li>
<li><strong>開始(作業フォルダー)</strong>:<code>D:\Jobs\Scripts</code></li>
</ul>
<h2>資格情報の安全な扱い(バッチに直書きしない)</h2>
<ul>
<li><strong>Credential Manager + cmdkey</strong>:マシン/ユーザー コンテキストに保存し、タスクから利用。
<pre><code>cmdkey /add:<account>.file.core.windows.net /user:Azure\<account> /pass:<key>
net use \\<account>.file.core.windows.net\<share> /persistent:no
タスクは同一ユーザーで実行すること。
gMSA(グループ管理サービス アカウント):パスワードの自動ローテーションが効く運用に強い選択。タスクの「ユーザーまたはグループ」に DOMAIN\svc-xxx$ を指定できる。
Azure ID ベース SMB:ストレージ側を ID 連携し、Storage File Data SMB Share Contributor 等のロールや ACL を付与。キーレスでの監査性が高い。
「成功なのにコピーされない」を見抜くトラブルシューティング
| 症状 | 観点 | 具体的な確認 |
|---|---|---|
| タスク結果 0x0 だがファイル無し | 戻り値の取りこぼし | スクリプトが適切に exit /b/exit <code> しているか。robocopy の RC 判定を実装 |
| ログに「指定されたネットワーク名は利用できません」 | UNC/445 | UNC を使っているか、TCP 445 のアウトバウンドが通るか |
| 「アクセスが拒否されました」 | 権限/トークン | 最上位の特権を ON、共有/NTFS の権限を明示付与、パスワードを保存しない は OFF |
| 手動では通るがタスクで失敗 | プロファイル依存 | 資格情報は DPAPI/Credential Manager/gMSA に寄せる。相対パスや環境変数に依存しない |
| 起動時のみ失敗 | ネットワーク成立前 | トリガーに「遅延タスク(例: 2 分)」や「ネットワーク接続がある場合のみ開始」を設定 |
ログの取り方
- Task Scheduler > すべてのタスク履歴を有効化:Microsoft-Windows-TaskScheduler/Operational を確認。
- PowerShell:
Start-Transcriptを使用し、実行コマンド/例外を残す。 - イベント ログ出力:独自ソースを作り、致命エラーで
Write-EventLog。
チェックリスト(そのまま現場で使える)
- UNC パスで記述している(Z: 等を使っていない)。
- タスク「最上位の特権で実行」ON、「パスワードを保存しない」は OFF。
- 実行アカウントはサービス アカウント。Azure 側と NTFS 側に 変更 以上の権限あり。
- プログラム/引数/開始フォルダーを適切に指定(相対パス無し)。
- 戻り値を厳密に判定(robocopy: 8 以上で失敗)。
- TCP 445 のアウトバウンド許可。プロキシを経由しない経路を用意。
- 初回のみ手動実行でログを確認し、次いで「ユーザーがログオンしていなくても実行」で再検証。
セキュリティ設計の勘所
- 最小権限:コピー元は読み取りのみ、コピー先は作成/変更/削除の必要最小限。
- 秘密情報の回避:可能なら ID ベース SMB へ移行。キーベース運用時はキーを Key Vault 等で管理(運用手順でローテーション前提)。
- 監査:アクセス失敗/成功をイベント ログ・ストレージ監査で収集。タスク失敗時にメール/Teams 通知を組み合わせると復旧が速い。
代替策:AzCopy(HTTPS)で SMB の制約を回避
ネットワーク ポリシーや ISP の都合で TCP 445 を開けられない場合、AzCopy が有効です。HTTPS(443) で動作し、タスク スケジューラとも相性が良好です。
azcopy copy `
"https://<account>.file.core.windows.net/<share>/input?<SAS>" `
"D:\Data\Inbound" `
--recursive=true --check-length=true --overwrite=ifSourceNewer `
--log-level=INFO --output-type=text
AzCopy 自体の戻り値をタスクが受け取れるように、cmd /c 経由ではなく直接実行し、作業フォルダーをログ出力先にしておくと管理が楽です。
複数台運用の安定化テクニック
- GPO のスタートアップ スクリプト:複数サーバーで同一処理を走らせるなら、GPO にスクリプト(UNC パスで記述)を配布して統一。
- タスク XML を雛形化:検証済みのタスクを エクスポート → XML を雛形にして
schtasks /create /XMLで一括展開。 - ログ規約:
サーバー名/ジョブ名/日付でローテーション。D:\Jobs\Logs\{Job}\yyyyMMdd.log等。
よくある落とし穴(再確認)
- 「パスワードを保存しない」にチェック:これがあると非対話セッションでネットワークに出られません。必ず OFF。
- ドライブ レター依存:
Z:\は使わない。常に\\server\shareで。 - 相対パス/カレント ディレクトリ依存:開始(作業フォルダー)を明示。
- 戻り値未実装:成功と見えて失敗している典型。
exit /bと robocopy の判定を実装。 - 実行アカウントの権限不足:共有/NTFS の両面、ローカル ポリシーのユーザー権利割り当ても必ず確認。
ワンライナーでの健全性チェック
:: 資格情報を明示して UNC へ到達できるか
cmd /c "net use \\<account>.file.core.windows.net\<share> <key> /user:Azure\<account> & dir \\<account>.file.core.windows.net\<share> & net use \\<account>.file.core.windows.net\<share> /delete"
# PowerShell: UNC の到達性
Test-Path "\\<account>.file.core.windows.net\<share>" -ErrorAction SilentlyContinue
まとめ
今回の事象は、非対話セッション(Run whether user is logged on or not)で失われる前提――ドライブ レター、資格情報、昇格トークン――が主因です。対策はシンプルで、UNC で書く/最上位の特権で実行/パスワードを保存しないを OFF/権限を二段で明示。これに堅牢なエラー処理とログ、ネットワーク前提の確認を加えれば、タスクは確実に動きます。さらに AzCopy を組み合わせれば、TCP 445 の制約も回避できます。記事のスクリプトとチェックリストをそのまま適用し、「成功なのに動いていない」状態から脱却しましょう。
付録:設定の具体例(スクリーンで確認する項目)
| タブ | 項目 | 設定 |
|---|---|---|
| 全般 | ユーザーまたはグループ | ドメインのサービス アカウント(または gMSA) |
| 全般 | ユーザーがログオンしているかどうかに関係なく実行 | ON(本件の要件) |
| 全般 | 最上位の特権で実行する | ON |
| 全般 | パスワードを保存しない(このタスクはローカル コンピューターのリソースのみ) | OFF |
| トリガー | スケジュール/ログオン/スタートアップ | 要件に合わせて設定(遅延開始を推奨) |
| 操作 | プログラム/スクリプト | pwsh.exe または cmd.exe |
| 操作 | 引数 | PowerShell の場合:-NoLogo -NoProfile -ExecutionPolicy Bypass -File ... |
| 操作 | 開始(作業フォルダー) | スクリプトの配置フォルダー |
| 条件 | ネットワーク | ネットワーク接続がある場合のみ開始(任意) |
| 設定 | 期限切れ後、可能な場合はタスクをできるだけ早く実行する | ON(実行機会の取りこぼしを防止) |
付録:エラーコード早見表
| 対象 | コード | 意味 | 対応 |
|---|---|---|---|
| タスク スケジューラ | 0x0 | タスクとしては成功 | 実際の処理結果はスクリプトの終了コードで判定 |
| robocopy | 0–7 | 成功/一部差分/警告 | ログを確認(8 以上のみ失敗扱い) |
| robocopy | 8 以上 | 失敗 | 終了コードでエラーとして返す |
| net use | 1219 etc. | 異なる資格情報で既に接続済み | 一旦 net use \\... /delete して再接続 |
付録:簡易バッチ(最小構成)
@echo off
set "SRC=\\<account>.file.core.windows.net\<share>"
set "DST=D:\Dest"
net use "%SRC%" "<key>" /USER:Azure\<account> >nul 2>&1 || exit /b 10
xcopy "%SRC%\*" "%DST%\" /S /Y /I >nul 2>&1 || exit /b 20
net use "%SRC%" /DELETE >nul 2>&1
exit /b 0
上記の設計・手順・スクリプトを順に適用すれば、Windows Server 2022 のタスク スケジューラで「ユーザーがログオンしていなくても実行」でも確実に Azure File Share からのコピーが動作するようになります。

コメント