ツールをコマンドレットで使えるようにして同僚に配る(ついでにプロファイルフォルダの掌握とGitをインストールさせる)

# はじめに(2018-11-13修正)
酒飲みながら書いたのを、今読み返したらどこにも注記してなかったので、誤解と鉞が飛んでくるのを防ぐために大事な説明をします。

**プロファイルよりもモジュールでコマンドレットを配布するほうが推奨です。**
理由はこちらの記事がとても参考になります。
http://tech.guitarrapc.com/entry/2013/12/03/014013#簡易的な自動的なfunction読み込み--Profile

ただ今回はプロファイルを使って既に動くものを作っていたことと、

- 同僚にGitを入れさせることと
- 同僚のプロファイルのフォルダを掌握すること

が主な目的だったので、やむなくプロファイルを使って導入しました。
プロファイルのフォルダを掌握しておけば、あとからモジュール配布もできますし……

時間が出来たらモジュール化するので、その時は改めて記事を書きます。

**ひとりDevOpsは楽しい！！(動けば)**

# 要約
- PowerShellのプロファイル(bashの.bash_profile相当)を使ってツールをラップしてコマンドレットにすると大変使いやすい。
- プロファイルをGitlab等で管理して、「導入したければこのリポジトリをgit cloneして」と同僚に伝えることでほぼ導入が完了する。
- ついでにGitの入っていない同僚のPCに強制的にGitを導入させることができる。

結果として業務が捗るし、同僚たちがPowerShellを使い始めきっかけになる(気がしている)

# 背景
ひとりDevOpsして色々とツールを作ったのはいいけど、同僚たちはWindowsでCLIを使うことに馴染みがないしGitすら入っていなかったりしました。
せっかくツールを作っても使われないのでは、無価値と同じです。

どうやらPowerShellからスクリプトファイルに引数渡して実行して…という流れに何だか慣れていないらしい。
でも同僚たちはLinuxサーバのオペレーションはしてるし、CLIには馴染みがあるはずなのに……

「ならプロファイルでスクリプトをコマンドレットにしてしまえば、どのロケーションにいても便利に使えるしWindowsでCLI使うのに慣れるのでは？？」
と思い立ったのが始まりでした。

結果としてツールがより便利になって、いい感じに使われ始めました。
Windowsに囲まれた環境でCLIツールを導入させるなら、このやり方は結構いいと思います。

# 事前に必要な知識
## PowerShellスクリプト(ps1)の実行権限について
PowerShell初心者が真っ先にハマるのがps1ファイルの実行権限についてです。
初期状態ではps1ファイルの実行は禁止されています。
詳しくはこちらの記事が参考になります。
http://www.atmarkit.co.jp/ait/articles/0805/16/news139.html

``` powershell.exe
> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
```
上記が各所でよく紹介されるんですが、今回はスクリプトを署名なしで配布するので、
下記のようにカレントユーザーの範囲で署名なしスクリプトを実行することを許可することにします。

``` powershell.exe
> Set-ExecutionPolicy Unrestricted -Scope CurrentUser
```

## PowerShellのプロファイルについて
bashでいうところの.bash_profileみたいなものがPowerShellにもあります。
簡単に説明するとプロファイルに関数を書けば、コマンドレットとして使えます。

ユーザー単位なら\$env:USERPROFILE\Documents\WindowsPowerShell配下のプロファイルを参照します。
システム単位なら\$PSHOME配下のプロファイルを参照します。
詳しいことはこちらの記事が参考になります
⇒http://tech.guitarrapc.com/entry/2013/09/23/164357

今回はユーザー単位でツールを導入させる予定なので、\$env:USERPROFILE\Documents\WindowsPowerShell配下にプロファイルやスクリプトを保管します。


# やったこと
## スクリプトを作る
サンプルとして、Make-Folder.ps1というスクリプトを作ります。
これは引数で受け取った文字列のフォルダを作成するだけのスクリプトです。
mkdirのラッパーみたいなものですね。
保存場所は$env:USERPROFILE\Documents\WindowsPowerShell\Sample配下です。

