调试由自定义脚本扩展或运行命令运行的 PowerShell 脚本

适用于:✔️ Windows VM

本文介绍如何在使用自定义脚本扩展或运行命令功能的 PowerShell 脚本中测试和更正失败。

先决条件

概述

假设你使用了 自定义脚本扩展运行命令 功能来运行 PowerShell 脚本。 如果脚本失败,该怎么办? 可以使用多种方法来确定失败原因。

PowerShell 具有多个输出流。 自定义脚本扩展和运行命令脚本的日志将成功流发送到StdOut子状态,并将错误流发送到StdErr子状态。 这些子统计信息属于用于运行自定义脚本扩展脚本或运行命令脚本的扩展。

StdErrStdOut统计信息位于虚拟机(VM)的证书注册点(CRP)实例视图中。 这些子统计信息在多个位置可见,如下表所示。

Interface 如何查看子状态
Azure 门户
  1. 搜索并选择“虚拟机”。
  2. 从列表中选择 VM。
  3. 在 VM 概述页上,选择“ 扩展 + 应用程序>扩展”。
  4. 选择用于运行命令的扩展。 (它将被命名为任一 CustomScriptExtensionRunCommand.)
  5. 选择“ 查看详细状态”。
Azure PowerShell 输入 Get-AzVM cmdlet 以获取 Azure VM 的属性,如下所示:
Get-AzVM -ResourceGroupName <resource-group-name> -Name <vm-name> -Status
Azure CLI 输入 az vm get-instance-view 命令以获取有关 Azure VM 的实例信息,如下所示:
az vm get-instance-view --resource-group <resource-group-name> --name <vm-name> --query instanceView.extensions

通常导致脚本失败的错误显示在子状态中 StdErr 。 但是,脚本也可以在不记录该子状态中的致命错误条目的情况下失败。

手动并使用 PsExec 测试脚本

手动验证脚本是否从 VM 上的管理 PowerShell 控制台成功运行。

如果脚本手动工作,请使用 PsExec 通过本地系统帐户运行脚本。 对于自定义脚本扩展和运行命令,脚本都使用该帐户运行。 psexec -s输入后,可以使用本地系统帐户测试脚本,但不使用自定义脚本扩展或运行命令。 如果使用重现 psexec -s失败,则自定义脚本扩展和运行命令不是问题的原因。

使用 PsExec 进行测试

可以使用 PsExec 远程运行 PowerShell 测试脚本。 打开管理命令提示符窗口,然后输入以下 PsExec 命令。 将占位符替换为 PowerShell 脚本的完全限定名称:

psexec -accepteula -nobanner -s powershell.exe -NoLogo -NoProfile -File <C:\path\script-name.ps1>

或者,你可以以交互方式使用 PsExec。 在以下示例中 ,输入 whoami 命令以显示 PowerShell 正在本地系统(NT AUTHORITY\SYSTEM) 帐户下运行:

C:\>psexec -accepteula -nobanner -s powershell.exe -NoLogo -NoProfile

PS C:\Windows\system32> whoami
nt authority\system 

启用 PowerShell 脚本执行的日志记录

StdErr如果子状态未显示问题的原因,则可以打开多种类型的日志记录来集体显示脚本内容和输出。 此日志记录显示脚本尝试完成的操作以及运行脚本的结果。

若要启用不同类型的日志记录,请按照后续几个部分中的步骤进行操作。

警告

某些说明涉及更改 Windows 注册表。 如果使用注册表编辑器或使用其他方法错误地修改了注册表,则可能会发生严重问题。 这些问题可能需要重新安装操作系统才能解决。 Microsoft 不能保证可解决这些问题。 首先备份现有注册表项 ,然后自行修改注册表。

增加事件日志的最大大小

可以在安全日志和 Microsoft-Windows-PowerShell/Operational 事件日志中生成大量事件。 若要防止丢失这些记录的事件,请增加日志的最大大小。 但是,如果其中任一日志的最大大小为 100 MB 或更大( maxSize 值为 104,857,600 或更多),则保留最大大小设置。

