I've been banging my head against the wall for the past few hours, I'm sure it's because I don't quite understand something about how #{} and $_ work.
First the code:
Get-ADUser username -Properties mail | Set-ADUser -replace #{"proxyaddresses"="SMTP:"+$_.mail}
As you can see I'm trying to update the proxyaddresses fields with the user's email address.
Instead only the string is pulled:(output: proxyaddresses : {SMTP:}) and the pipeline is ignored, I'm assuming it's because it's empty for some reason, but it's not clear to me why.
I've tried variations such as "proxyaddresses="SMTP:$($_.mail)" I tried using default properties that are always sent with Get-ADUser such as UserPrincipalName
I know that something like this is possible because of this http://www.itprotoday.com/management-mobility/more-flexible-active-directory-one-liner and mutiple answers on SO using some variation of the linked example.
When I assign a variable to SMTP:$_.mail and then use that in the field instead like so:
Get-ADUser username -Properties mail | %{ $smtp = "SMTP:"+$_.mail
$_|Set-ADUser -replace #{"proxyAddresses"=$smtp}
This works (output: proxyaddresses :{SMTP:emailaddress#domain.com}). If I leave out the string like so: #{"proxyaddresses"=$_.mail}
I get the following error:
Set-ADUser : Cannot bind parameter 'Replace' to the target. Exception setting "Replace": "Object reference not set to an instance of an object."
At line:1 char:58
I'm not sure what this means.
I'd like some variation of my initial idea to work, but I'll settle for the workaround using an extra variable if there's no other way.
EDIT: There seems to be some confusion about what I'm asking, so I'll clarify:
Is there a way to use the pipeline variable $_ without a script block inside a hashtable, (inside a script block requires double piping like #TheIncorrigible1 suggested in his first answer.)?
EDIT: Based on this it seems this should not be having any issues.
You need to use ForEach-Object to access the pipeline in the way you're trying, otherwise it doesn't know what your pipeline object ($_) is:
Get-ADUser -Identity username -Properties mail |
ForEach-Object {
$_ | Set-ADUser -Replace #{ 'proxyaddresses' = 'SMTP:' + $_.mail }
}
Or the -PipelineVariable common parameter which explicitly assigns $_ to a variable:
Get-ADUser -Identity username -Properties mail -PipelineVariable user |
Set-ADUser -Replace #{ ProxyAddresses = "SMTP:$($user.mail)" }
ProxyAddresses is an array where the Primary Email address is set like SMTP:primary#example.com but there can and will be other elements there too like alias email addresses (that have the lowercase smtp: prefix), SIP: addresses etc.
NEVER try to simply overwrite whatever is already there by a single string found in the mail attribute of the user object, but merge them with the ones you want to add. Selectively replace the ones you want to be changed and build an array of valid addresses.
Basically you do
$oldErrorAction = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
$user = Get-AdUser -Identity $SAMAccountName -Properties mail
$primaryEmailAddress = $user.mail
$externalAddress = "smtp:<WHATEVER ALIAS YOU WOULD LIKE FOR THE USER">
$mailProxies = #("SMTP:$primaryEmailAddress", "smtp:$externalAddress")
# add more to this array if need be
$newProxies = #{'ProxyAddresses' = $mailProxies}
try {
$user | Set-ADUser -Clear ProxyAddresses
$user | Set-ADUser -Add $newProxies
}
catch {
Write-Warning "Could not set ProxyAddresses: $($_.Exception.Message)"
}
$ErrorActionPreference = $oldErrorAction
You may want to try set the variable first and then call on it in the script.
something like this,
$user = Get-ADUser username -Properties mail | select-object mail
Then write your script and call on the variable you may need to use foreach with a if statement to get it to function the way you want.
Related
I'm trying to get the password expiration date in active directory using powershell for users with hyphenated names (IE firstname.last-name) and on the hyphenated names it gives an invalid cmdlet error. How do I query the hyphenated names?
The current command I have is
net user $username /DOMAIN | find "Password expires"
Maybe use the ActiveDirectory module instead of the net commands:
$MaxPwdAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
Get-ADUser -Filter { Name -like "*-*" } -Properties 'PasswordLastSet', 'DisplayName' |
Select-Object Name,DisplayName,
#{ Name = 'PasswordExpires'; Expression = { $_.PasswordLastSet.AddDays( $MaxPwdAge ) } }
If needed You can change the filter to look at DisplayName instead -Filter { DisplayName -like "*-*" }
You may need to adjust the properties you're retrieving depending on what you want to include in the output. This is just an example but it works, and can be used to plot a path forward. It does seem like you have to calculate the expiration date. But I can work on that and see if there's a better way.
If you want to Query for a specific user:
Get-ADUser Name-Name -Properties 'PasswordLastSet',DisplayName |
Select-Object Name,DisplayName,
#{ Name = 'PasswordExpires'; Expression = { $_.PasswordLastSet.AddDays( $MaxPwdAge ) } }
This assumes the Hyphenated name is a samAccountName. If you need to search by DisplayName you'll have to resort back to filter, even if you are looking for only the one user.
Get-ADUser -Filter { DisplayName -eq "Name-Name" } -Properties 'PasswordLastSet',DisplayName |
Select-Object Name,DisplayName,
#{ Name = 'PasswordExpires'; Expression = { $_.PasswordLastSet.AddDays( $MaxPwdAge ) } }
Note: That you have to change the "Name-Name". Also in the last example I changed to using the -eq operator instead of -like. Obviously this assumes you know exactly what you're looking for. Though you can use -Like with DisplayName or even the surName attribute if you like.
I'm unable to pass the results of Get-ADOrganizationalUnit into a function in a script.
I am storing the results in a variable that is being used to add the canonical name of the OUs returned by the cmdlet, to a dropdown list on a form.
I'm then attempting to use the same variable in a function that when called, will determine the distinguished of the OU based on the canonical name selected.
Since the variable is being set when the form loads so that the drop down in question will be populated with the various OUs, I added in a write-output $myVar just to make sure nothing strange was going on prior to being passed into the function. I've attempted to make $myVar global with $Global:varName and I've attempted to pass in the variable when calling the function: myFunction $myVar $myVar1. If I then use write-output $myVar within the function, there is no output, but I can use write-host $myVar and it will return a string of just the distinguishedNames of all the OUs in $myVar.
I tested this in the shell directly as well and I didn't have any issues with associating the canonical name back to the DN of the OU, but don't know what I'm doing incorrectly to cause it to not work when used in a script.
I'm using this to get the OU data for the dropdown list and function:
$userOUs = Get-ADOrganizationalUnit -SearchBase $ouRoot -Filter * -Properties CanonicalName | Where-Object {$_.Name -like '*user*'}
Note: The dropdown list is populated successfully using $userOUs.
I call the function with a button:
$myBtn.Add_Click({ myFunction $userID $userOUs})
The function I'm trying to pass it to:
function myFunction($userID, $userOUs) {
Write-Output $userOUs #returns nothing
Write-Host $userOUs #returns the string containing all of the OUs' distinguished names
$selectedOU = $OUList.SelectedItem
$targetOUCanonicalName = "$domainPrefix$selectedOU" #I remove the domain name from the canonical name for display in the dropdown but add it back here
$targetOu = $userOUs | Where-Object {$_.CanonicalName -eq $targetCanonicalName} | select -ExpandProperty distinguishedName
Get-ADUser -Identity $userID | Move-ADObject -TargetPath $targetOU
}
Ultimately, the goal is to be able to use the $userOus variable to determine the DN of the OU based on the selection made in a drop down of OU canonical names. I'd like to try and keep this more dynamic without having to define everything in a switch statement.
I expect once I get a bump in the right direction regarding why the variable is not passing into the function the way that I need it to, I'll be able to accomplish that.
Edit: I don't intend to use write-host or write-output since the script will have a form, I'm just using that to try and figure out what's going on.
I've tried your version and it seems to work just fine:
function myFunction($userID, $userOUs) {
Write-Output $userOUs #returns nothing
Write-Host $userOUs #returns the string containing all of the OUs' distinguished names
#$selectedOU = $OUList.SelectedItem
#$targetOUCanonicalName = "$domainPrefix$selectedOU" #I remove the domain name from the canonical name for display in the dropdown but add it back here
#$targetOu = $userOUs | Where-Object {$_.CanonicalName -eq $targetCanonicalName} | select -ExpandProperty distinguishedName
# Get-ADUser -Identity $userID | Move-ADObject -TargetPath $targetOU
}
$userOUs = Get-ADOrganizationalUnit -Filter * -Properties CanonicalName | Where-Object {$_.Name -like '*user*' }
$userOUs
myFunction -userID $UserID -userOUs $userOUs
$UserOus display data for me, when I execute your function as above I also get output. The only thing that comes to mind is that the issue is somewhere else.
I'm looking to correct a small issue in AD where the field I mentioned has a space in the value.
For example:
legacyExchangeDN : /o= First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=6131a3a42ca946b98cc146345cfd0c2e-Ron E
Should look like this:
legacyExchangeDN : /o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=6131a3a42ca946b98cc146345cfd0c2e-Ron E
This is affecting multiple users and I can manually update the value in the AD Attribute Editor, but I was hoping to be able to automate this.
Here's what I have so far:
#List all users to check existing values
$ADUserList = Get-ADUser -Filter* -Properties * | fl name, EmailAddress, legacyExchangeDN
#Replace value with space with value without space
Set-ADUser reyer -Replace #{legacyExchangeDN="/o= ","/o="}
The second line produces the following error:
Set-ADUser : Multiple values were specified for an attribute that can have only one value
At line:1 char:1
+ Set-ADUser reyer -Replace #{legacyExchangeDN="/o= ","/o="}
It feels like I'm missing something easy here, but I cannot find it. I am testing this with one user. I would like to replace the value against all remaining users in one script once tested. If I could target just the users that match the criteria of having a space in the field that would surely be helpful for either storing it or piping that over.
My final result would look like:
Target users with a space(s) present in the text value of the legacyExchangeDN → /o=
Replace just the /o= with /o= while preserving the remainder of the text value → /o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=6131a3a42ca946b98cc146345cfd0c2e-Ron E
$Users = Get-ADUser -Filter { legacyExchangeDN -like '*/o= *' } -Properties #('name','EmailAddress','legacyExchangeDN')
ForEach ($User in $Users)
{
$Replace = $User.legacyExchangeDN -replace '/o=\s','/o='
Set-ADUser -Identity $User -Replace #{ legacyExchangeDN = $Replace }
Write-Host ('Updated "{0}" ({1}) to "{2}"' -f
#($User.name,$User.EmailAddress,$Replace))
}
This will be faster as well. The mantra is: Filter Left, Format Right. Set-ADUser takes an ADUser object. -Replace takes a [HashTable] where you're assigning a new value to the named property (as it appears in LDAP).
(update: removed pipeline for foreach)
To implement logging, replace Write-Host with:
'Updated "{0}" ({1}) to "{2}"' -f #($User.name,$User.EmailAddress,$Replace) |
Out-File -FilePath "$env:UserProfile\ADChanges.txt" -Append
I'm beginner in powershell and I need your help.
I need to compare the department attribute from the AD containing some text amd replacing by another value.
But it doesn't work. Do I made a mistake below? Cheers
//Find the user and save the user in the variable
$member = get-Aduser -f {GivenName -eq 'Jack'}
//check if the Departement field match with "Dep20 "
if($member.department -eq "Dep20")
{
//Set "Dep21" in department field
$member.Department = 'Dep21';
set-AdUser -f {GivenName -eq $member.givenName} -departement $member.Department;
}
Some issues with your initial script
First
Get-AdUser won't give you the property Department by default.
You could have confirmed this by actually looking at the output of your Get-AdUser statement. You do need to add it to the list of properties explicitely.
get-Aduser -f {GivenName -eq 'Jack'} -Properties Department
Also, you did make a mistake in the Set-AdUser cmdlet. The parameter name you have written, at the time of my answer, is -departement. Instead, you need to set -department.
Finally, Get-AdUser could return multiple users (or none).
Therefore, you need to account for that by checking how many $member were returned or to do a foreach to process none (if 0) or all of them the same.
At least, that part is subjective to what you need but here would be my approach.
$member = get-Aduser -Filter 'GivenName -like "Jack*"' -Properties Department
$member | foreach {
if ($member.Department -eq 'Dep20')
{
$_.Department = 'Dep21'
set-AdUser $_ -Department $_.Department;
}
}
Edit:
I modified my answer to switch the Filter parameter from a scriptblock (as your question) for a string filter as per mklement0 comment.
Because the Filter parameter is actually a string, giving it a script block will create problems on multiple occasions and you are better restrict yourself to the string type for this parameter.
See this for a more detailed explanation on the matter.
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.