Using PowerShell to add the proxyAddresses attribute with specific format? - powershell

I need to add new email aliases to the users in the specific OU, with a specific format like:
user Alias / sAMAccountName = First Lastname
newAlias = FLastname#NewBrandX.com
User with Apostrophe
user Alias / sAMAccountName = John O'Dea
newAlias = JOdea#NewBrandX.com
User with either First or Lastname with spaces
user Alias / sAMAccountName = Peter Van Denberg
newAlias = PVanDenberg#NewBrandX.com
However, my script skill is quite limited, so I wonder if anyone can assist in the script modification below:
$newproxy = "#NewBrandX.com"
$userou = 'OU=Users,OU=Seattle,DC=usa,DC=contoso,DC=com'
$paramGetADUser = #{
Filter = 'Enable -eq $true'
SearchBase = $userou
Properties = 'SamAccountName', 'ProxyAddresses', 'givenName', 'Surname'
}
$users = Get-ADUser #paramGetADUser
Foreach ($user in $users)
{
$FirstName = $user.givenName.ToLower() -replace '\s', ''
$LastName = $user.surname.ToLower() -replace '\s', '' -replace "'", ''
Set-ADUser -Identity $user.samaccountname -Add #{ proxyAddresses = "smtp:" + $FirstName + $LastName + $newproxy }
}

It looks to me like you want a create a new proxy address in format
First character of GivenName
Surname without apostrophes or spaces
followed by "#NewBrandX.com".
Your code however takes the full GivenName.
To add to the ProxyAddresses array, you need to replace the entire [string[]] array
$newproxy = "#NewBrandX.com"
$userou = 'OU=Users,OU=Seattle,DC=usa,DC=contoso,DC=com'
$paramGetADUser = #{
Filter = 'Enable -eq $true'
SearchBase = $userou
Properties = 'ProxyAddresses' # SamAccountName, GivenName and Surname are returned by default
}
$users = Get-ADUser #paramGetADUser
foreach ($user in $users) {
$newAddress = ("smtp:{0}{1}$newproxy" -f $user.GivenName.Trim()[0], ($user.Surname -replace "[\s']+").ToLower())
$proxies = #($user.ProxyAddresses)
# test if this user already has this address in its ProxyAddresses attribute
if ($proxies -contains $newAddress) {
Write-Host "User $($user.Name) already has '$newAddress' as proxy address"
}
else {
Write-Host "Setting new proxy address '$newAddress' to user $($user.Name)"
$proxies += $newAddress
# proxyAddresses needs a **strongly typed** string array, that is why we cast the $proxies array with [string[]]
$user | Set-ADUser -Replace #{ proxyAddresses = [string[]]$proxies }
}
}

Related

PowerShell Get-ADUser - Using custom AD attibutes as IF condition

