Depurar scripts de PowerShell ejecutados por Extensión de Script Personalizado o Comando de Ejecución

Applies to: ✔️ máquinas virtuales de Windows

Resumen

En este artículo se describe cómo probar y corregir un error en un script de PowerShell que usa la característica Extensión de script personalizado o Ejecutar comando.

Importante

Nuevo Pruebe la asistencia de máquina virtual para resolver los principales problemas. Se recomienda ejecutar VM assist for Windows o VM assist for Linux. Estas herramientas de diagnóstico basadas en scripts le ayudan a identificar problemas comunes que afectan al agente invitado de máquina virtual Azure y el estado general de las máquinas virtuales.

Si experimenta problemas de rendimiento en las máquinas virtuales, ejecute estas herramientas primero antes de ponerse en contacto con Soporte técnico de Microsoft.

Requisitos previos

Información general

Supongamos que usó la Extensión de Script Personalizado o Ejecutar comando para ejecutar un script de PowerShell. ¿Qué hace si se produce un error en el script? Tiene varios métodos disponibles para determinar la causa del error.

PowerShell tiene más de un flujo de salida. Los registros de la extensión de script personalizado y los scripts Ejecutar comando envían la secuencia de Éxito al StdOut subestado y la secuencia de Error al StdErr subestado. Estos subestados pertenecen a la extensión usada para ejecutar el script de la Extensión de Script Personalizado o el script de Ejecutar Comando.

Los subestados StdOut y StdErr están en la vista de instancia del punto de registro de certificados (CRP) para la máquina virtual (VM). Estos subestados son visibles en diferentes ubicaciones, como se muestra en la tabla siguiente.

