it-swarm-ja.com

Windowsタスクスケジューラチャート?

日時に基づいてWindowsタスクスケジューラのすべてのタスクのグラフを作成できるユーティリティ/ツールがあるかどうか誰かが知っているので、サーバーの過負荷を引き起こす特定の時点で重複があるかどうかを知ることができますか?

スケジュールされたすべてのタスク用のサーバーがあり、ex-adminによる設計が不十分なため、最近実行速度が低下しています。各タスクの実行ウィンドウを確認する必要があります。Excelでグラフを手動で作成できますが、それも同様です。一つずつ通過することがたくさんあります。

うまくいけば、いくつかのユーティリティがこれを行うことができます。

4
Root Loop

このPowershellスクリプトは、タスクスケジューラのイベントログを読み取り、起動されたすべてのタスクについてCSV Task NameStart DateFinish Date、およびDurationにエクスポートします。次に、このデータを選択したスプレッドシートにフィードして、ガントチャートを作成できます。

要件:

  • PowerShell 2.0
  • Windows Server 2008\Vista

スクリプトは次の引数を受け入れます。

  • Computersクエリするコンピューター名の配列。指定しない場合、ローカルコンピュータにクエリを実行します。
  • MaxEventsイベントログから読み取るイベントの最大量。デフォルトは100です。
  • PathCSVが保存されるディスク上の既存のフォルダー。指定しない場合、スクリプトフォルダが使用されます。 CSVの名前は次のようになります:COMPUTERNAME_TaskScheduler.csv
  • ユーザーリモート認証のユーザー名。
  • パスワード:ユーザーのパスワード。指定しない場合、スクリプトによって要求されます。
  • Verbosescriptは、Write-Verboseメッセージを介して何が起こっているかを示します。

例(PowerShellコンソールから実行):

ローカルコンピューターからデータを取得し、最後の100イベントを処理し、CSVをスクリプトフォルダーに保存します。

.\TS_Gantt.ps1

リモートコンピューターからデータを取得し、最後の200イベントを処理し、CSVをc:\ts_ganttフォルダーに保存します。

.\TS_Gantt.ps1 -Computers Sun, Earth, Moon -MaxEvents 200 -Path 'c:\ts_gantt'

スクリプト(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
    }

更新(1):別のユーザー(ユーザー名\パスワードパラメーター)として認証する機能を追加し、XMLを使用したフィルタリングに切り替えました。 より高速 そしてVista\Server 2008 PCに対してこのスクリプトを実行できるようにする必要があります(回避 このバグ )。また、PowerShell2.0と互換性があります。

更新(2):スクリプトのパス検出を微調整したので、PowershellISEで壊れないようになりました。また、一部のPCでは、タスクスケジューラのログが無効になっていることがわかりました。ロギングが有効になっていることを確認するには、次のようにします。

  1. All Tasks Historyが有効になっているかどうかを確認してください。 Disable All Tasks History(ugh)と読む必要があります:

All Tasks History

  1. タスクスケジューラのOperationalイベントログが有効になっているかどうかを確認します。開いた:

    Event Viewer → Applications and Services Log → Microsoft → Windows → Task Scheduler → Operational →それを右クリックします(または右ペインに移動します) Properties

Task Scheduler Operational Event Log

Task Scheduler Operational Event Log Properies

更新(3):欠落または使用できないイベントログの処理を修正し、Verboseメッセージの束を追加しました。

2
beatcracker