I'm new to scripting with Powershell for Active Directory, and attempting to pull out users in a list and enumerating their groups; however when I do so the resultant information contains just the name of the group which is duplicated under many differently named OUs. Looking at them in AD Users and Computers under the Member Of tab shows the Name and the Active Directory Domain Services Folder which contains exactly the differentiating info I need, or alternately I could use the DistinguishedName which isn't as nicely formatted for readability but would also work.
Problem with simplified examples: If a group name is the same across different OUs (like "TestUsers") then the script currently dumps multiple group names without differentiation "TestUsers, TestUsers, TestUsers" instead of showing the underlying OUs in a clean format "Michigan\TestUsers, NewYork\TestUsers" or Distinguished Name format "CN=TestUsers,CN=Michigan,etc";"CN=TestUsers,CN=NewYork,etc".
$alist = "Name`tAccountName`tDescription`twhenCreated`tAcctEnabled`tGroups`n"
$userlist = Get-ADUser -SearchBase "OU=Service Accounts,OU=Information Systems,DC=conteso,DC=local" -Filter * -Properties * | Select-Object -Property Name,SamAccountName,Description,EmailAddress,LastLogonDate,Manager,Title,Department,Company,whenCreated,Enabled,MemberOf | Sort-Object -Property Name
$userlist | ForEach-Object {
$grps = $_.MemberOf | Get-ADGroup | ForEach-Object {$_.Name} | Sort-Object
$arec = $_.Name,$_.SamAccountName,$_.Description,$_.whenCreated,$_.Enabled
$aline = ($arec -join "`t") + "`t" + ($grps -join "`t") + "`n"
$alist += $aline
}
$alist | Out-File C:\psscripts\service_accounts_groups.csv
Any help is appreciated!
I agree that DistinguishedName already has this information but I think it is easier to just use Get-AdGroup to get some friendlier information. A little string manipulation on the CanonicalName of the group, while not as efficient maybe, would be easier to work with.
$grps = $_.MemberOf | Get-AdGroup -Properties CanonicalName | ForEach-Object{
$CN = ($_.CanonicalName -Split "/")
"{0}\{1}" -f $CN[-2],$CN[-1]
}
or as a one liner if you prefer.
$grps = $_.MemberOf | Get-AdGroup -Properties CanonicalName | ForEach-Object{$CN = ($_.CanonicalName -Split "/"); "{0}\{1}" -f $CN[-2],$CN[-1]}
What we do is take the CanonicalName which can be considered the path of the object in Active Directory. Since you only wanted the parent container we split up the path and join only the last two parts. The group object and its container.
Same result could come from just getting the second last element from CanonicalName and appending the group name to it. Might look at little nicer
$grps = $_.MemberOf | Get-AdGroup -Properties CanonicalName | ForEach-Object{"{0}\{1}" -f ($_.CanonicalName -Split "/")[-2],$_.Name}
Since the "$_.memberof" property already has the Distinguished name, you don't need to pass it to Get-Adgroup again. so simply replace your $grps line like this
$grps = $_.MemberOf -replace "CN=","" -replace "OU=","" -replace "DC=","" -replace ",","\" | Sort-Object
Or if you still want to proceed with your approach, use it with distinguishedname property instead of name property - like this (with the text replace)
$grps = $_.MemberOf | Get-ADGroup | ForEach-Object {$_.DistinguishedName -replace "CN=","" -replace "OU=","" -replace "DC=","" -replace ",","\"} | Sort-Object
Or as suggested by Matt in the comment, simply use canonicalname property.
$grps = $_.MemberOf | Get-ADGroup -Properties canonicalname | select -ExpandProperty canonicalname
Cheers, GJ
Related
I need to write a PowerShell script that will count the users in 4 groups:
group1
group2
group3
group4
The script needs to skip duplicates if a user is in multiple groups.
Previously I was using the following script to count users in each group separately, but it is including duplicates and I need the accurate count of users from all groups.
$ADInfo = Get-ADGroup -Identity '<groupname>' -Properties Members
$ADInfo.Members |Where-Object {(Get-ADUser $_ -Properties extensionAttribute4).extensionAttribute4 -eq 'o365_facstaff'} |Select -Unique | Measure-Object
You could adjust this to send a collection down the pipeline. Something like the below should work:
$Groups = 'Group1','Group2','Group3','Group4'
$Groups |
Get-ADGroup -Properties Member |
Where-Object {(Get-ADUser $_ -Properties extensionAttribute4).extensionAttribute4 -eq 'o365_facstaff'} |
Select-Object -Unique |
Measure-Object
However, I would probably go about it a little different:
$Groups = 'Group1','Group2','Group3','Group4'
$Groups |
Get-ADGroupMember |
Where-Object{ $_.objectCLass -eq 'user'} |
Get-ADUser -Properties extensionAttribute4 |
Where-Object{ $_.extensionAttribute4 -eq 'o365_facstaff' } |
Select-Object -Unique |
Measure-Object
Note: In your original code the Get-ADUser command might error if you send it a group since groups can be members of other groups. That's why there's an extra Where{} clause in the above example to only looking for objectClass 'user'.
If you care to mess with the group Distinguished Names you could also try using an LDAP filter:
$Groups = #(
'Group1_DN'
'Group2_DN'
'Group3_DN'
'Group4_DN'
)
$LDAPFilter = "(&(extensionAttribute4=o365_facstaff)(|(memberOf={0})(memberOf={1})(memberOf={2})(memberOf={3})))" -f $Groups
Get-ADUser -LDAPFilter $LDAPFilter |
Select-Object -Unique |
Measure-Object
This will return all users where extensionAttribute4 is 'o365_facstaff' that are members of any 4 groups. Then it will run through Select -Unique & Measure-Object as before.
Note: Performance probably isn't a big factor in this case. However, it's always a best practice to move filtering left in the command as opposed to post filtering with a Where{} clause or other secondary processing. In this case, leveraging the cmdlet's built-in filtering parameters.
I'm looking to sort numerically my output with the numbers at the end of each.
Get-ADComputer -Filter * -SearchBase "OU=ComputerOU,DC=dc,DC=com" -Properties * | Select-Object -ExpandProperty Name
What i would like:
QCL-00010
JPL-00011
TUL-00012
TUL-00013
QCL-00014
What i have:
JPL-00011
QCL-00010
QCL-00014
TUL-00012
TUL-00013
Thank you in advance
Using the example names, where all numeric values have the same length, padded with leading zeroes, you could simply extend the code you have:
Get-ADComputer -Filter * -SearchBase "OU=ComputerOU,DC=dc,DC=com" |
Select-Object -ExpandProperty Name |
Sort-Object {($_ -split '-')[-1]}
However, to be on the safe side I'd cast to [int] as well:
Get-ADComputer -Filter * -SearchBase "OU=ComputerOU,DC=dc,DC=com" |
Select-Object -ExpandProperty Name |
Sort-Object {[int]($_ -split '-')[-1]}
Result:
QCL-00010
JPL-00011
TUL-00012
TUL-00013
QCL-00014
BTW. if all you need is the .Name property, do not ask for ALL properties with -Properties *
If every computer in the OU matches the nomenclature, I would just use a calculated property with Sort-Object and some pretty standard text munging:
Get-ADComputer -Filter * -SearchBase "OU=ComputerOU,DC=dc,DC=com" |
Sort-Object -Property #{e={$_.Name.Substring($_.Name.IndexOf('-') + 1)}} |
Select-Object -ExpandProperty Name
Also, you should avoid -Properties * with Get-AD* commands unless you absolutely have to. Name is returned by default.
There are a couple of ways to do something like this, somewhat depending on how static the format is.
$Names = #(
'JPL-00011'
'QCL-00010'
'QCL-00014'
'TUL-00012'
'TUL-00013'
)
$Names | Sort-Object {[Void]($_ -match "(\d{5}$)"); $matches[1]}
Here I used the matches collection as the sort expression.
I'm trying to get a dump of all user records and their associated groups for a user ID revalidation effort. My security officer wants it in CSV format.
This works great:
Get-ADUser -Filter * -Properties * | Select-Object -Property Name,SamAccountName,Description,EmailAddress,LastLogonDate,Manager,Title,Department,whenCreated,Enabled,Organization | Sort-Object -Property Name | ConvertTo-CSV
However, that does not include the groups the user is a member of.
Attempts at something like this have failed:
Get-ADUser -Filter * -Properties * | Select-Object -Property Name,SamAccountName,Description,EmailAddress,LastLogonDate,Manager,Title,Department,whenCreated,Enabled,Organization, #{$_.MemberOf |Get-Group|ForEach-Object {$_.Name}} | Sort-Object -Property Name | ConvertTo-CSV
This also failed:
Get-ADUser -Filter * -Properties * | Sort-Object -Property Name | ForEach-Object {
$_ | Format-List -Property Name,SamAccountName,Description,EmailAddress,LastLogonDate,Manager,Title,Department,whenCreated,Enabled
$_.MemberOf | Get-ADGroup | ForEach-Object {$_.Name} | Sort-Object
} | ConvertTo-CSV
I'm probably missing something simple.
Any help would be greatly appreciated.
Thanks!
From a Windows Server OS execute the following command for a dump of the entire Active Director:
csvde -f test.csv
This command is very broad and will give you more than necessary information. To constrain the records to only user records, you would instead want:
csvde -f test.csv -r objectClass=user
You can further restrict the command to give you only the fields you need relevant to the search requested such as:
csvde -f test.csv -r objectClass=user -l DN, sAMAccountName, department, memberOf
If you have an Exchange server and each user associated with a live person has a mailbox (as opposed to generic accounts for kiosk / lab workstations) you can use mailNickname in place of sAMAccountName.
For posterity....I figured out how to get what I needed. Here it is in case it might be useful to somebody else.
$alist = "Name`tAccountName`tDescription`tEmailAddress`tLastLogonDate`tManager`tTitle`tDepartment`tCompany`twhenCreated`tAcctEnabled`tGroups`n"
$userlist = Get-ADUser -Filter * -Properties * | Select-Object -Property Name,SamAccountName,Description,EmailAddress,LastLogonDate,Manager,Title,Department,Company,whenCreated,Enabled,MemberOf | Sort-Object -Property Name
$userlist | ForEach-Object {
$grps = $_.MemberOf | Get-ADGroup | ForEach-Object {$_.Name} | Sort-Object
$arec = $_.Name,$_.SamAccountName,$_.Description,$_.EmailAddress,$_LastLogonDate,$_.Manager,$_.Title,$_.Department,$_.Company,$_.whenCreated,$_.Enabled
$aline = ($arec -join "`t") + "`t" + ($grps -join "`t") + "`n"
$alist += $aline
}
$alist | Out-File D:\Temp\ADUsers.csv
csvde -f test.csv
This command will perform a CSV dump of every entry in your Active Directory server. You should be able to see the full DN's of users and groups.
You will have to go through that output file and get rid off the unnecessary content.
the first command is correct but change from convert to export to csv, as below,
Get-ADUser -Filter * -Properties * `
| Select-Object -Property Name,SamAccountName,Description,EmailAddress,LastLogonDate,Manager,Title,Department,whenCreated,Enabled,Organization `
| Sort-Object -Property Name `
| Export-Csv -path C:\Users\*\Desktop\file1.csv
HI you can try this...
Try..
$Ad = Get-ADUser -SearchBase "OU=OUi,DC=company,DC=com" -Filter * -Properties employeeNumber | ? {$_.employeenumber -eq ""}
$Ad | Sort-Object -Property sn, givenName | Select * | Export-Csv c:\scripts\ceridian\NoClockNumber_2013_02_12.csv -NoTypeInformation
Or
$Ad = Get-ADUser -SearchBase "OU=OUi,DC=company,DC=com" -Filter * -Properties employeeNumber | ? {$_.employeenumber -eq $null}
$Ad | Sort-Object -Property sn, givenName | Select * | Export-Csv c:\scripts\cer
Hope it works for you.
I have taken the code snipits from many sites, and almost have what I need. The only problem is I need to just return groups that are of GroupCategory Security.
I am trying to search an OU in Active Directory, return the users and then list each Security group they are a member of, sorted by name (both user and then the groups they belong to). Output that to txt file
$FilePath = 'C:\'
$EndDate = (Get-Date).tostring("yyyyMMdd")
$FileName = 'GroupMembership By User - ' + $EndDate + '.txt'
$Users=Get-ADUser -Filter * -Properties * -SearchBase "OU=My Accounts,DC=DOMAIN,DC=COM" | sort-object -property Name
ForEach ($User in $Users) {
$GroupMembership = ($User.memberof | foreach-object {(Get-ADGroup $_).Name ;}) -join ',';
$User.Name + ',' + $GroupMembership #|out-file -append "$FilePath$FileName"
}
#| Where-Object {$_.GroupCategory -EQ "Security"}
My output is comma delimited and sorted by username on each line, but I can't seem to get the groups sorted (less important overall) nor the Group listing to exclude the Distribution groups (must have). THe last line commented out, will return just security groups, but no matter where I put it, it doesn't work or fails the command.
TIA
How about this then. You had the logic in a comment. I guess you were not sure where to put it. I also remove some of the redundancy in creating $GroupMembership
$Users | ForEach-Object {
$GroupMembership = $_.memberof | Get-ADGroup | Where-Object{$_.GroupCategory -eq "Security"} | Sort-Object Name | Select -ExpandProperty Name
(#($_.Name) + $GroupMembership) -join ","
} | Add-Content $FilePath$FileName
To try and use the pipeline without an errors I changed your ForEach construct. This way we can tack on the Add-Content at the end of the pipe.
$GroupMembership is populated with the output of Get-AdGroup and we remove the distribution groups* with your Where-Object clause. A simple sort and expand leaves us with just the group names.
* I have to experiment but I'm not sure how that reacts to mail enabled security groups.
Can someone help me?
I have to create a .txt file with the following format:
user("SamAccountName","GivenName Surname"){}
I'm able to create just this:
#Get AD Users Info
cls
$SamAccountName = New-Item 'c:\SamAccountName.txt' -type file -Force
Get-ADUser -Filter * -Properties SamAccountName |
select -First 15 | Sort-Object SamAccountName |
Format-Table SamAccountName | Out-File $SamAccountName
$content = Get-Content $SamAccountName
$content | Foreach {$_.TrimEnd() } | where {$_ -ne ""} | Select-Object -Skip 3 | Set-Content $SamAccountName
#Write quotes (make it nice and readable!)
$ACTIVEDIRECTORY = New-Item 'c:\ACTIVEDIRECTORY.lst' -type file -Force
Clear-Content $ACTIVEDIRECTORY
$quotes= '"'
(Get-Content $SamAccountName) |
ForEach-Object {Add-Content $ACTIVEDIRECTORY "$quotes$_$quotes"}
Get-Content $ACTIVEDIRECTORY
This give me this result:
"GivenName"
"GivenName"
"GivenName"
I agree with #notjustme about your question being a mess, but I'll throw an answer out there anyway.
Your first problem is how you set these two variables. They end up being objects, but you're trying to use them like file paths.
$SamAccountName = "c:\SamAccountName.txt"
$ACTIVEDIRECTORY = "c:\ACTIVEDIRECTORY.lst"
This will get you a comma separated list of your users, which is (sort of) what part of your code is doing.
Get-ADUser -Filter * | Select-Object SamAccountName, GivenName, SurName -First 15 | Sort-Object SamAccountName | Export-Csv $SamAccountName -NoTypeInformation
This will get you the list of users with the string format you specified at the top, which is odd, but whatever.
Import-Csv $SamAccountName | ForEach-Object {"user(`"$($_.SamAccountName)`",`"$($_.GivenName) $($_.Surname)`"){}"} | Out-File $ACTIVEDIRECTORY
Can't try it out myself at the moment but... from experience...
-properties * | select SamAccountName, GivenName, Surname
Should get ya in the ballpark.
Re-read your code a few times and well... it seems a mess to be honest. You state what you wanna accomplish and then there's a crud-load of other stuff that make close to no sense in this case. Maybe I'm just too tired, I'll check back in the morning and edit/delete if needed.
Allright, re-read a few more times and the headache is given but still...
In general I'd like to advise you (or anyone, really) if Powershell get's confuzzling to break it down in easily processable pieces.
For the users;
$users = Get-ADUser -Filter { Name -like '*' } -Properties * | select SamAccountName, Givenname, Surname
For the output;
foreach ($user in $users)
{
'("{0}""{1}{2}"' -f $user.SamAccountName, $user.GivenName, $user.Surname
}
Unfortunatly I have no way to try this out myself at the moment but it should be in the ballpark... if you end up getting some errors, lemme know and I'm sure I can guide ya through it. I'm not 100% sure about them single and doublequotes in this particular case... I'd have to try it out.