I try to add value X to a Active Directory Member Y that has already some values in extensionAttribute10. How do I add a specific string into that? Also I want to scan the extensionAttribute and remove a specific entry.
Testparameters:
$ThisUser = "Testaccount01"
#{extensionAttribute10=Computer01, Computer02}
$adding = "Computer03"
$delete = "Computer01"
assuming the target is a user account:
#Replace existing value with a new one
set-aduser -identity [userSamAccountName] -replace #{extensionAttribute10=$value}
#Add new value to
set-aduser -identity [userSamAccountName] -add #{extensionAttribute10=$value}
If your target is a different object type, e.g. a computer use the related cmdlets, the syntax is equal....
As response to your comment: Sure the code does work but you need to consider the datatype of the target attribute, which is not an array it's a single value. So if you need to store multiple values there you need to transform the array into a delimited string, e.g.:
$value = #('comp1','comp2')
$valueString = $value -join ","
set-aduser -identity [userSamAccountName] -add #{extensionAttribute10=$valueString}
But be aware that this attribute has a size limit.
Ok based on your code sample, you cast the output as string array ([string[]]) but as outlined earlier you need a string and not an array:
your code
$newAttribute = [string[]]($user.extensionAttribute10 -split '[,\s]+' | ?{$_ -ne $delete}) + $add
replace with:
$valueArray = #($user.extensionAttribute10 -split ',' | ?{$_ -ne $delete})
$valueArray += $add
$valueString = $valueArray -join ","
set-aduser -identity [userSamAccountName] -replace #{extensionAttribute10=$valueString}
$ThisUser = "Test"
$delete = "Computer2"
$add = "Computer3"
$user = Get-ADUser -Identity $ThisUser -Properties extensionAttribute10
$newAttribute = [string[]]($user.extensionAttribute10 -split '[,\s]+' | ?{$_ -ne $delete}) + $add
Set-ADUser $thisUser -Replace #{extensionAttribute10=$newAttribute}
Related
I'm trying to create a script that will grab all users in an OU and change the current primary alias to a secondary alias while adding a new primary smtp address and retaining any other secondary aliases. We have users with 0 aliases, some with 1, some with 2, and some with 3. I am running into an issue when any one of the $sp1, $sp2, $sp3, $sp4, $sp5 are either white space or null. I'm still learning powershell so I'm not sure how to handle that without a lot of pain lol.
$Users = Get-AdUser -Filter * -SearchBase "OU=TestScriptedSMTPAddrChange,OU=***,DC=***,DC=com" -Properties proxyAddresses | Select-Object SamAccountName, proxyAddresses #Change first OU= to the OU you want to change
Foreach ($SAN in $Users){
$SecondaryProxyRaw = $SAN.proxyAddresses #grab proxyAddresses and dump them
$SecondaryProxyRed = $SecondaryProxyRaw.replace('SMTP','smtp') #change primary SMTP addr to secondary smtp addr
$sp1,$sp2,$sp3,$sp4,$sp5 = $SecondaryProxyRed.split(" ",1) #split the proxyAddresses array into variables
$NewPrimaryProxy = "SMTP:$($SAN.SamAccountName)#newdomain.com"} #assign new primary SMTP address
Set-ADUser -Identity $SAN.SamAccountName -replace #{proxyAddresses = "$NewPrimaryProxy","$sp1","$sp2","$sp3","$sp4","$sp5"}
}
Get-AdUser -Filter * -SearchBase "OU=TestScriptedSMTPAddrChange,OU=***,DC=***,DC=com" -Properties proxyAddresses | Select-Object SamAccountName, UserPrincipalName, #{Name="Proxyaddresses";Expression={$_.proxyAddresses -join "*"}}
You should not rely on a user having 1, 3 or perhaps 77 addresses in its proxyAddresses attribute by trying to split these into a fixed number of variables.
Just get them all, replace the uppercase SMTP: into lowercase 'smtp:', filter out the possible one that would be equal to the new proxy address and add the new primary address to the array.
Then, replace the entire proxyAddresses array with the strongly typed (i.e. cast to string[]]) new array.
$Users = Get-AdUser -Filter * -SearchBase "OU=TestScriptedSMTPAddrChange,OU=***,DC=***,DC=com" -Properties proxyAddresses
foreach ($SAN in $Users) {
$NewPrimaryProxy = 'SMTP:{0}#newdomain.com' -f $SAN.SamAccountName
# if you like you can sort the proxies but for the system this will have no effect
$proxies = #($SAN.ProxyAddresses -replace '^SMTP:', 'smtp:' | Where-Object { $_ -ne $NewPrimaryProxy }) + $NewPrimaryProxy
# Note: proxyAddresses needs a Strongly typed string array, that is why we cast $proxies array with [string[]]
$SAN | Set-ADUser -Replace #{proxyAddresses = [string[]]$proxies}
}
.split(" ",1)
doesn't split at all - by definition it returns the input string as-is, because you're asking for 1 token only - see the docs for the .NET [string] type's .Split() method.
To split by runs of whitespace, you can use the unary form of PowerShell's -split operator:
# Split by whitespace and collect tokens in an array.
# -ne '' filters out empty elements, so that if
# $SecondaryProxyRed is effectively empty, $sps becomes an empty array.
$sps = -split $SecondaryProxyRed -ne ''
You can then create an array with $NewPrimaryProxy as the first element, followed by the elements of $sps, if any:
Set-ADUser -Identity $SAN.SamAccountName -replace #{
proxyAddresses = #($NewPrimaryProxy) + $sps
}
I am trying to do an update to Active Directory from a CSV.
I want to check each value to see if the AD and CSV values match.
If the AD value and CSV values don't match, then I want to update the AD value.
finally I want to create a log of the values changed, which would eventually be exported to a CSV report.
Now there is about 30 values I want to check.
I could do an if statement for each value, but that seems like the hard way to do it.
I am try to use a function, but I cant seem to get it working.
I am getting errors like:
set-ADUser : replace
At line:94 char:9
+ set-ADUser -identity $ADUser -replace #{$ADValue = $DIAccount ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (JDoe:ADUser) [Set-ADUser], ADInvalidOperationException
+ FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.SetADUser
set-ADUser : The specified directory service attribute or value does not exist
Parameter name: Surname
At line:94 char:9
+ set-ADUser -identity $ADUser -replace #{$ADValue = $DIAccount ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (JDoe:ADUser) [Set-ADUser], ArgumentException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.SetADUser
Any suggestions would be welcome
Code I am using:
Function AD-Check ($ADValue, $ADUser, $ADAccount, $UpdateAccount)
{
If ($ADAccount -ne $UpdateAccount)
{
set-ADUser -identity $ADUser -replace #{$ADValue = $UpdateAccount}
$Change = "Updated"
}
Else
{
$Change = "No Change"
}
Return $Change
}
$Import = get-content C:\temp\ADUpdates.csv
Foreach ($user in $Import)
{
$Account = get-aduser $User.Samaccountname -Properties *
#First Name Check
$Test = AD-Check "GivenName" $Account.samaccountname $Account.givenname $user.givenname
$ChangeGivenName = $Test
#Initials Check
$Test = AD-Check "Initials" $Account.samaccountname $Account.Initials $user.Initials
$ChangeInitials = $Test
#Last Name Check
$Test = AD-Check "Surname" $Account.samaccountname $Account.SurnameSurname $user.Surname
$ChangeSurname = $Test
}
Reply to Theo, cant seem to add this any other way...
Thanks Theo, it seems to make sense, but getting an error.
Select-Object : Cannot convert System.Collections.Specialized.OrderedDictionary+OrderedDictionaryKeyValueCollection to one of the following types {System.String,
System.Management.Automation.ScriptBlock}.
changed the following to get all properties for testing and it works.
$Account = Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue -Properties $propsToCheck
Left the following and it kicks the error
$oldProperties = $Account | Select-Object $propsToCheck
Using the following just for testing:
$propertiesMap = [ordered]#{
SamAccountName = 'sAMAccountName'
mail = 'mail'
GivenName = 'givenName'
Initials = 'initials'
Surname = 'sn'
Office = 'physicalDeliveryOfficeName'
MobilePhone = 'mobile'
DistinguishedName = 'DistinguishedName'
}
Starting of with a WARNING:
Replacing user attributes is not something to be taken lightly and you
need to check any code that does that on a set of testusers first.
Keep the -WhatIf switch to the Set-ADUser cmdlet so you
can first run this without causing any problems to the AD.
Only once you are satisfied all goes according to plan, remove the -WhatIf switch.
Please carefully read all inline comments in the code.
In your code you use an input CSV file, apparently with properties and values to be checked/updated, but instead of using Import-Csv, you do a Get-Content on it, so you'll end up with just lines of text, not an array of parsed properties and values..
Next, as Mathias already commented, you need to use the LDAP attribute names when using either the -Add, -Remove, -Replace, or -Clear parameters of the Set-ADUser cmdlet.
To do what you intend to do, I would first create a hashtable to map the PowerShell attribute names to their LDAP equivalents.
To see which property name maps to what LDAP name, you can use the table here
# create a Hashtable to map the properties you want checked/updated
# the Keys are the PowerShell property names as they should appear in the CSV
# the Values are the LDAP AD attribute names in correct casing.
$propertiesMap = [ordered]#{
SamAccountName = 'sAMAccountName'
GivenName = 'givenName'
Initials = 'initials'
Surname = 'sn'
Office = 'physicalDeliveryOfficeName'
Organization = 'o'
MobilePhone = 'mobile'
# etcetera
}
# for convenience, store the properties in a string array
$propsToCheck = $propertiesMap.Keys | ForEach-Object { $_.ToString() }
# import your CSV file that has all the properties you need checked/updated
$Import = Import-Csv -Path 'C:\temp\ADUpdates.csv'
# loop through all items in the CSV and collect the outputted old and new values in variable $result
$result = foreach ($user in $Import) {
$sam = $user.SamAccountName
# try and find the user by its SamAccountName and retrieve the properties you really want (not ALL)
$Account = Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue -Properties $propsToCheck
if (!$Account) {
Write-Warning "A user with SamAccountName '$sam' does not exist"
continue # skip this one and proceed with the next user from the CSV
}
# keep an object with the current account properties for later logging
$oldProperties = $Account | Select-Object $propsToCheck
# test all the properties and create a Hashtable for the ones that need changing
$replaceHash = #{}
foreach ($prop in $propsToCheck) {
if ($Account.$prop -ne $user.$prop) {
$ldapAttribute = $propertiesMap[$prop] # get the LDAP name from the $propertiesMap Hash
# If any of the properties have a null or empty value Set-ADUser will return an error.
if (![string]::IsNullOrWhiteSpace($($user.$prop))) {
$replaceHash[$ldapAttribute] = $user.$prop
}
else {
Write-Warning "Cannot use '-Replace' with empty value for property '$prop'"
}
}
}
if ($replaceHash.Count -eq 0) {
Write-Host "User '$sam' does not need updating"
continue # skip this one and proceed with the next user from the CSV
}
# try and do the replacements
try {
##########################################################################################################
# for safety, I have added a `-WhatIf` switch, so this wll only show what would happen if the cmdlet runs.
# No real action is performed when using '-WhatIf'
# Obviously, there won't be any difference between the 'OLD_' and 'NEW_' values then
##########################################################################################################
$Account | Set-ADUser -Replace $replaceHash -WhatIf
# refresh the account data
$Account = Get-ADUser -Identity $Account.DistinguishedName -Properties $propsToCheck
$newProperties = $Account | Select-Object $propsToCheck
# create a Hashtable with the old and new values for log output
$changes = [ordered]#{}
foreach ($prop in $propsToCheck) {
$changes["OLD_$property"] = $oldProperties.$prop
$changes["NEW_$property"] = $newProperties.$prop
}
# output this as object to be collected in variable $result
[PsCustomObject]$changes
}
catch {
Write-Warning "Error changing properties on user '$sam':`r`n$($_.Exception.Message)"
}
}
# save the result as CSV file so you can open with Excel
$result | Export-Csv -Path 'C:\temp\ADUpdates_Result.csv' -UseCulture -NoTypeInformation
I am stumped. I have what I thought should work in a script to pull all groups and get the users in those groups from AD.
$ADGroupList = Get-ADGroup -Filter * | Select Name | Sort Name
ForEach($Group in $ADGroupList)
{
$GroupName = $Group.substring($Group.length -7, $Group.length -8)
$members = Get-ADGroupMember -Identity "$GroupName" | Select Name, SAMAccountName | Sort Name
ForEach($member in $members)
{
Write-Host ($member.Name + "," + $member.SAMAccountName + "," + $Group.name)
}
}
It keeps failing with the following error:
Method invocation failed because [Selected.Microsoft.ActiveDirectory.Mangement.ADGroup] doesn't contain a method named 'substring'
Where did I go wrong? I thought SubString was a PowerShell method?
You are looking to get substring of the Name of the object. Change your group statement to this,
$GroupName = $Group.Name.substring($Group.Name.length -7, $Group.Name.Length -8)
# string.substring(startIndex , number of characters)
Note
Powershell's substring method takes in the index and number of characters you need from that index. the method .substring does not have start and end as one might think.
Documentation:
Substring()
Return part of a longer string.
Syntax
.Substring( StartIndex [, length] )
Key
StartIndex Characters to skip from the start of the string.
Use 0 to start from the beginning of the string.
Cannot be less than zero.
length The number of characters to return.
Cannot be less than zero or longer than the string.
If omitted all remaining characters will be returned.
In case anyone stumbles on this in the future here is the end result including the out put to a csv:
$ADGroupList = Get-ADGroup -Filter * | Select Name | Sort Name
"Group,UserName,Name" | Out-File -FilePath OutputUserGroups.csv -Append -Encoding ASCII
ForEach($Group in $ADGroupList)
{
$GroupName = $Group.Name.substring(7)
$members = Get-ADGroupMember -Identity "$GroupName" | Select Name, SAMAccountName | Sort Name
ForEach($member in $members)
{
($Group.name + "," + $member.SAMAccountName + "," + $member.Name) | Out-File -FilePath OutputUserGroups.csv -Append -Encoding ASCII
}
}
I am trying to write a PS function that can generate SamAccountName in a specific way, I have found another question here but it is not similar to what i'm trying to do.
I need the SamAccountName to be generated like this:
UNIT + initial_of_firstName + initial_of_lastName
for example employee Jane Doe in unit FLN could have FLNjd as SamAccountName, and the function should check if that ID is taken by another user and if true then SamAccountName should be:
UNIT + initial_of_firstName + first_two_initials_of_lastName such as FLNjdo
and if that is also taken then SamAccountName should be:
UNIT + first_two_initials_of_firstName + first_two_initials_of_lastName such as FLNjado
and if that is also taken then SamAccountName should be:
UNIT + first_two_initials_of_firstName + first_two_initials_of_lastName + 1 such as FLNjado1
and from here it starts adding numbers 2, 3, 4.... as long as the function finds that the ID exists.
I only managed to extract the initials needed:
$first_of_fname = ($_.Fname).Substring(0,1).ToLower()
$first_of_lname = ($_.Lname).Substring(0,1).ToLower()
$FirstandSecond_of_fname = ($_.Fname).Substring(0,2).ToLower()
$FirstandSecond_of_lname = ($_.Lname).Substring(0,2).ToLower()
I need now to know how to generate the the SamAccountName in the above form and order.
ANy help will be very much appreciated.
You can do the following:
Function Get-SamAccountName {
Param(
[Parameter(Position=0,ValueFromPipelineByPropertyName)]
$Unit,
[Parameter(Position=1,ValueFromPipelineByPropertyName)]
$First,
[Parameter(Position=2,ValueFromPipelineByPropertyName)]
$Last
)
$fcount = 1
$lcount = 1
$inc = 1
$Sam = "{0}{1}{2}" -f $unit,$first.substring(0,$fcount).ToLower(),$last.substring(0,$lcount++).ToLower()
while (Get-AdUser -Filter "SamAccountName -eq '$Sam'") {
if ($fcount -le 2) {
$Sam = "{0}{1}{2}" -f $unit,$first.substring(0,$fcount++).ToLower(),$last.substring(0,$lcount).ToLower()
} else {
$Sam = "{0}{1}{2}{3}" -f $unit,$first.substring(0,2).ToLower(),$last.substring(0,2).ToLower(),$inc++
}
}
$Sam
}
Usage Option 1: Using Named Parameters
Get-SamAccountName -Unit 'FLN' -First 'Sam' -Last 'Wise'
Usage Option 2: Using positional parameters (unnamed)
Get-SamAccountName 'FLN' 'Sam' 'Wise'
Usage Option 3: Having A Text List Of Units and Names
$users = Get-Content users.txt
$users
FLN,Sam,Wise
HRS,John,Doe
COM,Jane,Doe
$users | ConvertFrom-Csv -Header 'Unit','First','Last' | Foreach {
$_ | Get-SamAccountName
}
The pipe to ConvertFrom-Csv can be skipped here if your file is already a CSV with the proper headers.
Explanation:
The function returns a string that represents an available SamAccountName value.
$fcount is the first name character count that we want to retrieve. $lcount is the last name character count that we want to retrieve. $inc is the number we want to append to the username if needed.
$var++ notation increments the $var value by one after its line is executed. The timing of $fcount++ and $lcount++ allows us to retrieve (1,1),(1,2),(2,2) characters from (first,last) while minimizing code. $inc++ will increment after the current $inc value is retrieved.
-f is the string format operator.
The while loop will only execute if the current created SamAccountName value is found.
Note: The code shortening complexity may or may not be worth it. It does not gain much from a performance perspective, but I do feel code complexity on this level is merely relative.
To take a different approach, instead of multiple if statements, you can use the switch statement "backwards" to find out what is $null or not available, and fall back to the default switch which is the final while iteration loop incrementing the count.
#Get substrings
$first_of_fname = ($_.Fname).Substring(0,1).ToLower()
$first_of_lname = ($_.Lname).Substring(0,1).ToLower()
$FirstandSecond_of_fname = ($_.Fname).Substring(0,2).ToLower()
$FirstandSecond_of_lname = ($_.Lname).Substring(0,2).ToLower()
#create patters to match on
$pattern1 = $UNIT + $first_of_fname + $first_of_lname
$pattern2 = $UNIT + $first_of_fname + $FirstandSecond_of_lname
$pattern3 = $UNIT + $FirstandSecond_of_fname + $FirstandSecond_of_lname
#evaluate what is available
switch($null){
(Get-ADUser -Filter "samAccountName -like '$pattern1'"){
Write-Host "$pattern1 - Free"
break
}
(Get-ADUser -Filter "samAccountName -like '$pattern2'"){
Write-Host "$pattern2 - Free"
break
}
(Get-ADUser -Filter "samAccountName -like '$pattern3'"){
Write-Host "$pattern3 - Free"
break
}
default {
$cnt = 1
while (Get-ADUser -Filter "SamAccountName -like '$pattern3$cnt'") {
$cnt++
}
Write-Host "$pattern3$cnt - Free"
break
}
}
I export a CSV file with SAM account name and MembersOf groups.
When I import that same CSV file and try to use the fields of group. I would to split them. When they are split I'm able to use them in Add-ADPrincipalGroupMemberShip.
elseif ($Answer -eq 'n') {
Write-Warning "Verander de groepen in het CSV bestand: $ExCSV"
Get-ADUser -Identity $ExSam -Properties sAMAccountName, MemberOf |
Select-Object #{Name="User";expression={$ImSam}},
#{Name="Group";expression={$ImGroups}} |
Export-Csv "$ExCSV" -Delimiter "," -Force -NoTypeInformation -Encoding UTF8
[ValidateSet('OK')] $correct = Read-Host "Type 'OK' als je klaar bent"
if ($correct = "OK") {
$csv = Import-Csv "$ExCSV"
foreach ($user in $csv) {
$csv2 = $csv.Group -replace ' ', ', '
}
Write-Output $csv2
Add-ADPrincipalGroupMembership -Identity $ImSam -MemberOf "$csv2"
return
}
}
The output I write is:
The error message I receive:
My CSV:
It looks like you want to transform a string that contains a list of names into an array of those names.
It is indeed the -split operator that enables that, which allows sophisticated splitting of a string into an array of tokens using regular expressions.
In the simplest case, if the string to split ($csv.Group, in your case) is always in the form '<name1>, <name2>[, ...]', use:
PS> 'test4, test5' -split ', '
test4
test5
To make this more robust to guard against leading and trailing whitespace and missing spaces after the comma:
PS> ' test4, test5,test6 ' -split ' +|,' -ne ''
test4
test5
test6
Specifically, you need an array of names rather than a string list, because Add-ADPrincipalGroupMembership's -MemberOf parameter expects an array of group identities (names).
Assuming that you've modified $csv2 to contain an array, as described above, note that then you mustn't reference the variable enclosed in "..." ("$csv2"), as that would turn the value back into a (single) string.
Therefore, make the call as follows:
Add-ADPrincipalGroupMembership -Identity $ImSam -MemberOf $csv2 # !! No "..."