``` Make-Folder.ps1
# 引数の取得
Param([string]$folder_name)

# 動作確認用に文字列を出力しておく
Write-Output "${folder_name}を作ります"

# フォルダを作る
mkdir $folder_name
```
※↑サンプルなので引数チェックをしてないのは許してください。


このままでは、

``` powershell.exe
> .$env:USERPROFILE\Documents\WindowsPowerShell\Sample\Make-Folder.ps1 <フォルダ名>
```
のように参照する感じでしか使えないので、プロファイルでコマンドレットとして使えるようにします。

## プロファイルを作る
\$env:USERPROFILE\Documents\WindowsPowerShell\配下にMicrosoft.PowerShell_profile.ps1を作ります。
既にファイルがある場合は追記してください。

``` Microsoft.PowerShell_profile.ps1
function Make-Folder {
    # 引数の取得
    param (
        [parameter(mandatory=$true)][string]$folder_name
    )
    # Make-Folder.ps1に$file_nameを渡して実行する
    .$env:USERPROFILE\Documents\WindowsPowerShell\Sample\Make-Folder.ps1 $folder_name
}
```

このとき注意したいのは、スクリプトファイルのパスの記述です。
相対パスで".\Sample\Make-Folder.ps1"のように記述すると、コマンド実行時の"カレントフォルダ\Sample\Make-Folder.ps1"を参照することになるので絶対参照で記述してください。
あと先頭のdot-source"."を忘れずに。

当たり前ですが、別にps1でなくてもプロファイルでスクリプトをラップできます。
GolangやRustで作ったツールのWindows実行バイナリをラップしてコマンドレットにすると、使いやすくてとても便利です。

またPowerShellのParamはコマンドラインパーサとしても便利なので、「PowerShellで引数解析して、メインの処理は実行バイナリで～」みたいないいとこ取りもできます。

とりあえずこれでMake-Folderコマンドレットが使えるようになりました。
試しに使ってみましょう

``` powershell.exe
> Make-Folder hogehoge
```
仕込んでおいた動作確認用の文字列が出力されるので、実行されているのがMake-Folder.ps1であることがわかります。


## リモートリポジトリの作成
サンプルのリポジトリをGithubに公開しました。
https://github.com/iranika/WindowsPowerShell

私の場合は、業務でGithubの公開リポジトリを使うのに色々と問題があったので
社内LAN上に建てた「ひとりDevOps用のGitlab」を使って同僚向けに公開していますが、
Bitbucketの非公開リポジトリなどを使うのもありだと思います。
そのあたりは環境に合わせていい感じの選択をしてください。

## 同僚に配布
「このツールを導入したければ、カレントユーザーの範囲で署名なしスクリプトの実行許可をして、このリポジトリをDocuments配下でgit cloneして」と同僚に伝えれば完璧……ではないのです。

実はWindows10だと\$env:USERPROFILE\Documents\WindowsPowerShellというフォルダは、既に作られていることが多いです。
なので、\$env:USERPROFILE\Documentsのロケーションでgit cloneしても既にフォルダがあるため下記のように失敗します。

```
$ git clone https://github.com/iranika/WindowsPowerShell.git
fatal: destination path 'WindowsPowerShell' already exists and is not an empty directory.
```

解決方法は簡単で
1. 既存のWindowsPowerShellフォルダを消す
2. 既存のWindowsPowerShellフォルダをリネームする
上記のいずれかで解決できます。

今回は「そもそもPowerShell使ってないんだろ？ならお前のプロファイル環境好きにさせてもらうぞ」
というスタンスなので消してもいいですし、心配ならリネームして切り戻せるようにすればいいのです。

あとはツールが更新された時に同僚にアナウンスしてgit pullしてもらえばいいんですが、
私は同僚に配るプロファイルにgit pullを仕込んでPowerShell起動時に最新版にアップデートするようにしました。
起動が遅くなるのでちょっと嫌がらせですね。

## あとがき
別にプロファイルの配布はGit使わなくてもできるんですが、「Gitが推奨です」といえば渋々でも入れてくれます。
今のところ同僚にはダウンローダーとしてしか使われていないGitですが、そのうち「ブランチ切って、変更をコミットしてマージリクエストなげて」とお願いしたいです。