簡體   English   中英

Powersell - 遠程查詢用戶是否跨域存在[最快]

[英]Powersell - Remotely Query if User Exists Across Domain [Fastest]

抽象的

所以我為一家在我的域中擁有大約 10k 計算機資產的公司工作。 我的問題是查詢計算機上是否存在用戶以查看他們是否曾經登錄過該計算機所花費的時間。 我們需要這種審計功能,以防他們做了不該做的事情。

我已經研究過兩種方法來完成這項任務,還有我沒有想到的第三種替代解決方案;

- 方法 A:查詢每台計算機的“C:\Users<USER>”以查看 LocalPath 是否存在

- 方法 B:檢查“HKU:<SID>”的每個計算機注冊表以查看 SID 是否存在

-方法C:你們都比我聰明,有更好的方法嗎? XD

方法一 Function

$AllCompFound = @()
$AllADComputer = Get-ADComputer -Properties Name -SearchBase "WhatsItToYa" -filter 'Name -like "*"' | Select-Object Name
ForEach($Computer in $AllADComputers) {
 $CName = $Computer.Name
 if (Get-CimInstance -ComputerName "$CName" -ClassName Win32_Profile | ? {"C:\Users\'$EDIPI'" -contains $_.LocalPath}) {
  $AllCompFound += $CName
 } else {
  #DOOTHERSTUFF
 }
}

注意:我有另一個 function 提示我輸入要檢查的用戶名。 在我工作的地方,它們是數字,因此區分大小寫不是問題。 我對這個 function 的問題是,我相信“if”語句每次都返回 true,因為它運行了,而不是因為它與用戶名匹配。

方法B Function

$AllCompFound = @()
$AllADComputer = Get-ADComputer -Properties Name -SearchBase "WhatsItToYa" -filter 'Name -like "*"' | Select-Object Name
$hive = [Microsoft:Win32.RegistryHive]::Users
ForEach($Computer in $AllADComputers) {
 try {
 $base = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hive, $Computer.Name)
 $key = &base.OpenSubKey($strSID)
 if ($!key) {
  #DOSTUFF
 } else {
  $AllCompFound += $Computer.Name
  #DOOTHERSTUFF
 }
} catch {
 #IDONTTHROWBECAUSEIWANTITTOCONTINUE
} finally {
 if($key) {
  $key.Close()
 }
 if ($base) {
  $base.Close()
 }
}
}

注意:我有另一個 function 將用戶名轉換為在此 function 之前的 SID。它有效。

我的眼睛開始呆滯的地方是使用 Invoke-Command 並實際返回一個值,以及是否將所有這些查詢作為它們自己的 PS-Session 運行。 我的方法 A 返回誤報,而我的方法 B 似乎在某些計算機上掛起。

這些方法都不夠快,無法獲得 10k 個結果,我一直在使用較小的計算機池,以便在需要時測試這些結果。 我絕不是專家,但我認為我有很好的理解,所以任何幫助表示贊賞!

首先,使用 WMI Win32_UserProfile ,而不是 C:\Users 或注冊表。

其次,使用從 pc 到某些數據庫的報告,而不是從服務器到 pc 的報告。 這通常要好得多。

關於 GPO:如果您獲得訪問權限,您可以不時通過 GPP(而非 GPO)為此類報告添加\刪除計划任務。

第三:使用PoshRSJob做並行查詢。

Get-WmiObject -Class 'Win32_USerProfile' | 
    Select @(
        'SID', 
        @{ 
            Name = 'LastUseTime'; 
            Expression = {$_.ConvertToDateTime($_.LastUseTime)}}
        @{ 
            Name = 'NTAccount'; 
            Expression = { [System.Security.Principal.SecurityIdentifier]::new($_.SID).Translate([System.Security.Principal.NTAccount])}}
        )

轉換為 NTAccount 時要小心:如果 SID 不轉換,則會導致錯誤,因此,最好不要從用戶空間收集 NTAccount。

如果您沒有其他變體,則使用PoshRSJob並行作業

並列示例(可能有一些錯別字)

 $ToDo = [System.Collections.Concurrent.ConcurrentQueue[string]]::new() # This is Queue (list) of computers that SHOULD be processed <# Some loop through your computers #> <#...#> $ToDo.Enqueue($computerName) <#LoopEnd#> $result = [System.Collections.Concurrent.ConcurrentBag[Object]]::new() # This is Bag (list) of processing results # This function has ComputerName on input, and outputs some single value (object) as a result of processing this computer Function Get-MySpecialComputerStats { Param( [String]$ComputerName ) <#Some magic#> # Here we make KSCustomObject form Hashtable. This is result object return [PSCustomObject]@{ ComputerName = $ComputerName; Result = 'OK' SomeAdditionalInfo1 = 'whateverYouWant' SomeAdditionalInfo2 = 42 # Because 42 } } # This is script that runs on background. It can not output anything. # It takes 2 args: 1st is Input queue, 2nd is output queue $JobScript = [scriptblock]{ $inQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]$args[0] $outBag = [System.Collections.Concurrent.ConcurrentBag[Object]]$args[1] $compName = $null # Logging inside, if you need it $log = [System.Text.StringBuilder]::new() # we work until inQueue is empty ( then TryDequeue will return false ) while($inQueue.TryDequeue([ref] $compName) -eq $true) { $r= $null try { $r = Get-MySpecialComputerStats -ComputerName $compName -EA Stop [void]$log.AppendLine("[_]: $($compName): OK.") [void]$outBag.Add($r) # We append result to outBag } catch { [void]$log:AppendLine("[E]: $($compName). $($_.Exception.Message)") } } # we return log. return $log.ToString() } # Some progress counters $i_max = $ToDo.Count $i_cur = $i_max # We start 20 jobs. Dont forget to say about our functions be available inside job $jobs = @(1.,20) <# Run 20 threads #> | % { Start-RSJob -ScriptBlock $JobScript -ArgumentList @($ToDo, $result) -FunctionsToImport 'Get-MySpecialComputerStats' } # And once per 3 seconds we check. how much entries left in Queue ($todo) while ($i_cur -gt 0) { Write-Progress -Activity 'Working' -Status "$($i_cur) left of $($i_max) computers" -PercentComplete (100 - ($i_cur / $i_max * 100)) Start-Sleep -Seconds 3 $i_cur = $ToDo,Count } # When there is zero, we shall wait for jobs to complete last items and return logs, and we collect logs $logs = $jobs | % { Wait-RSJob -Job $_ } | % { Receive-RSJob -Job $_ } # Logs is LOGS. not result # Result is in the result variable: $result | Export-Clixml -Path 'P./ath/to/file,clixml' # Exporting result to CliXML file, or whatever you want

請注意:$JobScript 中沒有 output 完成,因此必須完美完成,並且 function Get-MySpecialComputerStats必須以不尋常的方式進行測試,以返回可解釋的值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM