创建具有未知数量属性的对象(powershell)

时间:2014-10-02 14:47:21

标签: powershell active-directory

我正在开发一个Powershell脚本,该脚本将采用OU(可选),域名(默认为company.local)以及要返回的一系列AD属性(默认为name,lastlogondate)。我想将输出发送到CSV文件。

我有两个问题。

  1. 该脚本返回从所有域控制器检索到的请求属性。我想只想为每个人输入,其中“LastLogonDate”具有最新值。
  2. 我不知道如何使用随机数量的密钥来构建属性哈希表。
  3. 关于我应该如何处理这些问题的任何想法?感谢。

    这是我现在正在使用的代码:

    [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
            }
        }
    } 
    

3 个答案:

答案 0 :(得分:0)

在我的评论中,假设最终证明手段合理。我们可以使用一些简单的cmdlet来处理数据,而不是尝试确定循环内部的最新时间。我认为你现在这样做的方式是比较所有用户之间的时间,而不是你想要的。我提出以下内容。高于此代码的所有内容我都保持不变(对于测试我编辑了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循环中,以便我们查询所有请求用户的所有dc(因为LastLogon时间戳可能不同)。我删除了If语句和$ time的引用,因为我们将要处理。你可以把它拿出去,但我为Domain Controller添加了一个属性用于测试,因为我很好奇。将这些结果捕获到变量$data中。每个用户选择$data组。对于每个组,对登录时间进行排序,然后选择最后一个最近的登录时间。最后一行来自此SO question

动态字段

好吧,你有$OutputProperties可以包含任何东西。没有必要担心包含的内容。在之前的Select-Object中,您拥有所需的属性。只需回显$user,它就会被分配到$data然后进行排序。

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

我知道这很多,我会更新答案并不是那么冗长,但我想在重做之前加入TheMadTechnician的想法。我看得越多,我认为我可以改变的越多。我现在正在提高效率。

答案 1 :(得分: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}

为每个直流上的每个搜索开始一项工作。这很容易失控。如果这是一个问题,我会提到限制作业量here的帖子。等待所有作业完成,然后处理输出,查找每个用户的最新lastlogondate。将$OutputProperties转换为数组会停止对拆分的需求并将其用于所有选择,从而无需担心动态属性,因为它们始终位于输出中。由于脚本的最后一部分需要lastlogondate,我添加了一个if语句,以便在用户不使用它时添加它。

答案 2 :(得分:0)

我喜欢使用Job的想法,但却无法使用它进行错误处理。由于我们仍然有这么多的Server 2003 DC,我只是采用了最初的想法。这是最后的脚本。感谢您的反馈。

<#
.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}