Este script de Powershell leerá el registro de eventos del programador de tareas y lo exportará a CSV Task Name
, Start Date
, Finish Date
y Duration
Para todas las tareas que se lanzaron. Luego, puede enviar estos datos a la hoja de cálculo de su elección y construir un diagrama GANTT.
Requisitos:
- PowerShell 2.0
- Windows Server 2008 \ Vista
Script acepta los siguientes argumentos:
- Ordenadores : matriz de nombres de computadora para consultar. Si no se especifica, se consultará la computadora local.
- MaxEventos : Cantidad máxima de eventos para leer del registro de eventos. El valor predeterminado es 100.
- Camino : carpeta existente en el disco, donde se guardarán los CSV. Si no se especifica, se utilizará la carpeta de script. Los CSV se nombran así:
COMPUTERNAME_TaskScheduler.csv
.
- Usuario : Nombre de usuario para autenticación remota.
- Contraseña : Contraseña para el usuario. Si no se especifica, será solicitado por el script.
- Verboso : guión le dirá lo que está pasando a través de
Write-Verbose
mensajes
Ejemplos (ejecutados desde la consola de PowerShell):
Obtenga datos de la computadora local, procese los últimos 100 eventos, guarde el archivo CSV en la carpeta del script:
.\TS_Gantt.ps1
Obtenga datos de computadoras remotas, procese los últimos 200 eventos, guarde archivos CSV en el c:\ts_gantt
carpeta:
.\TS_Gantt.ps1 -Computers Sun, Earth, Moon -MaxEvents 200 -Path 'c:\ts_gantt'
Guión ( TS_Gantt.ps1
):
Param
(
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[string[]]$Computers = $env:COMPUTERNAME,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[ValidateRange(1, 2147483647)]
[int]$MaxEvents = 100,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[ValidateScript({
if(!(Test-Path -LiteralPath $_ -PathType Container))
{
throw "Folder doesn't exist: $_"
}
$true
})]
[ValidateNotNullOrEmpty()]
[string]$Path,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[string]$User,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[string]$Password
)
# Get script path, to save CSV's, if not specified
if(!$Path)
{
if($psISE.CurrentFile.FullPath)
{
$Path = $psISE.CurrentFile.FullPath | Split-Path
}
elseif($script:MyInvocation.MyCommand.Path)
{
$Path = $script:MyInvocation.MyCommand.Path | Split-Path
}
else
{
$Path = $PWD.Path
}
Write-Verbose "No Path specified, defaulting to: $Path"
}
# Get user credentials, if needed
if($User)
{
Write-Verbose "User specified: $User"
if($Password)
{
Write-Verbose 'Password specified, converting to credentials object'
$SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $User, $SecurePassword
}
else
{
Write-Verbose 'Password not specified, requesting from user.'
$Credentials = Get-Credential -UserName $User -Message "Enter password for user: $User" -ErrorAction Stop
if(!$Credentials)
{
Write-Verbose 'User cancelled password request'
}
}
}
# https://mnaoumov.wordpress.com/2014/05/15/task-scheduler-event-ids/
$TaskStartId = 100
$TaskFinishId = 102
$FilterXml = @"
<QueryList>
<Query Id="0" Path="Microsoft-Windows-TaskScheduler/Operational">
<Select Path="Microsoft-Windows-TaskScheduler/Operational">*[System[(EventID=$TaskStartId or EventID=$TaskFinishId)]]</Select>
</Query>
</QueryList>
"@
# Hashtable to hold results
$Result = @{}
# Loop through computers
foreach ($PC in $Computers){
# Grab the events from a PC
$Params = @{
ComputerName = $PC
FilterXml = $FilterXml
MaxEvents = $MaxEvents
}
if($Credentials)
{
$Params += @{Credential = $Credentials}
}
Write-Verbose "Trying to get Task Scheduler's event log. Computer: $PC"
try
{
$Events = Get-WinEvent @Params -ErrorAction Stop
Write-Verbose "Success"
}
catch
{
Write-Error "Can't access Task Scheduler's event log. Computer: $PC"
continue
}
if(!$Events)
{
Write-Error "Task Scheduler's event log is empty! Computer: $PC"
continue
}
Write-Verbose 'Extracting additional data from events'
$Events |
ForEach-Object {
# Hashtable for new properties
$Properties = @{}
# Convert the event to XML and iterate through each one
# of the XML message properties to extract additional data
([xml]$_.ToXml()).Event.EventData.Data |
ForEach-Object {
$Properties.Add($_.name, $_.'#text')
}
# Add extracted properties to the event object
$_ | Add-Member -NotePropertyMembers $Properties
}
# Set default start\finish date for event in case
# it's still running or was started before $MaxEvents
$DefaultStartDate = $Events[-1].TimeCreated
$DefaultFinishDate = Get-Date
Write-Verbose "Default task start date: $DefaultStartDate"
Write-Verbose "Default task finish date: $DefaultFinishDate"
Write-Verbose 'Processing events...'
# Group events by ID and process them
$PcEvents = $Events |
Group-Object -Property InstanceId |
ForEach-Object {
# Get Name and start\finish Date
$TaskName = $_.Group[0].TaskName
$StartDate = ($_.Group | Where-Object {$_.OpcodeDisplayName -eq 'Start'}).TimeCreated
$FinishDate = ($_.Group | Where-Object {$_.OpcodeDisplayName -eq 'Stop'}).TimeCreated
# If we can't get dates, set them to defaults
if(!$StartDate)
{
$StartDate = $DefaultStartDate
}
elseif(!$FinishDate)
{
$FinishDate = $DefaultFinishDate
}
# Hashtable holding object's properties
$ItemProp = @{
Name = $TaskName
StartDate = $StartDate
FinishDate = $FinishDate
Duration = $FinishDate - $StartDate
}
# Output new object to the pipeline
New-Object psobject -Property $ItemProp |
Select-Object Name, StartDate, FinishDate, Duration
}
# Add data to results
$Result += @{$PC = $PcEvents}
}
# Loop through results
$Result.GetEnumerator() |
ForEach-Object {
# Export results to CSV, one file per computer
$CsvPath = Join-Path -Path $Path -ChildPath ($_.Key + '_TaskScheduler.csv')
Write-Verbose "Saving data to CSV: $CsvPath"
$_.Value | Export-Csv -LiteralPath $CsvPath -Force -NoTypeInformation
}
Actualización (1): Agregué la capacidad de autenticarme como usuario diferente (nombre de usuario / parámetros de contraseña) y cambié a filtrado usando XML, que es Más rápido y debe permitir ejecutar este script en las computadoras Vista \ Server 2008 (evita este error ). Además, PowerShell 2.0 compatible ahora.
Actualización (2): He ajustado la detección de la ruta del script, por lo que ahora no debería interrumpirse en Powershell ISE. Además, he descubierto que en algunas PC, el registro del Programador de tareas está deshabilitado. Esto es lo que debe hacer para verificar que el registro esté habilitado:
- Compruebe, si tiene
All Tasks History
habilitado Debería leer Disable All Tasks History
(ugh):
Compruebe si el programador de tareas Operational
El registro de eventos está habilitado.
Abierto:
Visor de eventos → Registro de aplicaciones y servicios → Microsoft → Windows → Programador de tareas → Operacional → haga clic con el botón derecho en él (o vaya al panel derecho) Propiedades
Actualización (3): Se corrigió el manejo de los registros de eventos faltantes o no disponibles, se agregaron varios Verbose
mensajes