通过 Azure PowerShell 使用证书创建服务主体

当某个应用或脚本需要访问资源时,用户可以为该应用设置一个标识,并使用其自身的凭据进行身份验证。 此标识称为服务主体。 使用此方法可实现以下目的:

  • 将不同于自己的权限的权限分配给应用标识。 通常情况下,这些权限仅限于应用需执行的操作。
  • 执行无人参与的脚本时,使用证书进行身份验证。

重要

请考虑使用 Azure 资源的托管标识作为应用程序标识,而不是创建服务主体。 如果代码在支持托管标识的服务上运行并访问支持 Microsoft Entra 身份验证的资源,则托管标识是更好的选择。 若要详细了解 Azure 资源的托管标识(包括当前支持它的服务),请参阅什么是 Azure 资源的托管标识?

本文演示如何创建使用证书进行身份验证的服务主体。 若要使用密码设置服务主体,请参阅使用 Azure PowerShell 创建 Azure 服务主体

必须拥有最新版本 PowerShell 才能演练本文中的示例。

注意

建议使用 Azure Az PowerShell 模块与 Azure 交互。 请参阅安装 Azure PowerShell 以开始使用。 若要了解如何迁移到 Az PowerShell 模块,请参阅 将 Azure PowerShell 从 AzureRM 迁移到 Az

所需的权限

若要完成本文,必须在 Microsoft Entra ID 和 Azure 订阅中都有足够的权限。 具体而言,必须能够在 Microsoft Entra ID 中创建应用并向角色分配服务主体。

检查帐户是否有足够权限的最简方法是使用门户。 请参阅检查所需的权限

将应用程序分配给角色

要访问订阅中的资源,必须将应用程序分配到角色。 判定哪个角色能为应用程序提供适当的权限。 若要了解可用角色,请参阅 Azure 内置角色

可将作用域设置为订阅、资源组或资源级别。 较低级别的作用域会继承权限。 例如,将某个应用程序添加到资源组的“读者” 角色意味着该应用程序可以读取该资源组及其包含的所有资源。 若要允许应用程序执行诸如“重新启动”、“启动”和“停止”实例之类的操作,请选择“参与者”角色 。

使用自签名证书创建服务主体

下面的示例介绍了简单的方案。 它使用 New-AzADServicePrincipal 创建具有自签名证书的服务主体,并使用 New-AzRoleAssignment读取者角色分配给该服务主体。 角色分配的范围限定为当前所选 Azure 订阅。 若要选择其他订阅,请使用 Set-AzContext

注意

New-SelfSignedCertificate cmdlet 和 PKI 模块目前在 PowerShell Core 中不受支持。

$cert = New-SelfSignedCertificate -CertStoreLocation "cert:\CurrentUser\My" `
  -Subject "CN=exampleappScriptCert" `
  -KeySpec KeyExchange
$keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData())

$sp = New-AzADServicePrincipal -DisplayName exampleapp `
  -CertValue $keyValue `
  -EndDate $cert.NotAfter `
  -StartDate $cert.NotBefore
Sleep 20
New-AzRoleAssignment -RoleDefinitionName Reader -ServicePrincipalName $sp.AppId

该示例休眠 20 秒,让新的服务主体有时间传遍 Microsoft Entra ID。 如果脚本等待时间太长,则会看到一条错误消息,指出:“目录 {DIR-ID} 中不存在主体 {ID}”。若要解决此错误,请稍等片刻,然后再次运行 New-AzRoleAssignment 命令。

可以使用 ResourceGroupName 参数将角色分配范围限制为特定资源组。 还可以使用 ResourceTypeResourceName 参数将范围限制为特定资源。

如果没有 Windows 10 或 Windows Server 2016,请从 PKI 解决方案下载 New-SelfSignedCertificateEx cmdlet。 解压其内容,并导入所需的 cmdlet。

# Only run if you could not use New-SelfSignedCertificate
Import-Module -Name c:\ExtractedModule\New-SelfSignedCertificateEx.ps1

在脚本中替换以下两行代码以生成证书。

New-SelfSignedCertificateEx -StoreLocation CurrentUser `
  -Subject "CN=exampleapp" `
  -KeySpec "Exchange" `
  -FriendlyName "exampleapp"
$cert = Get-ChildItem -path Cert:\CurrentUser\my | where {$PSitem.Subject -eq 'CN=exampleapp' }

通过自动执行的 PowerShell 脚本提供证书

每当以服务主体方式登录时,请提供 AD 应用所在目录的租户 ID。 租户是 Microsoft Entra ID 的实例。

$TenantId = (Get-AzSubscription -SubscriptionName "Contoso Default").TenantId
$ApplicationId = (Get-AzADApplication -DisplayNameStartWith exampleapp).AppId