Interfaz Cómo ver el subestado
Azure portal
  1. Busque y seleccione Máquinas virtuales.
  2. Seleccione la máquina virtual en la lista.
  3. En la página de información general de la máquina virtual, seleccione Extensiones + aplicaciones>Extensiones.
  4. Seleccione la extensión que se usó para ejecutar el comando. (Se denominará cualquiera de las dos CustomScriptExtension o RunCommand.
  5. Seleccione Ver estado detallado.
Azure PowerShell Escriba el cmdlet Get-AzVM para obtener las propiedades de una máquina virtual de Azure, como se indica a continuación:
Get-AzVM -ResourceGroupName <resource-group-name> -Name <vm-name> -Status
CLI de Azure Escriba el comando az vm get-instance-view para obtener información de instancia sobre una máquina virtual de Azure, como se indica a continuación:
az vm get-instance-view --resource-group <resource-group-name> --name <vm-name> --query instanceView.extensions

El error que normalmente hace que el script falle aparece en el StdErr subestado. Sin embargo, los scripts también pueden fallar sin registrar una entrada de error fatal en ese subestado.

Probar el script manualmente y mediante PsExec

Compruebe manualmente que el script se ejecuta correctamente desde una consola administrativa de PowerShell en la máquina virtual.

Si el script funciona manualmente, use PsExec para ejecutar el script mediante la cuenta del sistema local. Para la Extensión de Script Personalizado y el Run Command, los scripts se ejecutan mediante esa cuenta. Al introducir psexec -s, puede probar el script utilizando la cuenta del sistema local, pero sin utilizar ni la Extensión de Script Personalizado ni el Comando Ejecutar. Si el error se reproduce con psexec -s, entonces la Extensión de Script Personalizado y el Comando Ejecutar no son la causa del problema.

Prueba mediante PsExec

Puede usar PsExec para ejecutar un script de prueba de PowerShell de forma remota. Abra una ventana de comandos con privilegios administrativos y luego ingrese el siguiente comando PsExec. Reemplace el marcador de posición por el nombre completo del script de PowerShell:

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

O bien, puede usar PsExec de forma interactiva. En el ejemplo siguiente, escriba el comando whoami para mostrar que PowerShell se ejecuta en la cuenta del sistema local (NT AUTHORITY\SYSTEM):

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

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

Activación del registro de la ejecución de scripts de PowerShell

Si el StdErr subestado no muestra la causa del problema, puede activar varios tipos de registro para mostrar colectivamente el contenido y la salida del script. Este registro muestra lo que intenta lograr el script y el resultado de ejecutar el script.

Para activar diferentes tipos de registro, siga los pasos descritos en las siguientes secciones.

Advertencia

Algunas de las instrucciones implican cambiar el registro de Windows. Es posible que se produzcan problemas graves si modifica el Registro de forma incorrecta mediante el Editor del Registro u otro método. Estos problemas pueden requerir la reinstalación del sistema operativo. Microsoft no puede garantizar que estos problemas se puedan resolver. Realice primero una copia de seguridad de las entradas del Registro existentes y, a continuación, modifique el registro en su propio riesgo.

Aumento del tamaño máximo de los registros de eventos

Se puede generar un gran número de eventos tanto en el registro de seguridad como en el registro de eventos de Microsoft-Windows-PowerShell/Operational. Para evitar la pérdida de estos eventos registrados, aumente el tamaño máximo de los registros. Pero si cualquiera de estos registros tiene el tamaño máximo de 100 MB o mayor (un maxSize valor de 104 857 600 o más), deje el valor de tamaño máximo tal como está.

Para comprobar el tamaño máximo del registro, use el comando wevtutil y la get-log opción para recuperar información sobre los registros de eventos:

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

Verá un resultado que se asemeja al texto siguiente. En estos casos, el tamaño máximo del registro es mucho menor que 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

Para aumentar el tamaño máximo del registro de seguridad o del registro de eventos de Microsoft-Windows-PowerShell/Operational a 100 MB, ejecute wevtutil junto con la opción set-log:

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

Activar la auditoría de creación de procesos

Use el siguiente comando para activar la auditoría de creación de procesos:

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

Activación de la transcripción de 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

Activar el registro de módulos de 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 *

Activar el registro de bloques de scripts de PowerShell

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

Descripción de la salida

La auditoría de la creación de procesos escribirá el identificador de evento 4688 y el identificador de evento 4689 en el registro de eventos de seguridad. El identificador de evento 4688 es para la creación del proceso e incluye la línea de comandos de proceso. El identificador de evento 4689 es para la finalización del proceso.

La transcripción crea un archivo de texto en el directorio C:\Transcripciones\<output-date> . El directorio se creará automáticamente si aún no existe.

Por ejemplo:

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

El registro de módulos registrará el identificador de evento 4103 en el registro de eventos de Microsoft-Windows-PowerShell/Operational. El evento "4103" incluye el nombre y la salida del cmdlet.

Si ejecuta Write-Host $Env:ComputerName, la parte superior del id. de evento 4013 muestra el texto siguiente, donde value="<vm-name>" indica que la salida del comando era el nombre de la máquina virtual:

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

El registro de bloques de script registrará el identificador de evento 4104 en el registro de eventos Microsoft-Windows-PowerShell/Operational. Los eventos "4104" contienen el contenido del script. Los scripts que superan el tamaño máximo de mensaje de un evento se registran como varios eventos "4104".

Después de activar el registro y reproducir el error del script, ejecute el siguiente script para exportar los eventos pertinentes a un archivo de valor separado por comas (CSV). La consulta siguiente examina las 24 horas anteriores (86 400 000 milisegundos) de datos. Escriba el valor 36000000 para recuperar solo la hora más reciente. El valor 6048000000 recuperará la semana más reciente.

$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

Desactivación del registro de la ejecución de scripts de PowerShell

Para deshacer los cambios realizados para habilitar el registro de scripting de PowerShell en la máquina virtual, siga estos pasos:

  1. Si anteriormente aumentó el tamaño máximo del registro de seguridad o del registro de Microsoft-Windows-PowerShell/Operational, revierta esos valores a los tamaños máximos predeterminados:

    wevtutil set-log "Security" /ms:20971520
    wevtutil set-log "Microsoft-Windows-PowerShell/Operational" /ms:15728640
    
  2. Desactivar la auditoría de creación de procesos:

    reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit" /v "ProcessCreationIncludeCmdLine_Enabled" /t REG_DWORD /d 0 /f
    
  3. Desactivar la transcripción:

    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. Desactivar el registro de módulos:

    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. Desactivar el registro de bloques de script:

    reg add "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" /v "EnableScriptBlockLogging" /t REG_DWORD /d 0 /f
    
  6. Quite la carpeta de transcripción:

    Remove-Item -Path 'C:\Transcripts' -Force -Recurse
    
  7. Realice una copia de seguridad y borre el registro de seguridad o el de Microsoft-Windows-PowerShell/Operational:

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

    O borre el registro de seguridad y el registro de Microsoft-Windows-PowerShell/Operational sin realizar una copia de seguridad de ellos:

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

Prueba del registro de comandos de ejecución en la máquina virtual

Descargue el script de prueba Test-CustomScriptExtension.ps1 en el directorio local actual. A continuación, ejecute el script en la máquina virtual mediante el cmdlet Invoke-AzVMRunCommand . Use las propiedades de la máquina virtual para reemplazar los marcadores de posición del nombre del grupo de recursos y el nombre de la máquina virtual.

$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

Prueba del registro de la extensión de script personalizado en la máquina virtual

Ejecute el script de prueba Test-CustomScriptExtension.ps1 en la máquina virtual mediante el cmdlet Set-AzVMCustomScriptExtension. Use las propiedades de la máquina virtual para reemplazar los marcadores de posición del nombre del grupo de recursos, el nombre de la máquina virtual y la ubicación.

$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

Como alternativa, puede ejecutar ese script de prueba en la máquina virtual mediante el cmdlet Set-AzVMExtension . Tendrá que especificar un parámetro `ExtensionType` junto con varios otros cambios de parámetros `CustomScriptExtension`. Use las propiedades de la máquina virtual para reemplazar los marcadores de posición del nombre del grupo de recursos, el nombre de la máquina virtual y la ubicación.

$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

Errores comunes en el script de prueba de la Extensión de Script Personalizado

  • Código de salida distinto de cero: después de ejecutar el script Test-CustomScriptExtension.ps1 , espere recibir el siguiente mensaje de error:

    La operación de larga ejecución falló con el estado "Falló".
    Información adicional: la máquina virtual ha notificado un error al procesar la extensión "CustomScriptExtension".
    Mensaje de error: "La ejecución del comando finalizó, pero se produjo un error porque devolvió un código de salida distinto de cero de: '2'"

    El script de prueba ejecuta el comando Exit 2, y se espera que la Extensión de Script Personalizado falle intencionadamente si el script devuelve un código de salida distinto de cero. (En este ejemplo, 2 es el código de salida distinto de cero). En este ejemplo se muestra cómo aparece un error en el registro adicional de PowerShell que ha habilitado.

  • Cambio está en conflicto: Este error indica que la máquina virtual ya tiene instalada la extensión Custom Script como recurso con el nombre Microsoft.Compute.CustomScriptExtension, pero está especificando un nombre de recurso diferente, CustomScriptExtension, para la ejecución actual.

    No se puede actualizar "handlerVersion" o "autoUpgradeMinorVersion" para la extensión de máquina virtual "CustomScriptExtension".
    El cambio está en conflicto con otras extensiones bajo el controlador 'Microsoft.Compute.CustomScriptExtension', con la versión del controlador de tipo '1.10' y autoUpgradeMinorVersion 'True'.
    ErrorCode: OperaciónNoPermitida
    ErrorMessage: No se puede actualizar handlerVersion o autoUpgradeMinorVersion para la extensión de máquina virtual "CustomScriptExtension".
    El cambio está en conflicto con otras extensiones en el controlador 'Microsoft Compute CustomScriptExtension', con versión de typeHandler '1.10' y autoUpgradeMinorVersion 'True'.
    ErrorTarget:
    StatusCode: 409
    ReasonPhrase: Conflicto

    Puede especificar lo que desee para el nombre del recurso. Sin embargo, si ya tiene instalada la extensión de script personalizado, debe realizar cualquiera de las siguientes acciones:

    • Use el mismo nombre para las ejecuciones posteriores.
    • En primer lugar, quite ese recurso de extensión antes de usar un nombre de recurso diferente.
  • Configuración de URI de archivo no válida: este error indica que la extensión de script personalizado se instaló originalmente junto con los URI de archivo especificados en la configuración protegida, pero ahora se especifican en la configuración pública (o viceversa):

    La operación de larga ejecución falló con el estado 'Fallido'.
    Información adicional: la máquina virtual ha notificado un error al procesar la extensión "CustomScriptExtension".
    Mensaje de error: "Configuración no válida: FileUris está presente en las secciones de configuración protegida y pública; debe especificarse en una sola sección".

Soluciones a errores comunes en el script de prueba de la extensión de script personalizado

  • Para corregir el error "El cambio está en conflicto", intente volver a ejecutar Set-AzVMCustomScriptExtension o Set-AzVMExtension, pero establezca el parámetro -Name al nombre del recurso de la extensión de script personalizado que ya está instalado en la máquina virtual. Para el error de ejemplo, al quitar la extensión, especifique un parámetro -Name de Microsoft. Compute.CustomScriptExtension. Pero el -Name parámetro debe coincidir con el nombre del recurso de la extensión que ya está instalada en la máquina virtual.

    El nombre que se va a usar para -Name será el nombre del recurso en esta parte del error: "El cambio está en conflicto con otras extensiones en el controlador "<resource-name>".

    También puede comprobar el nombre de recurso correcto para usar obteniéndolo del estado de la máquina virtual. Escriba el cmdlet Get-AzVM , como se indica a continuación:

    $status = Get-AzVM -ResourceGroupName <resource-group> -Name <vm-name> -Status
    $status.Extensions |
    Where-Object Type -EQ 'Microsoft.Compute.CustomScriptExtension' |
    Select-Object Name
    
  • Para mitigar el error "El cambio está en conflicto" y el error "FileUris", puede eliminar la extensión de script personalizado existente ingresando el cmdlet Remove-AzVMCustomScriptExtension:

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

    Si especifica el nombre de recurso incorrecto, el valor devuelto StatusCode es NoContent:

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

    Si especifica el nombre de recurso correcto, el valor devuelto StatusCode es OK:

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

Referencias

Aviso de declinación de responsabilidades sobre la información de contacto de terceros

Microsoft proporciona información de contacto de terceros para ayudarle a encontrar información adicional sobre este tema. Esta información de contacto puede cambiar sin previo aviso. Microsoft no garantiza la precisión de la información de contacto de terceros.