設置 Azure 服務主體:無人值守訪問指南

當您需要使用腳本和工具在Azure中自動化任務時,您是否考慮使用服務帳戶或Azure服務主體?有些人只會創建一個新的服務帳戶,將其與所需的所有管理角色關聯起來,並將其排除在MFA之外。

I know what you’re thinking – “that is a horrible idea”. Of course, it is! And for sure, your IT Sec will give you a lot of grief if you did all that.

但還有其他選擇嗎?您如何使用具有有限範圍的特權憑據,而不需要排除它們免於多重身份驗證?您很幸運,因為這篇文章將教您如何做到這一點。

在本文中,您將了解Azure服務主體是什麼。您將學習如何使用不同類型的憑據(如密碼、密鑰和憑證)創建服務主體。

有很多工具可以創建Azure服務主體。包括使用Azure門戶Azure Active Directory管理中心Azure AD PowerShellAzure CLIAzure PowerShell。本文將重點介紹Azure PowerShell工具。

還感興趣嗎?繼續閱讀,讓我們開始吧!

要求

由於這是一篇「學以致用」的文章,以下是一些前提條件,以便您能夠跟著進行操作。

  • 需要訪問 Azure 訂閱。最好使用測試租用戶進行操作。如果您沒有,您可以註冊免費試用
  • 需要一台安裝有 PowerShell 5.1 的 Windows 10 電腦。
  • 必須安裝Azure PowerShell 模組

Azure 服務主體 vs. 服務帳戶

自動化工具和腳本通常需要管理員或特權訪問權限,例如,為腳本設定憑證要求時,可能需要設置存儲帳戶或按計劃啟動和停止虛擬機器。大多數管理員可能會使用完全特權的使用者帳戶(稱為服務帳戶)來設定腳本的憑證要求。

A service account is essentially a privileged user account used to authenticate using a username and password. And, if used with automation, a service account is most likely excluded from any conditional access policies or multi-factor authentication.

另一方面,Azure 服務主體可以設定使用用戶名和密碼或憑證進行身份驗證。可以將其視為一個沒有實際使用者的用戶身份,而是應用程式的身份。

可以為 Azure 服務主體分配足夠的訪問權限,僅限於特定的 Azure 資源。例如,您可以創建一個具有基於角色的訪問的 Azure 服務主體,可以訪問整個訂閱或僅限於單個 Azure 虛擬機器。

創建 Azure 服務主體的主要考慮因素

在創建 Azure 服務主體之前,您應該了解需要計劃的基本細節。這些細節可能看起來很簡單,但它們將使創建 Azure 服務主體變得高效且輕鬆。

顯示名稱。一切都始於名稱,Azure 服務主體必須有一個名稱。這裡沒有固定的規則,但您的組織可能有一個指定的命名慣例。

  • 要使用的憑證類型。您可以選擇創建一個將使用密碼或憑證進行身份驗證的 Azure 服務主體。這並不意味著您只能選擇其中一種,您可以同時使用兩者。

對於服務主體,用戶名和密碼更適合稱為應用程式 ID 和密鑰。

  • 憑證的有效期。無論您分配密碼還是憑證憑證,您都必須為其有效期定義一個開始和結束日期。憑證的有效期通常取決於您願意多久進行證書和密碼的輪換/更新。
  • 訪問範圍。您正在創建一個將具有對訂閱、資源組或選定資源的訪問權限的 Azure 服務主體嗎?
  • 角色。有幾個可用的角色,如ContributorReaderOwner等。您需要定義哪種角色是服務主體的「足夠」角色。

使用自動分配的密鑰創建Azure服務主體

在Azure中創建新的服務主體的核心是New-AzAdServicePrincipal cmdlet。在此示例中,將使用以下值創建新的服務主體:

DisplayNameAzVM_Reader

範圍AzVM1(虛擬機)

角色Reader

密碼自動分配

憑證有效期:1年

獲取目標範圍(虛擬機)的ID

如您所見,此新服務主體的範圍僅限於名為AzVM1的虛擬機。但是,-Scope參數不僅接受名稱,而是整個資源的ID。因此,在此示例中,首先要獲取的是AzVM1虛擬機的ID。要做到這一點,請使用以下代碼。

Get-AzVM | Format-Table Name, ID

