2014-10-02 94 views
0

我正在使用一個Powershell腳本,它將採用OU(可選),域名(默認爲company.local)以及一組要返回的AD屬性(默認是名字,lastlogondate)。我想將輸出發送到一個CSV文件。創建具有未知屬性數量的對象(powershell)

我有兩個問題。

  1. 該腳本返回所請求的屬性,從所有域控制器中檢索到。我只想爲每個人輸入,這個人最近的值是「LastLogonDate」。
  2. 我不知道如何處理隨機數字鍵構建屬性哈希表。

有關我應該如何處理這些問題的任何想法?謝謝。

這是我現在使用的代碼:

[CmdletBinding()] 
param(
    [string]$DomainName = "company.local", 

    [string[]]$SearchPath = 'OU=people,DC=company,DC=local', 

    [string[]]$OutputProperties = 'Name,lastlogondate' 
) 

Import-Module ActiveDirectory 

$props = @{} 

$temp = New-Object 'System.DirectoryServices.ActiveDirectory.DirectoryContext'("domain","$DomainName") 
$dcs = [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($temp) 

Foreach ($ou in $SearchPath) { 
    $users = Get-ADUser -Filter * -SearchBase $ou -Properties $OutputProperties.Split(",") -Server $DomainName | Select $OutputProperties.Split(",") 

    $time = 0 

    Foreach ($dc in $dcs) { 
     Foreach ($user in $users) { 
      If ($user.LastLogonDate -gt $time) { 
       $time = $user.LastLogonDate 
      } 
      $props.'LogonTime' = $time 
      $props.'Name'=$user.Name 
      New-Object Psobject -Property $props 
     } 
    } 
} 
+0

對於初學者,我猜你想查看所有域控制器上的所有用戶的詳細信息?如果'$ users = Get-ADUser -Filter * ....'行不在'Foreach($ dc'循環)中,你還必須將'-Server'更改爲-'Server $ dc'。您對後處理感到滿意嗎?獲取所有細節,然後爲每個用戶找到最新的一個,然後篩選出舊的? – Matt 2014-10-02 15:41:48

回答

0

我喜歡使用Job的想法,但在處理錯誤時遇到了麻煩。由於我們仍然擁有如此多的Server 2003數據中心,我只是按照最初的想法去做。這是最後的腳本。感謝您的反饋。

<# 
.Synopsis 
    Searches ActiveDirectory and returns a user-specified list of properties 
.DESCRIPTION 
    This script takes a user-specified list OUs and a user-specified list of desired properties. 
.NOTES 
    Author: Mike Hashemi 
    V1 date: 15 August 2014 
    V2 date: 6 October 2014 
     - Converted the main part of the script, into a function. 
     - Added routie to gather all DCs in a domain, for the ability to return LastLogonDate. 
.LINK 
    http://stackoverflow.com/questions/26163437/creating-objects-with-unknown-number-of-properties-powershell 
.PARAMETER DomainName 
    Default value is 'company.local'. This parameter represents the DNS domain name, of the domain. 
.PARAMETER SearchPath 
    Default value is 'OU=people,DC=company,DC=local'. This parameter represents a comma-separated list of OUs to search. 
.PARAMETER OutputProperties 
    Default value is 'Name,Enabled,LastLogonDate'. This parameter represents a comma-separated list of properties to return. 
.EXAMPLE 
    .\get-ADUserProperties-Parameterized.ps1 
    This example get's a list of all users in 'OU=people,DC=company,DC=local' and outputs the Name, Enabled, and LostLogonDate attributes. 
.EXAMPLE 
    .\get-ADUserProperties-Parameterized.ps1 -SearchPath 'OU=people,DC=company,DC=local','OU=managers,DC=company,DC=local' 
    This example get's a list of all users in the 'OU=people,DC=company,DC=local' and 'OU=managers,DC=company,DC=local' OUs and outputs the 
    Name, Enabled, and LostLogonDate attributes. 
