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
Related
I have this script that extracts the users that belong to the groups I need.
$GroupList = Get-Content C:\Scripts\grouplist.txt
$Results = foreach ($Group in $GroupList) {
$Description = Get-ADGroup -Identity $Group -Properties Description | Select-Object -ExpandProperty Description
Get-ADGroupMember -Identity $Group |
Select-Object -Property SamAccountName, Name, #{Name='GroupName';Expression={$Group}}, #{Name='Description';Expression={$Description}}
}
$Results
$Results | Export-csv -Path C:\Scripts\SecurityGroups.csv -NoTypeInformation
The problem is that I only need users in the enabled state.
And I can't. Could you help me please?
Thanks.
As mentioned per the comments you can use the Get-Aduser cmdlet after you populated your results. Here you have to filter out groups, otherwise the cmdlet will throw exceptions for every group.
$results = #()
foreach ($group in $grouplist) {
$description = (Get-ADGroup $group -Properties description).description
$members = Get-ADGroupMember $group | ?{$_.objectClass -eq "user"} | % {Get-ADUser $_ -Properties enabled}
$results += $members | ? {$_.Enabled -eq $true } | select samaccountname, name, #{name='groupname';expression={$group}}, #{name='description';expression={$description}}
}
Alternatively, you can use LDAP-filters. This option is noticeably faster since you only make one request per group, not one per user.
$results = #()
foreach ($group in $grouplist) {
$group = Get-ADGroup $group -Properties description
$members = Get-ADUser -LDAPFilter "(&(memberof=$($group.DistinguishedName))(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))"
$results += $members | select samaccountname, name, #{name='groupname';expression={$group}}, #{name='description';expression={$group.description}}
}
With LDAP-filters you could also request members of multiple groups at once, but since you want the context in which group a user was found this is not an option here..
I new in Powershell world and trying to write a script which perform following:
Get List of Computers from a text file
Get list of Users from a text file
Control if the computer name is added in LogonWorkstations field in each user account
Here is the script I have written as of yet.
$Computers = Get-Content Computers.txt
$Users = Get-Content -Path Users.txt | Sort-Object -Unique
$ADUsers = Get-ADUser -Filter * -Properties LogonWorkstations -SearchScope Subtree -SearchBase "OU=ck,OU=users-com,DC=domain,DC=com" |
Where-Object {$Users -contains $_.Name} | Format-List Name,LogonWorkstations
As the script shows I read and retrieve property for Users and have list of computers in text file.
There are 50+ computers and users my question is how can I compare this line wise example check if computer from line 1 of Computers.txt exist in LogonWorkstations property of user from line 1 of Users.txt?
If each line of both files are corresponding, you can use a simple for loop to iterate through both lists simultaneously. $ADUsers will contain the output of ADUser objects matching the conditions.
$ADUsers = for ($i = 0; $i -lt $Users.Count; $i++) {
Get-ADUser -Filter "Name -eq '$($Users[$i])'" -Properties LogonWorkstations |
Where-Object { ($_.LogonWorkstations -split ',') -contains $Computers[$i] }
}
Since LogonWorkstations contains a comma-separated string, you will have to do some string manipulation. Using the -split operator on the , character will result in an array of strings. The -contains operator works nicely when comparing an item or collection of items to a single item.
If you want to compare the LogonWorkstations value of a user to any computer in the list, you can do something like the following:
$ADUsers = foreach ($User in $Users) {
Get-ADUser -Filter "Name -eq '$User'" -Properties LogonWorkstations | Where-Object {
Compare-Object -Ref ($_.LogonWorkstations -split ',') -Dif $Computers -IncludeEqual -ExcludeDifferent
}
}
Compare-Object here will only return a value if there is an exact match.
Note: I believe the LogonWorkstations attribute has been replaced with UserWorkstations attribute. Both may work now but may not be guaranteed in the future.
I haven't tried the below code but hopefully, you will be able to work out any little issues:
$computers = Get-Content -Path #PATHTOCOMPUTERS.TXT
$users = Get-Content -Path #PATHTOUSERS.TXT | Sort-Object -Unique
#Set a counter to zero
$counter = 0
foreach ($user in $users){
try{
#Get the current user from AD
$adUser = Get-ADUser -Filter { Name -eq $user} -Properties LogonWorkStations -ErrorAction Stop
#Uses the current index using $counter to get the correct computer from computers.txt and
#checks if the user has it setup as a LogonWorkStation
if ($adUser.LogonWorkstations -eq $computers[$counter]){
Write-Host "$user has $computer as a logon workstation"
}else{
Write-Host "$user doesn't have $computer as a logon workstation"
}
}catch{
Write-Host "Failed to get the AD user"
}
#Increment the $counter
$counter++
}
$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.
I have a requirement to generate a CSV report to get group members. However, I there are many child domains which contains groups starting with ADM.
I need report in the following format:
GroupName User Company LasLogon CN
ADM_AM UserOne CP1
I've found one script on internet:
Get-ADGroup -Server dc1.chd1.pd.local -Filter 'Name -like "ADM*"' |
ForEach-Object{
$hash=#{GroupName=$_.Name;Member=''}
$_ | Get-ADGroupMember -ea 0 -recurs |
ForEach-Object{
$hash.Member=$_.Name
New-Object psObject -Property $hash
}
} |
sort groupname,member
This script only gives me GroupName and UserName but not other information.
How can I generate this report?
I'm not sure what "ADM_AM, UserOne, CP1" is, but i got this much for you. I'm still new to powershell so forgive me if this is a lot of code =)
$array = #()
Foreach ($group in (Get-ADGroup -Server dc1.chd1.pd.local -Filter 'Name -like "ADM*"'))
{
$hash=#{Username ='';GroupName=$group.Name;Company='';LastLogon='';CN=''}
$members = $hash.GroupName | Get-ADGroupMember -Recursive -ErrorAction SilentlyContinue
Foreach($member in $members)
{
$properties = $member.SamAccountName | Get-ADUser -Properties SamAccountName, Company, lastLogon, CN
$hash.Username = $properties.SamAccountName
$hash.Company = $properties.Company
$hash.LastLogon = $properties.lastLogon
$hash.CN = $properties.CN
$obj = New-Object psObject -Property $hash
$array += $obj
}
}
$array | Export-Csv C:\ -NoTypeInformation
Here is what I would do, Im sure you can shorten it. You shoud specify a searchbase. Once you have the members samaccountname, you can use Get-ADUser to get whatever fields you want.
$GrpArr = #()
$Groups = get-adgroup -filter {name -like "adm*"} -searchbase "ou=Groups,dc=all,dc=ca" | select samaccountname
foreach ($group in $groups)
{
$GrpArr += $group
$members = get-adgroupmember $group | select samaccountName
foreach ($member in $members)
{
$memprops = get-aduser $member -properties company
$comp = $memprops.company
$grpArr += "$member,$comp"
}
}
$grpArr | export-csv c:\temp\Groups.csv -NoTypeInformation
I need to return all members of multiple security groups using PowerShell. Handily, all of the groups start with the same letters.
I can return a list of all the relevant security groups using the following code:
Get-ADGroup -filter 'Name -like"ABC*"' | Select-Object Name
And I know I can return the membership list of a specific security group using the following code:
Get-ADGroupMember "Security Group Name" -recursive | Select-Object Name
However, I can't seem to put them together, although I think what I'm after should look something like this (please feel free to correct me, that's why I'm here!):
$Groups = Get-ADGroup -filter 'Name -like"ABC*"' | Select-Object Name
ForEach ($Group in $Groups) {Get-ADGroupMember -$Group -recursive | Select-Object Name
Any ideas on how to properly structure that would be appreciated!
Thanks,
Chris
This is cleaner and will put in a csv.
Import-Module ActiveDirectory
$Groups = (Get-AdGroup -filter * | Where {$_.name -like "**"} | select name -expandproperty name)
$Table = #()
$Record = [ordered]#{
"Group Name" = ""
"Name" = ""
"Username" = ""
}
Foreach ($Group in $Groups)
{
$Arrayofmembers = Get-ADGroupMember -identity $Group | select name,samaccountname
foreach ($Member in $Arrayofmembers)
{
$Record."Group Name" = $Group
$Record."Name" = $Member.name
$Record."UserName" = $Member.samaccountname
$objRecord = New-Object PSObject -property $Record
$Table += $objrecord
}
}
$Table | export-csv "C:\temp\SecurityGroups.csv" -NoTypeInformation
If you don't care what groups the users were in, and just want a big ol' list of users - this does the job:
$Groups = Get-ADGroup -Filter {Name -like "AB*"}
$rtn = #(); ForEach ($Group in $Groups) {
$rtn += (Get-ADGroupMember -Identity "$($Group.Name)" -Recursive)
}
Then the results:
$rtn | ft -autosize
Get-ADGroupMember "Group1" -recursive | Select-Object Name | Export-Csv c:\path\Groups.csv
I got this to work for me... I would assume that you could put "Group1, Group2, etc." or try a wildcard.
I did pre-load AD into PowerShell before hand:
Get-Module -ListAvailable | Import-Module
This will give you a list of a single group, and the members of each group.
param
(
[Parameter(Mandatory=$true,position=0)]
[String]$GroupName
)
import-module activedirectory
# optional, add a wild card..
# $groups = $groups + "*"
$Groups = Get-ADGroup -filter {Name -like $GroupName} | Select-Object Name
ForEach ($Group in $Groups)
{write-host " "
write-host "$($group.name)"
write-host "----------------------------"
Get-ADGroupMember -identity $($groupname) -recursive | Select-Object samaccountname
}
write-host "Export Complete"
If you want the friendly name, or other details, add them to the end of the select-object query.