What I need to do is to export specific AD users and some of their properties to a CSV file. What I need to have there is some of the default properties like Name, SamAccountName, Enabled and some custom ones: businesscategory, extensionAttribute9 etc.
I'm struggling with my if - else statements, as they seem to not be comparing employeenumber to $null
$name = Read-Host -Prompt "Please enter the name for output file."
$filename = $name+".csv"
$domain = #('DOMAIN1','DOMAIN2','DOMAIN3','DOMAIN4')
$result = foreach ($item in $domain) {
Get-ADUser -server $item -Properties businesscategory, extensionAttribute4,
extensionAttribute9, extensionAttribute13, employeenumber, Enabled -ResultPageSize 100 -Filter *
if (($null -ne $_.employeenumber) -and ($_.employeenumber -notlike '*svc*')) {
Select-Object Name,
SamAccountName,
UserPrincipalName,
#{n="businesscategory"; e={$_.businesscategory -join ", "}},
#{n="extensionAttribute4";e={$_.extensionAttribute4 -join ", "}},
#{n="extensionAttribute9";e={$_.extensionAttribute9 -join ", "}},
#{n="extensionAttribute13";e={$_.extensionAttribute13 -join ", "}},
DistinguishedName, employeenumber, Enabled
} else { (...)
The above is part of my code where it should enter into first if. It does that, but it exports all accounts, whether employeenumber is present or not.
Another issue is that the exported CSV doesn't contain columns created from custom attributes, instead it shows some other properties that I did not ask for.
This used to work fine if I used Where-Object instead of if - else and checked the values like below:
Where-Object {
($_.SamAccountName -notlike '*proprietary*') -and
($_.UserPrincipalName -notlike '*proprietary*') -and
($_.SamAccountName -notlike '*mailbox*') -and (...)
Unfortunately I need to use if - else to make more complex comparisons and selections, but can't figure it out
The problem is in this line:
$result = foreach ($item in $domain) {
Get-ADUser -server $item -Properties ... # => not assigned to any variable
Then in this line:
if (($null -ne $_.employeenumber) -and ($_.employeenumber -notlike '*svc*')) {
Since $_ doesn't exist, you are comparing something like:
$null -ne $null -and $null -notlike '*svc*'
Which will always be $false. It's also worth mentioning that this is a foreach loop, different from ForEach-Object, the automatic variable $_ ($PSItem) doesn't mean anything here.
The next problem comes when using Select-Object as the beginning of the statement, there is no object being piped to it.
Select-Object Name, SamAccountName, UserPrincipalName, ...
In this case, the if condition could be removed completely with some LDAP Filtering:
# employee number is not `$null` AND employee number is not like `*svc*`
-LDAPFilter "(&(employeenumber=*)(!employeenumber=*svc*))"
The code would look like this:
$name = Read-Host -Prompt "Please enter the name for output file."
$filename = $name + ".csv" # Consider using `$HOME` here, or an absolute Path
$param = #{
LDAPFilter = "(&(employeenumber=*)(!employeenumber=*svc*))"
ResultPageSize = 100
Properties = #(
'businesscategory'
'extensionAttribute4'
'extensionAttribute9'
'extensionAttribute13'
'employeenumber'
)
}
'DOMAIN1','DOMAIN2','DOMAIN3','DOMAIN4' | ForEach-Object {
$param['Server'] = $_
foreach($user in Get-ADUser #param) {
[pscustomobject]#{
Name = $user.Name
SamAccountName = $user.SamAccountName
UserPrincipalName = $user.UserPrincipalName
BusinessCategory = $user.businesscategory -join ", "
extensionAttribute4 = $user.extensionAttribute4 -join ", "
extensionAttribute9 = $user.extensionAttribute9 -join ", "
extensionAttribute13 = $user.extensionAttribute13 -join ", "
DistinguishedName = $user.DistinguishedName
employeenumber = $user.employeenumber
Enabled = $user.Enabled
Domain = $_ # Adding the Domain of this user here
}
}
} | Export-Csv $filename -NoTypeInformation

Email Notification on Upon User Attributes update

im currently working on a automated User Attributes updating scrips, and currently that working fine, i am having issues however collecting the list of changes so they can be bundled inot an email notification for the administrator
here is what i have so far,
i want to form a list of all changes so that i can add that to an send-mailmessage to an admin for each user, but only when there is a change.
at the moment i only get whatever the latest thing changed is, not a list.
$csvFile = 'C:path.csv' # Enter a path to your import CSV file
$validUsernameFormat = '[^a-zA-Z_.]' # identifies anything that's _not_ a-z or underscore or .
$Mailpassword = ConvertTo-SecureString -string “4a1fd5e9f7e26f” -AsPlainText -Force
$MailCred = New-Object System.Management.Automation.PSCredential -argumentlist "38da1ca9daf082", $Mailpassword
$mailBody = $NewUserParams | out-string
# read the input csv and loop through
Import-Csv -Path $csvFile | ForEach-Object {
$firstName = $_.FirstName.Trim()
$surname = $_.Surname.Trim()
#$validUsernameFormat = "[^a-zA-Z_.]" # identifies anything that's _not_ a-z or underscore or .
$vaildusername = "($firstName'.'$surname)" -replace $validUsernameFormat, '' #removes anything that isn'tin $validUsernameFormat
$truncateifgreaterthanXchar = '(?<=^.{20}).*'
$username = $vaildusername -replace $truncateifgreaterthanXchar
$DefaultPassword = 'Pa$$w0rd'
$securePassword = ConvertTo-SecureString -String $DefaultPassword -AsPlainText -Force
# test if a user with that name already exists
$user = Get-ADUser -Filter "SamAccountName -eq '$username'" -ErrorAction SilentlyContinue
if ($user) {
$CurrentAttributes = Get-ADUser -Identity $username -Properties *
# You don't need this line because you are already declaring the variable in the next one
# [psobject]$CorrectAttributes
$CorrectAttributes = #{
SamAccountName = $username
Name = "$firstname $surname"
DisplayName = "$firstname $surname"
UserPrincipalName = "$username#domain.com"
GivenName = $firstname
Surname = $surname
Path = "CN=Users,DC=domain,DC=com" #change to switch based of Users Branch
City = $_.City
Country = $_.Country #NOTE: This Feild must be the 2 digit Country Code, NOT the String Name of athe Country.
department = $_.OrgDepartmentName
Employeeid = $_.EmployeeId
mobile = $_.Mobile
Manager = $_.Manager
Office = $_.Branch
postalCode = $_.PostalCode
POBox = $_.PostOfficeBox
scriptPath = $_.scriptPath
Street = $_.StreetName
Title = $_.Title
}
[System.Collections.ArrayList]$MailAttributesList = #()
foreach ($attribute in $CorrectAttributes.Keys) {
if ($currentAttributes.$attribute -ne $correctAttributes.$attribute) {
$params = #{Identity = $username; $attribute = $correctAttributes.$attribute }
$mailUpdatedAttribute = $CorrectAttributes.$attribute | Out-String
Set-ADUser #params
[void]$MailAttributesList.add("$attribute")
}
else {
Write-Host "$username '$attribute' is correct"
}
$MailAttributesList
$MailAttributesList = #()
}
}
Taken from the part where you test if the user exists and then check which attributes need to be updated, I'd do this:
# personally, I hate using -Properties *
# better to list the properties you are trying to update:
$userAttribs = 'SamAccountName','Name','DisplayName','UserPrincipalName',
'GivenName','Surname','Path','City','Country','Department',
'EmployeeId','MobilePhone','Manager','Office','PostalCode',
'POBox','ScriptPath','Street','Title'
# test if a user with that name already exists
$user = Get-ADUser -Filter "SamAccountName -eq '$username'" -Properties $userAttribs -ErrorAction SilentlyContinue
if ($user) {
# always https://learn.microsoft.com/en-us/powershell/module/addsadministration/set-aduser
# for the correct parameter names!
$CorrectAttributes = #{
SamAccountName = $username
Name = "$firstname $surname"
DisplayName = "$firstname $surname"
UserPrincipalName = "$username#domain.com"
GivenName = $firstname
Surname = $surname
Path = "CN=Users,DC=domain,DC=com" #change to switch based of Users Branch
City = $_.City
Country = $_.Country #NOTE: This Feild must be the 2 digit Country Code, NOT the String Name of athe Country.
Department = $_.OrgDepartmentName
EmployeeId = $_.EmployeeId
MobilePhone = $_.Mobile
Manager = $_.Manager # must be a DistinguishedName, GUID, SID or SamAccountName
Office = $_.Branch
PostalCode = $_.PostalCode
POBox = $_.PostOfficeBox
ScriptPath = $_.scriptPath
Street = $_.StreetName
Title = $_.Title
}
# create a new empty hashtable to store the properties that need updating
$UpdateAttribs = #{}
# capture all changes on behalf of the email body
$changes = foreach ($prop in $CorrectAttributes.Keys) {
if ($user.$prop -ne $CorrectAttributes[$prop]) {
# output an object with the property to be changed for the email
[PsCustomObject]#{
Property = $prop
OldValue = $user.$prop
NewValue = $CorrectAttributes[$prop]
}
# add the value to update in the the $UpdateAttribs hashtable
$UpdateAttribs[$prop] = $CorrectAttributes[$prop]
}
}
if ($changes) { # or do: if ($UpdateAttribs.Count)
# perform the changes
$user | Set-ADUser #UpdateAttribs
# send the email to the admin
$mailParams = #{
To = 'admin#yourcompany.com'
From = 'attributeChanger#yourcompany.com'
Subject = 'User attributes changed'
Body = "Changed AD attributes for user $username`r`n" + ($changes | Format-Table -AutoSize | Out-String)
SmtpServer = 'smtp.yourcompany.com'
UseSsl = $true
Credential = $MailCred
# maybe more parameters go here..
}
Send-MailMessage #mailParams
}
else {
Write-Host "All attributes for user '$username' are correct"
}
}
else {
Write-Warning "User $username does not exist"
}
P.S. The code could be shortened if you have your input CSV file use the correct headers, so they match the property names for Set-ADUser exactly..

