PowerShellでサーバアクセスするときのパスワード入力で楽する

この記事は[ZOZOテクノロジーズ #3 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech3) 15日目の記事になります。

今年は全部で5つのAdvent Calendarが公開されています。

- [ZOZOテクノロジーズ #1 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech)
- [ZOZOテクノロジーズ #2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech2)
- [ZOZOテクノロジーズ #4 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech4)
- [ZOZOテクノロジーズ #5 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech5)

## 概要

境界内リソース管理ではまだまだ社内ActiveDirectoryメンテナンスをする機会は多い。

ActiveDirectory参加済み端末でメンテナンスをするなら以下のような感じ。

``` Power-Shell
PS> Get-ADUser -Filter {mail -eq "takumi.ito@zozo.com"} -Properties DisplayName
```

ドメイン参加PCであれば信頼関係が結ばれているためログインアカウント認証情報で利用できるので楽

しかしMicrosoft365やAzureなど境界外メンテナンスを行う場面も多いし、参加していないサーバへの処理、ログインアカウント以外の認証情報を利用したい場合など認証情報入力を求められ得る場面は多い。
楽にしよう。

## 方法

`Get-ADUser` はCredentialオプションで `PSCredential` オブジェクトを渡すことができる。

``` Power-Shell
PS> $ADCard = Get-Credential -User Domain\takumi.ito -Message "ドメインログインパスワードを入力してください。"
*パスワード入力
PS> Get-ADUser -Filter {mail -eq "takumi.ito@zozo.com"} -Properties DisplayName -Credential $ADCard -AuthType Negotiate -Server Server01
```

これで `PSCredential` を利用して `Get-ADUser` が利用出来る。

AuthTypeやServerについては未参加端末などの場合は必要。

### パスワード打ちたくない

スクリプトに平文でパスワードを打ち込むわけにもいきませんので `System.Security.SecureString` で保存

#### setPassword.ps1

これで保存してみよう

``` Power-Shell
$Credential = Get-Credential -User Domain\takumi.ito -Message "ドメインログインパスワードを入力してください。"

$PasswordFile=Join-Path (Get-Variable | Where-Object {$_.Name -eq 'HOME'}).Value "\My Documents\WindowsPowerShell\password.sec"

If(-Not (Test-Path $PasswordFile)){
    $PasswordFile = New-Item -Path $PasswordFile -ItemType file -Force
}

$Credential.Password | ConvertFrom-SecureString | Set-Content $PasswordFile -Encoding UTF8
```
ファイルの中身はSecureStringになります。

```
PS> Get-Content $PasswordFile
01000000d08c9ddf0115d1118c7a00c04fc297eb010000005d41...
```
#### $profile への設定
今回は `%UserProfile%\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1` を利用

``` Power-SHell
$PasswordFile=Join-Path (Get-Variable | Where-Object {$_.Name -eq "HOME"}).Value "\My Documents\WindowsPowerShell\password.sec"

$Password = Get-Content $PasswordFile -Encoding UTF8 | ConvertTo-SecureString

$ADCard = New-Object System.Management.Automation.PSCredential "Domain/takumi.ito",$Password

$M365Card = New-Object System.Management.Automation.PSCredential "takumi.ito@zozo.com",$Password
```

これで境界内では `$ADCard` 境界外は `$M365Card` がシェルを起動するとセットされているので利用できる。

認証情報が出来ているという前提で以下の様に楽をする

```
PS> Get-ADUser -Filter {mail -eq "takumi.ito@zozo.com"} -Properties DisplayName -Credential $ADCard -AuthType Negotiate -Server Server01

PS> Connect-MsolService -Credential $M365Card
PS> Get-MsolUser -UserPrincipalName takumi.ito@zozo.com 
```

境界内の未参加サーバや境界外のメンテの時に都度打ち込まなくて良いので地味に楽かなと思います。
パスワード強度の強化で複雑化したパスワードの入力ミスでロックアウトなども避けたいですし。

ただし Microsoft365 などの Credential オプションは多要素の場合動かなかったりするので `管理者によって許可された信頼済みIP` からの処理をするなど対応も必要な場合はあります。

明日のZOZOテクノロジーズアドベントカレンダー#3 @tomtomtommy18 さんです。
是非そちらもご覧ください！

## 参考

- [Microsoft Docs > Get-ADUser](https://docs.microsoft.com/en-us/powershell/module/addsadministration/get-aduser?view=win10-ps)
- [Microsoft Docs > Get-Credential](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/get-credential?view=powershell-6)
- [Microsoft Docs > SecureString Class](https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring?view=netframework-4.8)

### ４つあるprofileによる違い

#### %windir%\system32\WindowsPowerShell\v1.0\profile.ps1
すべてのユーザーおよびすべてのシェルに適用
####%windir%\system32\WindowsPowerShell\v1.0\ Microsoft.PowerShell_profile.ps1
すべてのユーザーに適用されますが、Microsoft.PowerShell シェルのみに適用|
####%UserProfile%\My Documents\WindowsPowerShell\profile.ps1
現在のユーザーのみに適用ですべてのシェルに作用|
####%UserProfile%\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
現在のユーザーと Microsoft.PowerShell シェルのみに適用