在PowerShell中運行上面的代碼時,您應該看到虛擬機名稱和ID的列表,類似於下面的屏幕截圖。

Get the list of VM names and IDs

使用密鑰創建Azure服務主體

現在,您已經擁有了目標範圍(即AzVM1虛擬機)的ID,可以使用以下命令創建具有reader角色的新服務主體。新服務主體的屬性將存儲在$sp變量中。

$sp = New-AzAdServicePrincipal `
	-DisplayName AzVM_Reader `
	-Scope '/subscriptions/5e252811-b376-4136-b8ae-d3b8abe2c9c3/resourceGroups/ATA/providers/Microsoft.Compute/virtualMachines/AzVM1'
	-Role 'Reader'

通過上述命令,服務主體已使用以下值創建:

The properties of the new service principal

解密密鑰

現在您已經擁有應用程式ID和密鑰,這是服務主體的用戶名和密碼。然而,密鑰的值顯示為System.Security.SecureString。您想知道密碼是什麼。為此,使用以下命令將密碼轉換為純文本。

#將加密的密碼轉換為純文本
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(
        $sp.Secret
    )
)

上述命令將將$sp.Secret的安全字串值轉換為純文本。參考下面的圖像。

Secure string password value converted to plain text

驗證Azure服務主體角色分配

如何知道這是否有效?您可以使用Azure Portal檢查資源的存取控制清單。例如,在下面的圖像中,您可以看到AzVM_Reader服務主體現在對AzVM1虛擬機具有Reader訪問權限。

Azure resource access control

此外,您可以使用Get-AzRoleAssignment -ObjectID $sp.id命令獲取Azure服務主體的角色分配。參考下面的截圖。

Get the role assignment(s) of the service principal

使用密碼創建Azure服務主體

如果您想對Azure服務主體分配的密碼或密鑰有更多控制,請在服務主體創建期間使用-PasswordCredential參數。如果密碼必須符合複雜性要求,這將非常有用。

在此示例中,將使用以下值創建新的Azure服務主體:

顯示名稱ATA_RG_Contributor

範圍ATA(資源群組)

角色: 貢獻者

密碼: 長度為20個字符,包含6個非字母數字字符

憑證有效期: 5年

獲取目標範圍(資源組)的ID

這個新服務主體的範圍涵蓋整個名為ATA的資源組。首先要獲取的是ATA資源組的ID。為此,使用下面的代碼,但請確保將-Name參數的值更改為您的資源組名稱。

# 獲取資源組的 ResourceId 值
$Scope = (Get-AzResourceGroup -Name ATA).ResourceId
$Scope

然後,您應該在$Scope變數中看到資源組的ResourceID

Getting the Resource Group ID

生成密碼字符串

下一步是生成符合長度為20個字符,包含6個非字母數字字符複雜性要求的密碼。為此,您可以使用.NET的靜態方法GeneratePassword()

# 使用 GeneratePassword() 靜態方法生成隨機密碼
Add-Type -AssemblyName 'System.Web'
$password = [System.Web.Security.Membership]::GeneratePassword(20, 6)
$password

在上面的代碼中,GeneratePassword(20, 6),第一個值表示密碼的長度,第二個值表示要包含的非字母數字字符的數量。結果如下圖所示。

Randomly generated password using the .NET GeneratePassword() static method

創建密碼憑證對象

現在您已經有了密碼字串,下一步是創建Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential物件。這個物件將包含存儲在$password變量中的密碼字串和有效期為5年。複製下面的代碼並在您的Azure PowerShell會話中運行。

