在foreach中(嵌套)中的Foreach

时间:2019-03-01 06:52:42

标签: powershell

我正尝试通过一组组循环所有禁用的用户,以检查用户是否具有列出的任何组的成员身份。我的想法是,对于列表中的每个用户,请他们进行遍历,并检查它们是否在列出的组之一中。那将需要嵌套foreach循环,对吗?我得到的输出是这样的:

...
user1
user2
user3

is not a member of group1

这是源代码:

$dUsers = Get-ADUser -Filter {enabled -eq $false} |
          FT samAccountName |
          Out-String
$groups = 'Group1', 'Group2'

foreach ($dUser in $dUsers) {
    foreach ($group in $groups) {
        $members = Get-ADGroupMember -Identity $group -Recursive |
                   Select -ExpandProperty SamAccountName

        if ($members -contains $dUsers) {
            Write-Host "[+] $dUser is a member of $group"
        } else {
            Write-Host "[-] $dUser is not a member of $group"
        }
    }
}

我拉头发是因为我觉得有一个简单的解决方案,但我迷路了。


更新

我想将所有禁用的用户放在变量$dUsers中。 如果我手动将用户放在这样的变量中,它实际上会起作用:

$dUsers = 'user1','user2','user3'

哪个给我以下输出:

user1 is not a member of group1
user1 is not a member of group2
user2 is not a member of group1
user2 is not a member of group2
...

这使我感到疑问,当变量为:

$dUsers = Get-ADUser -Filter {enabled -eq $false} |
          FT samAccountName |
          Out-String

有人对此有澄清吗?


更新

这是最终代码。即使只有两个组,运行也要花费很长时间。

$dUsers = Get-ADUser -Filter {enabled -eq $false} | Select-Object -Expand SamAccountName
$groups = 'Group1', 'Group2'


Write-host '[+] Checking if any disabled user is member of any SSL groups'
Write-host '[+] This might take a while. Get a coffee!'
write-host '[+] Running...'`n
foreach ($dUser in $dUsers) {
    foreach ($group in $groups) {
        $members = Get-ADGroupMember -Identity $group -Recursive | Select -ExpandProperty SamAccountName

        if($members -contains $dUser) {
            Write-Host "$dUser is a member of $group"
        } Else {
         # Remove or comment out the line below to get a clutterfree list.
         # Write-Host "$dUser is not a member of $group"
        }
    }
}

4 个答案:

答案 0 :(得分:1)

根据您所使用的PowerShell版本,此用例和其他用例都有一个cmdlet。

  

我正在尝试循环所有禁用的用户

就做...

Search-ADAccount -AccountDisabled | 
Select-Object -Property Name, Enabled,
@{Name = 'GroupName';Expression = {$_.DistinguishedName.Split(',')[1] -replace 'CN='}} 

# Results 

Name               Enabled GroupName                                              
----               ------- ---------                                              
...                                         
testuser2 NewTest    False Users                                                  
Guest                False Users

或其他cmdlet ...

# Get disabled users and their group membership, display user and group name
ForEach ($TargetUser in (Get-ADUser -Filter {Enabled -eq $false}))
 {
 "`n" + "-"*12 + " Showing group membership for " + $TargetUser.SamAccountName
 Get-ADPrincipalGroupMembership -Identity $TargetUser.SamAccountName | Select Name
 }


# Results
...
------------ Showing group membership for testuser1
Domain Users
Users

------------ Showing group membership for testuser2
Domain Users

关于...

  

一组分组

只需使用常规比较运算符为所需的组名称选择或过滤DN。

至于...

  

不幸的是,我对Powershell并不熟悉。

...请务必花费必要的时间来逐步完善它,以限制将要遇到的误解,困惑,错误等。在网上有很多免费的免费视频/基于文本的培训/演示文稿。

示例: Videos

使用可为您编写代码的工具,您以后可以根据需要对其进行调整。

Step-By-Step: Utilizing PowerShell History Viewer in Windows Server 2012 R2

Learning PowerShell with Active Directory Administrative Center (PowerShell History Viewer)

通过MS PowerShell Script / Module Gallery.

以及大量示例脚本和模块

答案 1 :(得分:1)

