Translate SID to accountName

I’m trying to create a script that checks “LogonAsBatch” rights on remote servers. It returns the SID so I tried to create a function to convert the SID to name by using “wmic useraccount where SID=## get name” command. This command needs to execute on the remote server. The script is not converting the SID to name. I’m not sure if I’m calling the wmic command correctly. I keep getting an error “NotSpecified: (:slight_smile: , RemoteException” along with the server it’s on. Tried ChatGPT but it doesn’t know what it’s doing.

# Import servers from TXT file
$servers = Get-Content -Path C:\tmp\server_input.txt

# Create an array to store results
$results = @()

# Function to translate user account to SID using wmic
function Translate-UserAccountToSID {
    param (
        [string]$Server,
        [string]$Username
    )

    try {
        $sid = Invoke-Command -ComputerName $Server -ScriptBlock {
            param($Username)
            $output = cmd.exe /c "wmic useraccount where SID='$Username' get name"
           # $sid = $output -replace '\s+', ''  # Remove whitespace and newline characters
        } -ArgumentList $Username 

        return $output
    } catch {
        Write-Host "Error translating user account to SID on $($Server): $_"
        return $null
    }
}
# Iterate through each server
foreach ($server in $servers) {
    Write-Host "Checking 'Logon as batch job' rights on $server"
    
    try {
        # Get the security settings using secedit
        $securitySettings = Invoke-Command -ComputerName $server -ScriptBlock {
            secedit /export /cfg "$env:temp\SecuritySettings.inf"
            Get-Content "$env:temp\SecuritySettings.inf" | Out-String
        }

        # Parse the security settings to get SIDs with "Logon as a service" rights
        $logonAsBatchUsernames = $securitySettings | Select-String -Pattern "SeBatchLogonRight\s*=\s*(.*)" | ForEach-Object { $_.Matches.Groups[1].Value -split ',' }

        # Convert 
        $accountNames = $logonAsBatchUsernames | ForEach-Object {
            $username = $_
            $accountName = Translate-UserAccountToSID -Server $server -Username $username
            $accountName
        }

        # Add result to array
        $result = [PSCustomObject]@{
            ServerName = $server
            UsersWithLogonAsBatchRights = $logonAsBatchUsernames -join ', '
            AccountNames = $accountNames -join ', '
        }
        $results += $result
    } catch {
        Write-Host "Error checking 'Logon as batch job' rights on $($server): $_"
    }

    Write-Host ""
}

# Output results
$results | Format-Table -AutoSize

# Optionally, export results to CSV file
#$results | Export-Csv -Path c:\tmp\output\LogonAsBatchResults.csv -NoTypeInformation
#Write-Host "Results exported to LogonAsBatchResults.csv"

ERROR:
NotSpecified: (:slight_smile: , RemoteException
No Instance(s) Available.
+ CategoryInfo : NotSpecified: (No Instance(s) Available.:String) , RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ PSComputerName : SERVERNAME

NotSpecified: (:slight_smile: , RemoteException

Could you go back and format your code properly? I think it got messed up and the code blocks got split on certain parts that perhaps was not intentional. Thanks!

Formatting your code: How to format code on PowerShell.org

without reading your code in too much detail. i see you’re just having issues with translating sid to account name… have you tried this?

    $SIDString = 'SID'
    $SIDObject= New-Object System.Security.Principal.SecurityIdentifier("$SIDString")
    $Account = $SIDObject.Translate( [System.Security.Principal.NTAccount]) | Select-Object Value

I’d assume any system you run this on is part of your domain so this should work. I know this bit of code is in my personal code scratch but think it’s a really common approach others use.

Is this what you mean? When I run, it still just returns the SID instead of account name.

        $accountNames = $logonAsBatchUsernames | ForEach-Object {
            try {
            $SIDObject = New-Object System.Security.Principal.SecurityIdentifier($_)
            $account = $SIDObject.Translate([System.Security.Principal.NTAccount]) | Select-Object Value
            $account
            } catch {
            Write-Host "Error translating SID to account name: $_"
            $_
            }
        }

I can’t easily test all your code unfortunately, but basically if I assume $logonAsBatchUsernames has one or more valid SIDS in it, the code works for me. I did not include the -ExpandProperty on the Value sorry about that - but I’ll add it below. but I took your code and added one of my test accounts SID into the $logonasbatchusernames variable:

 $accountNames = $logonAsBatchUsernames | ForEach-Object {
            try {
            $SIDObject = New-Object System.Security.Principal.SecurityIdentifier($_)
            $account = $SIDObject.Translate([System.Security.Principal.NTAccount]) | Select-Object -ExpandProperty Value
            $account
            } catch {
            Write-Host "Error translating SID to account name: $_"
            $_
            }
        }
$accountNames

That outputs

Domain\Username

for me.

Unless the computer in $server is a domain controller, then you are hitting a double hop issue. You’re invoking a remote command then trying to connect to AD remotely. Without delegation or configuration, this will only work against a DC. Perhaps you could just pull the sid from the remote session, do your name translation locally (back to single hop to AD) and finish your script, whether local or remote.

FWIW, this is what I use to translate SIDS on remote systems.

$Sid = 'S-1-5-21-XXXXXXXXXX-XXXXXXXXX-XXXXXXXX-XXXXXX'
$System = 'RemoteHost'
([wmi]"\\$System\root\cimv2:Win32_SID.SID='$Sid'").AccountName

FWIW, this requires the user to have remote wmi access. Another alternative is to use the ADSI accelerator.

$sid = 'S-1-5-21-XXXXXXXXXX-XXXXXXXXX-XXXXXXXX-XXXXXX'
$userobj = [ADSI]"LDAP://<SID=$sid>"
$user.Properties['SamAccountName']
# or whatever property you're after
$user.Properties['UserPrincipalName']

Gotta love ADSI. Good to know, thanks Doug :slight_smile: