detect if computer is not member of at least one group - powershell

I have a need to detect computer objects that are not a member of at least one group. I have come up with this script, but instead of listing just the machines that are not a member of at least one group, it returns all workstations. What am I doing wrong?
Get-ADComputer -Filter * -Property * | where {
$_.memberof -notmatch 'Group1' -and
$_.memberof -notmatch 'Group2' -and
$_.memberof -notmatch 'Group3'
} | Format-Table Name

The MemberOf property contains a list of distinguished names. You can't check if it doesn't contain something with the -notmatch operator. Instead get a list of the distinguished names of your groups:
$groups = 'Group1', 'Group2', 'Group3' |
ForEach-Object { Get-ADGroup -Filter "Name -eq '$_'" } |
Select-Object -Expand DistinguishedName
and check if the MemberOf property doesn't contain any of them:
Get-ADComputer -Filter * -Property * | Where-Object {
-not (Compare-Object $groups $_.MemberOf -IncludeEqual -ExcludeDifferent)
} | Format-Table Name
The Compare-Object is required, because you need to check if one array contains any of the elements of another array. Something like $_.MemberOf | Where-Object {$groups -contains $_} would also work.
Note that the MemberOf property does not include the primary group of a computer. If the primary group must also not be one of the groups from your list you need an additional check in the Where-Object filter:
Get-ADComputer -Filter * -Property * | Where-Object {
-not (Compare-Object $groups $_.MemberOf -IncludeEqual -ExcludeDifferent) -and
$groups -notcontains $_.PrimaryGroup
} | Format-Table Name

You're using the -NotMatch operator, which will evaluate to true if the entries do not exactly match. You'd be better served using -NotContain, like so
Get-ADComputer -Filter * -Property * | where {$.memberof -notContains 'Group1' -and $.memberof -notContains 'Group2' -and $_.memberof -notContains 'Group3'} | Format-Table Name

Related

Compare-Object - How do I know which is the difference object and which is the reference object?

I have two objects and want to compare them to find users found in both.
The below appeared to work OK until I tested the output. I found that although the number of users returned was correct, the actual values weren't.
Compare-Object -ReferenceObject $PilotUsers -DifferenceObject $Leavers -IncludeEqual |
Where-Object {$_.sideindicator -eq '=='}
If I switch the ref and dif objects around I get the correct result. As I'm only interested in users in both I don't see how it matters which way around they are set. Clearly there is a need to understand how this works for future occasions. Searches online haven't shed any light on this unfortunately.
My objects are created as below. Both return a pscustomobject with one property.
$PilotUsers = Get-aduser -Filter * -Properties memberof |
Select-Object samaccountname, memberof |
Where-Object {$_.memberof -like "*Licensing_Pilot Users*"}|
Select-Object #{n='ID'; e={$_.samaccountname}}
$Leavers = Import-Csv -Path $LeaverFile.FileName |
Select-Object #{n='ID'; e={$_.payroll.substring(2,5).trimstart('0')}}
Please can someone explain or point me in the right direction? The ultimate goal is to delete users found in the leavers object from the AD group object.
First of all, your query for $pilotUsers is highly inefficient. The right syntax for the query should be:
$PilotUsers = Get-ADuser -Filter * -Properties MemberOf |
Where-Object {$_.memberof -like "*Licensing_Pilot Users*"} |
Select-Object #{n='ID'; e={$_.samaccountname}}
A more efficient way to search for users that are direct members (non recursive) of groups with name like Licensing_Pilot Users is this:
$PilotUsers = Get-ADGroup -Filter {Name -like "*Licensing_Pilot Users*"} | Get-ADGroupMember |
Where-Object {$_.ObjectClass -eq 'user'} |
Select-Object #{n='ID'; e={$_.samaccountname}}
Now for the comparison, I will assume that you're getting a list of sAMAccountNames from your CSV file. This is how you can filter both variables to find the results you want.
# To get all pilotUsers that are in leavers
$pilotUsers.ID | Where-Object {$_ -in $leavers.ID}
$pilotUsers.ID.Where({$_ -in $leavers.ID})
# To get all leavers that are in pilotUsers
$leavers.ID | Where-Object {$_ -in $pilotUsers.ID}
$leavers.ID.Where({$_ -in $pilotUsers.ID})
# To get all users that are in both arrays
#(
$pilotUsers.ID | Where-Object {$_ -in $leavers.ID}
$leavers.ID | Where-Object {$_ -in $pilotUsers.ID}
) | select -Unique
You need to specify the property name you want to use for the comparison, in this case -Property ID:
# Emulating your data sets
$ref = 1..10|Select #{Name='ID';Expression={"$_"}}
$dif = 6..15|Select #{Name='ID';Expression={"$_"}}
# This will return pure nonsense
Compare-Object -ReferenceObject $ref -DifferenceObject $dif -IncludeEqual |
Where-Object {$_.sideindicator -eq '=='}
# This will return the results you're expecting
Compare-Object -ReferenceObject $ref -DifferenceObject $dif -IncludeEqual -Property ID |
Where-Object {$_.sideindicator -eq '=='}

