所有域控制器CSV输出的Lastlogon

时间:2016-12-08 14:54:46

标签: powershell csv domaincontroller

我遇到了几乎符合我要求的以下脚本。它查询所有域控制器并获取最近登录的时间和日期。 我想要做的是针对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

请有人请帮忙。

2 个答案:

答案 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