遅いWindowsPC のGUIなんて・・・・
軽快な、ubuntu の shell での操作に慣れてしまうと、低スペック高年式のWindows PC を取り扱うと・・・その遅さに思わず、
「PowerShellでもなんでもいい、もっさりGUI要らん」
となりますよね。
- 要らないアプリこそ取っ払いたいのに、GUIでちまちまやるのが遅い。
- Microsoft Standard Installer は並列処理してくれない・・・のはやむ得ないから、とりあえずバッチにしたい。
・・・などと思ったので、記事にしてみました。
msiexecとPowerShellを活用したバッチ処理を実装してみる
MSIパッケージについて
MSI(Microsoft Installer)パッケージは、Windowsの標準的なソフトウェアインストール形式です。各MSIパッケージには一意のProduct Code(GUID)が割り当てられ、システムのレジストリに登録されます。
msiexecコマンドの基本
msiexec /x {ProductCode} /quiet /norestart
-
/x
: アンインストールオプション -
/quiet
: サイレント実行 -
/norestart
: 再起動の抑制
課題:製品名や製造元からProduct Codeを特定する
通常のアンインストール処理では、正確なProduct Code(GUID)が必要です。しかし、実際の運用では「Adobe製品をすべて削除」や「特定バージョンのソフトウェアのみ削除」といった要求が発生します。
解決策:msi-search/msi_search.ps1の活用
GitHubで公開されているmsi-search/msi_search.ps1
スクリプトは、インストール済みMSIパッケージの情報を効率的に検索する機能を提供します。
スクリプトの主要機能
-
レジストリ検索:
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
からインストール情報を取得 -
WMI クエリ:
Win32_Product
クラスを使用したシステム情報の取得 - フィルタリング: 製造元(Publisher)、製品名(DisplayName)による絞り込み
- Product Code抽出: アンインストールに必要なGUIDの特定
実装例
1. 基本的な検索スクリプト
# 製造元による検索
.\msi_search.ps1 -Publisher "Adobe Systems"
# 製品名による部分一致検索
.\msi_search.ps1 -DisplayName "*Office*"
2. バッチアンインストール処理
# 検索結果からProduct Codeを取得してアンインストール
$products = .\msi_search.ps1 -Publisher "Adobe Systems"
foreach ($product in $products) {
$productCode = $product.ProductCode
Write-Host "Uninstalling: $($product.DisplayName)"
Start-Process -FilePath "msiexec.exe" -ArgumentList "/x", $productCode, "/quiet", "/norestart" -Wait
}
3. エラーハンドリングとログ出力
$logFile = "C:\Temp\uninstall_log.txt"
$products = .\msi_search.ps1 -Publisher "Adobe Systems"
foreach ($product in $products) {
try {
$productCode = $product.ProductCode
$displayName = $product.DisplayName
Write-Output "$(Get-Date): Starting uninstall of $displayName" | Tee-Object -FilePath $logFile -Append
$process = Start-Process -FilePath "msiexec.exe" -ArgumentList "/x", $productCode, "/quiet", "/norestart" -Wait -PassThru
if ($process.ExitCode -eq 0) {
Write-Output "$(Get-Date): Successfully uninstalled $displayName" | Tee-Object -FilePath $logFile -Append
} else {
Write-Output "$(Get-Date): Failed to uninstall $displayName (Exit Code: $($process.ExitCode))" | Tee-Object -FilePath $logFile -Append
}
}
catch {
Write-Output "$(Get-Date): Error processing $displayName - $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
}
}
実行時の注意点
権限要件
- 管理者権限での実行が必要です。
- 実行ポリシーが、設定されてないとPowerShellスクリプトは起動しません。 Powershellを楽に実行してもらうには
- UAC(ユーザーアカウント制御)の考慮
パフォーマンス最適化
- WMI クエリは処理時間が長い場合があるため、レジストリベースの検索を優先
- 大量のソフトウェアが対象の場合は、並列処理の検討
リスク管理
# 事前確認(ドライラン)の実装
param(
[switch]$WhatIf
)
if ($WhatIf) {
Write-Host "以下の製品がアンインストール対象です:"
$products | ForEach-Object { Write-Host "- $($_.DisplayName)" }
return
}
応用例
1. 特定バージョンの一括削除
# 古いバージョンのJavaを一括削除
$oldJava = .\msi_search.ps1 -DisplayName "*Java*" | Where-Object {
$_.Version -lt "8.0.0"
}
2. CSV出力による管理
$results = .\msi_search.ps1 -Publisher "Microsoft Corporation"
$results | Export-Csv -Path "C:\Temp\installed_software.csv" -NoTypeInformation
3. リモート実行
Invoke-Command -ComputerName $servers -ScriptBlock {
# リモートサーバーでの一括アンインストール処理
}
トラブルシューティング
よくある問題と対処法
-
Exit Code 1618: 別のインストール処理が実行中
- Windows Installerサービスの状態確認
- 処理の順次実行
-
Exit Code 1605: 製品が見つからない
- Product Codeの再確認
- レジストリ情報との整合性チェック
-
権限エラー:
- 管理者権限での実行確認
- グループポリシーの制限確認
セキュリティ考慮事項
- スクリプトの署名検証
- 実行ポリシーの適切な設定
- ログファイルのアクセス制御
- 誤削除防止のための事前確認機能
まとめ
msiexecとPowerShellスクリプトを組み合わせることで、製造元や製品名をキーとした効率的なMSIパッケージの一括アンインストールが可能になります。msi-search/msi_search.ps1のようなツールを活用することで、複雑なProduct Code特定作業を自動化し、大規模環境でのソフトウェア管理を効率化できます。
ただし、本番環境での実行前には必ず十分なテストと事前確認を行い、適切なバックアップとログ記録を実装することが重要です。
参考リソース
- Microsoft公式ドキュメント: msiexecコマンドリファレンス
- GitHub: msi-search/msi_search.ps1
- PowerShell Gallery: 関連モジュール
- Windows Installer エラーコード一覧