.EXAMPLE 
    .\get-ADUserProperties-Parameterized.ps1 -SearchPath 'OU=people,DC=company,DC=local' -OutputProperties Name,telephoneNumber | Export-CSV c:\users.csv -NoTypeInformation 
    This example get's a list of all users in the 'OU=people,DC=company,DC=local' OU and outputs the Name and Telephone Number attributes. 
    The output is exported to a CSV. 
#> 
[CmdletBinding()] 
param(
    [string]$DomainName = 'managed.local', 

    [string[]]$SearchPath = 'OU=people,DC=company,DC=local', 

    [string[]]$OutputProperties = 'Name,Enabled,LastLogonDate' 
) 

Function Get-TheUsers { 
    #Create the hash table, for later. 
    $props = @{} 

    Try { 
     #The next two lines get the list of domain controllers, using the supplied DNS domain name. 
     Write-Verbose ("Getting domain controllers from {0}" -f $DomainName) 
     $temp = New-Object 'System.DirectoryServices.ActiveDirectory.DirectoryContext'("domain","$DomainName") 
     $dcs = [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($temp) 
    } 
    Catch [System.Management.Automation.MethodInvocationException] { 
     Write-Error ("Unable to connect to remote domains. Please run the script from a DC in {0}. " -f $DomainName) 
     Exit 
    } 
    Catch { 
     Write-Error ("There was an unexpected error. The message is: {0}" -f $_.Exception.Message) 
     Exit 
    } 

    Foreach ($ou in $SearchPath) { 
     Write-Verbose ("Getting users in {0}" -f $ou) 
     Foreach ($dc in $dcs) { 
      If ($dc.OSVersion -like '*2003*') { 
       Write-Warning ("Skipping {0}, because it is not a Server 2008 (or higher) DC." -f $dc) 
      } 
      Else { 
       Write-Verbose ("Searching {0} on {1}." -f $ou,$dc) 
       Try { 
        $users = Get-ADUser -Filter * -SearchBase $ou -Properties $OutputProperties.Split(",") -Server $dc -ErrorAction Stop | Select $OutputProperties.Split(",") 
       } 
       Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { 
        Write-Error ("Unable to search {0}. It appears to be a non-existent OU. The specific error message is: {1}" -f $ou, $_.Exception.Message) 
        Exit 
       } 

       Foreach ($user in $users) { 
        ForEach($property in $OutputProperties.Split(",")) { 
         $props.$property = $user.$property 
        } 
        New-Object Psobject -Property $props 
       } 
      } 
     } 
    } 
} 

Try { 
    Import-Module ActiveDirectory -ErrorAction Stop 
} 
Catch [System.IO.FileNotFoundException] { 
    Write-Error ("Unable to load the required module. The specific message is: {0}" -f $_.Exception.Message) 
    Exit 
} 

$data = Get-TheUsers 

#Takes the output of the Get-ADUser query and groups by the first property in $OutputProperties, then uses the LastLogonDate property (if present) 
#to sort again and select only the last (most recent) entry. 
Write-Verbose ("Sorting data.") 
$data | Group-Object Name | ForEach-Object {$_.Group | Sort-Object LogonTimeDate | Select-Object -Last 1} 
0

Exanding對我的評論假設不擇手段。我們可以用一些簡單的小命令來處理數據,而不是試圖確定循環中的最新時間。我認爲你現在這樣做的方式是比較所有用戶之間的時間,而不是你想要的時間。我介紹以下內容。上面這段代碼的一切,我離開了相同的(爲了測試我編輯的param S)

$data = Foreach ($ou in $SearchPath) { 

    Foreach ($dc in $dcs) { 

    $users = Get-ADUser -Filter * -SearchBase $ou -Properties $OutputProperties.Split(",") -Server $dc | Select $OutputProperties.Split(",") 

     Foreach ($user in $users) { 
      $props.'LogonTime' = $user.LastLogonDate 
      $props.'Domain Controller' = $dc 
      $props.'Name'=$user.Name 
      New-Object Psobject -Property $props 
     } 
    } 
} 
$data | Group-Object Name | ForEach-Object{$_.Group | Sort-Object LogonTime | Select-Object -Last 1} 

我感動Get-ADUser直流循環中,使我們查詢所有DC的所有要求的用戶(因爲它是可能的LastLogon時間戳可能不同)。我刪除了If聲明和對$ time的引用,因爲我們將在後面處理。你可以把它拿出來,但我添加了一個Domain Controller的屬性進行測試,因爲我很好奇。將這些結果捕獲到變量$data中。由每個用戶以$data組。對於每個組,排序登錄時間並選擇最近的那個。最後一行來源於此SO question

動態字段

好了,所以你必須$OutputProperties可能包含任何數據。沒有必要擔心包含什麼。在之前的Select-Object中,你有你想要的屬性。只是回顯$user,它將被分配到$data然後進行排序。

Foreach ($user in $users) { 
    $user 
} 

我知道這是很多,我會更新的答案並不像冗長,但我想結合TheMadTechnician的想法之前,我重做。我越看越這個,我認爲我可以改變得越多。我正在使它更高效。

+0

爲了提高速度,我建議爲每臺服務器運行Get-ADUser作爲一項工作來運行,那麼你可以使用Do/While((Get-Job).count -gt 0)循環來檢索結果並創建你的用戶數組進行處理。這樣它就不會坐在那裏等待每個服務器進行查詢下一個,只是我的兩個位 – TheMadTechnician 2014-10-02 16:25:03

+0

@TheMadTechnician不要破舊這就是我從其他人的想法和建設性的批評中學習的方法,將嘗試做出這些工作並更新,謝謝 – Matt 2014-10-02 16:33:22

+0

好主意,他們解決了第一個問題。我添加了一些代碼來跳過Server 2003 DCs(因爲他們沒有ADWS)。 如何在不知道用戶要檢索哪些屬性的情況下使用屬性和值創建散列表? – StackExchangeGuy 2014-10-02 19:19:10

0

我不適合在此時刪除我的其他答案。使用@TheMadTechincian的評論中的兩位我想看看在使用作業時是否可以改進這一點。這是我第一次使用Start-Job,但我認爲它有訣竅。

[CmdletBinding()] 
param(
    [string]$DomainName = "domain.local", 
    [string[]]$SearchPath = 'OU=container,dc=domain,dc=local', 
    [string[]]$OutputProperties = @("Name","lastlogondate","samaccountname") 
) 

Import-Module ActiveDirectory 

If(!($OutputProperties -contains "lastlogondate")){$OutputProperties += "lastlogondate"} 
$temp = New-Object 'System.DirectoryServices.ActiveDirectory.DirectoryContext'("domain","$DomainName") 
$dcs = [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($temp) 

$dcs | ForEach-Object{ 
    ForEach($singleOU in $SearchPath){ 
     $arguments = $singleOU,$OutputProperties,$_ 
     [void](Start-Job -ScriptBlock { 
       Get-ADUser -Filter * -SearchBase $args[0] -Properties $args[1] -Server $args[2] 
       } -ArgumentList $arguments) 
    } 
} 

While((Get-Job -State 'Running').Count) 
{ 
    Start-Sleep -Milliseconds 200 
} 

Get-Job | Receive-Job| Group-Object Name | ForEach-Object{$_.Group | Sort-Object lastlogondate | Select-Object $OutputProperties -Last 1} 

爲每個dc上的每個ou搜索啓動一項作業。這很容易失控。如果這是一個問題,我會參考限制作業數量的職位here。等待所有作業完成,然後處理輸出,查找每個用戶最近的lastlogondate。將$OutputProperties轉換爲數組將不再需要拆分,並將其用於所有選擇,因爲它們將始終位於輸出中,因此無需擔心動態特性。由於腳本的最後部分需要lastlogondate我添加了一個if語句,在用戶沒有使用它的情況下會添加它。

+0

@themadtechnician是否看起來像是對這些工作的適當使用? – Matt 2014-10-03 02:36:30

相關問題