I have a script for getting all the members of various AD Groups but recently we have been needed to get more then just the names of the members and need to get various properties such as Title or Office. I tried adjusting my script so that you could input what properties you needed to define but it isn't working as I would like so I was hoping someone might be able to help.
Here is my original script:
$EndDate = (Get-Date).ToString("yyyy-MM-dd_HHmm")
$FileName = $ADGroup + '_' + $EndDate + '_ADGroupReport.csv'
$FilePath = 'P:\Information Technology\IT Reports\' + $FileName
function Get-Members {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true, HelpMessage="Enter a valid AD Group.")]
[string]$ADGroup,
[Parameter(Mandatory=$false, HelpMessage="Enter valid AD Properties seperated by a comma.")]
$Properties
)
process {
if (!$Properties) {
Get-ADGroupmember -identity $ADGroup | select name | Export-Csv -Path $FilePath
} else {
Get-ADGroupmember -identity $ADGroup | where{$_.ObjectClass -eq "user"} | Get-ADUser -Properties '$Properties' | select name, $Properties | Export-Csv -Path $FilePath
}
}
}
$Group = Read-Host -Prompt 'Enter a valid AD Group:'
$Prop = Read-Host -Prompt 'Enter any additional properties needed:'
Get-Members -ADGroup $Group -Properties $Prop
This appears to work in the try phase if I only enter 1 property, but then fails on the finally stage. If I enter multiple properties, such as title,office then it fails at both stages.
The error I get when entering additional properties is below. My assumption is that it doesn't like this as a string and would prefer an array? I am not sure honestly on how to handle this.
Get-ADUser : One or more properties are invalid.
Parameter name: $Properties
At line:10 char:79
+ ... bjectClass -eq "user"} | Get-ADUser -Properties '$Properties' | selec ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (REDACTED) [Get-ADUser], ArgumentException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.GetADUser
Before anyone says anything, I am aware the try block will fail if no properties are entered, its only in there for my testing currently.
I could see three issues on your code, first one, single quotes on -Properties '$Properties' will not allow the expansion of the variable $Properties, it would be as passing the literal string.
Should be:
Get-ADUser -Properties $Properties
Second issue, doing this Select-Object Name, $Properties will throw the following exception (assuming $Properties would have been an array):
$properties = 'email', 'title'
[PSCustomObject]#{
Name = 'User'
Email = 'user#domain.com'
Title = 'Administrator'
} | Select-Object Name, $properties
Cannot convert System.Object[] to one of the following types {System.String, System.Management.Automation.ScriptBlock}.
You could do something like this so that Name is always included in the $Properties variable:
$properties = ,'name' # => This is hardcoded
$properties += 'email', 'title' # => This is user input
[PSCustomObject]#{
Name = 'User'
Email = 'user#domain.com'
Title = 'Administrator'
} | Select-Object $properties
Name Email Title
---- ----- -----
User user#domain.com Administrator
Third issue is the one pointed out by Theo on his comment, Read-Host will store the user input as string, so, assuming the user is actually using , as delimiter of each property, you can convert the input to an array using -split or .split():
$prop = ,'name'
$prop += (
Read-Host -Prompt 'Enter any additional properties needed'
).Split(',').ForEach('Trim')
Supposing the input was for example email, title:
Enter any additional properties needed: email, title
name
email
title
Related
I would like to change 150 employees their job title.
I have a csvfile called Titletest.csv with columns UserPrincipalName [the user.name under it] and Title [job title under it]
The PowerShell script:
Import-Module ActiveDirectory
$users = Import-Csv -Path c:\scripts\Titlestest.csv | Foreach-Object {
$user = $_.user
$title = $_.title
#Selects the specified user and sets Job Title
Get-ADUser -Filter {(UserPrincipalName -eq $user)} | Set-ADUser -Title $title
}
I get errors saying:
Get-ADUser : Variable: 'user' found in expression: $user is not defined.
At line:14 char:1
+ Get-ADUser -Filter {(UserPrincipalName -eq $user)} | Set-ADUser -Titl ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADUser], ArgumentException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.GetADUser
Can someone please advise?
Thank you.
The reason for your error is because $user has no assignment. You are attempting to assign $user the value of a property that does not exist. The header user apparently does not exist in your CSV file. See below for how to convert a csv into PowerShell objects and access their properties.
# Sample CSV TitleTest.csv
UserPrincipalName,Title
covid19#domain.com,Usurper
jsmith#domain.com,CEO
bossman#domain.com,CFO
Import-Csv -Path c:\scripts\TitleTest.csv | Foreach-Object {
$user = $_.UserPrincipalName
$title = $_.Title
Get-ADUser -Filter 'UserPrincipalName -eq $user' | Set-ADUser -Title $title
}
Explanation:
When using Import-Csv on a proper CSV file, the first row of delimited data will be converted to the properties of all input objects. All succeeding rows will be converted to individual objects with the header properties and output as a collection (array) of those objects. If the -Header parameter is used, then values passed into the parameter will become the properties of the objects. It is important to have the same number of delimited items on each row to ensure proper mapping.
Once you are dealing with objects, you can access their property values using the member access operator .. The syntax is object.property. So since you have headers UserPrincipalName and Title, you will need to use $_.UserPrincipalName and $_.Title to access the associated values.
$_ is the current pipeline object within your Foreach-Object {} script block.
Note that you don't technically need to define $user and $title here. You can just access the properties directly from the current object:
Import-Csv -Path c:\scripts\TitleTest.csv | Foreach-Object {
Get-ADUser -Filter "UserPrincipalName -eq '$($_.UserPrincipalName)'" |
Set-ADUser -Title $_.Title
}
I am very new to powershell, still trying to figure out how it works. I have so far written a short script to take details from a CSV and poulate properties in AD.
If I use the username i.e smithj it works fine but I can't get it to take a name like John Smith and find the account it is associated with. This is the same with the manager field, it will take the username but I cant get it to take a full name.
Any help or advice would be much appreciated.
Import-module ActiveDirectory
$List = Import-CSV "\\SharedServer\shared\MYCSV.csv" | % {
$User = $_.UserName
$ID = $_.EmployeeID
$EmployeeNumber = $_.EmployeeNumber
$Description = $_.Description
$Department = $_.Department
$Title = $_.Title
$AccountExpirationDate = $_.AccountExpire
$Manager = $_.Manager
Set-ADUser $User -employeeID $ID -EmployeeNumber $EmployeeNumber -Description $Description -Department $Department -Title $Title -Manager $Manager -AccountExpirationDate $AccountExpirationDate
}
Depending on what the CSV contains for UserName and Manager, the best would be to have the SamAccountName or DistinguishedName because these attributes are unique within the same domain.
UserPrincipalName or EmailAddress would also do nicely for targeting the correct user.
From your question however, I gather that the CSV has the users Name in there that should correspond to the Name property of an AD user.
In that case I agree with I.T Delinquent that you can use that in the Filter parameter for Get-ADUser and that is also what my example code below uses.
Then there is the question of how you have entered the date for the AccountExpirationDate in the CSV file..
This parameter wants a DateTime object, not a string, so you'll have to convert that before use.
Finally, I would suggest using Splatting for cmdlets like Set-ADUser that take a lot of parameters.
Something like this:
Import-CSV "\\SharedServer\shared\MYCSV.csv" | ForEach-Object {
$user = Get-ADUser -Filter "Name -eq '$($_.UserName)'" -ErrorAction SilentlyContinue
if (!$user) {
Write-Warning "User '$($_.UserName)' not found"
}
else {
# convert the date string from the CSV into a real DateTime object
# Since I cannot see the CSV, you may need to do this using [DateTime]::ParseExact()
$expireDate = Get-Date $_.AccountExpire
# create a Hashtable for the parameters
$userProps = #{
'EmployeeID' = $_.EmployeeID
'EmployeeNumber' = $_.EmployeeNumber
'Description' = $_.Description
'Department' = $_.Department
'Title' = $_.Title
'AccountExpirationDate' = $expireDate
}
# get the manager object from the name
$manager = Get-ADUser -Filter "Name -eq '$($_.Manager)'" -ErrorAction SilentlyContinue
if ($manager) {
$userProps['Manager'] = $manager.DistinguishedName
}
$user | Set-ADUser #userProps
}
}
When using UserPrincipalName or EmailAddress, change the Filter into "UserPrincipalName -eq '$($_.UserName)'" or "EmailAddress -eq '$($_.UserName)'".
You might even want to experiment with Ambiguous Name Resolution..
I would use Get-ADUser and then pipe the object that was returned into Set-ADUser. Here is a quick example:
Get-ADUser -Filter " Name -eq 'Name here' " | Set-ADUser -employeeID $ID
I am trying to add an AD group into user profiles based on an OU
I had a similar script working, so tried to modify it and failed. I am guessing it's the " -Identity $_" it maybe, but I am not good enough to debug.
#Create a new class to hold the info for our CSV entry
Class CSVEntry{
[String]$UserName
[String]$GroupName
[String]$TimeStamp
}
#Creating a list to hold the CSV entries
$Results = New-Object 'System.Collections.Generic.List[PSObject]'
#Defined the name of the group here
$GroupName = 'GROUPS NAME'
$ou = 'ou=XX,ou=XX,ou=XX,dc=XX,dc=local'
Get-ADUser -Filter * -SearchBase $ou | ForEach-Object{
#Add the user to the group here
Add-ADPrincipalGroupMembership -MemberOf $GroupName Identity $_
#Write-Host $_.Name - $groupName
#Build a custom CSVEntry object and add it to the list
$newRecord = [CSVEntry]::new()
$newRecord.UserName = $_.Name
$newRecord.GroupName = $groupName
$newRecord.TimeStamp = Get-Date
#Add the new record to the list
$Results.Add($newRecord)
}
#Export the list of CSV entries
$Results | Export-Csv C:\PS\AddADGroupToUsers.csv
errors:
Add-ADPrincipalGroupMembership : A positional parameter cannot be found that accepts argument 'CN=NAME,OU=XX,OU=XX,OU=XX,OU=XX,DC=XX,DC=LOCAL'.
At line:18 char:5
+ Add-ADPrincipalGroupMembership -MemberOf $GroupName Identity $_
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Add-ADPrincipalGroupMembership], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.ActiveDirectory.Management.Commands.AddADPrincipal
GroupMembership
EDIT:
So, the script doesn't actually do any changes, the group doesn't get added to the users. the output on screen is:
WARNING: User is already a member of group XYZ
WARNING: User is already a member of group XYZ
WARNING: User is already a member of group XYZ
UserName GroupName TimeStamp
-------- --------- ---------
shows ok XYZ 14/10/2019 14:50:23
shows ok XYZ 14/10/2019 14:50:23
shows ok XYZ 14/10/2019 14:50:23
All I have changed is the group name to XYZ and username shows ok in the second half. But, shows blank in the top, and I assure you that a) the user isn't already in the group and b) the script isn't adding them
Current tweaked code, warts and all but sanitised:
$groupName = 'GROUP'
$ou = 'setcorrectly'
$cred = Get-Credential -credential dom\usr
$results = Get-ADUser -Filter * -SearchBase $ou -Credential $cred | ForEach-Object {
#Add the user to the group here
try {
Add-ADGroupMember -Identity $groupName -Members $_.DistinguishedName -Credential $cred -ErrorAction Stop
}
catch {
Write-Warning "User $($_.Name) is already a member of group $groupName"
}
# output a PsCustomObject that gets collected in the $results variable
[PsCustomObject]#{
'UserName' = $_.Name
'GroupName' = $groupName
'TimeStamp' = Get-Date
}
}
# output on console
$results | Format-Table -AutoSize
# Export to CSV file
$results | Export-Csv C:\PS\AddADGroupToUsers.csv -NoTypeInformation
Read-Host -Prompt "Press Enter to exit"
CSV output shows the second half of the screen output only, and doesn't say anything is already a member
Below uses Add-ADGroupMember to add user(s) to 1 group instead of Add-ADPrincipalGroupMembership which is used to add 1 user to multiple groups.
It also uses [PsCustomObject]s to output the results, so you don't need to use the Class CSVEntry.
# Define the name of the group here.
# can be either:
# A distinguished name
# A GUID (objectGUID)
# A security identifier (objectSid)
# A Security Account Manager account name (sAMAccountName)
$groupName = '<NAME OF THE GROUP>'
$ou = 'ou=XX,ou=XX,ou=XX,dc=XX,dc=local'
$results = Get-ADUser -Filter * -SearchBase $ou | ForEach-Object {
#Add the user to the group here
$userName = $_.Name
try {
Add-ADGroupMember -Identity $groupName -Members $_.DistinghuishedName -ErrorAction Stop
# output a PsCustomObject that gets collected in the $results variable
[PsCustomObject]#{
'UserName' = $_.Name
'GroupName' = $groupName
'TimeStamp' = Get-Date
}
}
catch {
Write-Warning "User $userName is already a member of group $groupName"
}
}
# output on console
$results | Format-Table -AutoSize
# Export to CSV file
$results | Export-Csv C:\PS\AddADGroupToUsers.csv -NoTypeInformation
Edit
If you want the $results variable to ALSO contain users that are already a member of the group, you could simply move the creation of the [PsCustomObject] below the catch{..} block:
$results = Get-ADUser -Filter * -SearchBase $ou | ForEach-Object {
#Add the user to the group here
$userName = $_.Name
try {
Add-ADGroupMember -Identity $groupName -Members $_.DistinghuishedName -ErrorAction Stop
$status = "User added successfully"
}
catch {
Write-Warning "User $userName is already a member of group $groupName"
$status = "User is already a member"
}
# output a PsCustomObject that gets collected in the $results variable
[PsCustomObject]#{
'UserName' = $userName
'GroupName' = $groupName
'TimeStamp' = Get-Date
'Status' = $status
}
}
Hope that helps
$ADInfo = Get-ADUser -filter {Surname -Like $ntaccount1} | Sort-Object Name
$ADInfo `
| Format-Table `
#{ Name = "Full Name"; Expression = { $_.name } },
#{ Name = "User ID"; Expression = { $_.samaccountname } } -Autosize;
This will only search the Directory by Surname (Last name) and then it outputs the full name and user id of the results. How do I have it look for every variable instead of just Surname? I want to mimic as if you were searching in the actual active directory program, but in powershell.
I'm looking into other ideas but just to see if this is in the direction you are looking for I made up and LDAP filter for what I think the AD Find does
$searchString = "Matt"
get-aduser -LDAPFilter "(|(displayName=$($searchString)*)(sn=$($searchString)*)(givenName=$($searchString)*)(cn=$($searchString)*)(samaccountname=$($searchString)*))"
So this will search all of the properties in AD and return users if they match "Matt*". Following the same logic but making the seach more configurable to suit your needs. Results should be the same for both as written. This way you can add/remove properties to search for.
$searchString = "Matt"
$properties = "displayName","sn","givenName","cn","samaccountname"
$ldapFilter = "(|$($properties | ForEach-Object{"($_=$($searchString)*)"}))"
Get-Aduser -LDAPFilter $ldapFilter
Using Plain Filter
For whatever reason -LDAPFilter is not working for you. We can use similar logic to get -Filter working. The property names will change to match the PowerShell Filter
$searchString = "Matt"
$properties = "FirstName","LastName","Name","DisplayName","SamAccountName"
$Filter = ($properties | ForEach-Object{"($_ -Like '$searchString*')"}) -Join " -Or "
Get-Aduser -Filter $Filter
Currently i have:
import-module activedirectory
$path = get-content "E:\test.txt" | Out-String
$path | ForEach-Object { Get-ADGroupMember $_ | Select-Object name }
This gets the names (lastname_firstInitial) of the users within the group specified in the text file.
The text file looks somthing like:
groupname1
groupname2
...
groupname50
is there a way to output the full name of the user "displayname" property, get-adgroupmember does not support the -property displayname or firstname & last name - i also need the names to appear next to the correct group they were pulled from.
Currently i can only retrieve the "logon names" but have no idea what group they were pulled from.
Thank you.
Phil
When issuing this command you get a list of all members, each containing a unique ID in the AD. You could use this to fetch the actual ADUserObject and work your way from there.
$Filename = "C:\temp\groups.txt"
#ForEach ( $GroupName in [System.IO.File]::ReadLines($Filename) ) {
ForEach ( $GroupName in (Get-Content $Filename) ) {
$UserIDs = (Get-ADGroupMember $GroupName).ObjectGUID
ForEach ( $UserID in $UserIDs ) {
$ADUser = Get-ADUser $UserID -Properties DisplayName
Write-Output "$GroupName : $($ADUser.DisplayName)"
}
}