我们刚刚为我们自己的部门实施了 RD Gateway,为推动整个机构的远程办公做准备。一切都已设置好,运行良好,但我一直在试图找出如何最好地监控/报告用户。我看到第三方软件可以做到这一点,但是否有任何内置或通过 powershell/脚本可以使用,可以向我提供用户日常活动的报告?比如说,“用户 A 此时连接,连接了这么长时间,发送/接收了这么多数据”?基本上就是你在事件查看器中看到的一些相同内容。理想情况下,我希望能够进行此设置,以便每天给我发送一次包含每日使用情况的电子邮件,当主管询问他们的员工是否真的在工作(或至少在线发送和接收 x 量的数据)时,我会有一些指标可以给他们。我意识到实际工作产出是相关的,更多的是管理问题,但我希望能够在被问到时尽可能多地提供信息。
有什么想法吗?谢谢!
答案1
不久前我写了一个脚本,我会把它放在这篇文章的底部,但是首先我需要说几件事。
它并不完美。它甚至没有达到我经常使用它的程度。我之所以发布它,是因为如果您决定使用 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>$_lastEvent)]]" }
3 { $ReturnQuery += $tempString -f "4778", "and *[System[(EventRecordID>$_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