PowerShell pipe ExpandProperty to another Select-Object - powershell

I want to get the aliases for all email recipients in my O365 tenant.
If I were doing this one traditional Exchange, the easiest is to Get-Recipient and pull out the SMTPAddress property using Exchange Manangement Console. (Note the Select piped into another Select; works like a champ.)
Get-Recipient | Select Name,SamAccountName -ExpandProperty EmailAddresses | Select Name,SamAccountName,SmtpAddress
The issue is when I try to do the same thing in EXO, the "EmailAddresses" property is output as a flat string and so when I try to pipe it to a 2nd select, the only Property is the Length, not the actual value of the string.
The following kind of works, but I feel like there has to be a better way?
Get-EXORecipient | Select Name -ExpandProperty EmailAddresses | % {Write-Output "$($_.Name),$($_)"}

What -ExpandProperty EmailAddresses expands to are strings, as you state.
The additional properties that these strings are decorated with if you also pass (positionally implied) -Property arguments, such as Name in your example, are present, but will typically not surface unless you explicitly access them on the now-decorated output strings.
If you want output objects that contain each email address as a property, more work is needed.
Get-EXORecipient | ForEach-Object {
foreach ($emailAddress in $_.EmailAddresses) {
[pscustomobject] #{
EmailAddress = $emailAddress
Name = $_.Name
# ... add further properties of interest here
}
}
}

Related

Powershell: Extract Exchange property and Display in array

I need to get all Exchange users into an Array, with a column for their SIP address and another column for all SMTP addresses (as seen in the EmailAddresses field).
So for now I am trying this against a single user and should that work out I can use "-ResultSize Unlimited" and do all. Although for now:
$list_UsersExtCloud =
Get-Recipient e12367 |
Select-Object Alias, EmailAddresses, #{Name = 'SIP'; Expression = { $_.EmailAddresses | Where-Object { $_ -like "*sip*" } -join ',' } }
I could extract the SIP by iterating using a FOR LOOP through each user properties with EmailAddresses | where {$_ -like "*sip*"}, but I'm trying to extract it in the EXPRESSION statement itself.
How can I get this to work?
You are on the right track, however you are trying to join the objects without referencing the sub-properties you are actually interested in. Try something like:
$list_UsersExtCloud =
Get-Recipient e12367 |
Select-Object Alias, EmailAddresses, #{ Name = 'SIP'; Expression = { ($_.EmailAddresses | Where-Object { $_.Prefix -eq "SIP" }).ProxyAddressString -join ',' } }
I changed the Where clause to use the Prefix. I grouped the address objects then unrolled the ProxyAddressString from them. This give good input to the -join. You can also do this with the AddressString property, I wasn't sure which you were really interested in.
Note: If you let this output without assigning to a variable the EmailAddresses property will likely bump the SIP property off the screen. I found that a little confusing while working this out, thought I'd mention it.
Warning: Get-Recipient has a known issue. When making very large queries it won't page properly and will return an LDAP cookie error. So this may blow up if you have enough recipients. An alternative is to query for mail enabled objects using Get-ADObject then customize the object similarly using Select-Object.

Powershell - add extra column called RoleUserA* inside CSV file

