@Ryan Bolger 's solution mostly works, however there's an issue where if there's 2 accounts with the same name, but different domains, the SID will be duplicated since the translate as it was only works on the username itself. Below is a modified version of their function that addresses this.
Function Get-LocalGroupMembers {
[Cmdletbinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$GroupName
)
[adsi]$adsiGroup = "WinNT://$($env:COMPUTERNAME)/$GroupName,group"
$Members = @()
$adsiGroup.Invoke('Members') | ForEach-Object{
$username = $_.GetType().InvokeMember('Name','GetProperty',$null,$_,$null)
$path = $_.GetType().InvokeMember('AdsPath','GetProperty',$null,$_,$null).Replace('WinNT://','')
$class = $_.GetType().InvokeMember('Class','GetProperty',$null,$_,$null)
$userObj = $null
if ($path -notlike "*S-1-5*"){ #If the path does not contain a SID, then AD is reachable and we can translate successfully
$Parts = $path.Split("/")
<#
$Parts can look like:
Local Account - Domain, ComputerName, AccountName
Domain Account - Domain, AccountName
#>
$Domain = $Parts[0]
if ($Parts[1] -eq $env:COMPUTERNAME){ #Based on above comment, if arg 2 is the ComputerName, it's a local account
$userObj = New-Object System.Security.Principal.NTAccount($username)
}else{#Otherwise it's a domain account and we need to translate using the domain
$userObj = New-Object System.Security.Principal.NTAccount($Domain, $username)
}
$sid = $userObj.Translate([System.Security.Principal.SecurityIdentifier])
}else{ #Otherwise AD not reachable and the SID is just the username
$sid = $username
}
$Members += [pscustomobject]@{
Username = $username
Type = $class
SID = $sid
Path = $path
}
}
return $Members
}