カッコイイ！スクリプトを書こう！

職場のネットワーク環境が変更になるということで、その設定を行う為に設定を自動化するスクリプトを組みました。
Windows機でガチガチセキュリティの端末だったのでPowerShellを使って組んだのですが、今回はスクリプトの内容では無くスクリプト実行時の見せ方について共有したいと思います。

## スクリプトは一瞬で終了する。
今回はWindowsのサプリカントの設定を自動化しました。
これは Wi-Fi接続認証をAuthenticator経由のRADIUSサーバで行う為に証明書認証をする為の設定です。
学校の先生方では設定が複雑すぎてミスが多発し、作業に平均1時間〜2時間かかっていました。

しかし、スクリプトにすれば設定は一瞬です。
実際10秒程で完了しました。

```powershell サプリカント設定.ps1
# 802.1X サプリカント設定処理
try {
    $interfaceName = "Wi-Fi"
    $SSID = "Your-SSID"
    $xmlPath = "YourWiFiProfile.xml"

    # XML ファイルの存在確認
    if (-not (Test-Path $xmlPath)) {
        throw "WiFi プロファイルの XML ファイルが見つかりません: $xmlPath"
    }

    # プロファイルの追加
    Write-Host "WiFi プロファイルを追加しています..." -ForegroundColor Cyan
    $result = netsh wlan add profile filename="$xmlPath" interface="$interfaceName" 2>&1
    if ($LASTEXITCODE -ne 0) {
        throw "プロファイルの追加に失敗しました: $result"
    }
    Write-Host "プロファイルが正常に追加されました。" -ForegroundColor Green

    # 自動接続を無効化
    Write-Host "WiFi プロファイル設定を更新しています..." -ForegroundColor Cyan
    $result = netsh wlan set profileparameter name="$SSID" connectionmode=manual 2>&1
    if ($LASTEXITCODE -ne 0) {
        throw "プロファイルパラメータの設定に失敗しました: $result"
    }
    Write-Host "プロファイルパラメータが正常に設定されました。" -ForegroundColor Green

    Write-Host "802.1X サプリカントの設定が完了しました。" -ForegroundColor Green
}
catch {
    # エラー処理
    Write-Host "エラーが発生しました。" -ForegroundColor Red
    Write-Host $_.Exception.Message -ForegroundColor Red
}
```

XMLファイルの中身は割愛しますが、別途XMLファイルにSSIDや証明書のサムプリント等を記述しています。

スクリプトは100％完全動作するので全く問題なかったのですが、コーディングからチェック等含めると完成までに３日かかりました。
設定マニュアルしかないのでネットワーク構成や仕様を調査や想像しながら作らないといけなかったので、それらが公開されていたらもっと早く完成していたと思います。
でもここで個人的問題にぶち当たりました。

## 一瞬で設定出来てしまう問題
スクリプトが一瞬で完了してしまうと、
「何か動いてよく分からないけれど完了した！ありがとう！」
で終わってしまいます。

勿論それはそれで、いいんですが、こちとら無償で作成した努力は全く見えません。
プログラムを作った事がある人ならば、この労力を理解してくれると思うのですが、
そうでなければ「何かよく分からんけど簡単に出来るんだね！」で終わってしまうんですね。

なので何だか癪だと思い無駄にスクリプトを大袈裟にしてみました。

