Назад | Перейти на главную страницу

Функции / возможности отчетов шлюза удаленных рабочих столов

Мы только что внедрили RD Gateway для нашего собственного отдела в рамках подготовки к тому, чтобы все агентство стало работать на дому. Все это настроено и отлично работает, но я пытался понять, как лучше всего отслеживать / сообщать о пользователях. Я вижу стороннее программное обеспечение, которое будет делать это, но есть ли что-нибудь встроенное или с помощью PowerShell / сценариев, которые я мог бы использовать, что дало бы мне отчет о повседневной активности пользователей? Что-то вроде: «Пользователь А, подключенный в это время, был в сети так долго, отправил / получил столько данных»? В основном это то же самое, что вы можете увидеть в средстве просмотра событий. В идеале я хотел бы иметь такую ​​настройку, чтобы один раз в день он отправлял мне электронное письмо с ежедневным использованием, когда руководитель спрашивает, действительно ли его человек работает (или, по крайней мере, отправляет и получает онлайн x количество данных), Я дам им несколько показателей. Я понимаю, что реальный результат работы важен и является скорее управленческой проблемой, но я хотел бы иметь возможность предложить как можно больше со своей стороны, когда меня об этом попросят.

Мысли? Спасибо!

Некоторое время назад я написал сценарий, который я включу в конце этого сообщения, но сначала мне нужно сказать о нем несколько вещей.

Это не идеально. Он даже не работает достаточно хорошо, чтобы я использовал его на регулярной основе. Я публикую его, потому что там есть кое-что, что поможет вам, если вы решите развернуть собственное решение с помощью PowerShell. Это должно пройти примерно 80% пути. Причина написания этого сценария, вероятно, была больше связана с экспериментами с Google Charts и запросом журналов событий с использованием фильтров XML, чем с поиском работоспособного решения для отчетов удаленного рабочего стола. Я, по-видимому, жестко запрограммировал там несколько вещей (например, имя терминального сервера), которые я хотел вернуть и исправить (прежде чем я отказался от него), как вы можете понять из моих комментариев. Сценарий должен либо создать график, либо вернуть «Нет активности». Иногда просто зависает ...

В любом случае, он создаст цветной график активности пользователя за день, который выглядит следующим образом:

Пожалуйста, не используйте этот сценарий в качестве примера того, как это должен готово, это как я сделал это давным-давно. Без дальнейших церемоний...

## TO DO
# 1. Make timing queries correct (without hard-coding the +6)

param
(
    [parameter(Mandatory=$true)]
    [DateTime]
    # Specifies date of information to be returned
    $QueryDate,

    [parameter(Mandatory=$true)]
    [String]
    # Specifies user to run report on
    $User,

    [String]
    # Specify server to query (default: TSSRV)
    $Server="TSSRV",

    [Switch]
    # Automatically view graph upon completion of command
    $ShowGraph
)

