はじめに
業務効率化のために作成したPowerShellツール。そのままでも便利ですが、運用フェーズで最大の壁となるのが 「実行ポリシー(Execution Policy)」 です。
-
.ps1ファイルはデフォルトで実行が制限されている。 - ユーザーに「ポリシーを Bypass して」と説明するのは現実的ではない。
- 配布したスクリプトの中身を不用意に書き換えられたくない。
これらの課題を、Gemini と共に開発した「PS1 to BAT 変換スクリプト」でスマートに解決します。
この記事の作成にあたっては、Gemini を活用しました。
ベースとなったスクリプト
私が以前に開発していた .ps1 ファイルをBase64形式でエンコードし、ダブルクリックで実行可能な .bat ファイルを生成するスクリプトです。
ベースとなったスクリプト
# PowerShell でファイル選択ダイアログを利用する
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$dialog = New-Object System.Windows.Forms.OpenFileDialog
$dialog.Filter = "ps1ファイル(*.ps1)|*.ps1"
$dialog.InitialDirectory = ".\"
$dialog.Title = "ファイルを選択してください"
# 複数選択を許可したいときは Multiselect 設定する
$dialog.Multiselect = $False
# ダイアログを表示
if($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){
# 複数選択を許可しているときは $dialog.FileNames を利用する
$FileName = Split-Path $dialog.FileName -Leaf
}else{
exit
}
$enc = [System.Text.Encoding]::GetEncoding("utf-16")
$PS += "@echo off`r`n"
$PS += "PowerShell -NoProfile -ExecutionPolicy Unrestricted -EncodedCommand "
$PS += [System.Convert]::ToBase64String($enc.GetBytes([System.IO.File]::ReadAllText($dialog.FileName)))
$PS += "`r`n"
# $PS += "pause"
$PS | Out-File -Encoding Default ($FileName + '.bat')
このツールの核となる考え方は、 「PowerShellのコードを文字列として扱い、実行ポリシーを回避して呼び出す」 という点にあります。
Gemini との共同作業により完成した変換ツール
# Windows Forms(ダイアログ用)のロード
Add-Type -AssemblyName System.Windows.Forms
# スクリプトの実行場所を作業ディレクトリに設定
Set-Location $PSScriptRoot
# --- ファイル選択ダイアログの設定 ---
$fileDialog = New-Object System.Windows.Forms.OpenFileDialog
$fileDialog.Filter = "PowerShellスクリプト (*.ps1)|*.ps1"
$fileDialog.InitialDirectory = (Get-Location).Path
$fileDialog.Title = "バッチ化するPS1ファイルを選択してください"
$fileDialog.Multiselect = $false
# ダイアログを表示し、キャンセルされた場合は終了
if ($fileDialog.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) {
Write-Host "キャンセルされました。"
exit
}
# --- ファイル情報の取得 ---
$selectedPath = $fileDialog.FileName
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($selectedPath)
$outputBatPath = Join-Path $PSScriptRoot ($baseName + ".bat")
# --- エンコード処理 ---
# PowerShellの-EncodedCommandは UTF-16LE (Little Endian) のBase64文字列を要求します
$psCode = Get-Content -Path $selectedPath -Raw
$bytes = [System.Text.Encoding]::Unicode.GetBytes($psCode) # Unicodeは内部的にUTF-16LE
$base64Code = [System.Convert]::ToBase64String($bytes)
# --- バッチファイルの内容構成 ---
# @echo off を含め、PowerShellを呼び出す1行のコマンドを作成
$batContent = @"
@echo off
powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand $base64Code
"@
# --- ファイルの書き出し ---
try {
# 既存のバッチファイルを上書きして保存(EncodingをDefaultにすることでShift-JIS/ANSI保存)
$batContent | Out-File -FilePath $outputBatPath -Encoding Default -Force
Write-Host "成功: '$outputBatPath' を作成しました。" -ForegroundColor Cyan
}
catch {
Write-Error "ファイルの保存中にエラーが発生しました: $_"
}
改良のポイント
-
変数名の適正化:
-
$dialog→$fileDialog: 用途を明確化。 -
$PS→$batContent: 最終的な中身が何であるかを示唆。 -
$enc→ 直接[System.Text.Encoding]::Unicodeを使用するように変更(意図が伝わりやすいため)。
-
-
エンコードの厳密化:
-
utf-16よりもUnicodeと記述する方がPowerShell内では一般的で、-EncodedCommandが要求する UTF-16 Little Endian と確実に一致します。
-
-
ファイル操作の近代化:
-
[System.IO.File]の代わりにPowerShell標準のGet-Contentなどを使用し、可読性を高めます。
-
-
Join-Pathの活用:- パスの結合に
+を使うとスラッシュの有無でエラーが起きやすいですが、Join-Pathを使うと安全にパスを作成できます。
- パスの結合に
-
ヒアドキュメント (
@"..."@) の利用:-
+=で文字列を連結するよりも、バッチファイル全体の構造が視覚的に分かりやすくなります。
-
-
-ExecutionPolicy Bypass:-
UnrestrictedよりもBypassの方が、警告を最小限に抑えて実行できるため、バッチ化の用途には適しています。
-
-
既存ファイルのチェック:
- 同名のファイルがある場合に上書きするかどうかの安全策を追加します。
保存時のエンコードについて
この「変換スクリプト自体」を保存する際は、「UTF-8 (BOM付き)」 で保存することをお勧めします。
-
メモ帳(Notepad)で保存する場合:
名前を付けて保存する際、右下のエンコードで「UTF-8」を選択すれば、最近の Windows 10/11 のメモ帳なら自動的に(実質的に)BOM付き、あるいは適切な形式で保存されます。 -
VS Codeの場合:
右下のエンコード設定をクリックして「Save with Encoding」から「UTF-8 with BOM」を選択。 -
理由:
日本語コメントの文字化けを防ぎ、PowerShellがスクリプトを正しく解釈するためです。
この手法を導入した成果
自作のスクリプトを改良したことで、以下のような運用が可能になりました。
-
配布が楽に:
.ps1本体を同梱する必要がなく、1つの.batファイルを渡すだけで完結。 - サポート工数の削減: 「実行ポリシーでブロックされる」「文字化けして動かない」といった問い合わせが激減。
- 安全な運用: Base64化されているため、エンドユーザーが誤ってスクリプトの中身を書き換えてしまうリスクを低減。
おわりに
今回の改良にあたっては、Gemini に「Windowsの内部的な文字コードの扱い」や「Intuneでの配布を見据えたパスの構成」について技術的な壁打ち相手になってもらいました。AIを単なる「コード生成」に使うのではなく、 「自作ツールの弱点を補強するアドバイザー」 として活用することで、個人の開発では見落としがちな細かい仕様まで考慮したツールへと進化させることができました。
免責事項
本スクリプトの使用により生じた損害やトラブルについて、筆者は一切の責任を負いません。内容を十分に理解した上で、自己責任でのご利用をお願いいたします。