```powershell サプリカント設定.ps1
# PowerShell 実行設定
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest

# スクリプトの場所を作業ディレクトリに設定
Set-Location $PSScriptRoot

# ログ設定(一応吐くようにしている)
$logDriveName = "D:"
$logFilePath = "$logDriveName\WiFiSupplicantSetup.log"
$exitCode = -1

# コンソール出力の色設定
$infoColor = "Cyan"
$successColor = "Green"
$warningColor = "Yellow"
$errorColor = "Red"

# タイプライター関数
function Show-TypewriterEffect {
    param (
        [string]$Text,
        [string]$Color = $infoColor
    )
    $Text.ToCharArray() | ForEach-Object {
        Write-Host $_ -NoNewline -ForegroundColor $Color
        Start-Sleep -Milliseconds 50
    }
    Write-Host ""
}

# プログレスバーの関数
function Show-ProgressBar {
    param (
        [string]$Activity,
        [int]$DurationSeconds
    )
    for ($i = 1; $i -le 100; $i++) {
        Write-Progress -Activity $Activity -Status "$i% Complete:" -PercentComplete $i
        Start-Sleep -Milliseconds ($DurationSeconds * 10)
    }
    Write-Progress -Activity $Activity -Completed
}

# エラーログ書き込み
function ErrorLog([string]$msg) {
    Write-Host $msg -ForegroundColor $errorColor
    $msg | Out-File -Append $logFilePath -Encoding UTF8
}

# 通常ログ書き込み
function Log([string]$msg) {
    Show-TypewriterEffect $msg $infoColor
    $msg | Out-File -Append $logFilePath -Encoding UTF8
}

try {
    if (-not (Test-Path $logDriveName)) {
        $msg = "ドライブ $logDriveName が見つかりません。処理を開始できません。"
        Write-Host ""
        Write-Host ""
        Write-Host $msg -ForegroundColor $errorColor
        Write-Host ""
        Pause
        exit $exitCode
    }

    Log "***"
    Log "開始"
    Log ("({0})  サプリカントセットアッププロセスを開始します。" -f (Get-Date -Format "yyyy/MM/dd-HH:mm:ss.ffff"))
    Log ""

    # NIC 設定用の実行ファイルを実行
    Log "Nic_set.exe を使用してNICの設定を開始します。"
    $nicSetPath = "xxx.exe"
    if (-not (Test-Path $nicSetPath)) {
        throw "Nic_set.exe が見つかりません: $nicSetPath"
    }
    Show-ProgressBar -Activity "NIC設定中" -DurationSeconds 10
    $nicSetProcess = Start-Process -FilePath $nicSetPath -Wait -PassThru
    if ($nicSetProcess.ExitCode -ne 0) {
        throw "Nic_set.exe が異常終了しました。終了コード: $($nicSetProcess.ExitCode)"
    }
    Show-TypewriterEffect "NICの設定が完了しました。" $successColor

    # 1分間待機してNIC設定の完了を確実にする
    Log "NIC設定を確実に完了させるために1分間待機します。"
    Show-ProgressBar -Activity "NIC設定完了待機中" -DurationSeconds 60

    # CA_kick.exe を実行して証明書をインストール
    Log "CA_kick.exe を使用して証明書のインストールを開始します。"
    $caKickPath = "yyy.exe"
    if (-not (Test-Path $caKickPath)) {
        throw "xxx.exe が見つかりません: $caKickPath"
    }
    Show-ProgressBar -Activity "証明書インストール中" -DurationSeconds 10
    $caKickProcess = Start-Process -FilePath $caKickPath -Wait -PassThru
    if ($caKickProcess.ExitCode -ne 0) {
        throw "xxx.exe が異常終了しました。終了コード: $($caKickProcess.ExitCode)"
    }
    Show-TypewriterEffect "証明書のインストールが完了しました。" $successColor

    # 1分間待機して証明書のインストールを完了させる
    Log "証明書のインストールを確実に完了させるために1分間待機します。"
    Show-ProgressBar -Activity "証明書インストール完了待機中" -DurationSeconds 60

    Log "802.1X サプリカントのセットアップを開始します。"

    $interfaceName = "Wi-Fi"
    $SSID = "xxx-xxxx"

    # XML ファイルの読み込み
    $xmlPath = "yyy-zzz.xml"
    if (-not (Test-Path $xmlPath)) {
        throw "WiFi プロファイルの XML ファイルが見つかりません: $xmlPath"
    }

    # プロファイルの追加（管理者権限なしコマンド）
    Show-ProgressBar -Activity "WiFiプロファイル追加中" -DurationSeconds 5
    $result = netsh wlan add profile filename="$xmlPath" interface="$interfaceName" 2>&1
    if ($LASTEXITCODE -ne 0) {
        throw "プロファイルの追加に失敗しました: $result"
    }
    Show-TypewriterEffect "プロファイルが正常に追加されました。: $result" $successColor

    # 自動接続を無効化
    Show-ProgressBar -Activity "プロファイル設定更新中" -DurationSeconds 5
    $result = netsh wlan set profileparameter name="$SSID" connectionmode=manual 2>&1
    if ($LASTEXITCODE -ne 0) {
        throw "プロファイルパラメータの設定に失敗しました: $result"
    }
    Show-TypewriterEffect "プロファイルパラメータが正常に設定されました。: $result" $successColor

    Log "802.1X サプリカントのセットアップが完了しました。"
    Log ("({0})  全てのセットアッププロセスが完了しました。" -f (Get-Date -Format "yyyy/MM/dd-HH:mm:ss.ffff"))
    $exitCode = 0
}
catch {
    # エラーハンドリング：スタックトレースをログに書き込む
    ErrorLog "処理中にエラーが発生しました。"
    $ex = $_.Exception
    $strException = @"
+ メッセージ          : $($ex.Message)
+ スタックトレース    : $($ex.StackTrace)
"@
    ErrorLog $strException
    $exitCode = 1
}
finally {
    Log ""
    Log "終了 (Exit Code: $exitCode)"
    Log "***"
    Write-Host "この画面を終了するには Enter キーを押してください..." -ForegroundColor $infoColor
    Read-Host
    exit $exitCode
}
```
具体に何をしたかと言いますと、
実行中にエフェクトを導入しました。
今回使用したのは、以下２つです。
* タイプライター風エフェクト
* プログレスバーエフェクト

タイプライター風エフェクトは自動で文字が打ち込まれる感じで、実行している人の目を惹きます。
プログレスバーを使用する事で時間がかかり「作業している感」が出ます。
時間がある程度かかると「すごいプログラムなんだなぁ！」と心理的になる訳です。

ただ実際問題として、設定が確実に反映されるよう敢えて1分待機させる事で確実に設定が完了して、次のステップに進むようにする事でエラーを防ぐことが出来ました。
でも何も表示されていないと動いているのか動いていないのか分からないのでエフェクトとして表示しておくと、どんな状況か分かるのは良いなと思いました。

## 結果
今回の経験で思った事は自身の評価をちょっと大袈裟にするのも悪くないかな！って事です。
見た目も良くなるし、使ってもらう人にも「何かめちゃくちゃスゲー！」
って思って貰える事は、それほど悪い事ではないなと。
特にエンジニアばかりの職場じゃない場所で、善意でコードやスクリプトを作成する場合は、偉そうにするとかじゃないけれど、大袈裟にしておく事で少しくらい多めに感謝して貰ってバチは当たらないと思う訳です。

勿論バランスが重要だと言うことは頭に入れておいてください。

以下は今回PowerShellの実行イメージをReactで作成し、GIFにしたのです。
Powershellでも実際このように動きました。
![a.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2841127/50515b65-da55-313c-83cd-64efb60c8647.gif)