function constructQuery
{
    param
    (
        [DateTime]
        $date = $QueryDate,

        [string]
        $username = $User,

        [Parameter(Mandatory=$false)]
        [string]
        $TargetLogonId,

        [Parameter(Mandatory=$false)]
        [ValidatePattern("[0-3]")]
        [int]
        $queryType, # 0 = EventID4647, 1 = EventID4634, 2 = EventID4779, 3 = EventID4778

        [Parameter(Mandatory=$false)]
        [alias("LastEvent")]
        [int]
        $_lastEvent
    )

    [string]$ReturnQuery = "" #initialize blank query

    [string]$DateFormatString = "{0:yyyy'-'MM'-'dd'T'HH':'mm':'sss'Z'}"
    [string]$StartDateTimeString = $DateFormatString -f $date.AddHours(6) # add 6 stupid hours for timezone...
    [string]$EndDateTimeString = $DateFormatString -f ($date.AddDays(1)).AddHours(6) # add 6 stupid hours for timezone...

    [string]$TimeQueryString = "TimeCreated[@SystemTime>='$StartDateTimeString' and @SystemTime<'$EndDateTimeString']]] and "

    [string]$TargetLogonString = "*[EventData[Data[@Name=`'TargetLogonId`']=`'$TargetLogonId`']]"
    [string]$LogonIDString = "*[EventData[Data[@Name=`'LogonID`']=`'$TargetLogonId`']]"

    [string]$QueryHeader = "<QueryList><Query Id=`"0`" Path=`"Security`">"
    [string]$QueryFooter = "</Query></QueryList>"

    $ReturnQuery += $QueryHeader

    if(!$TargetLogonId) # Query for Logons
    {
        $ReturnQuery += "<Select Path=`"Security`">" + `
                            "*[System[(EventID=4624) and " + `
                            $TimeQueryString + `
                            "*[EventData[Data[@Name=`'LogonType`']=10]] and " + `
                            "*[EventData[Data[@Name=`'TargetUserName`']=`'$username`']]" + `
                        "</Select>" + `
                        "<Suppress Path=`"Security`">" + `
                            "*[EventData[Data[@Name=`'LogonGuid`']=`'{00000000-0000-0000-0000-000000000000}`']]" + `
                        "</Suppress>"
    }
    else
    {
        [string]$tempString = "<Select Path=`"Security`">" + `
                              "*[System[(EventID={0}) and " + `
                              $TimeQueryString

        switch ($queryType)
        {
            {$_ -eq 0 -or $_ -eq 1} { $tempString += $TargetLogonString }
            {$_ -eq 2 -or $_ -eq 3} { $tempString += $LogonIDString }
            default {Write-Host ERROR; exit}
        }

        $tempString += "{1}" + `
                      "</Select>"

        switch($queryType)
        {
            0 { $ReturnQuery += $tempString -f "4647", "" }
            1 { $ReturnQuery += $tempString -f "4634", "and *[EventData[Data[@Name=`'LogonType`']=10]]" }
            2 { $ReturnQuery += $tempString -f "4779", "and *[System[(EventRecordID&gt;$_lastEvent)]]" }
            3 { $ReturnQuery += $tempString -f "4778", "and *[System[(EventRecordID&gt;$_lastEvent)]]" }
            default {Write-Host ERROR; exit}
        }
    }

    $ReturnQuery += $QueryFooter    
    $ReturnQuery
}

function search
{
    param
    (
        [string]
        $_User = $User,

        [Parameter(Mandatory=$false)]
        [string]
        $_TargetLogonId,

        [ref]
        $_lastEvent,

        [ref]
        $_currentEvent,

        [int]
        $_queryType, # Logoff = 1, Disconnect = 2, Reconnect = 3

        [int]
        $_eventIDNumber
    )

    try
    {
        [string]$query = constructQuery -Date $QueryDate -username $User -TargetLogonId $_TargetLogonId -queryType $_queryType -LastEvent $_eventIDNumber
        $query | Out-File -Append c:\TEMP\queries.txt # DEBUG
        $_lastEvent.Value = $_currentEvent.Value # DEBUG
        $_currentEvent.Value = Get-WinEvent -ComputerName $Server -FilterXML $query -Oldest -ErrorAction Stop # DEBUG
        #"SUCCESS" | Out-File -Append c:\TEMP\queries.txt # DEBUG
        $true
    }
    catch
    {
        $false
    }
}

function eventURLBuilder
{
    param
    (
        [System.Diagnostics.Eventing.Reader.EventLogRecord]
        $_currentEvent,

        [System.Diagnostics.Eventing.Reader.EventLogRecord]
        $_lastEvent,

        [alias("Disconnected")]
        [switch]
        $_disconnected
    )

    $lastEventDT = Get-Date $_lastEvent.TimeCreated
    $dTimeStart = [decimal]$lastEventDT.ToString("HH") + [decimal]($lastEventDT.ToString("%m")/60)
    if($_disconnected) { $baseString = "B,$disconnectedColor,0," + ("{0:N2}" -f $dTimeStart) + ":" }
    else { $baseString = "B,$activeColor,0," + ("{0:N2}" -f $dTimeStart) + ":" }
    $currentEventDT = Get-Date $_currentEvent.TimeCreated
    $dTimeFinish = [decimal]$currentEventDT.ToString("HH") + [decimal]($currentEventDT.ToString("%m")/60)

    $baseString + ("{0:N2}" -f $dTimeFinish) + ",0|" # return string
}

