複数のWindows ServerからOS情報とPowerShellのビルド番号を一括取得しようとすると、Get-WmiObjectで「The RPC server is unavailable (0x800706BA)」が出て止まることがあります。本記事ではCSVの読み方とExport-Csvの出力作法を正したうえで、RPC/WMI/WinRMの切り分けと安定化のコツを実務目線で整理します。
やりたいことと、つまずきやすいポイント
サーバー台数が増えてくると、運用台帳や監査対応のために「複数サーバーのOS情報(WMI)」「PowerShellのビルド番号」を一括で集めたくなります。典型的には次のような流れです。
- サーバー一覧(CSVまたはテキスト)を読み込む
- 各サーバーへ問い合わせて、OS情報(例:Win32_OperatingSystem)を取得する
- 同じサーバーから $PSVersionTable.PSVersion.Build を取得する
- 結果をCSVにまとめる
ところが、実務では次の2系統の問題が絡み合ってハマりがちです。
- スクリプト側の問題:CSVの読み方が曖昧、ヘッダー行をホスト名として扱ってしまう、Export-Csvに出力データを渡していない
- 環境側の問題:WMIはRPC/DCOM依存のため、到達性・DNS・ファイアウォール・WMI許可が揃っていないと「RPC server is unavailable (0x800706BA)」になりやすい
まずは「スクリプトの形」を正して、次に「通信として通る状態か」を切り分けるのが最短ルートです。
入力が“CSV”か“テキスト”かで、読み方を固定する
最初に決めたいのは、サーバー一覧ファイルの形式です。ここが曖昧だと、ヘッダー行や区切り文字が原因で無駄なエラーが増えます。
| ファイル形式 | 例 | 推奨コマンド | よくある事故 |
|---|---|---|---|
| CSV(ヘッダーあり) | ComputerName SVR01 | Import-Csv | Get-Contentで回してヘッダー文字列までホスト名扱い |
| テキスト(1行=1台) | SVR01 SVR02 | Get-Content | コメント行・空行を拾って余計な接続失敗が増える |
CSVのサンプル(おすすめ)
最低限、ホスト名(またはFQDN)を表す列名を1つ決めます。ここでは ComputerName を使います。
ComputerName
SVR01
SVR02
SVR03
列を増やすのも簡単です(部署、役割、備考など)。
ComputerName,Role,Note
SVR01,ADDS,DC1
SVR02,File,NYC
SVR03,Web,IIS
なぜGet-ContentでCSVを回すと失敗しやすいのか
CSVは先頭行がヘッダーであることが多く、Get-Contentでそのままループすると、ヘッダー(例:ComputerName)がホスト名として処理されます。結果として、存在しないホストへ問い合わせてしまい、WMI/RPCの失敗(0x800706BA)を“増やす”要因になります。
Export-Csvで「何も出力されない」原因はほぼこれ
Export-Csvは、出力するオブジェクト(データ)がパイプで渡されて初めてCSVを書きます。パスだけ指定しても、書き出す対象がなければ空のままです。
| やりがち | 結果 | 理由 |
|---|---|---|
Export-Csv -Path C:\out.csv | ほぼ何も出ない | 出力するオブジェクトを渡していない |
$obj | Export-Csv -Path C:\out.csv | 期待通り出る | パイプでオブジェクトを渡している |
さらに、ループ内で -Append している場合も、毎回「そのループで作ったオブジェクト」を渡さないと出力されません。実務では、いったん結果を全部オブジェクト化して最後に1回だけExport-Csv する形が安定します(途中で落ちても原因が追いやすく、出力の列ズレも減ります)。
まず動く“型”:WMIでOS情報、Invoke-CommandでPSビルドを取ってCSV化
ここでは、入力がCSV(ヘッダー付き)で、列名が ComputerName である前提のサンプルを紹介します。WMIとWinRM(PowerShell Remoting)は要件が異なるため、両方の成否を列で分けて記録すると切り分けが楽になります。
実用スクリプト(エラーも記録してCSVに残す)
param(
[string]$ServerListPath = 'C:\servers.csv',
[string]$OutPath = 'C:\server_inventory.csv'
)
# サーバー一覧をCSVとして読み込む(ヘッダー行も列として扱える)
$servers = Import-Csv -Path $ServerListPath
$results = foreach ($row in $servers) {
$computer = $row.ComputerName
# ---- WMI: OS情報(RPC/DCOM) ----
$os = $null
$wmiError = $null
try {
$os = Get-WmiObject -ComputerName $computer -Class Win32_OperatingSystem -ErrorAction Stop
} catch {
$wmiError = $_.Exception.Message
}
# ---- WinRM: PowerShellビルド(PowerShell Remoting) ----
$psBuild = $null
$winrmError = $null
try {
$psBuild = Invoke-Command -ComputerName $computer -ScriptBlock {
$PSVersionTable.PSVersion.Build
} -ErrorAction Stop
} catch {
$winrmError = $_.Exception.Message
}
# WMIのLastBootUpTimeはDMTF形式なので、取れたときだけDateTimeに変換
$lastBoot = $null
if ($os -and $os.LastBootUpTime) {
$lastBoot = [Management.ManagementDateTimeConverter]::ToDateTime($os.LastBootUpTime)
}
[pscustomobject]@{
CollectedAt = (Get-Date)
ComputerName = $computer
OSCaption = if ($os) { $os.Caption } else { $null }
OSVersion = if ($os) { $os.Version } else { $null }
OSBuild = if ($os) { $os.BuildNumber } else { $null }
LastBoot = $lastBoot
PSBuild = $psBuild
WMIOk = [bool]$os
WinRMOk = ($psBuild -ne $null)
WMIError = $wmiError
WinRMError = $winrmError
}
}
# まとめて1回でCSV出力(列が安定し、速度も出やすい)
$results |
Export-Csv -Path $OutPath -NoTypeInformation -Encoding UTF8
このスクリプトが“実務で効く”理由
- Import-Csvでヘッダー行を正しく処理するため、ヘッダーをホスト名として誤処理しません。
- 各サーバーについて、WMI(RPC/DCOM)とWinRM(PowerShell Remoting)の成功/失敗を分けて残すので、問題がネットワークなのか、WinRM設定なのかが見えます。
- 例外(エラー)を握りつぶさず、WMIError / WinRMError に文字列として残すので、あとから原因調査ができます。
入力ファイルが“テキスト”しかない場合の対応
サーバー一覧が「1行=1台」のテキストで、ヘッダーが不要なら、次のように空行・コメント行を除外してから回すと安全です。
$servers = Get-Content -Path 'C:\servers.txt' |
Where-Object { $_ -and ($_ -notmatch '^\s*#') }
foreach ($computer in $servers) {
# 上のサンプルと同様に処理
}
「RPC server is unavailable (0x800706BA)」を最短で切り分ける
0x800706BA は、スクリプト文法が正しくても起きます。WMI(Get-WmiObject)はRPC/DCOMを使うため、リモート側・ネットワーク側・FW側のどこかが欠けると失敗します。切り分けは「名前解決→疎通→ポート→OS側設定→権限」の順が効率的です。
切り分けチェック表(現場でよく当たる順)
| 疑う順 | ありがちな原因 | 確認コマンド例(実行元) | 対処の方向性 |
|---|---|---|---|
| 名前解決 | DNSが引けない / hosts未登録 / 別セグメントで名前が違う | Resolve-DnsName SVR01nslookup SVR01 | FQDNで統一、DNS登録、台帳の表記揺れを直す |
| 疎通 | サーバー停止 / ルーティング不可 / ICMP遮断(参考) | Test-Connection SVR01 -Count 1 | 停止・隔離・経路の確認(ICMPは通らなくてもWMIが通る場合あり) |
| RPCエンドポイント | TCP 135が遮断 | Test-NetConnection SVR01 -Port 135 | ネットワークFWまたはWindows FWで許可 |
| 動的ポート | RPC/DCOMの動的ポート帯が遮断 | (環境依存。135だけ開けても失敗する) | FW設計の見直し、もしくはWinRM(5985/5986)への切替を検討 |
| Windows FW | WMI用受信規則が無効 | (対象サーバー側)Get-NetFirewallRule -DisplayGroup 'Windows Management Instrumentation (WMI)' | 「Windows Management Instrumentation (WMI-In)」等の有効化(GPO推奨) |
| サービス | RPC / WMIサービス停止、破損 | (対象サーバー側)Get-Service RpcSs, Winmgmt | サービス起動、イベントログ確認、WMIリポジトリ調査 |
| 権限 | WMI名前空間権限不足、UACリモート制限 | (エラーが0x80070005等になることも) | 実行アカウントを見直す、WMI権限付与、ローカル管理者利用 |
まず試す“軽い”事前チェック(スクリプトに組み込むと効く)
大量台数に対していきなりWMIを投げると、落ちたときの待ち時間が長くなりがちです。現場では「到達性」「ポート」の簡易チェックを先に入れて、失敗するサーバーを早めに判定すると全体が回りやすくなります。
$computer = 'SVR01'
# DNS/名前解決(失敗したら台帳の表記揺れを疑う)
Resolve-DnsName $computer -ErrorAction SilentlyContinue
# RPCエンドポイント(WMIで最低限必要)
Test-NetConnection $computer -Port 135
# WinRM(Invoke-Commandを使うなら)
Test-WSMan $computer
ここで Test-NetConnection -Port 135 が失敗するなら、WMIはほぼ確実に通りません。逆に135が通っても、動的ポート帯やWindows FWの設定次第でWMIが失敗することがあります。
WMI(RPC/DCOM)とWinRM(WSMan)の違いを理解すると迷いが減る
同じ「リモートで情報を取る」でも、WMIとPowerShell Remotingでは使う仕組みが違います。どちらを“主戦力”にするかを決めると、ネットワーク要件の整理が進みます。
| 手段 | 代表コマンド | 通信 | 主なポート | 向いている場面 |
|---|---|---|---|---|
| WMI(従来) | Get-WmiObject | RPC/DCOM | TCP 135 + 動的ポート | 既存運用でWMIが通る、WinRMを有効化できない |
| CIM(WSMan) | Get-CimInstance | WSMan(WinRM) | 5985/5986 | FWが厳しい環境、WinRMを統制できる |
| PowerShell Remoting | Invoke-Command | WSMan(WinRM) | 5985/5986 | リモートで複合処理(OS情報+PS情報など)をまとめて実行したい |
PowerShell 7で実行する場合の注意(Get-WmiObjectが使えない)
管理端末や踏み台でPowerShell 7(pwsh)を使っている場合、Get-WmiObject は標準では利用できません。その場合はCIM系コマンドレット(Get-CimInstance)へ寄せるのが基本です。
ただし、Get-CimInstanceは「どう接続するか」で要件が変わります。RPC/DCOMを使う(=WMIと同系統)のか、WSMan(WinRM)を使うのかを意識して選びます。
# RPC/DCOMでCIM(=WMIと同じく、ネットワーク要件は厳しめ)
$opt = New-CimSessionOption -Protocol Dcom
$cs = New-CimSession -ComputerName $computer -SessionOption $opt
Get-CimInstance -CimSession $cs -ClassName Win32_OperatingSystem
# WSManでCIM(WinRMが必要。RPCを避けたいならこちらが本命)
Get-CimInstance -ComputerName $computer -ClassName Win32_OperatingSystem
「0x800706BAを避けたい」目的なら、単にGet-WmiObjectをGet-CimInstanceに置き換えるだけでは解決しないことがあります。RPC/DCOMの経路を使っていないか、またはWinRMを使う設計にできないかを合わせて確認するのがポイントです。
「WMIがネットワーク的に厳しい」環境では、WinRM/WSManに寄せると、開けるポートが固定化できて運用が楽になるケースが多いです(もちろん、WinRMを有効にするための社内ルール・監査要件は別途確認が必要です)。
安定化の定石:WinRMでリモート実行し、サーバー“内部”でOS情報も取る
「WMIが通らない(0x800706BA)けれど、WinRMは許可できる」なら、OS情報の取得を“リモートWMI”ではなくリモートセッション内でのローカル取得に寄せるのが効果的です。つまり、管理端末→サーバーへの通信はWinRMだけにして、サーバー側でGet-CimInstanceを実行します。
WinRM一本化サンプル(OS情報+PSビルドを1回の接続で取得)
param(
[string]$ServerListPath = 'C:\servers.csv',
[string]$OutPath = 'C:\server_inventory_winrm.csv'
)
$servers = Import-Csv -Path $ServerListPath
$results = foreach ($row in $servers) {
$computer = $row.ComputerName
try {
Invoke-Command -ComputerName $computer -ErrorAction Stop -ScriptBlock {
$os = Get-CimInstance -ClassName Win32_OperatingSystem
[pscustomobject]@{
CollectedAt = (Get-Date)
ComputerName = $env:COMPUTERNAME
OSCaption = $os.Caption
OSVersion = $os.Version
OSBuild = $os.BuildNumber
LastBoot = $os.LastBootUpTime
PSBuild = $PSVersionTable.PSVersion.Build
}
}
} catch {
[pscustomobject]@{
CollectedAt = (Get-Date)
ComputerName = $computer
OSCaption = $null
OSVersion = $null
OSBuild = $null
LastBoot = $null
PSBuild = $null
Error = $_.Exception.Message
}
}
}
$results |
Export-Csv -Path $OutPath -NoTypeInformation -Encoding UTF8
WinRM一本化が向く/向かない判断基準
- 向く:ネットワークFWが厳しく、動的ポートを開けられない/GPOでWinRMを統制できる/対象台数が多い
- 向かない:WinRMが原則禁止/サーバーが古くWinRM設定が困難/ドメイン外や認証要件が複雑
Import-CsvのDelimiterエラーを確実に直す
Import-Csv : Cannot bind parameter 'Delimiter'... "String must be exactly one character long." は、ほぼ例外なく「-Delimiterに1文字以外を渡している」のが原因です。パスを渡す場所を間違えるケースが非常に多いので、コマンドを分解して確認します。
| 例 | 判定 | 理由 |
|---|---|---|
Import-Csv -Delimiter "C:\servers.csv" | NG | -Delimiterは区切り文字(1文字)用。パスを渡している |
Import-Csv -Path "C:\servers.csv" | OK | パスは-Pathに渡す(既定区切りはカンマ) |
Import-Csv -Path "C:\servers.csv" -Delimiter ";" | OK | 区切りがセミコロンのCSVなら1文字で指定 |
「CSVのはずなのに列が1列に潰れる」場合は、区切り文字がカンマではない(セミコロンなど)か、ダブルクォートの崩れでCSVとして成立していない可能性があります。まずはエディタで先頭数行を見て、区切り文字とヘッダーを確認するのが早道です。
運用で差が出る安定化テクニック
結果CSVに“状態列”を入れて、後工程を楽にする
単に値を集めるだけだと、途中で失敗したサーバーの再実行や、ネットワーク担当への依頼が面倒になります。次のような列を入れておくと、後からの調査が格段に楽です。
- CollectedAt:いつ採取したか(監査で効く)
- WMIOk / WinRMOk:どの経路で取れたか
- WMIError / WinRMError:失敗理由の要約(切り分けの一次情報)
台数が多い場合は“同時実行数”を意識する
台数が多いと、サーバー側・ネットワーク側の負荷や、実行元の待ち時間がボトルネックになります。PowerShell Remotingなら Invoke-Command -ComputerName に配列を渡し、-ThrottleLimit で同時実行数を制御できます(過剰に上げると、逆にタイムアウトや一時的なエラーが増えることがあります)。
$computers = (Import-Csv 'C:\servers.csv').ComputerName
Invoke-Command -ComputerName $computers -ThrottleLimit 20 -ScriptBlock {
# ここに採取処理
}
権限・認証で詰まったときの観点
- ドメイン環境では、対象サーバーのローカル管理者権限(またはWMI名前空間権限)が必要になることが多い
- ワークグループやローカルアカウント運用では、UACリモート制限などで挙動が変わるため、標準化した実行アカウントを用意したほうが安定しやすい
- WinRMを使う場合は、WinRM有効化(Enable-PSRemoting)やGPO設定、認証方式(Kerberos/NTLM)も合わせて確認する
最後に:直す順番を間違えない
「RPC server is unavailable (0x800706BA)」に遭遇したときは、まずスクリプトをImport-Csvで読み、Export-Csvへオブジェクトを渡す形に整えます。そのうえで、WMIが通らないなら到達性・TCP135・動的ポート・WMI受信規則を順に潰し、環境的に難しいならWinRM(WSMan)へ寄せるのが現実的です。原因を“文字列のエラー”として残す設計にしておくと、次回以降の運用も一気に楽になります。

コメント