若要检查日志的最大大小,请使用 wevtutil 命令和 get-log 选项检索有关事件日志的信息:

wevtutil get-log "Security"
wevtutil get-log "Microsoft-Windows-PowerShell/Operational"

你将看到类似于以下文本的输出。 在这些情况下,最大日志大小要小于 100 MB。

name: Security
enabled: true
type: Admin
owningPublisher:
isolation: Custom
channelAccess: O:BAG:SYD:(A;;CCLCSDRCWDWO;;;SY)(A;;CCLC;;;BA)(A;;CC;;;ER)(A;;CC;;;NS)
logging:
  logFileName: %SystemRoot%\System32\Winevt\Logs\Security.evtx
  retention: false
  autoBackup: false
  maxSize: 20971520
publishing:
  fileMax: 1
name: Microsoft-Windows-PowerShell/Operational
enabled: true
type: Operational
owningPublisher: Microsoft-Windows-PowerShell
isolation: Application
channelAccess: O:BAG:SYD:(A;;0x2;;;S-1-15-2-1)(A;;0x2;;;S-1-15-3-1024-3153509613-960666767-3724611135-2725662640-12138253-543910227-1950414635-4190290187)(A;;0xf0007;;;SY)(A;;0x7;;;BA)(A;;0x7;;;SO)(A;;0x3;;;IU)(A;;0x3;;;SU)(A;;0x3;;;S-1-5-3)(A;;0x3;;;S-1-5-33)(A;;0x1;;;S-1-5-32-573)
logging:
  logFileName: %SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-PowerShell%4Operational.evtx
  retention: false
  autoBackup: false
  maxSize: 15728640
publishing:
  fileMax: 1

若要将安全日志或 Microsoft-Windows-PowerShell/Operational 事件日志的最大大小增加到 100 MB,请结合set-log以下选项运行wevtutil

wevtutil set-log "Security" /ms:104857600
wevtutil set-log "Microsoft-Windows-PowerShell/Operational" /ms:104857600

启用进程创建审核

使用以下命令打开进程创建审核:

reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit" /v "ProcessCreationIncludeCmdLine_Enabled" /t REG_DWORD /d 1 /f

启用 PowerShell 听录

reg add "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\Transcription" /v "EnableTranscripting" /t REG_DWORD /d 1 /f
reg add "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\Transcription" /v "EnableInvocationHeader" /t REG_DWORD /d 1 /f
reg add "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\Transcription" /v "OutputDirectory" /t REG_SZ /d C:\Transcripts /f

打开 PowerShell 模块日志记录

reg add "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging" /v "EnableModuleLogging" /t REG_DWORD /d 1 /f
reg add "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging\ModuleNames" /v "*" /t REG_SZ /d *

启用 PowerShell 脚本块日志记录

reg add "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" /v "EnableScriptBlockLogging" /t REG_DWORD /d 1 /f

了解输出

进程创建审核会将事件 ID 4688事件 ID 4689 写入安全事件日志。 事件 ID 4688 用于进程创建,包括进程命令行。 事件 ID 4689 用于进程终止。

听录在 C:\Transcripts\<output-date> 目录中创建文本文件。 如果目录尚不存在,将自动创建该目录。

例如:

C:\transcripts\20201211\PowerShell_transcript。<vm-name>.a+BWp8CT.20201211034929.txt

模块日志记录会将事件 ID 4103 记录到 Microsoft-Windows-PowerShell/Operational 事件日志。 “4103”事件包括 cmdlet 名称和输出。

如果运行 Write-Host $Env:ComputerName,则事件 ID 4013 顶部会显示以下文本,其中 value="<vm-name>" 指示命令的输出是 VM 的名称:

CommandInvocation(Write-Host): "Write-Host"
ParameterBinding(Write-Host): name="Object"; value="<vm-name>"

