如何以PowerShell安全的進行自動化登入-以Microsoft Azure平台為例

如何以PowerShell安全的進行自動化登入-以Microsoft Azure平台為例

twMVC#34Jamis 聊天時他提出一個 PowerShell 問題,像我們在使用 PowerShell 操作 Microsoft Azure 時都必須進行 Login 的動作,例如,每次下 Connect-AzureRmAccount 都必須先輸入帳號密碼,這有點煩。第二個問題,在 Powershell Script 要進行多訂閱切換很不方便,例如,下 Connect-AzureRmAccount 就是會跳出輸入視窗,這根本無法自動化。

我們先處理跳出的登入視窗。如果是微軟官方提供 PowerShell Cmdlet,通常涉及「憑證」的 Cmdlet 都會提供一個 -Credential 參數,例如查詢在 Connect-AzureRmAccount 文件確認是否有 -Credential 參數:

Connect-AzureRmAccount
       [-Environment <String>]
       [[-Credential] <PSCredential>]
       [-TenantId <String>]
       [-Subscription <String>]
       [-ContextName <String>]
       [-SkipContextPopulation]
       [-Force]
       [-Scope <ContextModificationScope>]
       [-DefaultProfile <IAzureContextContainer>]
       [-WhatIf]
       [-Confirm]
       [<CommonParameters>]

我們就能把「憑證」提供給 -Credential 參數來使用。憑證如果會重覆使用,我們通常會在當下視窗(PowerShell 稱為 Session)的憑證儲存在變數裡重覆使用,例如:

$cred = Get-Credential
Connect-AzureRmAccount -Credential $cred
  1. 雖然 Get-Credential 還是會跳出輸入視窗,由於我們把值存下來,此後就能重覆使用 $cred 的值。$cred 的帳號會是明文,密碼會被加密。
  2. Connect-AzureRmAccount -Credential $cred 單就登入作業而言,已經不會再跳出視窗,算是已經登入的自動化。

另一種用法是:

$cred = Get-Credential
.\YourPowerShell.ps1

在當下 Session,$cred 除非被重新賦值或關閉 Session 視窗,不然值會一直存在。YourPowerShell.ps1 可以直接取 $cred 來使用。換句話說,當我們離開(或關閉)此 PowerShell 的 Session 後,還是免不了要重新製作一個 $cred,不然 YourPowerShell.ps1 不能正常執行。再者,像自動化腳本根本不可能去手動輸入 Get-Credential,怎麼辦?這時你可以在 .ps1 中多寫個 function 來取得所需的 PSCredential 物件:

function GetBruceCredential() {
    $account = "kkbruce"
    $secString = ConvertTo-SecureString "TaiwanNo1" -AsPlainText -Force
    $cred = New-Object System.Management.Automation.PSCredential ($account, $secString)
    return $cred
}

這個方法只有唯一一個非常明顯的缺點,就是在你的 PowerShell Script 中需要寫入明文帳密資訊,優點是,它能解決自動化過程中需要輸入帳密的問題。

還有沒有更好解決的辦法,可以完成自動化作業,又能不寫入明文帳密呢?是的,可以。不然枉費標題多加"安全的"三個字。

PowerShell 是個物件的世界,而 PowerShell 提供了一個將這些物件進行匯出的功能:Export-Clixml,說明簡單直白:Creates an XML-based representation of an object or objects and stores it in a file.

$cred | Export-Clixml -Path .\bruce-credential.xml
$cred = Import-Clixml -Path .\bruce-credential.xml

你可以先呼叫 Export-Clixml 將憑證物件進行匯出的動作,XML 的內容部份,因為 $cred 本來密碼的部份已被加密,當然匯出的內容也是加密內容。比起我們自己寫的 GetBruceCredential 而言,安全性已提高太多了。

<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Management.Automation.PSCredential</T>
      <T>System.Object</T>
    </TN>
    <ToString>System.Management.Automation.PSCredential</ToString>
    <Props>
      <S N="UserName">kkbruce</S>
      <SS N="Password">d08c9ddf0115d...hash...d98f49b896</SS>
    </Props>
  </Obj>
</Objs>

當 PowerShell Script 需要憑證時,也只需一行 Import-Clixml 來將憑證資訊匯入,就能在 PowerShell Script 使用,這樣就能完成安全的自動化作業的腳本。改寫一下我們的 function:

function GetBruceCredential() {
    $cred = Import-Clixml -Path .\bruce-credential.xml
    return $cred
}

注意,Export-Clixml 採用 Windows Data protection API 來進行憑證資訊的加密,也就是說,唯有同一台電腦同一個帳號才能讀取(Import-Clixml)使用。如果非同一台電腦或帳號,你應該會看到以下錯誤訊息:

c : Key not valid for use in specified state.
At line:1 char:13
+ $credtest = Import-Clixml -Path .\bruce-credential.xml
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Import-Clixml], CryptographicException
    + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicException,Microsoft.PowerShell.Commands.ImportClixmlCommand

Export-ClixmlImport-Clixml 的主機與身份限制或許會造成一些麻煩,但安全本來就需要付出一些些代價。

ConvertTo-SecureStringConvertFrom-SecureString,雖然平常是在本機記憶體內操作,但如果你把 System.Security.SecureString 的內容寫至檔案,拿到其他電腦讀入,並轉換為 SecureString 時,你能發現和 Import-Clixml 大至相同的錯誤訊息。

最後一個問題,像我有個人、MVP、公司等多個訂閱,我想要進行切換也很簡單,只要將每個訂閱的憑證 XML 事先製作好,需要時載入對應的 XML 憑證進來,這樣就能自由的切換訂閱帳戶的切換。最後注意,使用 -Credential 方法進行登入的帳號不能啟用任何二階段驗證,它可沒有辦法等你去輸入簡訊驗證碼或點擊驗證App!

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。