スタッフブログ

PowerShellの至る所に潜む罠


SCD プラットフォームビジネスエンジニアの新宅です。
まもなく4月を迎えようとしており、入社から1年経とうとしております。
また、4月ということで、弊社では新人が来週から出社する予定となっております。

この1年間だけでも、様々なことが起こり、また様々な案件に関わってきました。
その中で、私も少しずつ慣れてきた部分はありますが、まだ不十分だと思うこともあります。
新卒入社直後は、何もかもが分からないところからのスタートですから、新人をサポートできるところはしていきたいと思っている今日この頃です。

さて、今回はPowerShellの話題です。

What’s PowerShell?

PowerShellの主な特徴

PowerShellは、Windows 7/Server 2008以降にプリインストールされているコマンドラインツールです。コマンドプロンプトと異なり、現在のフォルダパスの前に「PS」という文字が付くことと、カラフルな文字・ウィンドウが特徴的です。

Windows 10 Anniversary Update(1607)まではコマンドプロンプトがデフォルトになっており、Windows 10 Creators Update(1703)以降はこのPowerShellがデフォルトになっております。PowerShellがインストールされている環境であれば、コマンドプロンプト上で「PowerShell」コマンド もしくは PowerShell上で「cmd」コマンド を打つことによって、ウィンドウや参照フォルダ位置を保ったまま、相互に切り替えることが可能です。
OSなどによってバージョンに差はありますが、基本的に上位互換性(上位バージョンが過去バージョンの互換性)を持っており、またWindows 7/8.1などをお使いの場合でも、追加インストールによってアップグレードすることが可能です。最近では、Linux向けにもPowerShellがリリースされているようです。

コマンドプロンプトとの機能・学習コストの比較

従来のコマンドプロンプトは、比較的シンプルなコマンドが多く覚えやすいですが、MS-DOS由来のコマンド群が中心で、お世辞にも現代的とは言えず、機能も不足していました。「Ctrl+V」による貼り付けですら、Windows 10 初版で初めて導入されたように、ほとんど改善の手が入っていなかったため、こちらはレガシーなものと言えるでしょう。コマンドプロンプトのバッチファイルは、少し詳しい方ならご存知かもしれませんが、通常は「.bat」ファイルになるでしょう。
一方で、PowerShellは現代的な機能を多数導入しています。その分複雑になることは間違いなく、非常に覚えるためのコストが掛かるというデメリットがあります(それでも後述のコマンドの特徴が分かれば、若干コストが下がるとは思います)。しかしながら、「モジュール」と呼ばれる追加コマンド機能群によって、様々な機能を追加することが可能です。例えば、Microsoft Azure、Office 365、Exchange Online、SharePoint Online…といった、各種Microsoftクラウドサービスの管理用コマンドをこの「モジュール」のインストールによって、必要なものを追加していくことができるという、非常に便利なメリットがあります。さらに、各種変数を「$(変数名)」と用意して、代入・呼び出しをすることが可能になっているため、自動化がより柔軟になるメリットがあります。PowerShellのバッチファイルは、「.bat」ではなく、通常は「.ps1」ファイルになります。

PowerShellのコマンドの特徴

コマンドプロンプトのコマンドは、種類が少ないこともあり、「cd」のように非常に短いコマンド名でした。

一方、PowerShellのコマンドは、種類が極めて豊富です。すべてのモジュールを含めると何種類あるのか、もはや誰にも数えられません。
そこで、PowerShellのコマンド名の基本スタイルは、「(動詞)-(名詞)」となっております。例えば、コマンドプロンプトの「cd」に相当するコマンドは、「Set-Location」というコマンド名になっています。

PowerShellの変数の罠について

PowerShellの変数の概要

先ほど、このように記述しました。

各種変数を「$(変数名)」と用意して、代入・呼び出しをすることが可能になっているため、自動化がより柔軟になるメリットがあります。

もっとも簡単な例を挙げると、

$string = “Hello World!”
Write-Host $string

と実行した場合、

Hello World!

と表示されるという具合です。ちなみに、Write-Hostは、他でいうechoコマンドに近い感じのものです。

