I'm relatively new to PowerShell, so this may be something simple. I am attempting to display the translated name and original SIDs for the Groups associated with the current login. The Translated names are easy enough:
[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups.Translate([System.Security.Principal.NTAccount]).Value
This display as such and includes an orphaned SID that could not be translated:
Everyone
BUILTIN\Administrators
BUILTIN\Users
NT AUTHORITY\INTERACTIVE
CONSOLE LOGON
NT AUTHORITY\Authenticated Users
NT AUTHORITY\This Organization
LOCAL
S-1-12-X-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXX
NT AUTHORITY\Cloud Account Authentication
The orphaned SID has been redacted with Xs. This is part of a larger script and I am attempting to capture this section as HTML like the following:
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent() | Select-Object Name, @{n='Groups';e={$_.Groups | Foreach-Object {
try
{
$_.Translate([System.Security.Principal.NTAccount]).Value + ' (' + $_.Value +')'
}
catch [Exception]
{
Out-Null
}
}}} | ConvertTo-Html -As List -Fragment
In this example, I am suppressing the Exception, but I have not found a way to display the object that is causing the exception ("Translate" with "1" argument(s): "Some or all identity references could not be translated.").
Is there a way to list the object that caused the exception? I tried with the following:
$_.Exception.TargetObject
The TargetObject was empty. Is there perhaps an if/else construct that could be used if an exception is encountered?
I'm relatively new to PowerShell, so this may be something simple. I am attempting to display the translated name and original SIDs for the Groups associated with the current login. The Translated names are easy enough:
[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups.Translate([System.Security.Principal.NTAccount]).Value
This display as such and includes an orphaned SID that could not be translated:
Everyone
BUILTIN\Administrators
BUILTIN\Users
NT AUTHORITY\INTERACTIVE
CONSOLE LOGON
NT AUTHORITY\Authenticated Users
NT AUTHORITY\This Organization
LOCAL
S-1-12-X-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXX
NT AUTHORITY\Cloud Account Authentication
The orphaned SID has been redacted with Xs. This is part of a larger script and I am attempting to capture this section as HTML like the following:
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent() | Select-Object Name, @{n='Groups';e={$_.Groups | Foreach-Object {
try
{
$_.Translate([System.Security.Principal.NTAccount]).Value + ' (' + $_.Value +')'
}
catch [Exception]
{
Out-Null
}
}}} | ConvertTo-Html -As List -Fragment
In this example, I am suppressing the Exception, but I have not found a way to display the object that is causing the exception ("Translate" with "1" argument(s): "Some or all identity references could not be translated.").
Is there a way to list the object that caused the exception? I tried with the following:
$_.Exception.TargetObject
The TargetObject was empty. Is there perhaps an if/else construct that could be used if an exception is encountered?
The problem in this question is that the Foreach-Object
cmdlet uses the current item ($PSItem
or $_
) and the Catch
block also uses (overrules) the current item to reference the exception information.
A minimal reproducible example to show this, would be:
1..5 | Foreach-Object { try { 1 / ( 3 - $_ ) } catch { "Error processing: $_" } }
0.5
1
Error processing: Attempted to divide by zero.
-1
-0.5
Note that the same issue exists in an embedded Foreach-Object
loop.
Probably the easiest (and cleanest) way around this is to assign the current item to another variable which might be done further into the pipeline than the solution you present in your self-answer:
1..5 | Foreach-Object { $n = $_; try { 1 / ( 3 - $_ ) } catch { "Error processing: $n" } }
0.5
1
Error processing: 3
-1
-0.5
For your specific case, the Foreach-Object
loop might probably be something similar to this:
... | Foreach-Object {
$Sid = $_
try { "$($_.Translate([System.Security.Principal.NTAccount]).Value) ($($_.Value))" }
catch { $Sid.Value } # or better: catch [<Specific Exception>] { $Sid.Value }
}
Unless there is a way to capture the error caused further up the pipeline, this workaround did the job for my purposes.
$getToken = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$groupSIDs = $getToken.Groups
$getGroups = foreach($sid in $groupSIDs) {
try {
(($sid).Translate([System.Security.Principal.NTAccount]).Value) + " ($sid)"
}
catch {
$sid.Value
}
}
$getToken | Select-Object Name, @{n='Groups';e={$getGroups}} | ConvertTo-Html -As List -Fragment
UPDATE: Since I have already defined [System.Security.Principal.WindowsIdentity]::GetCurrent()
in the variable $getToken
, I might as well re-use it where appropriate.
Of course, you can. Just replace Out-Null
to $_.Value
But! if you will maintain this code in future, you won't be pleasure to yourself =))
Maybe something like this is your case:
$CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$User = [PSCustomObject]@{
Name = $CurrentUser.Name
Groups = @()
}
foreach ($GroupSID in $CurrentUser.Groups) {
try {
$User.Groups += "$($GroupSID.Translate([System.Security.Principal.NTAccount]).Value) ($($GroupSID.Value))"
}
catch {
Write-Warning "Cannot translate $($GroupSID.Value)"
$User.Groups += $GroupSID.Value
}
}
# Join array to string for Html
$User | Select-Object Name, @{n='GroupList'; e={$PSItem.Groups -join ', '}} | ConvertTo-Html -As List -Fragment
You could use Update-TypeData
to add a ScriptMethod
where you define the try
/ catch
logic:
$updateTypeDataSplat = @{
TypeName = 'System.Security.Principal.SecurityIdentifier'
MemberType = 'ScriptMethod'
MemberName = 'SafeTranslate'
Value = {
try {
'{0} ({1})' -f $this.Translate([System.Security.Principal.NTAccount]), $this
}
catch {
# Can use `$_.Exception` here to get more details on why it failed
'Failed to Translate ({0})' -f $this
}
}
}
Update-TypeData @updateTypeDataSplat
Then you could do:
[System.Security.Principal.WindowsIdentity]::GetCurrent() |
Select-Object Name, @{ N='Groups'; E={ $_.Groups.SafeTranslate() }} |
ConvertTo-Html -As List -Fragment
As far as I know, you can not iterate further up the pipeline, you will have to split your code a little. Something like this - I think you will have to further work out the html conversion but it could get you started:
$user = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$groups = $user.groups
$result = @()
$result += [pscustomobject]@{
name = $user.name
groups = @()
}
foreach ($g in $groups){
Try{
$result[0].groups += $g.Translate([System.Security.Principal.NTAccount]).Value + ' (' + $g.Value +')'
}
Catch{
$result[0].groups += $g
}
}
$result