脚本块日志记录会将事件 ID 4104 记录到 Microsoft-Windows-PowerShell/Operational 事件日志。 “4104”事件包含脚本的内容。 超过事件的最大消息大小的脚本将记录为多个“4104”事件。

打开日志记录并重现脚本失败后,请运行以下脚本,将相关事件导出到逗号分隔值 (CSV) 文件。 以下查询检查前 24 小时(86,400,000 毫秒)的数据。 输入值 3600000 以仅检索最近一小时。 值 604800000 将检索最近一周。

$path = "PSEvents_$($env:COMPUTERNAME)_$(Get-Date ((Get-Date).ToUniversalTime()) -Format yyyyMMddHHmmss).csv"

$hours = 1 # Increase this to have it query more than just the last 1 hour
$now = Get-Date
$startTimeUtc = Get-Date ($now.AddHours(-$hours).ToUniversalTime()) -Format 'yyyy-MM-ddTHH:mm:ssZ'
$endTimeUtc = Get-Date ($now.ToUniversalTime()) -Format 'yyyy-MM-ddTHH:mm:ssZ'

$filterXML = @"
<QueryList>
    <Query Id="0" Path="Security">
        <Select Path="Security">
            Event
            [
                System
                [
                    (EventID = '4688' or EventID = '4689')
                    and
                    TimeCreated
                    [
                        @SystemTime &gt;= '$startTimeUtc'
                        and
                        @SystemTime &lt;= '$endTimeUtc'
                    ]
                ]
                and
                EventData
                [
                    Data[@Name="SubjectUserSid"] = "S-1-5-18"
                ]
            ]
        </Select>
    </Query>
    <Query Id="1" Path="Microsoft-Windows-PowerShell/Operational">
        <Select Path="Microsoft-Windows-PowerShell/Operational">
            Event
            [
                System
                [
                    (EventID ='4103' or EventID ='4104')
                    and
                    Security
                    [
                        @UserID ='S-1-5-18'
                    ]
                    and
                    TimeCreated
                    [
                        @SystemTime &gt;= '$startTimeUtc'
                        and
                        @SystemTime &lt;= '$endTimeUtc'
                    ]
                ]
            ]
        </Select>
    </Query>
</QueryList>
"@

$events = Get-WinEvent -FilterXml $filterXML | Sort-Object -Property RecordId
$events = $events | Select-Object -Property RecordId,
    TimeCreated, Id, MachineName, LogName, TaskDisplayName, Message
$events | Export-Csv -Path $path -NoTypeInformation

关闭 PowerShell 脚本执行的日志记录

若要撤消在 VM 上启用 PowerShell 脚本日志记录所做的更改,请执行以下步骤:

  1. 如果以前增加了安全日志或 Microsoft-Windows-PowerShell/Operational 日志的最大大小,请将这些值还原为默认的最大大小:

    wevtutil set-log "Security" /ms:20971520
    wevtutil set-log "Microsoft-Windows-PowerShell/Operational" /ms:15728640
    
  2. 关闭进程创建审核:

    reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit" /v "ProcessCreationIncludeCmdLine_Enabled" /t REG_DWORD /d 0 /f
    
  3. 关闭听录:

    reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v "EnableTranscripting" /t REG_DWORD /d 0 /f
    reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v "EnableInvocationHeader" /t REG_DWORD /d 0 /f
    reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v "OutputDirectory"
    
  4. 关闭模块日志记录:

    reg add "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging" /v "EnableModuleLogging" /t REG_DWORD /d 0 /f
    reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging\ModuleNames" /v "*"
    
  5. 关闭脚本块日志记录:

    reg add "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" /v "EnableScriptBlockLogging" /t REG_DWORD /d 0 /f
    
  6. 删除听录文件夹:

    Remove-Item -Path 'C:\Transcripts' -Force -Recurse
    
  7. 备份并清除安全日志和 Microsoft-Windows-PowerShell/操作日志:

    wevtutil clear-log Security /bu:Security.evtx
    wevtutil clear-log Microsoft-Windows-PowerShell/Operational /bu:Microsoft-Windows-PowerShell_Operational.evtx
    

    或者清除安全日志和 Microsoft-Windows-PowerShell/操作日志,而无需备份它们:

    wevtutil clear-log Security
    wevtutil clear-log Microsoft-Windows-PowerShell/Operational
    