I want to add an extra, empty column called RoleUserA* to a CSV file, as attempted below. RoleUserA* is not an AD Attribute.
Get-ADUser -Filter {Enabled -eq $true} -Properties * |
Select givenName, sn, displayname, samaccountname, RoleUserA*, title |
Export-Csv -Path "c:\tmp\users.csv" -NoTypeInformation -Encoding UTF8
Desired output:
givenName,sn,displayname,samaccountname,RoleUserA*,title
user01,sn1,User01 SN,user01,,specialist
user02,sn2,User02 SN,user02,,specialist
However, RoleUserA* isn't being added as a column.
The (positionally implied) -Property parameter of the Select-Object cmdlet (whose built-in alias is select) interprets its arguments as wildcard expressions.
Therefore, RoleUserA* looks for existing properties on the input objects whose name starts with RoleUserA - and simply adds none to the output objects if no existing property matches; a simple example:
# Because 'bar*' matches no existing properties, it is *ignored*.
PS> [pscustomobject] #{ foo = 1 } | Select-Object foo, bar*
foo
---
1
While escaping * as `* ('RoleUserA`*') so that it is used literally, as a (non-existent, in this case) property name, should be possible, it unfortunately isn't as of PowerShell 7.2.2,[1] as Mathias R. Jessen points out, and he also points to the solution that does work:
Use a calculated property, whose Name entry (shortened to n below) is always treated literally; using { $null } as the Expression (e) entry script block creates the property with value $null:[1]
Get-ADUser -Filter 'Enabled -eq $true' -Properties * |
Select givenName, sn, displayname, samaccountname, #{n='RoleUserA*';e={ $null }}, title |
Export-Csv -Path "c:\tmp\users.csv" -NoTypeInformation -Encoding UTF8
Note: In scripts used long-term, it's better to spell out the entry keys of the calculated property in full, i.e.:
#{ Name = 'RoleUserA*'; Expression = { $null } }
[1] This should be considered a bug, or at least needs documenting - see GitHub issue #17068
[2] Using just { } - i.e. an empty script block, almost works the same, but it actually creates an "Automation Null" value (the System.Management.Automation.Internal.AutomationNull.Value singleton), which in expressions behaves the same as $null, but acts differently in a pipeline - see this answer for more information.
That said, in the context of converting to CSV this difference won't matter.

How to add a header ExpandProperty in PowerShell?

I am trying to build a script to help me figure out service accounts using Kerberos Constrained Delegation. Two of the properties that I am interested in are multi-valued, so I am using the -ExpandProperty switch. Unfortunately, I haven't figured out a 'clean' way to output the property name with the expanded values. Because the two expanded properties have similar values and can overlap, I need to do something to show where ServicePrincipalNames ends and msDS-AllowedToDelegateTo begins. The code below works, but it seems like there should be a way of getting the same (or very similar) output without having to use Write-Output.
$Account = "svcSomeService"
# Query AD once
$Details = Get-ADUser -Identity $Account -Properties *
# Main result set
$Details | Select-Object -Property SamAccountName, DisplayName, Enabled, PasswordNeverExpires, PasswordExpired, LockedOut, AccountNotDelegated, TrustedForDelegation, TrustedToAuthForDelegation, KerberosEncryptionType
# Expand muulti-value column ServicePrincipalNames
Write-Output "ServicePrincipalNames"
Write-Output "---------------------"
$Details | Select-Object -ExpandProperty ServicePrincipalNames #Tried with and without Format-Table
# Expand muulti-value column msDS-AllowedToDelegateTo
Write-Output "`n"
Write-Output "msDS-AllowedToDelegateTo"
Write-Output "------------------------"
$Details | Select-Object -ExpandProperty msDS-AllowedToDelegateTo #Tried with and without Format-Table
You can construct a [pscustomobject] that houses the expanded values in distinct properties:
[pscustomobject] #{
ServicePrincipalNames = $Details.ServicePrincipalNames
msDS-AllowedToDelegateTo = $Details.msDS-AllowedToDelegateTo
}
Note:
$Details.ServicePrincipalNames is a more efficient alternative to $Details | Select-Object -ExpandProperty ServicePrincipalNames, via the member-access enumeration feature.
As for display formatting on the caller's side: Since the output object has only two properties, it will implicitly render as a table (implicit Format-Table), which doesn't provide much space for showing the individual values. Piping to Format-List helps, but additionally requires you to raise $FormatEnumerationLimit to avoid truncation;[1] to provide a simple example:
$FormatEnumerationLimit=100 # !! As of v7.2.2: only effective in *global* scope
[pscustomobject] #{ prop1 = 1..100; prop2 = 80..1 } | Format-List
[1] Due to an unfortunate bug up to at least PowerShell 7.2.2, setting this preference variable is only effective in the global scope - see GitHub issue #888.

Sorting Office 365 user account / mailbox properties