Get-AdUsers from specific AD Groups and filtering results

I am able to export to users that are not members of particular groups such as IT_Group like below. But, this script gives me all membership of users within memberof column in csv output. If they are members of any groups that matches "IT" they should be displayed within memberof column in csv output like below.
Also , If user is not member to any group that is beginning with IT_ then it will write "any IT group is not member" keyword within memberof column in csv output.
There are 3 security groups such as IT_Group,IT_Group1,IT_Group2
I have tried so far :
Get-ADUser -Filter {(emailaddress -like "*#contoso.com" -and Enabled -eq $false -and sAMAccountName -like "TEST*") -or (emailaddress -like "*#contoso.com" -and Enabled -eq $false -and sAMAccountName -like "PROD*")} -SearchBase "OU=USERS,DC=contoso,DC=com" -SearchScope Subtree -Properties * | Where { [string]$_.memberof -notmatch 'IT_Group'} | Select-Object name , samaccountname ,#{Name="MemberOf";Expression={($_.MemberOf | %{(Get-ADGroup $_).sAMAccountName}) -Join ";"}} |Export-CSV -Path "C:\tmp\output.csv" -NoTypeInformation -Encoding UTF8
My Desired output :
name,samaccountname,memberof
User01,TEST1,IT_Test
User02,PROD1,IT_Prod
User03,TEST4,any IT group is not member
The -Filter should not be written as script block ({..}), but as a normal string.
This should do what you are after:
$filter = "(Enabled -eq 'False' -and EmailAddress -like '*#contoso.com') -and (SamAccountName -like 'TEST*' -or SamAccountName -like 'PROD*')"
Get-ADUser -Filter $filter -SearchBase "OU=USERS,DC=contoso,DC=com" -SearchScope Subtree -Properties EmailAddress, MemberOf | ForEach-Object {
if ($_.MemberOf -match 'CN=IT_(Test|Prod)') {
# the user is a member of any IT_Group, get the names of all groups for this user
$groups = foreach ($grp in $_.MemberOf) { (Get-ADGroup -Identity $grp).Name }
$_ | Select-Object Name, SamAccountName, #{Name = 'MemberOf'; Expression = {$groups -join ', '}}
}
else {
# the user is not a member of any IT_Group
$_ | Select-Object Name, SamAccountName, #{Name = 'MemberOf'; Expression = {'Not a member of any IT_Group'}}
}
} | Export-CSV -Path "C:\tmp\output.csv" -NoTypeInformation -Encoding UTF8
Parsing the name of an object from the DistinghuishedName is tricky, because there can be special characters in there. That is why this code uses the Get-ADGroup cmdlet to get the group names.
If the SamAccountNames do not matter and you want to get ALL users in OU OU=USERS,DC=contoso,DC=com that are not Enabled AND have an EmailAddress ending in #contoso.com, than simply change the $filter variable to
$filter = "Enabled -eq 'False' -and EmailAddress -like '*#contoso.com'"
As per your latest comment, you would only want to list the groups IT_Test and/or IT_Prod for users that are member of any of these two groups, the code below should do that:
$filter = "(Enabled -eq 'False' -and EmailAddress -like '*#contoso.com') -and (SamAccountName -like 'TEST*' -or SamAccountName -like 'PROD*')"
Get-ADUser -Filter $filter -SearchBase "OU=USERS,DC=contoso,DC=com" -SearchScope Subtree -Properties EmailAddress, MemberOf | ForEach-Object {
$testgroups = $_.MemberOf | Where-Object { $_ -match 'CN=IT_(Test|Prod)'}
if ($testgroups) {
# the user is a member of group IT_Test and/or IT_Prod, get the names of these groups for this user
$groups = foreach ($grp in $testgroups) { (Get-ADGroup -Identity $grp).Name }
$_ | Select-Object Name, SamAccountName, #{Name = 'MemberOf'; Expression = {$groups -join ', '}}
}
else {
# the user is not a member of any IT_Group
$_ | Select-Object Name, SamAccountName, #{Name = 'MemberOf'; Expression = {'Not a member of any IT_Group'}}
}
} | Export-CSV -Path "C:\tmp\output.csv" -NoTypeInformation -Encoding UTF8
Hope that helps
This code get all users that have groups begining with "IT_" it's provided by $_.memberof -like 'CN=IT_*'.Then for each user getting his name,login and groups what beggins from"CN=IT_",format it with -replace and add it to csv file without rewrite.
$users=Get-ADUser -Filter {filter options} -Properties MemberOf| Where-Object { $_.memberof -like '*CN=IT_*'}
foreach ($user in $users){
$user|Select-Object name , samaccountname ,#{Name="MemberOf";Expression={((($_.MemberOf | Select-String -Pattern 'CN=IT_*')-replace "CN=")-replace ",.+$") -Join ";"}} |Export-CSV -Delimiter ';' -Path "D:\testdir\uss.csv" -NoTypeInformation -Encoding UTF8 -Append
}

