Im not sure why I cannot get this to work, but I have a .csv of employee ID's and I want to add them to an AD group, but I cannot get this to work
Function Sync-ADGroup {
$userIDs = Import-CSV $CSV
foreach($ID in $IDs){
Get-ADUser -Filter "EmployeeID -eq '$ID'" -Properties SAMAccountName
}
}
Then I would add them to the group, but I cannot it to return the ADUserObject. Not sure what I am missing.
You need to reference the property name (column) of the user as it appears in the csv file. For example, if the value in the file is under the EmployeeID header:
foreach($userID in $userIDs)
{
Get-ADUser -Filter "EmployeeID -eq $($userID.EmployeeID)" -Properties SAMAccountName
}
For the above to work your csv file needs to look like:
EmployeeID
1234
2345
3456
Undeclared collection variable is used in foreach loop.
$userIDs = Import-CSV $CSV # Load stuff to "userIDs"
foreach($ID in $IDs){ # Enumerate "IDs", oops, should be "userIDs"
As $IDs is empty a variable, the loop doesn't do much.
In order to avoid this kind of errors, use the strict mode: Set-PSDebug -Strict. This will rise an error for using undeclared variable. (Rant: the strict mode should be the default in Powershell. I set it in my profile and all the scripts I write for good measure.)
Related
I've copied/created a script to get all the members of a group that a user is part of, including nested groups. However, my output isn't quite the way I want it.
It goes in one of two ways. Either it outputs as one big string, which looks nice, but has trailing spaces on each line so I cannot simply copy and paste it into AD. Or if I change the Out-String to use -stream, it comes out as a garbled mess, but may allow me to trim the spaces.
I currently have the output going into a TextBox in a simple GUI.
Function Get-ADUserNestedGroups {
Param
(
[string]$DistinguishedName,
[array]$Groups = #()
)
#Get the AD object, and get group membership.
$ADObject = Get-ADObject -Filter "DistinguishedName -eq '$DistinguishedName'" -Properties memberOf, DistinguishedName;
#If object exists.
If($ADObject)
{
#Enummurate through each of the groups.
Foreach($GroupDistinguishedName in $ADObject.memberOf)
{
#Get member of groups from the enummerated group.
$CurrentGroup = Get-ADObject -Filter "DistinguishedName -eq '$GroupDistinguishedName'" -Properties memberOf, DistinguishedName;
#Check if the group is already in the array.
If(($Groups | Where-Object {$_.DistinguishedName -eq $GroupDistinguishedName}).Count -eq 0)
{
#Add group to array.
$Groups += $CurrentGroup;
#Get recursive groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName $GroupDistinguishedName -Groups $Groups;
}
}
}
#Return groups.
Return $Groups;
}
Function Display-UserGroups {
#Get all groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName (Get-ADUser -Identity $userSAM).DistinguishedName;
$ResultsTextBox.Text = $Groups | Select-Object Name| Sort-Object name | Out-String
The output with the first way looks like:
Group Name1(Eight Spaces Here)
Group Name2(Eight Spaces Here)
The output with the second way looks like:
Group Name1GroupName2GroupName3
Thanks for your help!
You need to trim your output, which can easily be done with String.Trim method. However, it can only be applied against strings. $Groups will be an array of ADObject types. You will need to return the Name values of those objects and apply the Trim() method to the values.
($Groups | Select -Expand Name | Sort).Trim() -join "`r`n"
You can use $Groups += $CurrentGroup.trimEnd() to add the value with the trailing spaces trimmed.
A few other methods you might want to research
.trim() # Trim leading and trailing
.trimEnd() # Trim trailing
.trimStart() # Trim leading
The padding is being caused by PowerShell's formatting system. Select-Object is returning single property (Name) objects. PowerShell outputs that as a table, which may have some padding. Out-String is keeping that padding which is why it was reflecting in your final output...
The name property of the group is already a string. There's no need to use Out-String if you unroll the Name property from your $Groups variable/collection.
With some other adjustments for readability & testing. Below will emit a single string with each group on a new line. That should be paste-able into AD, by which I think you meant Active Directory User & Computers.
Function Get-ADUserNestedGroups
{
Param
(
[string]$DistinguishedName,
[array]$Groups = #()
)
# Get the AD object, and get group membership.
$ADObject = Get-ADObject $DistinguishedName -Properties memberOf, DistinguishedName
# If object exists.
If( $ADObject )
{
# Enummurate through each of the groups.
Foreach( $GroupDistinguishedName in $ADObject.memberOf )
{
# Get member of groups from the enummerated group.
$CurrentGroup = Get-ADObject $GroupDistinguishedName -Properties memberOf, DistinguishedName
# Check if the group is already in the array.
If( $Groups.DistinguishedName -notcontains $GroupDistinguishedName )
{
# Add group to array.
$Groups += $CurrentGroup
# Get recursive groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName $GroupDistinguishedName -Groups $Groups
}
}
}
# Return groups.
Return $Groups
}
$userSAM = 'UserName'
# Get all groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName (Get-ADUser -Identity $userSAM).DistinguishedName
($Groups.Name | Sort-Object) -join [System.Environment]::NewLine
Among the secondary changes you'll see I removed the -Filter parameter in a few places. The DistinguishedName can be used as the -Identity parameter so no need to filter.
Also, where you were checking if the $Groups collection already had the current group I used the -notContains operator instead. That should be faster then repeatedly iterating the collection with |Where{...} The code is also shorter and more readable.
Note: if we reverse the operands we could use -notin which may look
nicer still
An aside; You should avoid appending arrays with +=. Doing so can causes performance problems because it creates a new array any copies the contents over. The best way to deal with this is to allow PowerShell to accumulate the results for you. If you must do the append directly, look into using Array Lists. There's a lot information on this, just google it.
This question already has answers here:
Export hashtable to CSV with the key as the column heading
(2 answers)
Closed 4 years ago.
I'm trying to list all ad group memberships of specific users. The input would be a string of logins split with a comma 'login1,login2'.
So I go over each user and list their memberships with the username as title. Somehow it only shows the first entry. Also it shows the user groups in one row and I don't know how to change that.
Code below:
$users = $logon -split ','
$q = #()
foreach ($user in $users) {
$usernm = Get-ADUser -Filter 'samAccountName -like $user' | select Name
$useraccess = Get-ADPrincipalGroupMembership $user | Select-Object Name
$userobj = New-Object PSObject
$userobj | Add-Member Noteproperty $usernm.Name $useraccess.Name
$q += $userobj
}
Expected output would be something like:
fullnameuser1 fullnameuser2 list of users goes on...
------------- ------------- ------------------------
adgroup1 adgroup3 ...
adgroup2 adgroup4
... ...
In principle this would also mean that if i typed $q.'fullnameuser1' output would be:
fullnameuser1
-------------
adgroup1
adgroup2
...
Whenever the code is ran, it will only ever add the first user's access, also returning all groups on one row. So somehow I need to go over all the group memberships and add a row for each one.
First and foremost, PowerShell does not expand variables in single-quoted strings. Because of that Get-ADUser will never find a match unless you have a user with the literal account name $user. Also, using the -like operator without wildcards produces the same results as the -eq operator. If you're looking for an exact match use the latter. You probably also need to add nested quotes.
Get-ADUser -Filter "samAccountName -eq '${user}'"
Correction: Get-ADUser seems to resolve variables in filter strings by itself. I verified and the statement
Get-ADUser -Filter 'samAccountName -eq $user'
does indeed return the user object for $user despite the string being in single quotes.
If you want a fuzzy match it's better to use ambiguous name resolution.
Get-ADUser -LDAPFilter "(anr=${user})"
You may also want to avoid appending to an array in a loop, and adding members to custom objects after creation. Both are slow operations. Collect the loop output in a variable, and specify the object properties directly upon object creation.
$q = foreach ($user in $users) {
...
New-Object -Type PSObject -Property {
$usernm.Name = $useraccess.Name
}
}
Lastly, I'd consider using the user's name as the property name bad design. That would be okay if you were building a hashtable (which is mapping unique keys to values), but for custom objects the property names should be identical for all objects of the same variety.
New-Object -Type PSObject -Property {
Name = $usernm.Name
Group = $useraccess.Name
}
Basily query all the users and store it in $users, example:
Get-ADUser -Filter * -SearchBase "dc=domain,dc=local"
And then you can export the results as csv or a table.
To Export as CSV :
Get-ADPrincipalGroupMembership <Username> | select name, groupcategory, groupscope | export-CSV C:\data\ADUserGroups.csv`
To Format the result as Table in the console itslef :
Get-ADPrincipalGroupMembership <Username> | select name, groupcategory, groupscope | Format-Table
I'm trying to write a simple PS line to take the exported .csv of AD groups in particular "regions" we have set up, then take the GroupScope (universal vs. global), and depending on the scope of the group, write the "department" attribute as either "Universal" or "Global." The reason for doing so is to help identify between the 2 scopes within SharePoint.
$uni="Universal"
Import-csv \\usershare\user\me\output\groups.csv | foreach {Get-ADGroup -Identity $_.Name -Properties * | Set-ADGroup if($_.GroupScope -eq $uni){-replace #{department=$uni}}}
This is returning the following error message though:
"A positional parameter cannot be found that accepts the argument 'True.'"
I'm probably missing something simple here but I just started out and I'm self-teaching by trial and error mostly. Thanks for any help you can provide!
You're use of the if block is not valid for what you are trying to accomplish, it will only return True or False and Set-ADGroup will not know what to do with that hence the error.
Try this:
$uni="Universal"
$csv = Import-Csv -Path '\\usershare\user\me\output\groups.csv'
foreach($i in $csv)
{
if($i.GroupScope -eq $uni)
{
Get-ADGroup -Identity $i.Name -Properties 'department' |
Set-ADGroup -Replace #{department=$uni}
}
}
I've set properties to only pull department as a smaller scope will speed up the query.
I have this script that works fine (output looks good), but it is taking longer than 12 hours now. There are 34220 records in the csv and it's only now on 2110. Maybe I need to load up all the user data first, then compare to the csv file? Thx for help...
import-module ActiveDirectory
$CCure = Import-csv C:\Scripts\CCure\CCure-Personnel-enabled.csv
ForEach ($Row in $CCure) {
[string]$ID = $Row.ObjectID
[string]$Name = $Row.Name
[string]$EmpID = $Row.Int5
If ($EmpID.Trim() -ne "0") {
$User = Get-ADUser -LDAPFilter "(&(&(&(objectclass=user)(objectcategory=person)(!userAccountControl:1.2.840.113556.1.4.803:=2))))((employeeId=*$EmpID))" -SearchBase 'DC=Enterprise,DC=mycompany,DC=org' -Properties SamAccountName,DisplayName,EmployeeId,enabled |
Select #{Name="CCure ObjectID";Expression={$ID}},SamAccountName,DisplayName,#{Name="CCure Name";Expression={$Name}},EmployeeId,#{Name="CCure Int5 Row";Expression={$EmpID}},enabled | Export-csv c:\scripts\ccure\EmployeeIds4-10-2016.csv -NoTypeInformation -append
}
}
Maybe I need to load up all the user data first, then compare to the csv file?
That's exactly what you need to do!
Since you want to correlate the users in the CSV by the EmployeeId attribute, I'd recommend pulling out all the (enabled) users that have the EmployeeId populated, and then store them in a hashtable where the EmployeeId is used as the key:
$ADUserTable = #{}
Get-ADUser -LDAPFilter "(&(!userAccountControl:1.2.840.113556.1.4.803:=2)(employeeId=*))' -SearchBase 'DC=Enterprise,DC=mycompany,DC=org' -Properties SamAccountName,DisplayName,EmployeeId |ForEach-Object {
$ADUserTable[$_.EmployeeId] = $_
}
Then, as you iterate over the rows in the CSV, lookup the user in the hashtable instead of searching AD again:
$ExistingUsers = ForEach ($Row in $CCure) {
# Import-Csv always creates string properties anyways
$ID = $Row.ObjectID
$Name = $Row.Name
$EmpID = $Row.Int5.Trim()
if ($EmpID -ne "0" -and $ADUserTable.ContainsKeys($EmpID))
{
$ADUserTable[$EmpID] |Select #{Name="CCure ObjectID";Expression={$ID}},SamAccountName,DisplayName,#{Name="CCure Name";Expression={$Name}},EmployeeId,#{Name="CCure Int5 Row";Expression={$EmpID}}
}
}
Do NOT export them to Csv until AFTER you've collected all the information - otherwise you're opening, writing to and closing the same file 35000 times!
So, at the very end:
$ExistingUsers |Export-csv c:\scripts\ccure\EmployeeIds4-10-2016.csv -NoTypeInformation
This will undoubtedly speed up execution of your script
Note: I've removed the Enabled property from Get-ADUser and Select-Object. Your LDAP Filter already guarantees that only Enabled users are returned, so I don't really see any value in adding it to the CSV
I am trying to write a powershell script that will update employeeID attribute in AD for each user
The script needs to update employeeID from my CSV file
Sample CSV:
user,employeeID
user1,1234567
At least now you are trying some code which looks like it should work. Your logic is sound. Are you sure your CSV does not contain and blanks? Some simple statements could rule those out.
Import-CSV "C:\Scripts\Users.csv" | ForEach-Object {
$User = $_.UserName
$ID = $_.EmployeeID
If($user -and $ID){
Set-ADUser $User -employeeID $ID
} Else {
Write-Warning "User or employee number is null. Check source."
}
The If statement would fail if either $user or $id was null. If that is not the case and your CSV does contain data maybe you are having an encoding issue.
I think it depends on how you're finding the user in Active Directory... You could try something like this:
# import the users
$myUsers = Import-Csv csvFile.csv
# loop through each user and update their employeeID property
foreach ($user in $myUsers)
{
# the Set-ADUser cmdlet requires a type of System.Hashtable<>,
# so let's create one
$updateRecord = #{"EmployeeID"=$user.EmployeeID}
# Set the user attribute
Set-ADUser -Identity $user -Replace #updateRecord
}
This might do the trick. Did you guys modify the AD Schema? Keep in mind that "EmployeeID" is assuming you have an EmployeeID Attribute on your Active Directory users.
Please let me know if it helps!