I'm accessing my cloud Office 365 Exchange Server via Powershell. Showing all properties of an account can be done via Get-Mailbox 'username' | Select * (MS reference).
On most systems those properties are already sorted (e.g. Get-ADUser 'username' -Properties *). Is it possible to sort the Get-Mailbox output? I thought Get-Mailbox 'username' | Select * | Sort-Object would do the trick but it didn't make any difference (I guess a property doesn't constitute an object). What's the right command to sort the properties for a single user?
Note: Sorting properties of multiple accounts works fine e.g. Get-Mailbox -filter * | select Name, DisplayName | Sort-Object Displayname
Update:
I managed to get a sorted properties list using
(Get-Mailbox 'mailboxname' | select *).PSObject.properties | ForEach-Object {$_.Name} | Sort-Object Name
it gets me the following output:
AcceptMessagesOnlyFrom
AccountDisabled
AddressBookPolicy
ArchiveWarningQuota
...
$_.Name gives me the values but so far I couldn't combine both into one list, I'm trying to get s.th. like this:
AcceptMessagesOnlyFrom = {}
AccountDisabled = False
AddressBookPolicy =
ArchiveWarningQuota = 45 GB (48,318,382,080 bytes)
...
I'm not completely sure this is what you are after, let me know if I'm wrong. As in my comment, Sort-Object can handle sorting a list or an object[] by one or more of it's properties; but sorting one single object by it's properties, say alphabetically, would require a combination of accessing the object's properties with .PSObject.Properties.Name and then sorting this list with Sort-Object. And after that we can use Select-Object with this sorted list to display the object as we want.
Using the object below as an example as I have no idea how of the type Microsoft.Exchange.Data.Directory.Management.Mailbox looks.
$mailbox = [pscustomobject]#{
DisplayName = 'someuser'
UserPrincipalName = 'someuser#somedomain.com'
Mail = 'someuser#somedomain.com'
IsLicensed = $true
}
$properties = $mailbox.PSObject.Properties.Name | Sort-Object
$mailbox | Select-Object $properties
As you can see, object's properties are now sorted alphabetically:
DisplayName IsLicensed Mail UserPrincipalName
----------- ---------- ---- -----------------
someuser True someuser#somedomain.com someuser#somedomain.com
By looking at your edit, seems like you are looking for a one-liner, so this is how it could look:
Get-Mailbox 'mailboxname' | ForEach-Object {
$_ | Select-Object ($_.PSObject.Properties.Name | Sort-Object)
}

Remove characters inside expression Powershell

I'm trying to remove characters from an expression, without the option to define another integer.
This is the expression I currently have:
Get-ADUser -Identity $PSItem -Properties mail, manager, l, title |
Select-Object -Property Name, SamAccountName, Mail, l, title, #{
Name = "ManagerGID"
Expression = { (Get-Aduser -identity $psitem.manager).samaccountname }
},
#{
Name = "ManagerName"
Expression = { (Get-Aduser -identity $psitem.manager).name }
},
#{
Name = "PicURL"
Expression = {
(ConvertFrom-Json -InputObject (Invoke-WebRequest ('http://picasaweb.google.com/data/entry/api/user/' + $psitem.mail + '?alt=json')) |
Select-Object -ExpandProperty entry |
Select-Object -ExpandProperty 'gphoto$thumbnail')
}
}
For the PicURL option, the results from google for the gphoto$thumbnail will be something similar to this:
#{$t=https://lh3.googleusercontent.com***********.jpg}
I would like to remove the #{$t= and the } in the start and end of the property. Unfortunately, couldn't find a proper way to do that without having to take out this expression from the pipeline, which is not very effective for the rest of the code.
If you're curious, this is for automatic org chart creation from AD, with google profile pics included.
The implication of seeing output #{$t=https://lh3.googleusercontent.com***********.jpg} is that your input object's gphoto$thumbnail property value is a custom object with a single property literally named $t, whose value is the URL of interest.[1]
Therefore, you can simply append another Select-Object call to extract the $t property's value:
... | Select-Object -ExpandProperty '$t'
Note the need to single-quote ('...') property name $t, so that it isn't mistaken for a variable reference, the same way that property name gphoto$thumbnail required single-quoting.
Alternatively, in v3+, consolidate the then-three Select-Object -ExpandProperty calls into a single ForEach-Object call:
... | ForEach-Object { $_.entry.'gphoto$thumbnail'.'$tp' }
[1] This hashtable-like representation is what PowerShell uses to stringify [pscustomobject] instances.