建立 Azure 服务主体:无人参与访问指南

当你需要使用脚本和工具在Azure中自动化任务时,你会考虑使用服务帐户还是Azure服务主体吗?对于一些人来说,创建一个新的服务帐户,赋予它所需的所有管理员角色,并排除它免受多因素身份验证的影响,这并不罕见。

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订阅。最好是在测试租户上进行操作。如果您没有,您可以注册免费试用
  • 访问运行Windows 10并具有PowerShell 5.1的计算机。
  • 必须安装Azure PowerShell模块

Azure服务主体与服务帐户

自动化工具和脚本通常需要管理员或特权访问权限。比如,提供存储账户或按计划启动和停止虚拟机。大多数管理员可能会使用完全特权的用户帐户(称为服务帐户)来设置脚本的凭据要求。

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。在此示例中,将使用以下值创建新服务主体:

DisplayName: AzVM_Reader

Scope: AzVM1 (虚拟机)

Role: Reader

Password: <自动分配>

Credential Validity: 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

解密秘密密钥

现在您拥有ApplicationIDSecret,这是服务主体的用户名和密码。但是,Secret的值显示为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门户检查资源的访问控制列表。例如,在下面的图片中,您可以看到AzVM_Reader服务主体现在对AzVM1虚拟机具有读取者访问权限。

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 的 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/