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

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

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

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

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

所需的权限

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

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

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

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

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

$sp = New-AzureRMADServicePrincipal -DisplayName exampleapp `
  -CertValue $keyValue `
  -EndDate $cert.NotAfter `
  -StartDate $cert.NotBefore
Sleep 20
New-AzureRmRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $sp.ApplicationId

该示例休眠 20 秒,让新的服务主体有时间传遍 Azure Active Directory。 如果脚本等待时长不足,会显示错误:“主体 {ID} 不存在于目录 {DIR-ID} 中”。 若要解决此错误,请等待片刻,然后重新运行 New-AzureRmRoleAssignment 命令。

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

如果未使用 Windows 10 或 Windows Server 2016,需要从 Microsoft 脚本中心下载自签名证书生成器。 解压其内容,并导入所需的 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。 租户是 Azure Active Directory 的实例。

Param (

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

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

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

 $Thumbprint = (Get-ChildItem cert:\CurrentUser\My\ | Where-Object {$_.Subject -match $CertSubject }).Thumbprint
 Login-AzureRmAccount -EnvironmentName AzureChinaCloud -ServicePrincipal `
  -CertificateThumbprint $Thumbprint `
  -ApplicationId $ApplicationId `
  -TenantId $TenantId

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

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

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

(Get-AzureRmADApplication -DisplayNameStartWith {display-name}).ApplicationId

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

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

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

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

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

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

 Login-AzureRmAccount -EnvironmentName AzureChinaCloud
 Import-Module AzureRM.Resources
 Set-AzureRmContext -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-AzureRMADServicePrincipal -DisplayName $ApplicationDisplayName
 New-AzureRmADSpCredential -ObjectId $ServicePrincipal.Id -CertValue $KeyValue -StartDate $PFXCert.NotBefore -EndDate $PFXCert.NotAfter
 Get-AzureRmADServicePrincipal -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-AzureRMRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $ServicePrincipal.ApplicationId | Write-Verbose -ErrorAction SilentlyContinue
    $NewRole = Get-AzureRMRoleAssignment -ObjectId $ServicePrincipal.Id -ErrorAction SilentlyContinue
    $Retries++;
 }

 $NewRole

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

以服务主体方式登录时,需提供 AD 应用所在目录的租户 ID。 租户是 Azure Active Directory 的实例。

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

 Login-AzureRmAccount -EnvironmentName AzureChinaCloud -ServicePrincipal `
  -CertificateThumbprint $Thumbprint `
  -ApplicationId $ApplicationId `
  -TenantId $TenantId

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

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

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

(Get-AzureRmADApplication -DisplayNameStartWith {display-name}).ApplicationId

更改凭据

若要更改 AD 应用的凭据(为了保障安全或由于凭据过期的缘故),请使用 Remove-AzureRmADAppCredentialNew-AzureRmADAppCredential cmdlet。

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

Remove-AzureRmADAppCredential -ApplicationId 8bc80782-a916-47c8-a47e-4d76ed755275 -All

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

New-AzureRmADAppCredential -ApplicationId 8bc80782-a916-47c8-a47e-4d76ed755275 `
  -CertValue $keyValue `
  -EndDate $cert.NotAfter `
  -StartDate $cert.NotBefore

调试

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

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

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

后续步骤