PowerShellで認証プロキシ越え

# 目的
Invoke-RestMethod コマンドレットや Invoke-WebRequest コマンドレットをユーザ認証のあるプロキシサーバを越えて使用するための設定を行う。

# 成功例

```powershell
$proxyServer  = "http://xxx.xxx.xxx:8080"    # 未設定の場合はIEの設定を利用する
$proxyUser    = "xxxxx"                      # 未設定の場合は認証なしとみなす
$proxyPassword= "xxxxx"                      # 未設定の場合はプロンプトする

if (-not $proxyServer) {
    $proxy = [System.Net.WebRequest]::GetSystemWebProxy()
    if ($proxy) { $proxyServer = $proxy.GetProxy("http://example.com").AbsoluteUri }
}
if ($proxyServer) {
    $proxy = New-Object System.Net.WebProxy $proxyServer, $True
    if ($proxyUser) {
        if ($proxyPassword) { 
            $securePassword = ConvertTo-SecureString $proxyPassword -AsPlainText -Force
            $proxy.Credentials = New-Object -TypeName System.Management.Automation.PSCredential $proxyUser,$securePassword
        } else {
            $proxy.Credentials = Get-Credential -UserName $proxyUser -Message "プロキシサーバのパスワードを入力して下さい。"
        }
    }
    [System.Net.WebRequest]::DefaultWebProxy = $proxy
}
```

# 失敗例（本当はこちらを使いたい）
なぜ失敗するのか不明（環境依存のバグかも。ネット上に Win7,　Win8.1 で失敗、Win10 で成功との情報あり）。
<font color='red'>(2019.7.23更新)　Win10での成功を確認しました。</font>

```powershell
$proxyServer  = "http://xxx.xxx.xxx:8080"    # 未設定の場合はIEの設定を利用する
$proxyUser    = "xxxxx"                      # 未設定の場合は認証なしとみなす
$proxyPassword= "xxxxx"                      # 未設定の場合はプロンプトする

if (-not $proxyServer) {
    $proxy = [System.Net.WebRequest]::GetSystemWebProxy()
    if ($proxy) { $proxyServer = $proxy.GetProxy("http://example.com").AbsoluteUri }
}
if ($proxyServer) {
    if ($proxyUser) {
        if ($proxyPassword) { 
            $securePassword = ConvertTo-SecureString $proxyPassword -AsPlainText -Force
            $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $proxyUser,$securePassword
        } else {
            $credential = Get-Credential -UserName $proxyUser -Message "プロキシサーバのパスワードを入力して下さい。"
        }
        $PSDefaultParameterValues = @{ "Invoke-RestMethod:Proxy"=$proxyServer; "Invoke-RestMethod:ProxyCredential"=$credential;
                                       "Invoke-WebRequest:Proxy"=$proxyServer; "Invoke-WebRequest:ProxyCredential"=$credential }
    } else {
        $PSDefaultParameterValues = @{ "Invoke-RestMethod:Proxy"=$proxyServer;
                                       "Invoke-WebRequest:Proxy"=$proxyServer  }
    }
}
```

# 追記（2019.7.27）
スクリプト内へのパスワード記載（平文）を避ける方法について３つ程考察してみます。

#### 方法１ 既定の資格情報（DefaultCredentials）の利用
プロキシサーバの認証が、Windowsと共通であれば、既定の資格情報が利用できますので、Invoke-RestMethod コマンドレットや Invoke-WebRequest コマンドレットのパラメータとして `ProxyCredential`の代わりに`-ProxyUseDefaultCredentials`を指定することができます。

#### 方法２ 資格情報マネージャーに格納された資格情報の利用
プロキシサーバの認証情報を資格情報マネージャーに格納されているキャッシュから取得します。資格情報マネージャーの情報にアクセスするためには、advapi32.dll の ReadCredW 関数を使う必要あります。
PowerShell から ReadCredW を呼び出して Credential オブジェクトとして返す関数の例が、[ここ](https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Credentials-d44c3cde)や[ここ](https://gist.github.com/toburger/2947424)にありますが、数行で実装という訳にはいきません。手軽に使うには、ちょっと...って感じですね。

<font color='red'>(2020.10.24追記)</font>
WinRTを利用して `$null = [Windows.Security.Credentials.PasswordVault,.,ContentType=WindowsRuntime]; $vault = (New-Object Windows.Security.Credentials.PasswordVault).RetrieveAll(); $vault.RetrievePassword(); $vault` で取得できるようです。

#### 方法３ 暗号化された文字列としてスクリプト内に埋め込む
ConvertFrom-SecureString コマンドレットで、\$securePassword を暗号化された文字列に変換して、それをスクリプト内に保存します。SecureString に戻すには ConvertTo-SecureString コマンドレットを使用します。

クリエイティブ・コモンズ 表示 - 継承 4.0 国際