Private/Collectors/Get-RemotePermission.ps1
|
function Get-RemotePermission { [CmdletBinding()] [OutputType([pscustomobject])] param([Parameter(Mandatory)][System.Management.Automation.Runspaces.PSSession]$Session) Invoke-Command -Session $Session -ScriptBlock { function Get-RawMembers([string]$GroupName) { # Get-LocalGroupMember is the clean source of SID + account, but it # throws "Failed to compare two elements in the array" on Server # 2016/2019 the moment the group contains an unresolvable AD SID # (deleted account, broken trust). The ADSI WinNT provider survives # those entries and gives us at least the path + objectSid bytes, # so use it as a fallback when Get-LocalGroupMember errors. try { $rows = Get-LocalGroupMember -Group $GroupName -ErrorAction Stop | ForEach-Object { [pscustomobject]@{ Sid = $_.SID.Value; Account = $_.Name } } return @($rows) } catch { } try { $grp = [ADSI]("WinNT://./" + $GroupName + ",group") $members = $grp.psbase.Invoke('Members') $rows = foreach ($m in $members) { $path = try { $m.GetType().InvokeMember('AdsPath','GetProperty',$null,$m,$null) } catch { $null } $name = try { $m.GetType().InvokeMember('Name','GetProperty',$null,$m,$null) } catch { $null } # WinNT://DOMAIN/User -> DOMAIN\User # WinNT://./User -> <Computer>\User $account = if ($path -match '^WinNT://([^/]+)/([^/]+)$') { "$($matches[1])\$($matches[2])" } else { $name } $sid = $null # Preferred: ask LSA to translate the resolved name. Works for # any local/domain user or group still resolvable from this host. if ($account) { try { $sid = (New-Object System.Security.Principal.NTAccount($account)).Translate([System.Security.Principal.SecurityIdentifier]).Value } catch { } } # Fallback: pull the raw objectSid bytes from ADSI. Survives # orphaned/cross-trust members where LSA translation fails. if (-not $sid) { try { $raw = $m.GetType().InvokeMember('objectSid','GetProperty',$null,$m,$null) if ($raw) { $byteArr = [byte[]]@($raw | ForEach-Object { [byte]$_ }) $sid = (New-Object System.Security.Principal.SecurityIdentifier($byteArr, 0)).Value } } catch { } } if ([string]::IsNullOrWhiteSpace($account) -and [string]::IsNullOrWhiteSpace($sid)) { continue } [pscustomobject]@{ Sid = $sid; Account = $account } } return @($rows) } catch { } return @() } [pscustomobject]@{ Administrators = @(Get-RawMembers 'Administrators') RemoteDesktopUsers = @(Get-RawMembers 'Remote Desktop Users') RemoteManagementUsers = @(Get-RawMembers 'Remote Management Users') } } | Select-Object Administrators, RemoteDesktopUsers, RemoteManagementUsers } |