# 創建密碼憑證物件
[Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential]`
    $PasswordCredential = @{
    StartDate = Get-Date;
    EndDate   = (Get-Date).AddYears(5);
    Password  = $password
}
$PasswordCredential

在PowerShell中運行上面的代碼將將憑證物件存儲到$PasswordCredential變量中。預期結果將類似於下面顯示的結果。

Creating the new password credential object in Azure PowerShell

創建帶有密碼的服務主體

現在您已經準備好所需的參數值來創建Azure服務主體。下面的代碼將使用顯示名為ATA_RG_Contributor的服務主體,並使用存儲在$PasswordCredential變量中的密碼。

# 使用密碼憑證創建服務主體
$sp = New-AzAdServicePrincipal `
    -DisplayName 'ATA_RG_Contributor' `
    -PasswordCredential $PasswordCredential
$sp

運行代碼後,將創建新的服務主體,並將屬性存儲在$sp變量中。請參考下面的示例結果。

The new Azure service principal is created

分配角色和範圍

在前一節中已經創建了Azure服務主體,但是沒有角色範圍。這是因為-Role-Scope參數不能與-PasswordCredential參數一起使用。這意味著需要進行額外的步驟來將角色和範圍分配給服務主體。

以下代碼使用New-AzRoleAssignment cmdlet 將 Azure 服務主體的範圍和角色指派給目標資源。

# 將角色指派給目標資源
New-AzRoleAssignment -$azSubscription = Get-AzSubscription -SubscriptionName VSE3
$Scope = "/subscriptions/$($azSubscription.ID)"
$TenantID = $azSubscription.TenantID$sp.ApplicationId `
    -Scope $Scope `
    -RoleDefinitionName 'Contributor'

下圖顯示了將角色和範圍指派給 Azure 服務主體後的預期結果。

Assigning role and scope using Azure Powershell

請務必保存服務主體的密碼,因為如果您未能保存或忘記密碼,將無法恢復。

使用服務主體密碼連接到 Azure

現在,讓我們開始使用服務主體。代碼中使用服務主體的憑證來登入 Azure PowerShell,而不是使用使用者帳戶。

# 获取显示名称为ATA_RG_Contributor的服务主体
$sp = Get-AzADServicePrincipal -DisplayName ATA_RG_Contributor

# 获取租户ID
$TenantID = (Get-AzContext).Tenant.ID

# 获取第一个服务主体名称
$user = $sp.ServicePrincipalNames[0]

# 将密码转换为安全字符串
$secPassword = $password | ConvertTo-SecureString -AsPlainText -Force

# 创建PSCredential对象
$credential = [PSCredential]::New($user,$secPassword)

# 连接到Azure
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $TenantID
# 获取显示名称为ATA_RG_Contributor的服务主体
$sp = Get-AzADServicePrincipal -DisplayName ATA_RG_Contributor

# 获取租户ID
$TenantID = (Get-AzContext).Tenant.ID

# 获取第一个服务主体名称
$user = $sp.ServicePrincipalNames[0]

# 将密码转换为安全字符串
$secPassword = $password | ConvertTo-SecureString -AsPlainText -Force

# 创建PSCredential对象
$credential = [PSCredential]::New($user,$secPassword)

# 连接到Azure
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $TenantID# 获取显示名称为ATA_RG_Contributor的服务主体
$sp = Get-AzADServicePrincipal -DisplayName ATA_RG_Contributor

# 获取租户ID
$TenantID = (Get-AzContext).Tenant.ID

# 获取第一个服务主体名称
$user = $sp.ServicePrincipalNames[0]

# 将密码转换为安全字符串
$secPassword = $password | ConvertTo-SecureString -AsPlainText -Force

# 创建PSCredential对象
$credential = [PSCredential]::New($user,$secPassword)

# 连接到Azure
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $TenantID

運行上述代碼後,您應該使用ATA_RG_Contributor服務主體和密碼憑據成功登錄到 Azure PowerShell。

Connect to Azure using a Service Principal with Password Credential

使用憑證創建 Azure 服務主體

除了密碼憑據外,Azure 服務主體還可以具有基於憑證的憑據。相關的憑證可以是由憑證授權機構或自簽發的憑證。

在此示例中,將使用以下值創建新的 Azure 服務主體:

顯示名稱VSE3_SUB_OWNER

範圍VSE3(訂閱)

角色擁有者

憑證有效期2年

獲取目標範圍(訂閱)的 ID

這個新的服務主體的範圍覆蓋了名為VSE3的 Azure 訂閱。首先要做的是獲取VSE3訂閱的 ID。為此,請使用下面的代碼,但請確保將-SubscriptionName參數的值更改為您的資源群組名稱。

# 獲取訂閱範圍和租戶 ID
$azSubscription = Get-AzSubscription -SubscriptionName VSE3
$Scope = "/subscriptions/$($azSubscription.ID)"
$TenantID = $azSubscription.TenantID

接下來,指定要創建的新 Azure 服務主體和自簽發憑證的名稱。

# 新 Azure 服務主體和自簽發憑證的名稱
$DisplayName = 'VSE3_SUB_OWNER'

創建自簽發憑證

以下代碼在個人憑證存儲中創建具有名稱為CN=VSE3_SUB_OWNER的自簽名密碼。證書的有效期設置為兩年。證書的屬性保存在$cert變量中。

# 生成自簽名證書
$cert = New-SelfSignedCertificate -CertStoreLocation "cert:\CurrentUser\My" `
    -Subject "CN=$($DisplayName)" `
    -KeySpec KeyExchange `
    -NotBefore ((Get-Date).AddDays(-1)) `
    -NotAfter ((Get-Date).AddYears(2))
$cert

下面的截圖顯示證書已經創建。

The self-signed certificate is created in the personal certificate store

如果您想以更熟悉的方式(GUI)查看新證書,可以在證書控制台(certmgr.mmc)中找到它。參考下面顯示的證書圖片。

Viewing the self-signed certificate

接下來是獲取自簽名證書的Base64編碼值並保存在$keyValue變量中。

# 獲取自簽名證書的Base64值
$keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData())

創建使用憑證的服務主體

現在證書已經創建,下一步是創建新的Azure服務主體。下面的代碼將創建使用自簽名證書作為憑證的Azure服務主體。憑證的有效期與證書的有效期相同。

$sp = New-AzADServicePrincipal -DisplayName $DisplayName `
    -CertValue $keyValue `
    -EndDate $cert.NotAfter `
    -StartDate $cert.NotBefore
