PowerShellでパスワードを暗号化して保持する方法

背景

PowerShellのスクリプトを実行中に別ユーザのCredential(認証情報)を使ってInvoke-Commandを実行する必要に迫られました。 Credentialを生成するためにはユーザIDとパスワードが必要ですが、パスワードをスクリプトにべた書きするのは良識ある技術者なら絶対に避けるべき道です。 PowerShellのコマンドレットを調べていくと、「ConvertFrom-SecureString」を見つけることができました。

これを使うとCredential情報から抽出したパスワードを暗号化してテキストファイルに保持することができます。 暗号化されたテキスト文字列のことをセキュアストリングといいます。

セキュアストリングはユーザやコンピュータ(サーバ)によって異なる文字列となる

今回の対応で一番記録に残したいのはこの部分です。

同じ「A」というパスワードをConvertFrom-SecureStringに通した場合、 得られるセキュアストリングは実行ユーザによって異なります。

ただし、ドメインユーザのように、複数サーバにログインできるユーザであっても、 ConvertFrom-SecureStringを実行するコンピュータごとに異なるセキュアストリングが得られることが分かりました。

確かにセキュアストリングをコピーして使えたら意味がないのですが、 ドメインユーザでも処理を行うサーバごとに直接そのマシン上でConvertFrom-SecureStringを使ってセキュアストリングを用意する必要があるのは想定外でした。

セキュアストリングの作り方

ここは完全に偉大な先人の経験を拝借するのが早いです。

スケジュールジョブ(PowerShell)でパスワードをセキュアに使う(セキュアストリング編)

# スクリプトのあるフルパス
$CurrentDir = Split-Path $MyInvocation.MyCommand.Path -Parent

# パスワードファイルのフルパスを生成
$PasswordFile = Join-Path $CurrentDir "password.txt"

# ID/Password の入力
$Credential = Get-Credential

# パスワードファイルの生成
$Credential.Password | ConvertFrom-SecureString | Set-Content $PasswordFile

これでpassword.txtにセキュアストリングが得られます。 セキュアストリングはこのスクリプトを実行したユーザ(Get-Credentialで認証したユーザではない)だけが復号可能ですので注意が必要です。

例えば、スクリプトの実行ユーザUser1、Invoke-CommandはUser2で実行したい場合は、 User1で上記スクリプトを実行し、Get-CredentialでUser2の認証を行う必要があります。

セキュアストリングの使い方

これも先ほどの先人の経験に習います。

$CurrentDir = Split-Path $MyInvocation.MyCommand.Path -Parent
$PasswordFile = Join-Path $CurrentDir "password.txt"

# セキュアストリングとしてパスワードを取り出す
$SecurePassword = Get-Content $PasswordFile | ConvertTo-SecureString

# クレデンシャルの生成
$Credential = New-Object System.Management.Automation.PSCredential "User2",$SecurePassword

# クレデンシャルを使ってInvoke-Commandの実行
Invoke-Command TergetServer -Credential $Credential -ScriptBlock { dir c:\ }

スクリプトの実行ユーザはセキュアストリングを生成したUser1です。 $CredentialにUser2の認証情報が取得できるので、あとはそれを実行したいコマンドレット(ここではInvoke-Command)に渡せば目的が果たせます。