## Public Variables ##
[int]$state = 0 # 0=Logged off, 1=Logged on, 2=Disconnected, 3=Reconnected 
[string]$eventsURL = "" # snippet of URL that will hold the data being charted
$totalDisconnectedTime = New-TimeSpan # will contain total disconnected time
$totalLogonTime = New-TimeSpan # will contain total logged on (not disconnected) time
[string]$activeColor = "4D7CFF" #76A4FB
[string]$disconnectedColor = "E86868" #990000
[int]$lastEventProcessed = 0
######################

try
{
    $LogonsQuery = constructQuery
    $LogonEvents = Get-WinEvent -ComputerName $Server -FilterXML $LogonsQuery -Oldest -ErrorAction Stop
}
catch
{
    Write-Host "No activity." -ForegroundColor Red
    exit
}

foreach ($LogonEvent in $LogonEvents)
{
    if($LogonEvent.RecordId -lt $lastEventProcessed)
    {
        continue
    }

    #Extract 'TargetLogonId' from $LogonEvent XML to match related session events
    [xml]$xmlEvent = $LogonEvent.ToXml()
    $xmlns = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $xmlEvent.NameTable
    $xmlns.AddNamespace("el", "http://schemas.microsoft.com/win/2004/08/events/event")
    [string]$TargetLogonId = $xmlEvent.SelectSingleNode("el:Event/el:EventData/el:Data[@Name = 'TargetLogonId']/text()", $xmlns).Value
    [int]$lastEventProcessed = $LogonEvent.RecordId

    $lastEvent = $LogonEvent    
    $currentEvent = $LogonEvent # initialize (value meaningless at this point)

    $state = 1 # Mark as "logged on"
    while ($state -ne 0)
    {
        if ($state -eq 1) #LOGGED ON
        {
            try
            {
                $hasEvent = search $User $TargetLogonId ([ref]$lastEvent) ([ref]$currentEvent) 2 $lastEventProcessed # 2 = disconnect
            }
            catch
            {
                Write-Host ERROR -ForegroundColor Red
                $_
                exit
            }
            if ($hasEvent)
            {
                try
                {
                    $totalLogonTime += (Get-Date $currentEvent.TimeCreated) - (Get-Date $lastEvent.TimeCreated)
                    $eventsURL += eventURLBuilder $currentEvent $lastEvent
                    $state = 2
                    $lastEventProcessed = $currentEvent.RecordId
                }
                catch
                {
                    Write-Host "ERROR" -ForegroundColor Red
                    $_
                    exit
                }
            }
            if ($state -ne 0 -and $state -ne 2)
            {
                try
                {
                    $hasEvent = search $User $TargetLogonId ([ref]$lastEvent) ([ref]$currentEvent) 1 $lastEventProcessed
                }
                catch
                {
                    Write-Host ERROR -ForegroundColor Red
                    $_
                    exit
                }
                if ($hasEvent)
                {
                    try
                    {
                        $totalLogonTime += (Get-Date $currentEvent.TimeCreated) - (Get-Date $lastEvent.TimeCreated)
                        $eventsURL += eventURLBuilder $currentEvent $lastEvent
                        $state = 0
                    }
                    catch
                    {
                        Write-Host "ERROR" -ForegroundColor Red
                        $_
                        exit
                    }
                }
            }
        }
        elseif ($state -eq 2) #DISCONNECTED
        {
            try
            {
                $hasEvent = search $User $TargetLogonId ([ref]$lastEvent) ([ref]$currentEvent) 3 $lastEventProcessed # 3 = reconnect
            }
            catch
            {
                Write-Host "ERROR" -ForegroundColor Red
                $_
                exit
            }
            if ($hasEvent)
            {
                try
                {
                    $totalDisconnectedTime += (Get-Date $currentEvent.TimeCreated) - (Get-Date $lastEvent.TimeCreated)
                    $eventsURL += eventURLBuilder $currentEvent $lastEvent -Disconnected
                    $state = 3
                    $lastEventProcessed = $currentEvent.RecordId
                }
                catch
                {
                    Write-Host "ERROR" -ForegroundColor Red
                    $_
                    exit
                }
            }
            if ($state -ne 0 -and $state -ne 3)
            {
                try
                {
                    $hasEvent = search $User $TargetLogonId ([ref]$lastEvent) ([ref]$currentEvent) 1 $lastEventProcessed
                }
                catch
                {
                    Write-Host "ERROR" -ForegroundColor Red
                    $_
                    exit
                }
                if ($hasEvent)
                {
                    try
                    {
                        $totalDisconnectedTime += (Get-Date $currentEvent.TimeCreated) - (Get-Date $lastEvent.TimeCreated)
                        $eventsURL += eventURLBuilder $currentEvent $lastEvent -Disconnected
                        $state = 0
                    }
                    catch
                    {
                        Write-Host "ERROR" -ForegroundColor Red
                        $_
                        exit
                    }
                }
            }
        }
        elseif ($state -eq 3) #RECONNECTED
        {
            try
            {
                $hasEvent = search $User $TargetLogonId ([ref]$lastEvent) ([ref]$currentEvent) 2 $lastEventProcessed # 2 = disconnect
            }
            catch
            {
                Write-Host ERROR -ForegroundColor Red
                $_
                exit
            }
            if ($hasEvent)
            {
                try
                {
                    $totalLogonTime += (Get-Date $currentEvent.TimeCreated) - (Get-Date $lastEvent.TimeCreated)
                    $eventsURL += eventURLBuilder $currentEvent $lastEvent
                    $state = 2
                    $lastEventProcessed = $currentEvent.RecordId
                }
                catch
                {
                    Write-Host "ERROR" -ForegroundColor Red
                    $_
                    exit
                }
            }
            if ($state -ne 0 -and $state -ne 2)
            {
                try
                {
                    $hasEvent = search $User $TargetLogonId ([ref]$lastEvent) ([ref]$currentEvent) 1 $lastEventProcessed
                }
                catch
                {
                    Write-Host "ERROR" -ForegroundColor Red
                    $_
                    exit
                }
                if ($hasEvent)
                {
                    try
                    {
                        $totalLogonTime += (Get-Date $currentEvent.TimeCreated) - (Get-Date $lastEvent.TimeCreated)
                        $eventsURL += eventURLBuilder $currentEvent $lastEvent
                        $state = 0
                    }
                    catch
                    {
                        Write-Host "ERROR" -ForegroundColor Red
                        $_
                        exit
                    }
                }
            }
        }
    }
}

