我遇到了几乎符合我要求的以下脚本。它查询所有域控制器并获取最近登录的时间和日期。
我想要做的是针对AD运行它,即使用"-Searchbase"
参数得到我希望能够以包含samaaccountnme,Searchbase位置和lastlogon时间的csv格式获得结果。
这是审计目的的主要内容。
Import-Module ActiveDirectory
function Get-ADUserLastLogon([string]$userName)
{
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$time = 0
foreach($dc in $dcs)
{
$hostname = $dc.HostName
$user = Get-ADUser $userName | Get-ADObject -Server $hostname -Properties lastLogon
if($user.LastLogon -gt $time)
{
$time = $user.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
Write-Host $username "last logged on at:" $dt }
Get-ADUserLastLogon -UserName testuser
我甚至尝试更改以下行,我认为会修复它但没有运气。
$user = Get-ADUser -Filter * -Properties * -Searchbase "OU=Staff,DC=Home,DC=ac,DC=uk" | Get-ADObject -Server $hostname -Properties lastLogon
Get-AduserLastLogon $Username
请有人请帮忙。
答案 0 :(得分:0)
以下是如何为所有DC上的用户获取最新lastLogon属性的示例:
# Get a list of every domain controller's name
$dcNames = Get-ADDomainController -Filter * |
Select-Object -ExpandProperty Name |
Sort-Object
# Get a collection of users in specified OU
$searchBase = "OU=Sales,DC=fabrikam,DC=com"
$users = Get-ADUser -Filter * -SearchBase $searchBase
# Hashtable used for splatting for Get-ADUser in loop
$params = @{
"Properties" = "lastLogon"
}
foreach ( $user in $users ) {
# Set LDAPFilter to find specific user
$params.LDAPFilter = "(sAMAccountName=$($user.SamAccountName))"
# Clear variables
$latestLogonFT = $latestLogonServer = $latestLogon = $null
# Iterate every DC name
foreach ( $dcName in $dcNames ) {
# Query specific DC
$params.Server = $dcName
# Get lastLogon attribute (a file time)
$lastLogonFT = Get-ADUser @params |
Select-Object -ExpandProperty lastLogon
# Remember most recent file time and DC name
if ( $lastLogonFT -and ($lastLogonFT -gt $latestLogonFT) ) {
$latestLogonFT = $lastLogonFT
$latestLogonServer = $dcName
}
}
if ( $latestLogonFT -and ($latestLogonFT -gt 0) ) {
# If user ever logged on, get DateTime from file time
$latestLogon = [DateTime]::FromFileTime($latestLogonFT)
}
else {
# User never logged on
$latestLogon = $latestLogonServer = $null
}
# Output user
$user | Select-Object `
SamAccountName,
@{Name = "LatestLogon"; Expression = {$latestLogon}},
@{Name = "LatestLogonServer"; Expression = {$latestLogonServer}}
}
答案 1 :(得分:0)
这里是一种使用多线程从用户输入列表中返回上次登录的方法。使用54个DC,我发现6个线程是最佳选择。根据您环境中DC的数量调整线程数。
######### Begin Functions ###########
Function Get-FilePath(){
<#
.SYNOPSIS
Prompts user to select folder for input/output file
.DESCRIPTION
Prompts user to select folder for input/output file using System.Windows.Forms.OpenFileDialog. This would be used to inport data from a csv/txt file and/or specifying the output file.
.PARAMETER initialDir
Defines the starting directory for the Open File Dialog.
.EXAMPLE
Open File Dialog box with initial directory sent to %AppData%
Get-Filepath -initialDir "%appdata%"
.EXAMPLE
Open File Dialog box with initial directory sent to C:\Scripts
Get-Filepath -initialDir "C:\Scripts"
.NOTES
If running from ISE, the Open File Dialog my open behind the ISE window. Simply move or minimize the ISE window.
#>
[cmdletbinding()]
Param(
[Parameter(Mandatory=$false)]
[String]$initialDir=[Environment]::GetFolderPath("Desktop")
)
Write-Verbose "Loading System.Windows.Forms"
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
Write-Verbose "Initial Directory set to $initialDir"
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.InitialDirectory = $initialDir
$OpenFileDialog.Filter = "CSV Files (*.csv)|*.csv|Text Files (*.txt)| *.txt|Input Files (*.txt;*.csv)|*.txt;*.csv|All Files (*.*)|*.*"
$OpenFileDialog.FilterIndex = 3
$OpenFileDialog.Title = "Select the input file"
$OpenFileDialog.ShowHelp = $true
$ShowDiag = $OpenFileDialog.ShowDialog()
If($ShowDiag -eq "Cancel"){
break
}
$OpenFileDialog.FileName
}
function Get-InputFile([string]$inputFilePath){
if($inputFilePath -is [String] -and $inputFilePath -eq [String]::Empty){
$inputFilePath = Get-FilePath -initialDir $PSScriptRoot
$inputFile = Get-Content $inputFilePath
}
else{
$inputFile = Get-Content $inputFilePath
}
return $inputFile,$inputFilePath
}
function WriteToFile($expFullPath, $result){
#calls streamwriter object to write to file quickly
$writer = New-Object System.IO.StreamWriter $expFullPath
#converts the output array to csv then writes each line to Export file csv.
$result | ConvertTo-CSV -NoTypeInformation | ForEach-Object{
If($_ -ne $null){
$writer.WriteLine( $_ )
}
}
# Closes Write function.
$writer.Close()
}
Function Invoke-Multi-Thread
{
Param($ScriptBlock = $MainSB,
$UserList = $UserList,
$DCList = $DCList,
$OutPath = $OutPath,
$MaxThreads = 5,
$SleepTimer = 30,
$MaxWaitAtEnd = 60,
$OutputType = "Text")
"Killing existing jobs . . ."
Get-Job | Remove-Job -Force
"Done."
$i = 0
ForEach ($User in $UserList){
While ($(Get-Job -state running).count -ge $MaxThreads){
Write-Progress -Activity "Creating Server List" `
-Status "Waiting for threads to close" `
-CurrentOperation "$i threads created - $($(Get-Job -state running).count) threads open" `
-PercentComplete ($i / $UserList.count * 100)
Start-Sleep -Milliseconds $SleepTimer
}
#"Starting job - $Input"
$i++
# Start-Job -FilePath $ScriptFile -ArgumentList $Computer -Name $Computer | Out-Null
Start-Job -ScriptBlock $ScriptBlock -ArgumentList $User,$DCList,$OutPath -Name $User | Out-Null
Write-Progress -Activity "Creating Server List" `
-Status "Starting Threads" `
-CurrentOperation "$i threads created - $($(Get-Job -state running).count) threads open" `
-PercentComplete ($i / $UserList.count * 100)
}
$Complete = Get-date
While ($(Get-Job -State Running).count -gt 0){
$UserListStillRunning = ""
ForEach ($System in $(Get-Job -state running)){$UserListStillRunning += ", $($System.name)"}
$UserListStillRunning = $UserListStillRunning.Substring(2)
Write-Progress -Activity "Creating Input List" `
-Status "$($(Get-Job -State Running).count) threads remaining" `
-CurrentOperation "$UserListStillRunning" `
-PercentComplete ($(Get-Job -State Completed).count / $(Get-Job).count * 100)
If ($(New-TimeSpan $Complete $(Get-Date)).totalseconds -ge $MaxWaitAtEnd){"Killing all jobs still running . . .";Get-Job -State Running | Remove-Job -Force}
Start-Sleep -Milliseconds $SleepTimer
}
#"Reading all jobs"
If ($OutputType -eq "Text"){
ForEach($Job in Get-Job){
# "$($Job.Name)"
# "****************************************"
Receive-Job $Job
}
}
ElseIf($OutputType -eq "GridView"){
Get-Job | Receive-Job | Select-Object * -ExcludeProperty RunspaceId | out-gridview
}
} # End Invoke-Multi-Thread
$MainSB = {
$user = $args[0].tostring()
$DCList = $args[1]
$OutPath = $args[2]
try {
$dn = Get-ADUser $user |select distinguishedName
$UserResults= @()
foreach ($server in $DCList)
{
$tempRes = @()
$tempRes = "" |select Server, Name, LastLogon
$res = Get-ADObject -Identity $dn.distinguishedName -Server $server -Properties lastLogon |select lastLogon
$tempRes.Server = $server
$tempRes.Name = $user
$tempRes.LastLogon = [datetime]::FromFileTime($res.lastLogon)
$UserResults += $tempRes
}
$recentTime="01/10/1500 1:00 AM"
$recentServer=""
foreach ($result in $UserResults)
{
if ($result.LastLogon -gt $recentTime)
{
$recentTime = $result.LastLogon
$recentServer = $result.Server
$recentUser = $result.Name
}}
$LastLogonRes = @()
$LastLogonRes = "" |select Server, Name, LastLogon
$LastLogonRes.Name = $recentUser
$LastLogonRes.LastLogon = $recentTime
$LastLogonRes.Server = $recentServer
return $LastLogonRes
}
catch
{
$LastLogonRes = @()
$LastLogonRes = "" |select Server, Name, LastLogon
$LastLogonRes.Name = $user
$LastLogonRes.LastLogon = "N/A"
$LastLogonRes.Server = "N/A"
return $LastLogonRes
}
}
###### End Main Script Block #############
$result = New-Object System.Collections.ArrayList
$sw = [System.Diagnostics.Stopwatch]::StartNew()
$sw2 = [System.Diagnostics.Stopwatch]::StartNew()
$interval = 0
$inputcount = $inputFile.count
$lastinterval = 0
# Start Date and time for file name
$StartDate = Get-Date
$runDate = $StartDate.ToString("yyyy.MM.dd.HHmmss")
#name of script
$scriptName = ($MyInvocation.MyCommand.Name).Split(".")[-2]
$scriptpath = $MyInvocation.MyCommand.Path
$scriptcontents = $MyInvocation.MyCommand.ScriptBlock
$scriptlastmodified = (Get-item -Path $scriptpath).LastWriteTime
#job names
$jobName = $runDate
$jobPath = $PSScriptRoot + "\jobs\" + $jobName + "\"
New-Item -Path $jobPath -ItemType directory | Out-Null
#error file name
$logFile = $runDate + "_" + $ScriptName + "_ErrorLog.txt"
$logFullPath = $jobPath + $logFile
Start-Transcript -Path $logFullPath
#lastmodified info for log
$hash = CertUtil -hashfile $MyInvocation.MyCommand.Name MD5
Write-Host @"
Script last modified: $scriptlastmodified
$($hash[1]) - is the MD5 CheckSum of the following Script:
$($MyInvocation.MyCommand.Path)$($MyInvocation.MyCommand)
"@
#lists any commandline parameters used
$boundparams = $MyInvocation.BoundParameters
if($($boundparams.count) -gt 0){
Write-Host "
There were $($boundparams.count) command line parameters used"
Write-Host "The parameters were:"$boundparams
}
Write-Host @"
"@
#name of export file
$expFileName = $runDate + "_" + $scriptName + "_Export.csv"
$expFullPath = $jobPath + $expFileName
#selects input file
#GetsData from input file
$inputFile,$inputFilePath = Get-InputFile($inputFilePath)
#Copies Input file to output folder.
Copy-Item "$inputFilePath" "$jobPath"
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$DCList=@()
foreach($dc in $dcs)
{
$DCList+= $dc.HostName
}
$users= gc $inputFilePath
write-host "Users count: " $users.count
$stopwatch = [system.diagnostics.stopwatch]::StartNew()
$LastLogonResults = Invoke-Multi-Thread -UserList $users -DCList $DCList -ScriptBlock $MainSB -MaxThreads 6 -OutPath $Out_Folder
$LastLogonResults = $LastLogonResults |select Server, name, lastLogon
#writes the results to file
WriteToFile($expFullPath) ($LastLogonResults)
$stopwatch.stop()
write-host $stopwatch.Elapsed.TotalSeconds
#closes progress bar
$sw.Stop()
Write-Progress -Activity $ScriptName -Status "Exported Results" -Completed
#exports csv rowcount and hashfile
$hash2 = CertUtil -hashfile $expFullPath MD5
Write-Host @"
$($hash2[1]) - is the MD5 CheckSum of the following Outputfile:
$expFullPath
"@
Write-Host "Script Run Time: (Days.Hours:Mins:Secs:Milliseconds)"
$sw.Elapsed.ToString("dd\.hh\:mm\:ss\:fff")
#closes progress bar
Write-Progress -Activity $ScriptName -Status "Exported Results" -Completed
Write-Host "OutFolder:$jobPath"
Write-Host "OutLog:$logFullPath"
Write-Host "OutData:$expFullPath"
Stop-Transcript
Write-host "Script Complete"
Pause