How do I get my script to use full names to find AD account?

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

Manipulating multiproperty AD attributes (ProxyAddresses)

I have a list of users with several values in their ProxyAddresses attribute e.g.
SMTP:JohnSmith#domain1.com
smtp:jsmith#domain2.com
smtp:ukCC10s#domain2.com
smtp:smith.john#domain3.com
and many other unknown ones.
What I want to do is:
Convert all existing addresses that begin with smtp/SMTP to lowercase
Add/replace the one that conforms to the SMTP:firstname.surname#Domain2.com standard (to make it the primary)
I've not got very far, running this just strips all proxyaddresses out and adds the one specified:
$userou = "OU=test2,OU=Test,OU=Users,DC=Corp,DC=Local"
$users = Get-ADUser -Filter * -SearchBase $userou -Properties SamAccountName, ProxyAddresses
foreach ($user in $users) {
Get-ADUser $user | Set-ADUser -Replace #{'ProxyAddresses'="SMTP:blah#blah.com"}
}
How do I enumerate through each value in a multivalue attribute?
The below code should do what you need.
It updates the ProxyAddresses multivalue property so that all 'smtp/SMTP' addresses will become lowercase and the new Primary emailaddress is computed and inserted in the list.
I have added a small helper function to replace diacritic characters that may appear in the users first or last name because especially Outlook 365 does not handle these characters very well.
$userou = "OU=test2,OU=Test,OU=Users,DC=Corp,DC=Local"
$users = Get-ADUser -Filter * -SearchBase $userou -Properties SamAccountName, ProxyAddresses, EmailAddress
function Replace-Diacritics {
# helper function to replace characters in email addresses that especially Outlook365 does not like..
Param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[string] $EmailAdress
)
$chars = #()
$normalized = $EmailAdress.Normalize( [Text.NormalizationForm]::FormD )
$normalized.ToCharArray() | ForEach-Object {
if( [Globalization.CharUnicodeInfo]::GetUnicodeCategory($_) -ne [Globalization.UnicodeCategory]::NonSpacingMark) {
$chars += $_
}
}
$chars -join ''
}
foreach ($user in $users) {
# create the new primary emailaddress
# remove invalid characters and replace diacritics ("Frédérique.Étrangér#Domain2.com" --> "frederique.etranger#domain2.com")
$newPrimary = ($("{0}.{1}#Domain2.com" -f $user.GivenName, $user.Surname) -replace '[\s()<>,;:''"{}/[\]\\]+', '').ToLower()
$newPrimary = "SMTP:" + (Replace-Diacritics ($newPrimary -replace '\.+', '.'))
# get all email addresses and convert them to lowercase. At the same time dedupe this array.
# this will also replace 'SMTP:' of the former Primary email address to become an alias ('smtp:')
$emailAliases = #($user.ProxyAddresses | Where-Object { $_ -match '^smtp:.*' -and $_ -ne $newPrimary } |
ForEach-Object { $_.ToLower() } |
Sort-Object -Unique)
# read all other existing stuff
$otherAddresses = #($user.ProxyAddresses | Where-Object { $_ -notmatch '^smtp:.*' })
# now merge all addresses into one array, the Primary email address on top for easier reading in ADUC
$newProxies = (#($newPrimary) + $emailAliases) + $otherAddresses
# finally replace the users ProxyAddresses property. I like:
$user | Set-ADUser -Clear ProxyAddresses
$user | Set-ADUser -Add #{'proxyAddresses'=$newProxies }
# but you could also do
# $user | Set-ADUser -Replace #{'proxyAddresses' = $newProxies}
# finally, put the new primary email address in the users 'mail' property
$user | Set-ADUser -EmailAddress $($newPrimary -replace 'SMTP:', '')
}
Untested, because I don't have an AD at my disposal here, but I'd expect something like this to work, since multivalued attributes should be returned as collections.
$addr = $user.ProxyAddresses -creplace '^SMTP:', 'smtp:'
$addr += 'SMTP:blah#blah.com'
$user | Set-ADUser -Replace #{ 'ProxyAddresses' = $addr }
To assign the correct new primary address to each user you could map the addresses to usernames in a hashtable and then do a lookup rather than assign the new primary address as a static value:
$addr += $newPrimaryAddress[$user.SamAccountName]

