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参数绑定错误。
修复步骤
- 获取证书对应的Secret对象:替换原脚本中
Get-AzKeyVaultCertificate的调用,改为获取Secret:$certSecret = Get-AzKeyVaultSecret -VaultName '你的密钥保管库名称' -Name '你的证书名称' - 转换Secret的SecureString为明文:Key Vault返回的Secret是SecureString类型,需要转换为明文后再转为Base64字节数组:
$secretPlainText = [System.Net.NetworkCredential]::new("", $certSecret.SecretValue).Password $pfxBytes = [Convert]::FromBase64String($secretPlainText) - 使用稳定的存储标志创建证书对象:在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
相关产品推荐
相关产品推荐