$chartURL = "http://chart.googleapis.com/chart?cht=lc&chtt=" + `
            $User + `
            "+Remote+Access+for+" + `
            $QueryDate.ToShortDateString() + `
            "|Total%3A+" + ($totalDisconnectedTime + $totalLogonTime).Hours + "+hours+and+" + ($totalDisconnectedTime + $totalLogonTime).Minutes + "+minutes" + `
            "-- Active%3A+" + $totalLogonTime.Hours + "+hours+and+" + $totalLogonTime.Minutes + "+minutes" + `
            "&chm="
$chartURL += $eventsURL
$chartURL = $chartURL.Remove($chartURL.Length - 1) # remove the extraneous '|' character
$chartURL += "&chxl=0:|12%3A00|3%3A00|6%3A00|9%3A00|12%3A00|3%3A00|6%3A00|9%3A00|12%3A00|1:||AM|PM||2:|&chxtc=0,5&chdl=|Logged+On|Disconnected&chco=FFFFFF|" + `
             $activeColor + `
             "|" + `
             $disconnectedColor + `
             "&chs=800x240&chxt=x,x,y&chd=t:100&chls=FFFFFF&chfd=0,x,0,24,1,100"
$saveFolder = "c:\TEMP\chart.png"
$clnt = new-object System.Net.WebClient
$clnt.DownloadFile($chartUrl,$saveFolder)
if($ShowGraph)
{
    &"$saveFolder"
}
$chartURL | Out-File c:\TEMP\chartURL.txt # DEBUG
Write-Host Chart Generated at $saveFolder -ForegroundColor Green

Чтобы запустить этот скрипт, сделайте следующее -

.\Get-TSUserReport.ps1 -ShowGraph -QueryDate "4/11/2012" -User jdoe