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.
Related
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.
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.
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
}
}
}
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 have a PowerShell script, which fills the Usrid custom attribute with EMP$ID value. $ID is a continuous number, which is normally stored in a text file and continuously written after setting the ID and set it one up. First I get with Get-ADUser all users without a ID, then will set the ID into Usrid attribute.
My problem is: I want to check if the ID or value exists.
If ID exists → $ID++
else → set ID from File and write it to file again
The if part in the script looks like this:
# I'm calling the content or the last ID
$lastid = Get-Content "C:\startid.txt"
# Convert the content into a decimal string
$Usrid = [System.Decimal]::Parse($lastid)
# Find out all userProxyFull Object without an ID
Get-ADObject -Filter {(objectClass -eq "userProxyFull") -and (-not(Userid -like "*"))} -Searchbase "DC=MY,DC=SEARCHBASE" -Searchscope subtree -Server myIP |
ForEach-Object {
# Then the problem part here, see description above
if ({Usrid -eq "EMP$ID"}) {
$ID++
Set-ADObject $_ -Partition "DC=my,DC=partition" -Add #{Usrid="EMP$ID"}
} else {
Set-ADObject $_ -Partition "DC=my,DC=partition" -Add #{Usrid="EMP$ID"}
}
}
But the script doesn't check. Or how can I check the highest ID and set the highest ID into Usrid attribute?
Assuming that Usrid needs to be unique you need to compare your input value against the already existing attribute values.
$existing = #{}
Get-ADUser -LDAPFilter '(UserId=*)' -Property Usrid | ForEach-Object {
$existing[$_.Usrid] = $true
}
while ($existing.ContainsKey("EMP$ID") {
$ID++
}
At this point you have a $ID where none of the existing accounts has a Usrid attribute with the value EMP$ID. Then you can go ahead and assign the attribute on all account objects where it doesn't have a value yet while incrementing $ID after each assignment:
Get-ADObject ... | ForEach {
Set-ADObject $_ -Partition "DC=my,DC=partition" -Add #{Usrid="EMP$ID"}
$ID++
}
Note, however, that the above is assuming that there are no gaps in your numbering (i.e. you don't have a situation where you have attribute values ..., EMP25, EMP26, EMP28, ...). If that assumption doesn't apply you're better off determining the next available ID by getting the highest already assigned ID and incrementing that value by 1:
$ID = Get-ADUser -LDAPFilter '(Userid=*)' -Property Usrid |
ForEach-Object { [int]($_.Usrid -replace '^EMP') } |
Sort-Object |
Select-Object -Last 1
$ID++