动态地在PowerShell中向数组添加元素

时间:2017-02-16 23:46:26

标签: arrays powershell

我试图针对AD运行查询以向我提供用户列表。由于我们的组织添加了标签" LastName,FirstName"在计算机对象的描述字段中,我还想列出每个用户分配的机器。这就是我到目前为止所做的:

$ADGroup = "SomeADGroup"
$UserList = Get-ADGroupMember -Identity $ADGroup -Recursive `
  | Select SamAccountName `
  | foreach-object {(Get-ADUser -Identity $_.SamAccountName `
  | Select @{n='AssignedUser';e={$_.Surname + ", " + $_.GivenName}}).AssignedUser}


$Results = New-Object System.Collections.Generic.List[System.Object]

foreach ($User in $UserList)
{
    $MachineList = @(((Get-ADComputer -Filter "Description -like `"*$User*`"" | Select Name).Name) -join $_ + ",").ToUpper()

    foreach ($Machine in $MachineList)
    {
        $Results +=
            @{
                'User' = $User
                'Machine' = $Machine
            }
    }
}

$Results | ForEach-Object { [pscustomobject] $_ } | Format-Table

结果是这样的:

User                 Machine
----                 -------

White, Scott         W107175
Jones, Henry         W107195
Flinstone, Fred      L109531,W108812

由于可以为每个用户分配多台计算机,因此我试图想出一种方法来动态添加"列"到数组,以便每个机器名称元素驻留在一列中。这最终可以更轻松地导出到CSV。我正在寻找类似于此的输出:

User                 Machine   Machine2
----                 -------   -------

White, Scott         W107175
Jones, Henry         W107195
Flinstone, Fred      L109531   W108812

3 个答案:

答案 0 :(得分:3)

好的,您要为现有对象添加未知数量的属性。使用Add-Member cmdlet并不难。接下来,您只需要构建一个循环,以便添加这些属性很容易理解,我们可以使用For循环来完成。最后,您需要确保数组中的第一条记录具有所有潜在属性。当我们到达那里时,我会解释一下。

首先,我们得到了用户。我改变了你所拥有的东西,因为我严格反对使用反引号作为续行字符,因为它很容易被空格所挫败。我还将$UserList从一个字符串数组更改为一个对象数组,因为无论如何你以后都想要对象。我们仍会捕获名字和姓氏,但作为User上的[PSCustomObject]属性。

$ADGroup = "SomeADGroup"
$UserList = Get-ADGroupMember -Identity $ADGroup -Recursive |
    Select SamAccountName |
    foreach-object {
        Get-ADUser -Identity $_.SamAccountName | 
            Select @{n='User';e={$_.Surname + ", " + $_.GivenName}}
    }

所以现在我们有一个带有一个属性的对象数组。现在我们循环遍历,搜索计算机,并将计算机捕获为字符串数组。这只是略微修改过的代码。

foreach ($User in $UserList)
    {         
        $MachineList = Get-ADComputer -Filter {Description -like "*$($User.User)*"} | Select -ExpandProperty Name

现在是我们改变一点点的地方。对于我们发现的每台机器,我们需要为我们已有的对象添加属性。由于我们不知道有多少,我们可以使用For循环并相应地对属性进行编号。

        for($i=0;$i -le $MachineList.Count;$i++)
            {
                Add-Member -InputObject $User -NotePropertyName "Machine$($i+1)" -NotePropertyValue $MachineList[$i]
            }
    }

所以现在$UserList是一个对象数组,每个对象都有User属性,但对于该用户而言,有许多Machine#属性。问题是PowerShell根据数组中的第一个对象输出对象数组。所以在你的例子中:

User                 Machine   Machine2           
----                 -------   -------    
White, Scott         W107175           
Jones, Henry         W107195           
Flinstone, Fred      L109531   W108812

Machine2列永远不会显示,因为该属性并不存在于Scott White的第一条记录中。因此,为了解决这个问题,我们需要找出数组中任何给定对象的所有可能属性,并将任何缺少的对象添加到第一条记录中。这可以使用PowerShell中每个对象的隐藏PSObject属性轻松完成。

首先,我们得到所有可能的属性名称列表:

$AllProps = $UserList | ForEach{$_.PSObject.Properties.Name} | Select -Unique

然后我们过滤掉第一条记录上已有的任何属性,剩下的任何内容都会再次使用Add-Member cmdlet作为新属性添加到记录中。

$AllProps | 
    Where{ $_ -notin $UserList[0].psobject.properties.name } | 
    ForEach{ Add-Member -InputObject $UserList[0] -NotePropertyName $_ -NotePropertyValue $null }

然后没有什么要做但输出结果,这些结果已经存储在$UserList中,因为我们制作了用户列表,并且只是用每个用户的机器更新了现有列表,而不是制作用户列表,然后是新的用户和机器列表。

$UserList |  Format-Table

答案 1 :(得分:0)

你很好地进入变量"变量"问领域;从根本上说你永远不会写

$User.Machine1
$User.Machine2
$User.Machine...

因为您不知道有多少台机器,所以您无法在代码中编写属性名称。数组就是答案,你的代码中有一个引用数组的变量,其中包含所有机器。那么你的代码是有意义的,它既简单又合理又有效,但它与你的CSV梦想不一致 - 因为你的数据不是CSV形的。

如果您为每个用户添加一台属性,则会让一些用户拥有一台计算机,而某些用户拥有四台...并且会使Format-TableConvertTo-Csv,{{3因为他们把他们看到的第一个对象作为模板,然后在第一个对象上没有的其他对象上的任何列,它们就会掉落。

所以你必须做TheMadTechnician正在做的事情,然后返回并动态地将所有空白的MachineN属性添加到所有对象,如果你只是说出前面的话就会非常简单"会有N列&#34 34。

然后你可以计算它们,我明白了:

$ADGroup = "SomeADGroup"
$ColumnCount = 10

Get-ADGroupMember -Identity $ADGroup -Recursive | Get-ADUser | ForEach-Object {

    $User = [ordered]@{ 'User' = $_.SurName + ", " + $_.GivenName }

    $Machines = @((Get-ADComputer -Filter "Description -like '*$($User.User)*'").Name)

    for ($i=0; $i -lt $ColumnCount; $i++)
    {
        $User["Machine$i"] = $Machines[$i]
    }

    [PSCustomObject]$User

} | ConvertTo-Csv

NB。我依赖数组越界行为。如果您需要避免这种情况,请将10列空字符串添加到Machines列表的末尾作为缓冲区。

但是我仍然很想通过字符串操作自己混合CSV,而不是完全通过对象,因为你并没有使用它们,因为它们应该'#' 39;可以使用,但只是在制作正确的属性方面只需要将它们扔掉以获得CSV。

偏离主题,您的代码非常错综复杂:

$UserList = Get-ADGroupMember -Identity $ADGroup -Recursive `
| Select SamAccountName `
| foreach-object {(Get-ADUser -Identity $_.SamAccountName `
| Select @{n='AssignedUser';e={$_.SurName + ", " + $_.GivenName}}).AssignedUser}

你得到AD组成员,然后你以一种绝对没有任何方式选择SamAccountName,然后你循环结果而不是将它们直接流水线化到Get-ADUser,没有任何好处,那么你选择一个计算属性和然后扔掉它并获得价值。代替:

Get-ADGroupMember -Identity $ADGroup -Recursive | Get-ADUser | ForEach-Object { $_.SurName + ', ' + $_.GivenName }

获取群组成员,获取其AD用户,并根据这些结果计算字符串。

$Results = New-Object System.Collections.Generic.List[System.Object]
->
$Results = @()

或者,更好的是:

$Results = @()
foreach ($x in $y) {
    $Results += ...
}

->

$Results = foreach ($x in y ) {
    ...
}

并在

| ForEach-Object { [pscustomobject] $_ } |

你......当你制作它们时可以让它们Out-GridView,然后你就不需要它了。

答案 2 :(得分:0)

$ADGroup = "Some AD Group"
$UserList = Get-ADGroupMember -Identity $ADGroup -Recursive | Get-ADUser

$Results = @()
$MaxMachineCount = 0

foreach ($User in $UserList)
{
    if ($User.SurName -and $User.GivenName -ne $null)
    {
        $CurrentUser = [ordered]@{ 'User' = $User.SurName + ", " + $User.GivenName }
        $MachineList = @(((Get-ADComputer -Filter "Description -like `"*$($CurrentUser.User)*`"") | Select -ExpandProperty Name))

        for ($i=0; $i -lt $MachineList.Count; $i++)
        {
            $CurrentUser.Add("Machine $i","$($MachineList[$i].ToUpper())")
            if ($MaxMachineCount -le $i) {$MaxMachineCount++}
        }
        $Results += [PSCustomObject]$CurrentUser
    }
}

for ($i=0; $i -lt $MaxMachineCount; $i++){$Results[0] | Add-Member -Name "Machine $i" -Value $null -MemberType NoteProperty -ErrorAction Ignore}

$Results | Format-Table -AutoSize

代码Format-Table -AutoSize的结尾部分也可以替代ConvertTo-CSV | Out-File "C:\Some\Directory\File.csv"

我添加了这个以在我的结果中创建其他列。以前,每个用户最多只能显示两台计算机。现在,该脚本返回每个用户的所有已分配的计算机。

for ($i=0; $i -lt $MachineList.Count; $i++){$Results[0] | Add-Member -Name "Machine $i" -Value $null -MemberType NoteProperty -ErrorAction Ignore

这是我的结果:

User                Machine 0 Machine 1 Machine 2 Machine 3 Machine 4 Machine 5 Machine 6 Machine 7 Machine 8
----                --------- --------- --------- --------- --------- --------- --------- --------- ---------
Froeman, Abe        L110911
Maclatchie, Matthew W109370   L108518
White, Walter       V999100   L107800   V999109   V999108   V999107   V999102   V999106   V999105   V999101

感谢@TessellatingHeckler和@TheMadTechnician提供的意见。