Whitespace & $null variable handling in PowerShell? - powershell

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
}

Related

Adding a string to a extensionattribute in Powershell

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}

Powershell - How to remove trailing spaces from a list of groups a user is in

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.

Foreach is not processing each item, tries to process all as one line

I am building a script to pull the mac addresses of a computer, and convert them to GUIDs that are then queried to AD to find a match and retrieve the hostname associate with that guid.
Everything I've read about "Foreach" suggests that the code I am using "SHOULD" work.
$NIC = Get-WMIObject Win32_NetworkAdapterConfiguration | select MacAddress
$NICMacs = $NIC.MacAddress
Foreach ($NICMac in $NICMacs)
{
$MacString = $NICMacs -replace ":", ""
$MactoGUID = "00000000000000000000" + $MacString
$MactoGUID = $MactoGUID -replace " ", ''
$NBG = [GUID]$MactoGUID
$CompDetails = Get-ADComputer -Filter 'netbootGUID -like $NBG' -Properties netBootGUID -Server our.AD.server.ca -Credential $sessionKey
This should process each mac address found, stripping away the: characters and prepending 20 '0s' then convert to GUID format and query AD for a match.
Instead, it takes all the mac addresses found, concatenates them to one line and tries to process with all those numbers. Of course AD rejects it as an incorrect GUID.
If I use the same code with only 1 mac address, it is correctly formatted.
Some of your steps can be combined and thus save intermediate vars using
Select-Object -ExpandProperty MacAddress
.PadLeft() method
As #RobV already pointed out in his comment usage of the wrong var caused the malfunction.
Foreach ($NICMac in (Get-WMIObject Win32_NetworkAdapterConfiguration |
Where-Object MacAddress -ne '' |
Select-Object -ExpandProperty MacAddress) ) {
$NBG = [GUID]($NICMac -replace ':').PadLeft(32,'0')
$CompDetails = Get-ADComputer -Filter 'netbootGUID -like $NBG' `
-Properties netBootGUID -Server our.AD.server.ca -Credential $sessionKey
}
I don't consider usage of a line continuation character esoteric,
here it allows keeping the overview.
I agree that Natural Line Continuations in PowerShell are preferable.
A couple items here:
In your code, in line 7, you reference $NICMacs, but you should be referencing $NICMac since the foreach is running through the logic separately, not as a batch.
You could simplify at least the top two lines into a single line without a pipe.
$NICMacs = (Get-WMIObject Win32_NetworkAdapterConfiguration).MacAddress
Foreach ($NICMac in $NICMacs)
{
$MacString = $NICMac -replace ":", ""
$MactoGUID = "00000000000000000000" + $MacString
[GUID]$NBG = $MactoGUID -replace " ", ''
$CompDetails = Get-ADComputer -Filter 'netbootGUID -like $NBG' -Properties netBootGUID -Server our.AD.server.ca -Credential $sessionKey
Edit: in my suggested code, I think making $NBG should work with the [GUID] tag in front but I've never tried it

Results from Powershell to Comma Separated String

I have the below script that I run, how can I get the output to be placed into a comma separated string?
Get-ADGroup -filter * -properties GroupCategory | ft objectguid, samaccountname
Current Output
objectguid samaccountname
---------- --------------
f5b71a40-9405-4874-b9b0-b35e45346f63 WinRMRemoteWMIUsers__
95a99aa1-a771-4f86-bdce-7a6a3f00057a Administrators
6a6ad877-180c-462b-9672-bfba20200cda Users
038b03cf-1171-4546-b0af-a7654823e289 Guests
ed3995ae-6f77-492f-bcb5-bc197fa24996 Print Operators
b1f8301a-78e1-47ab-9e51-8e55525acadf Backup Operators
3d23c4c0-71d9-4e08-a8ee-a9692a6ed61f Replicator
d86e5619-5893-41b7-b3a8-eda4c2725562 Remote Desktop Users
473592e4-71c8-458f-b7fc-b474809933ff Network Configuration Operators
c433a049-f9e5-404a-9940-5cf1f8ce4d82 Performance Monitor Users
1ce2daf3-5d97-4493-9ec1-c84a49326113 Performance Log Users
24b06a4c-f32b-4081-a002-6f1851f98256 Distributed COM Users
Desired Output
f5b71a40-9405-4874-b9b0-b35e45346f63,WinRMRemoteWMIUsers__,95a99aa1-a771-4f86-bdce-7a6a3f00057a,Administrators,6a6ad877-180c-462b-9672-bfba20200cda,Users,038b03cf-1171-4546-b0af-a7654823e289,Guests,ed3995ae-6f77-492f-bcb5-bc197fa24996,Print Operators,b1f8301a-78e1-47ab-9e51-8e55525acadf,Backup Operators,3d23c4c0-71d9-4e08-a8ee-a9692a6ed61f,Replicator,d86e5619-5893-41b7-b3a8-eda4c2725562,Remote Desktop Users,473592e4-71c8-458f-b7fc-b474809933ff,Network Configuration Operators,c433a049-f9e5-404a-9940-5cf1f8ce4d82,Performance Monitor Users,1ce2daf3-5d97-4493-9ec1-c84a49326113,Performance Log Users,24b06a4c-f32b-4081-a002-6f1851f98256,Distributed COM Users
I am passing this into a string and parsing the data remotely using JavaScript.
That's fairly easy and consists of two steps: First separating the GUID and account name with a comma:
$items = Get-ADGroup -filter * -properties GroupCategory |
select objectguid,samaccountname
$combined = $items |
ForEach-Object { $_.objectguid + ',' + $_.samaccountname }
and then joining all those strings with a comma into a single string:
$result = $combined -join ','
We could also do it differently: First decompose the objects into simply two strings:
$separated = $items | % { $_.objectguid; $_.samaccountname }
and then joining all those with a comma:
$result = $separated -join ','
or take advantage of the $OFS variable which specifies with what separator arrays are converted to strings. Same start, we need a single array with everything that should be joined with a comma, and then we set the $OFS variable and convert the array to a string:
$OFS = ','
$result = [string] $separated

Add "_" before all SMTP addresses in Exchange

I'm looking script that help me to add "_" before all SMTP addresses in Exchange.
Let's say we have a user John Doe. John has 3 different SMTP addresses:
john.doe#contoso.com
jdoe#domain.com,
john#contoso.com
I'd like to change disable all features in Exchange (like OWA, ActiveSync and so on), hide his account in GAL and set all his addresses to:
_john.doe#contoso.com
_jdoe#domain.com,
_john#contoso.com
I can do it for primary SMTP but I can't for rest of them :(
For Now I have try such solution:
Set-Mailbox $sam -HiddenFromAddressListsEnabled $true -DomainController $dmc
Set-CasMailbox $sam -OWAEnabled $false -ActiveSyncEnabled $false -MAPIEnabled $false -PopEnabled $false -ImapEnabled $false -DomainController $dmc
mbx = Get-Mailbox $sam -DomainController $dmc | select -expand EmailAddresses | %{$_.SmtpAddress}
foreach ($M in $mbx)
{
[string]$email += "'smtp:_"+$M+"',"
}
but it doesn't works for me. I'm quite fresh in PS so could you please help me.
Multiple issues:
The main problem is that you're generating a list of the new addresses, but you're not modifying the mailbox objects. Just running a foreach loop against results queried from a mailbox isn't going to modify that object. You need to use Set-Mailbox to actually set the new addresses:
Set-Mailbox -EmailAddresses <new_addresses>
The way you're creating the string, it will have a trailing comma. If you want to create a comma-separated string from strings generated by a loop, do it this way:
$email = $(foreach ($m in $mbx) {
"'smtp:_" + $m + "'"
}) -join ','
The loop returns an array of the strings generated by each iteration, and the -join ',' joins them into a comma-separated string.
That said, you actually don't want to join them into a comma-separated string, because the -EmailAddresses parameter of Set-Mailbox takes an array argument. So, you just want to use the return value of the loop as the argument.
Adding the smtp: prefix is unnecessary because that's the default.
On the other hand, if you're only modifying SMTP addresses, you do want to use Where-Object to filter the contents of the EmailAddresses property for SMTP addresses:
?{$_.Prefix.ToString() -eq 'smtp'}
Putting it all together:
foreach ($mbx in (Get-Mailbox <whatever>)) {
$modified_addresses = $mbx.EmailAddresses `
| ?{$_.Prefix.ToString() -eq 'smtp'} `
| select -ExpandProperty EmailAddresses `
| %{"_$_"}
Set-Mailbox $mbx -EmailAddresses $modified_addresses
}