Powershell - add extra column called RoleUserA* inside CSV file - powershell

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.

Related

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.

Get AD user properties from Active Directory

I am trying to filter the values of a property in Active Directory.
I tried:
Get-ADUser -filter * -Properties physicalDeliveryOfficeName | Where-Object (($_.physicalDeliveryOfficeName -like "NICE")) | Select-Object physicalDeliveryOfficeName, name
Get-ADUser -filter * -Properties physicalDeliveryOfficeName | Select-Object physicalDeliveryOfficeName, name | Where-Object (($_.physicalDeliveryOfficeName -like "NICE"))
I did not get any errors, but no results either.
I searched all users with physicaldeliverofficename is (myvalue). I would like to display name and office.
You have a syntax problem:
The Where-Object's (positionally implied) -FilterScript parameter expects a script block argument - { ... } - not a parenthesized expression ((...)).[1]
Therefore:
# Note the { ... } around the expression passed to Where-Object
Get-ADUser -Filter * -Properties physicalDeliveryOfficeName |
Where-Object { $_.physicalDeliveryOfficeName -eq "NICE" } # | ...
Note: Since "NICE" is a literal string rather than a wildcard pattern, I've used the -eq instead of the -like operator. If you doe need to find "NICE" as a substring, use something like -like "*NICE*" or, for case-sensitive matching, -clike "*NICE*", as Mathias R. Jessen suggests.
Note that you may alternatively use simplified syntax, which obviates the need for a script block and allows use of individual parameters (also note the absence of $_., which is implied):
Get-ADUser -Filter * -Properties physicalDeliveryOfficeName |
Where-Object physicalDeliveryOfficeName -eq "NICE" # | ...
Taking a step back:
Santiago Squarzon suggests performing the filtering at the source, by using Get-ADUser's -Filter or -LDAPFilter parameter, which is much more efficient; e.g.:
Get-ADUser -Filter 'physicalDeliveryOfficeName -eq "NICE"'
As an aside: There are many examples out there that use script-block syntax with -Filter (-Filter { ... }), but the -Filter parameter accepts a string and that string, even though it supports PowerShell-like syntax, is interpreted by the AD provider, so it's better to pass a string to begin with - see this answer for more information.
[1] If you use (...), the expression's value gets bound to the -Property parameter instead, and is therefore interpreted as a property name whose value - assuming such a property even exists - is interpreted as a Boolean that determines whether the input object at hand should be filtered in or not. If the expression doesn't evaluate to the name of a property that exists on an input object, $false is implied, and the input object is filtered out. In your case, this predictably resulted in no objects being filtered in and therefore no output.
The Select-Object cmdlet is used to select only the columns you want from a larger object or list.
For instance:
C:\git\Core> gsv Spooler | fl
Name : Spooler
DisplayName : Print Spooler
Status : Running
DependentServices : {Fax}
ServicesDependedOn : {RPCSS, http}
CanPauseAndContinue : False
CanShutdown : False
CanStop : True
ServiceType : Win32OwnProcess, InteractiveProcess
Get-Service returns Service Objects which have a lot of properties. If I only want certain ones, I'd use it like so:
C:\git\Core> gsv Spooler | Select Name,Status
Name : Spooler
Status : Running
You're using the cmdlet and probably discarding the columns which have the values you need. Run your one-liner again and remove the Select-Object cmdlet to see all of the columns availble, till you find the one that pertains to the Office.

PowerShell pipe ExpandProperty to another Select-Object

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
}
}
}

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.

Powershell - keep specific word in a string

I'm doing AD extract and I sort the field "distinguishedname" and I want to keep only a specific part of the value that represent the "parent OU" of the user itself.
I'm running this command to do an add extract of all users:
import-module activedirectory
get-aduser -filter * -properties *| Select-Object -Property SamAccountName,CN,co,ExtensionAttribute10,extensionAttribute11,extensionAttribute12,EmailAddress,whenCreated,Enabled,LastLogonDate,accountexpirationdate,distinguishedname |Sort-Object -Property Name | Export-Csv -Delimiter ";" -path "u:\theOutFile_NOFILTER_July.txt"
The column "distinguishedname" look like this:
distinguishedname
CN=familly\, user,OU=Remote Users,OU=New York,OU=My,DC=Company,DC=Local
CN=nameless\, cat,OU=Remote Users,OU=Ottawa,OU=My,DC=Company,DC=Local
CN=Cameron\, James,OU=Regular Users,OU=Hollywood,OU=My,DC=Company,DC=Local
CN=Bon\, Jean,OU=regular Users,OU=Springfield,OU=My,DC=Company,DC=Local
Note July 10
some time I will hit those line:
CN=Dog\, Cesar,OU=Special Accounts,OU=Regular Users,OU=Alma,OU=My,DC=Company,DC=Local
CN=keys\, Alicia,OU=Special Accounts,OU=Regular Users,OU=Paris,OU=My,DC=Company,DC=Local
CN=Clansy\, Door,OU=Map Drives,OU=Remote Users,OU=Rome,OU=My,DC=Company,DC=Local
In those case I am getting result such Remote Users an Regular Users instead of the City. I've tried some modification in your command you gave but in vain.
But I would like the first command to return this result instead:
distinguishedname
New York
Ottawa
Hollywood
Springfield
I can't effort to find how.
thanks in advance
Select-Object has a very versatile feature to create calculated properties using a hash in the place of a property name, where the key "Name" is set to the name of the calculated property (effectively, the column heading), and "Expression" is set to a code block that determines the value of the property, for each object in the pipeline. This will do what you want:
Get-Aduser -Filter * -Properties * | Select-Object -Property SamAccountName,CN,co,ExtensionAttribute10,extensionAttribute11,extensionAttribute12,EmailAddress,whenCreated,Enabled,LastLogonDate,accountexpirationdate,#{Name='distinguishedname'; Expression={[regex]::match($_.distinguishedname,'OU=.+?OU=(.+?),(OU|DC)=').Groups[1].Value}} | Sort-Object -Property Name | Export-Csv -Delimiter ";" -Path "u:\theOutFile_NOFILTER_July.txt"
Here's a breakdown what's going on:
Name='distinguishedname' tells it to create a new column called 'distinguishedname'. I used that name to match your example of the output you're looking for, but it doesn't have to be the name of an existing property. It would probably make more sense to change the name to something more descriptive of the values you're calculating, e.g. Name="parentOU".
[regex]::match is used to extract the desired portion from $_.distinguishedname using the regular expression OU=.+?OU=(.+?),(OU|DC)=, which isolates the name of the second OU in the list using a match group.
.Groups[1].Value returns the value of the first match group (the part matched by the contents of the first set of parentheses). .Value on its own without .Groups[1] would return the entire matched string, from the first OU= to the = following name of the parent OU. The following would work just as well, using zero-width assertions instead of a match group: [regex]::match($_.distinguishedname,'(?<=OU=.+?OU=).+?(?=,(OU|DC)=)').Value