$Thumbprint = (Get-ChildItem cert:\CurrentUser\My\ | Where-Object {$_.Subject -eq "CN=exampleappScriptCert" }).Thumbprint
Connect-AzAccount -Environment AzureChinaCloud -ServicePrincipal `
  -CertificateThumbprint $Thumbprint `
  -ApplicationId $ApplicationId `
  -TenantId $TenantId

使用证书颁发机构提供的证书创建服务主体

以下示例使用证书颁发机构颁发的证书创建服务主体。 分配的范围限定为指定的 Azure 订阅。 它将服务主体添加到读者角色。 如果在角色分配过程中发生错误,它会重试分配。

Param (
 [Parameter(Mandatory=$true)]
 [String] $ApplicationDisplayName,

 [Parameter(Mandatory=$true)]
 [String] $SubscriptionId,

 [Parameter(Mandatory=$true)]
 [String] $CertPath,

 [Parameter(Mandatory=$true)]
 [String] $CertPlainPassword
 )

 Connect-AzAccount -Environment AzureChinaCloud
 Import-Module Az.Resources
 Set-AzContext -Subscription $SubscriptionId

 $CertPassword = ConvertTo-SecureString $CertPlainPassword -AsPlainText -Force

 $PFXCert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @($CertPath, $CertPassword)
 $KeyValue = [System.Convert]::ToBase64String($PFXCert.GetRawCertData())

 $ServicePrincipal = New-AzADServicePrincipal -DisplayName $ApplicationDisplayName
 New-AzADSpCredential -ObjectId $ServicePrincipal.Id -CertValue $KeyValue -StartDate $PFXCert.NotBefore -EndDate $PFXCert.NotAfter
 Get-AzADServicePrincipal -ObjectId $ServicePrincipal.Id 

 $NewRole = $null
 $Retries = 0;
 While ($NewRole -eq $null -and $Retries -le 6)
 {
    # Sleep here for a few seconds to allow the service principal application to become active (should only take a couple of seconds normally)
    Sleep 15
    New-AzRoleAssignment -RoleDefinitionName Reader -ServicePrincipalName $ServicePrincipal.AppId | Write-Verbose -ErrorAction SilentlyContinue
    $NewRole = Get-AzRoleAssignment -ObjectId $ServicePrincipal.Id -ErrorAction SilentlyContinue
    $Retries++;
 }

 $NewRole

通过自动执行的 PowerShell 脚本提供证书

每当以服务主体方式登录时,请提供 AD 应用所在目录的租户 ID。 租户是 Microsoft Entra ID 的实例。

Param (

 [Parameter(Mandatory=$true)]
 [String] $CertPath,

 [Parameter(Mandatory=$true)]
 [String] $CertPlainPassword,

 [Parameter(Mandatory=$true)]
 [String] $ApplicationId,

 [Parameter(Mandatory=$true)]
 [String] $TenantId
 )

 $CertPassword = ConvertTo-SecureString $CertPlainPassword -AsPlainText -Force
 $PFXCert = New-Object `
  -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 `
  -ArgumentList @($CertPath, $CertPassword)
 $Thumbprint = $PFXCert.Thumbprint

 Connect-AzAccount -Environment AzureChinaCloud -ServicePrincipal `
  -CertificateThumbprint $Thumbprint `
  -ApplicationId $ApplicationId `
  -TenantId $TenantId

应用程序 ID 和租户 ID 不敏感,因此可以直接将它们嵌入脚本中。 如果需要检索租户 ID,请使用:

(Get-AzSubscription -SubscriptionName "Contoso Default").TenantId

如果需要检索应用程序 ID,请使用:

(Get-AzADApplication -DisplayNameStartWith {display-name}).AppId

更改凭据

若要更改 AD 应用的凭据(由于安全性损害或凭据过期),请使用 Remove-AzADAppCredentialNew-AzADAppCredential cmdlet。

若要删除应用程序的所有凭据,请使用:

Get-AzADApplication -DisplayName exampleapp | Remove-AzADAppCredential

要添加证书值,请按本文所示创建自签名证书。 然后,使用:

Get-AzADApplication -DisplayName exampleapp | New-AzADAppCredential `
  -CertValue $keyValue `
  -EndDate $cert.NotAfter `
  -StartDate $cert.NotBefore

调试

创建服务主体时,可能会收到以下错误:

  • “Authentication_Unauthorized”或“在上下文中找不到订阅。”- 如果帐户不具有在 Microsoft Entra ID 上注册应用所需的权限,会看到此错误。 通常,当只有 Microsoft Entra ID 中的管理员用户可注册应用且帐户不是管理员帐户时,会看到此错误。可要求管理员分配管理员角色,或者允许用户注册应用。

  • 帐户“不具有对作用域‘/subscriptions/{guid}’执行操作‘Microsoft.Authorization/roleAssignments/write’的权限”。 - 当帐户不具有足够权限将角色分配给标识时,会看到此错误。 可要求订阅管理员将你添加到“用户访问管理员”角色。

后续步骤