例えば、同じオプション内容を含むコマンドを何度も実行したり、CSVを読みだしたものをオプション内容にしたり、といったことができます。

変数の応用

CSVの中身をオプションに入れ、記述されているすべての行を繰り返し処理したいなら、

Import-Csv xxx.csv | ForEach-Object { [実際に発行したいコマンド][オプション名] $_.[CSVの列名]}

といったような形で記述すると、$_.[CSVの列名] の部分に順番に代入して実行してくれます。
実際の例を挙げると、Office 365のPowerShellによる管理で便利です。Import-Module MsolOnlineなど、PowerShellからOffice 365を管理できる環境を実行し、列名が「UserPrincipalName」だけのユーザーメールアドレス一覧のCSVを用意したうえで、下記コマンドを実行すると、CSVに記載されたユーザーのOffice 365の使用場所設定がすべて日本に切り替わります。

Import-Csv “xxx.csv” | ForEach-Object {Set-MsolUser -UserPrincipalName $_.UserPrincipalName -UsageLocation JP }

これは、「CSVを1行ごとにSystem.Object[]という配列に入れ(Import-Csv)、配列の要素ごとに(ForEach-Object)、Office 365の指定したユーザー(-UserPrincipalNameオプション値 $_.UserPrincipalName)の使用場所(-UsageLocation)を日本(JP)に変更(Set-MsolUser)する」ということになります。

下記のように、こういったコマンドの返り値を変数に入れることも可能です。

$myReport = ”JSON形式のString”
$return = ($myReport | ConvertFrom-Json).value | ConvertTo-Csv -NoTypeInformation

変数の型について

経験された方はもちろんのこと、ここでお気づきになった方もいらっしゃるかもしれません。

あれ?型指定一切してないじゃん…

PowerShellは、内部でオブジェクトの型が決まってはいますが、暗黙的に変換してしまうことがあるようです。代入する内容によって、代入される変数の型が変更されます。

下記コマンドで変数の型を確認することができます。

$(変数名).GetType()

PowerShellのスクリプトを諸事情で作成していましたが、ここで結構ハマりました。

非常に厄介なPowerShellの暗黙的な型変換

先ほどご紹介いたしました、

$myReport = ”JSON形式のString”
$return = ($myReport | ConvertFrom-Json).value | ConvertTo-Csv -NoTypeInformation

このコマンドで出てくる変数の$myReportと$returnとでは型が異なります。
この例では、$myReportはString型ですが、$myReportが1レコードごとのObject型配列に変換され、それが再びCSV配列に変換されたものが、$reportにCSV配列型として代入されます。このCSV配列型な$returnをString型だと思い込んで、String型でないと使用できないコマンドを利用しようとしたり、そのままどこかのAPIにPOSTしたりすると、エラーが返ってくることは間違いありません。

このCSV配列型な$returnを、String型に戻すには、「-join」によって、配列を改行記号で連結すると、複数行のStringに戻るというわけです。

$return = $return -join “`n”

…と言った感じで、非常に厄介です。

問題はまだ他にもある

Out-Fileコマンドという、Stringの内容をテキストファイルとして出力できる便利なコマンドがあります。しかし、このデフォルトが通常は「Shift-JIS」なのですが、時々「ISO-8859-1」エンコード(俗にいう「Latin1」エンコード)で出力されることがあります。ここでは割愛しますが、変換が厄介なケースがありました。

それでもPowerShellを利用する価値がある

まず、「Azure Automation」によって、PowerShellスクリプトファイル(.ps1)の内容をほぼそのまま、VMを用意することなく、定期スケジュール実行することが可能です。

Office 365ユーザーの大量管理にも、PowerShellが効果的です。ユーザーリストの取得から、各種アプリケーション設定まで、非常に多くのユーザーが存在していたとしても、素早く完了します。

つまり、Microsoftのクラウドを管理するためには、もはやPowerShellは避けて通れない道になりつつあるということです。少なくとも、今からコマンドプロンプトのバッチを作成するより、PowerShellで作成したほうが良いと言えます。