You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure自动化Runbook从Key Vault获取证书失败求助

Azure Automation Runbook从Key Vault获取证书失败排查与修复

问题描述

在Azure Automation Runbook中尝试从Azure Key Vault检索证书时遇到错误,已确认证书存在,自动化账户已分配Key Vault Certificate User和Key Vault Secrets User角色,API权限(Microsoft Graph Sites.FullControl.All、Microsoft Graph User.Read、SharePoint Sites.FullControl.All)配置正确,Entra ID中已启用基于证书的身份验证,但脚本执行仍报错。

错误输出

Completed


Environments                                                                                           Context
------------                                                                                           -------
{[AzureChinaCloud, AzureChinaCloud], [AzureCloud, AzureCloud], [AzureUSGovernment, AzureUSGovernment]} Microsoft.Azure.…
Exception calling ".ctor" with "3" argument(s): "Array may not be empty or null. (Parameter 'rawData')"
System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter 'ClientCertificate' because it is null.
   at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
   at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindNamedParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter)
   at System.Management.Automation.ParameterBinderController.BindNamedParameters(UInt32 parameterSets, Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
   at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
   at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
--- End of stack trace from previous location ---
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
   at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
   at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

原脚本代码

# Login with Managed Identity (for Azure Automation Account)
Write-Host "Logging in with Automation Account Managed Identity..."
Connect-AzAccount -Identity
Write-Host "Logged in with Managed Identity."

# Set API Key
$APIKey = Get-AutomationVariable -Name '...'
Write-Host "API Key retrieved: $APIKey"

# Set SharePoint Site URL and Folder
$SharePointSiteUrl = "..."
$SharePointFolder = "..."

# Set Azure App Registration credentials
$tenantId = "..."
$clientId = "..."

# Retrieve the certificate from Azure Key Vault
Write-Host "Retrieving certificate from Azure Key Vault..."
$certificate = Get-AzKeyVaultCertificate -VaultName '...' -Name '...'

# Check if certificate retrieval was successful
if ($certificate -eq $null) {
    Write-Host "Error: Certificate not found in Key Vault!"
    exit
}

# Convert from Base64 to Byte Array and create the certificate object
$pfxBytes = [Convert]::FromBase64String($certificate.SecretValueText)
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($pfxBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
Write-Host "Certificate retrieved successfully."

# Use the certificate to authenticate with Azure AD and get an access token
Write-Host "Authenticating with Azure AD using the certificate..."
$GraphConnection = Get-MsalToken -ClientCertificate $cert -ClientId $clientId -TenantId $tenantId

# Get the access token from the authentication response
$Token = $GraphConnection.AccessToken

# Check if the token was retrieved
if (-not $Token) {
    Write-Host "Error: Failed to retrieve access token!"
    exit
}

Write-Host "Access token retrieved successfully."

脚本中的所有占位符已替换为实际信息,而非保留为'...'的内容

问题根源与修复方案

问题根源

Get-AzKeyVaultCertificate返回的Certificate对象不包含SecretValueText属性,该属性属于Key Vault的Secret对象——证书的PFX格式数据实际存储在对应的Secret中。直接访问$certificate.SecretValueText会得到空值,导致后续转换字节数组失败,最终$cert为null,触发ClientCertificate参数绑定错误。

修复步骤

  1. 获取证书对应的Secret对象:替换原脚本中Get-AzKeyVaultCertificate的调用,改为获取Secret:
    $certSecret = Get-AzKeyVaultSecret -VaultName '你的密钥保管库名称' -Name '你的证书名称'
    
  2. 转换Secret的SecureString为明文:Key Vault返回的Secret是SecureString类型,需要转换为明文后再转为Base64字节数组:
    $secretPlainText = [System.Net.NetworkCredential]::new("", $certSecret.SecretValue).Password
    $pfxBytes = [Convert]::FromBase64String($secretPlainText)
    
  3. 使用稳定的存储标志创建证书对象:在Azure Automation环境中,添加PersistKeySet和MachineKeySet标志避免权限问题:
    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(
        $pfxBytes, 
        $null, 
        [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bor 
        [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor 
        [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet
    )
    

修复后的完整脚本片段

替换原脚本中证书检索及转换部分为以下代码:

# Retrieve the certificate's secret from Azure Key Vault (PFX data is stored as a secret)
Write-Host "Retrieving certificate secret from Azure Key Vault..."
$certSecret = Get-AzKeyVaultSecret -VaultName '你的密钥保管库名称' -Name '你的证书名称'

if ($certSecret -eq $null) {
    Write-Host "Error: Certificate secret not found in Key Vault!"
    exit
}

# Convert SecureString secret to plain text, then to byte array
$secretPlainText = [System.Net.NetworkCredential]::new("", $certSecret.SecretValue).Password
$pfxBytes = [Convert]::FromBase64String($secretPlainText)

# Create X509 certificate object with appropriate storage flags for Automation environment
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(
    $pfxBytes, 
    $null, 
    [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bor 
    [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor 
    [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet
)
Write-Host "Certificate retrieved and converted successfully."

内容的提问来源于stack exchange,提问作者StacksOnStacks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 12:32:02