How to retrieve a users UPN suffix as a string

I'm trying to get a specific AD User and change their UPN, but not their UPN suffix.
As you can see at the moment I have to manually enter their current UPN suffix which is a bit pointless since you have to go into AD to find that anyway, is there some string like $_.UPNSuffix that will call the user's current Suffix?
$container = "OU=MyOU,DC=MyDomain,DC=local"
$Filter = Read-Host -Prompt "Enter users Username/P-number"
$UPNSuffix = Read-Host -Prompt "Enter users current UPN Suffix"
$users = Get-ADUser -Filter "UserPrincipalName -like '$Filter*'" -SearchBase $container
Foreach ($user in $users)
{
$newFQDN = $user.GivenName + "." + $user.Surname
$NewDN = $user.GivenName + " " + $user.Surname
Set-ADUser -Identity $user -UserPrincipalName $newFQDN#$UPNSuffix -SamAccountName $newFQDN
Write-Host "User's UPN is now $newFQDN#$UPNSuffix"
}
You can get the UPN components by splitting on the # sign.
I would be doing something along the lines of:
$container = "OU=MyOU,DC=MyDomain,DC=local"
$Filter = Read-Host -Prompt "Enter users Username/P-number"
$users = Get-ADUser -Filter "UserPrincipalName -like '$Filter#*'" -SearchBase $container
Foreach ($user in $users)
{
$null, $UPNSuffix = $user.UserPrincipalName -split '#' # Dump the first part, store the 2nd
$newFQDN = $user.GivenName + "." + $user.Surname
$NewDN = $user.GivenName + " " + $user.Surname
Set-ADUser -Identity $user -UserPrincipalName "$newFQDN#$UPNSuffix" -SamAccountName $newFQDN
Write-Host "User's UPN is now $newFQDN#$UPNSuffix"
}
From a quick Google it doesn't seem that there is a dedicated field for the Suffix, but I figure you could get the UserPrincipalName property and then just split on the # and grab the second element of the split:
$UPN = (Get-ADUser -Identity $user -Property UserPrincipalName).UserPrincipalName
If ($UPN) {
$UPNSuffix = ($UPN -Split '#')[1]
} Else {
Write-Warning "Failed to get UserPrincipalName for $User"
}
Note: this is untested code.
It's possible to get the UPN suffixes from the uPNSuffixes field in the Partitions object who's located at :
CN=Partitions,CN=Configuration,DC=xxxxx,DC=com
Thanks from this post who provide an example in C# :
List all UPN Suffixes from Active Directory
I don't know how to implement that in powershell but in PHP, it's pretty simple :
ldap_read($ldapConnection, "CN=Partitions,CN=Configuration,DC=xxxxx,DC=com", "(objectclass=*)", array("*");
Maybe with Get-UserPrincipalNamesSuffix :
https://learn.microsoft.com/en-us/powershell/module/exchange/active-directory/get-userprincipalnamessuffix?view=exchange-ps
Hope this helps someone !