VM 上的测试运行命令日志记录

Test-CustomScriptExtension.ps1 测试脚本下载到当前本地目录。 然后,使用 Invoke-AzVMRunCommand cmdlet 在 VM 上运行脚本。 使用 VM 的属性替换资源组名称和 VM 名称的占位符。

$scriptUri = 'https://raw.githubusercontent.com/Azure/azure-support-scripts/blob/users/GitHubPolicyService/6294a303-e34d-4bad-b6cd-5ed54245f020/Images_Extensions/PowerShell/Test-CustomScriptExtension.ps1'
$localFileLocation = "$PWD\Test-CustomScriptExtension.ps1"
(New-Object System.Net.WebClient).DownloadFile($scriptUri, $localFileLocation)

$commandSettings = @{
    ResourceGroupName = '<resource-group-name>'
    VMName = '<vm-name>'
    CommandId = 'RunPowerShellScript'
    ScriptPath = $localFileLocation
}
Invoke-AzVMRunCommand @commandSettings

在 VM 上测试自定义脚本扩展日志记录

使用 Set-AzVMCustomScriptExtension cmdlet 在 VM 上运行测试脚本 Test-CustomScriptExtension.ps1 使用 VM 的属性替换资源组名称、VM 名称和位置的占位符。

$commandSettings = @{
    ResourceGroupName = '<resource-group-name>'
    VMName = '<vm-name>'
    Name = 'CustomScriptExtension'
    FileUri = 'https://raw.githubusercontent.com/Azure/azure-support-scripts/blob/users/GitHubPolicyService/6294a303-e34d-4bad-b6cd-5ed54245f020/Images_Extensions/PowerShell/Test-CustomScriptExtension.ps1'
    Run = 'Test-CustomScriptExtension.ps1'
    Location = '<azure-region-name-or-code>'
    ForceRerun = (Get-Date).Ticks
}
Set-AzVMCustomScriptExtension @commandSettings

或者,可以使用 Set-AzVMExtension cmdlet 在 VM 上运行该测试脚本。 必须指定与其他ExtensionTypeCustomScriptExtension几个参数更改一起的参数。 使用 VM 的属性替换资源组名称、VM 名称和位置的占位符。

$publicConfigSettings = @{
    'fileUris' = @('https://raw.githubusercontent.com/Azure/azure-support-scripts/blob/users/GitHubPolicyService/6294a303-e34d-4bad-b6cd-5ed54245f020/Images_Extensions/PowerShell/Test-CustomScriptExtension.ps1')
    'commandToExecute' = 'powershell -File Test-CustomScriptExtension.ps1 -ExecutionPolicy Unrestricted'
}
$commandSettings = @{
    Publisher = 'Microsoft.Compute'
    ExtensionType = 'CustomScriptExtension'
    Settings = $publicConfigSettings
    ResourceGroupName = '<resource-group-name>'
    VMName = '<vm-name>'
    Name = 'CustomScriptExtension'
    TypeHandlerVersion = '1.10'
    Location = '<azure-region-name-or-code>'
}
Set-AzVMExtension @commandSettings