$sp

您將獲得類似的輸出,如下圖所示。

The new Azure service principal with a certificate is created

分配角色和範圍

已經創建了Azure服務主體,但尚未分配角色範圍。這意味著需要進行額外的步驟來為服務主體分配角色和範圍。

以下的程式碼使用New-AzRoleAssignment cmdlet將所有者角色分配給服務主體的VSE3訂閱。

# 分配角色和範圍
New-AzRoleAssignment -ApplicationId $sp.ApplicationId `
    -Scope $Scope `
    -RoleDefinitionName 'Owner'

當程式碼執行時,下面的螢幕截圖顯示角色分配完成的確認訊息。

The service principal’s owner role is added to the subscription

使用具有基於憑證的憑證的服務主體連接到Azure

現在您已經建立了具有基於憑證的憑證的服務主體。這意味著您可以在不使用密碼的情況下使用它連接到Azure。而是使用計算機上可用的憑證作為驗證方法。

在這個例子中,服務主體的顯示名稱是VSE3_SUB_OWNER,憑證名稱是CN=VSE3_SUB_OWNER。下面的程式碼將從個人憑證存儲中獲取憑證的指紋,並將其用作登錄憑證。

# 獲取主題為CN=VSE3_SUB_OWNER的憑證
$cert = Get-ChildItem Cert:\CurrentUser\My\ | Where-Object { $_.Subject -eq 'CN=VSE3_SUB_OWNER' }

# 連接到Azure
Connect-AzAccount -ServicePrincipal -CertificateThumbprint $cert.ThumbPrint -ApplicationID $sp.ApplicationID -Tenant $TenantID

下面的螢幕截圖顯示,使用上述程式碼,僅使用ApplicationIDTenant憑證指紋成功登錄到Azure PowerShell。

Connecting to Azure using a Service Principal and Certificate

結論

Azure服務主體是在創建自動化任務和訪問Azure資源的工具時必須考慮的安全主體。可以選擇應用的範圍和角色,以提供”恰到好處”的訪問權限。

在本文中,您了解了如何使用PowerShell创建Azure服务主体。Azure服务主体可以具有密码、密钥或基于证书的凭据。每种类型的凭据都有其优势和适用的使用场景。

具有密码或密钥凭据的服务主体更易于移植,但由于凭据可以作为明文共享,因此被认为安全性较低。另一方面,基于证书的凭据是更安全的选择,但需要更多的维护工作。

本文介绍的技术只涵盖了使用Azure服务主体进行自动化的基础知识。还有许多其他配置Azure服务主体的方法,如添加、删除和重置凭据。您可以在使用过程中自行探索。

感谢您的阅读!

额外学习资源

以下是一些可能对本文有帮助的资源。

Source:
https://adamtheautomator.com/azure-service-principal/