您的代码中有两个问题:

  1. 您正在从Get-ADUser输出中创建一个字符串。通过Format-Table(别名为ft)输入该cmdlet的输出,然后Out-String创建一个字符串,并以表格形式显示所有匹配的帐户名,包括表头。

    如果您以使字符串的开头和结尾可见的方式输出$dUsers,您会看到类似这样的内容(开头和结尾的==标记了开头和结尾):

    PS> $dUsers | ForEach-Object { "==$_==" }
    ==samAccountName
    --------------
    user1
    user2
    user3==
    

    由于没有用户名与该字符串匹配的帐户,因此在任何组中都找不到匹配项,您将获得观察到的输出。

    这种Format-* cmdlet的滥用是一个常见的初学者错误。人们得到了格式正确的字符串输出,然后尝试使用它。仅在将数据直接呈现给用户时使用Format-* cmdlet,而在需要或打算进一步处理数据时则永远不要使用。{p>

    您真正想要的不是以用户名列表形式显示的字符串,而是用户名字符串的数组。通过扩展从SamAccountName获得的用户对象的Get-ADUser属性,可以实现这一点。

    $dUsers = Get-ADUser ... | Select-Object -Expand SamAccountName
    
  2. 第二个问题可能只是拼写错误。您的条件$members -contains $dUsers将不起作用,因为$members$dUsers都是数组(就是在解决第一个问题之后)。 -contains运算符期望将数组作为第一个操作数,并将单个值作为第二个操作数。

    更改

    $members -contains $dUsers
    

    $members -contains $dUser
    

答案 2 :(得分:0)

有两个用于AD组的命令。 首先,我看到您想要轻松地成为残疾用户的成员。

#Get the dissabled users from your AD with all their attributes (properties and select)
$dUsers = Get-ADUser -Filter {Enabled -eq $false} -Properties * | Select *
#Run a loop for each user to get the group membership
Foreach ($User in $dUsers) {
$User = $User.SamAccountName
Get-ADUser $User -Properties * | Select Name, SamAccountName, MemberOf | Format-Table -Wrap # > "D:\test\$user.txt" -HideTableHeaders
}

这可以工作,但我不喜欢我们得到的输出。 我更喜欢运行groupmembership命令并检查用户。

$GroupMembers = Get-ADGroupMember "groupname"| Select Name, SamAccountName
ForEach ($User in $GroupMembers)
{ 
$UserProperties = Get-ADUser $User.SamAccountName -Properties * | select *
If ($UserProperties.Enabled -eq $False) {
Write-Host $UserProperties.SamAccountName
}
}

编辑:

让我知道是否适合您。 此致。

答案 3 :(得分:0)

您应该尝试检查的第一件事是,只要您只对直接成员身份或对间接成员身份感兴趣。根据答案,您可以使用的选项有些变化。在进行此操作时,您可能会遇到Distinguished Names,所以如果不知道(主要是对象的路径),请检查它们是什么。

如果仅是直接成员资格,则将memberOfGet-ADUser配合使用就足够了。 memberOf属性包含用户的每个直接组成员身份以及该组的完整专有名称。

Get-ADUser test -Properties MemberOf | Select-Object -ExpandProperty memberOf

您可以通过多种方式匹配您要寻找的组。您可以获取这些组的完整专有名称,也可以进行部分匹配。由您决定如何进行。

如果您还需要间接成员身份,则可能需要拆分代码以使其更容易使用。例如,您可以首先找到用户并保存。然后找到这些组的所有组成员(您已经使用Get-ADGroupMember了),最后将两者进行比较。

当前,您将为每个用户再次构建整个组成员列表。这种方法将节省一些资源,因为您不会一遍又一遍地执行相同的查询。

最后,您还可以使用MemberOf方法,但是使用LDAP查询获得用户的每个直接和间接成员资格的列表。

$dn = (Get-ADUser example).DistinguishedName
$userGroups = Get-ADGroup -LDAPFilter ("(member:1.2.840.113556.1.4.1941:={0})" -f $dn)

此方法使用LDAP search query。这可能非常复杂,您也可以只通过稍微修改一下来检查其中一个组。

最后,即使您当前的方法也应该可行。问题在于您正在将AD对象与SAM帐户名列表进行比较。您还需要检查SAM帐户名。

if($members -contains $dUsers.SamAccountName)
if($members -contains $dUsers | Select-Object -ExpandProperty SamAccountName)

如果您也更改了$dUsers,则其中之一应该起作用。由于当前情况,您最终会遇到一条巨大的弦。您可能可以通过选中$dUsers.length进行检查。只需放下Format-TableOut-String