自定义脚本扩展测试脚本中的常见错误

  • 非零退出代码:运行 Test-CustomScriptExtension.ps1 脚本后,预期会收到以下错误消息:

    长时间运行的操作失败,状态为“失败”。
    其他信息:VM 在处理扩展“CustomScriptExtension”时报告了失败。
    错误消息:“命令执行已完成,但失败,因为它返回了非零退出代码:'2'”

    测试脚本运行 Exit 2 命令,如果脚本返回非零退出代码,则自定义脚本扩展应设计失败。 (在此示例中,2 是非零退出代码。此示例演示如何在启用的额外 PowerShell 日志记录中显示失败。

  • 更改存在冲突:此错误指示 VM 已将自定义脚本扩展安装为资源名称Microsoft.Compute.CustomScriptExtension,但你正在为当前执行指定 CustomScriptExtension 的其他资源名称

    无法更新 VM 扩展“CustomScriptExtension”的 handlerVersion 或 autoUpgradeMinorVersion。
    更改与处理程序“Microsoft.Compute.CustomScriptExtension”下的其他扩展冲突,类型Handler 版本为“1.10”和 autoUpgradeMinorVersion“True”。
    ErrorCode:OperationNotAllowed
    ErrorMessage:无法更新 VM 扩展“CustomScriptExtension”的 handlerVersion 或 autoUpgradeMinorVersion。
    更改与处理程序“Microsoft.Compute.CustomScriptExtension”下的其他扩展冲突,类型Handler 版本为“1.10”和 autoUpgradeMinorVersion“True”。
    ErrorTarget:
    StatusCode:409
    ReasonPhrase: Conflict

    可以指定资源名称所需的任何名称。 但是,如果已安装自定义脚本扩展,则必须执行以下操作之一:

    • 对后续执行使用相同的名称。
    • 在使用其他资源名称之前,请先删除该扩展资源。
  • 文件 URI 配置无效:此错误指示自定义脚本扩展最初与在受保护设置中指定的文件 URI 一起安装,但现在在公共设置中指定(反之亦然):

    长时间运行的操作失败,状态为“失败”。
    其他信息:VM 在处理扩展“CustomScriptExtension”时报告了失败。
    错误消息:“配置无效 - FileUris 存在于受保护和公共配置部分中;它必须在一个节中指定。”

自定义脚本扩展测试脚本中常见错误的解决方案

  • 若要修复“更改存在冲突”错误,请尝试重新运行 Set-AzVMCustomScriptExtensionSet-AzVMExtension将参数设置为 -Name VM 上已安装的自定义脚本扩展资源的资源名称。 对于示例错误,删除扩展时,请指定 -Name Microsoft.Compute.CustomScriptExtension 的参数。 但是,参数 -Name 必须是 VM 上已安装的扩展资源的任意资源名称。

    要用于-Name的名称将是错误此部分中的资源名称:“更改与处理程序”resource-name>“<下的其他扩展冲突。

    还可以通过从 VM 状态获取资源名称来验证要使用的正确资源名称。 输入 Get-AzVM cmdlet,如下所示:

    $status = Get-AzVM -ResourceGroupName <resource-group> -Name <vm-name> -Status
    $status.Extensions |
    Where-Object Type -EQ 'Microsoft.Compute.CustomScriptExtension' |
    Select-Object Name
    
  • 若要缓解“更改冲突”错误和“FileUris”错误,可以通过输入 Remove-AzVMCustomScriptExtension cmdlet 来删除现有的自定义脚本扩展:

    $params = @{
        ResourceGroupName = '<resourceGroupName>'
        VMName = '<vm-name>'
        Name = '<extension-resource-name>'
        Force = $True
    }
    Remove-AzVMCustomScriptExtension @params
    

    如果指定了不正确的资源名称,则 StatusCode 返回的值为 NoContent

    RequestId IsSuccessStatusCode StatusCode ReasonPhrase
    --------- ------------------- ---------- ------------
                             True  NoContent No Content
    

    如果指定正确的资源名称,则 StatusCode 返回的值为 OK

    RequestId IsSuccessStatusCode StatusCode ReasonPhrase
    --------- ------------------- ---------- ------------
                             True         OK OK
    

参考

联系我们寻求帮助

如果你有任何疑问或需要帮助,请创建支持请求联系 Azure 社区支持。 你还可以将产品反馈提交到 Azure 反馈社区

第三方联系人免责声明

Microsoft 会提供第三方联系信息来帮助你查找有关本主题的其他信息。 此联系信息可能会更改,恕不另行通知。 Microsoft 不保证第三方联系信息的准确性。