How to use GetEnumerator() to export hash table to a csv file in PowerShell?

I wrote a search function, which searches for some active directory attributes. I now want to export the content to a .csv file. If I see it correctly, the whole output is a hash table. But when I try to use the GetEnumerator() function, it doesn't do anything.
Can someone help and maybe explain what I did wrong? The code is below. Thanks in advance!
Import-Module ActiveDirectory
$users = Get-ADUser -Filter { employeeNumber -notlike '*' } -Properties memberOf, samAccountName | ForEach-Object -Process { #{ $_.samAccountName = $_.memberOf | Where-Object { $_ -like '*DAT*' } } }
$users.GetEnumerator() |
Select-Object -Property #{N='AD1';E={$_.Key}},
#{N='AD2';E={$_.Value}} |
Export-Csv -NoTypeInformation -Path H:\test123.csv
If you look at your code, you are creating a list of hashtables that contains your SamAccountName and Groups. But, when you use the enumerator, you are only thinking about the hashtable and not the list you created.
This is how you can iterate through a hashtable. You first create a hash table and add all elements to it.
$hash = #{}
Get-ADUser -Filter { employeeNumber -notlike '*' } -Properties memberOf, samAccountName | ForEach-Object -Process { $hash.Add($_.samAccountName, ($_.memberOf | Where-Object { $_ -like '*DAT*' })) }
$hash.GetEnumerator() |
Select-Object -Property #{N='AD1';E={$_.Key}},
#{N='AD2';E={$_.Value}} |
Export-Csv -NoTypeInformation -Path H:\test123.csv
Or you can continue with the list of hashtables but change how you are accessing the data. Each element of your list is a single hashtable with Keys (only one in it).
$users = Get-ADUser -Filter { employeeNumber -notlike '*' } -Properties memberOf, samAccountName | ForEach-Object -Process { #{ $_.samAccountName = $_.memberOf | Where-Object { $_ -like '*DAT*' } } }
$users.GetEnumerator() |
Select-Object -Property #{N='AD1';E={$_.Keys[0]}},
#{N='AD2';E={$_.Values[0]}} |
Export-Csv -NoTypeInformation -Path H:\test123.csv

Comparing values in arrays from Get-ADUser

I'm trying to write a script that removes users from a security group if they aren't in a specific OU.
I'm having trouble comparing my array of users from the OU, to the array of users from the security group.
To test I looped through the content in $testGroup and $userList. Both look similar to me but it's clear they don't compare as just outputting $userList -contains $user gives me a bunch of false results even though it should be true.
$userList = #()
$testGroup = #()
#Get current members of group. Using this instead of get-adgroupmember due to speed
$testGroup = Get-AdGroup "testGroup" -properties member | select-object -ExpandProperty member | get-aduser
#Define OUs that we want to get members from
$OUlist = "OU1","OU2"
#Populate $userList with members of each OU
$OUlist | foreach {
$userList += get-aduser -filter {Enabled -eq $True} -SearchBase "OU=$_,DC=dc,DC=dc2,DC=dc3"
}
#Check the group for anyone no longer in one of the approved OUs
$testGroup | foreach {
if($userList -notcontains $user){
#remove the user from $testGroup
}
}
Consider using Compare-Object with the property value set to compare by distinguished name; i.e.
compare-object -ReferenceObject $OUList -DifferenceObject $userList -Property 'DistinguishedName' |
?{$_.SideIndicator -eq '=>'} |
select -expand InputObject
Full Code:
(untested)
$userList = #()
$testGroup = #()
$groupName = 'testGroup'
#Get current members of group. Using this instead of get-adgroupmember due to speed
$testGroup = Get-AdGroup $groupName -properties member | select-object -ExpandProperty member | get-aduser
#Define OUs that we want to get members from
$OUlist = "OU1","OU2"
#Populate $userList with members of each OU
$OUlist | foreach {
$userList += get-aduser -filter {Enabled -eq $True} -SearchBase "OU=$_,DC=dc,DC=dc2,DC=dc3" | Get-AdUser
}
#Check the group for anyone no longer in one of the approved OUs & remove group group
Remove-ADGroupMember -Identity $groupName -Members (compare-object -ReferenceObject $OUList -DifferenceObject $userList -Property 'DistinguishedName' | ?{$_.SideIndicator -eq '=>'} | select -ExpandProperty InputObject)
There are a handful of issues... Using $Variable instead of $_ in $Variable | Foreach like MacroPower mentioned is one of them.
You can condense the whole thing like this:
# Get-ADGroupMember is easier than Get-ADGroup | Get-ADUser.
# You also only need the SamAccountName.
# $TestGroup will be an array automatially... No need to $TestGroup = #()
$TestGroup = (Get-ADGroupMember 'TestGroup').SamAccountName
#Define OUs using their full paths.
$OUList = #(
'OU=Whatever,DC=example,DC=com',
'OU=Something,DC=example,DC=com'
)
# Easily call the OU's from $OUList using $_.
# Again, we only need SamAccountName
# Again, $UserList will automaticall be an array no '= #()' needed.
$OUList | ForEach-Object {
$UserList += (Get-ADUser -Filter * -SearchBase $_).SamAccountName
}
# A proper foreach construct will let you work with $User instead of $_
foreach ($User in $TestGroup)
{
if ($User -notin $UserList)
{
# Put your action here.
}
}
A final note, you switch between camelCase, PascalCase, and lowercase all over the place. While there is no official standard for PowerShell consistency makes code easier to read. PascalCase also tends to be the recommended due to the .NET style guide.
Also, if you wanted to use a compare instead of the foreach ($User in $TestGroup):
$Compare = Compare-Object -ReferenceObject ($UserList | Select -Unique) -DifferenceObject $TestGroup
$Compare | ForEach-Object {
if ($_.sideindicator -eq '=>')
{
# Action here.
}
}
Here an example of comparing arrays:
$a1=#(1,2,3,4,5)
$b1=#(1,2,3,4,5,6)
$result = Compare-Object -ReferenceObject ($a1) -DifferenceObject ($b1) -PassThru
write-host $result
take also a look at this post compare arrays

foreach filtering out members already apart of group

$GetGroupsFromUser = Get-ADPrincipalGroupMembership $WPFnamelookupbox.Text |
Where-Object { $_.Name -like 'G1*' }
$Groups = Get-ADGroup -Filter {Name -like "G1*"}
foreach ($G in $Groups) {
if ($GetGroupsFromUser -contains $G) {
} else {
$WPFgroups.Items.Add($G.SamAccountName)
}
}
My goal is I want to only show groups that the user is not a member of.
So I made some progress going with the -contains operator. In order for -contains to work, I need to first create an array, correct?
You could use Compare-Object:
$GetGroupsFromUser = Get-ADPrincipalGroupMembership $WPFnamelookupbox.Text | Where-Object {$_.name -like 'G1*' }
$Groups = Get-ADGroup -Filter "name -like 'G1*'"
Compare-Object $Groups $GetGroupsFromUser | Where-Object {$_.SideIndicator -eq "<="}
-contains functions best when you are trying to find a match of an element in an array.
If you are just looking for the groups that matches a filter that a user does not already have we can use -notcontains inside a where clause as well for this.
$groupFilter = "G*"
$user = "user_bagel"
$allFilteredGroups = Get-ADGroup -Filter "name -like '$groupFilter'" | Select-Object -ExpandProperty name
$userFilteredGroups = Get-ADPrincipalGroupMembership $user | Where-object{$_.name -like $groupFilter} | Select-Object -ExpandProperty name
$allFilteredGroups | Where-Object{$userFilteredGroups -notcontains $_}
You don't need to expand the groups names as I have done. You will get similar results either way. Since you only wanted to know the names it seemed silly to keep the complete group object. In theory it will also perform faster this way. Setting up variables like $groupFilter makes it easier to make changes to your script down the line.