Private/Invoke-NSPLocalAccountRights.ps1

function Initialize-NSPLocalAccountNative {
    if('NSP.LocalAccount.NativeRights' -as [type]) { return }
    Add-Type -TypeDefinition @'
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace NSP.LocalAccount {
 public static class NativeRights {
  [StructLayout(LayoutKind.Sequential)] struct Attributes {
   public int Length; public IntPtr RootDirectory, ObjectName; public uint Flags;
   public IntPtr SecurityDescriptor, SecurityQualityOfService;
  }
  [StructLayout(LayoutKind.Sequential)] struct Text {
   public ushort Length, MaximumLength; public IntPtr Buffer;
  }
  [DllImport("advapi32.dll")] static extern uint LsaOpenPolicy(IntPtr system, ref Attributes attributes, uint access, out IntPtr handle);
  [DllImport("advapi32.dll")] static extern uint LsaEnumerateAccountRights(IntPtr handle, byte[] sid, out IntPtr rights, out uint count);
  [DllImport("advapi32.dll")] static extern uint LsaAddAccountRights(IntPtr handle, byte[] sid, Text[] rights, uint count);
  [DllImport("advapi32.dll")] static extern uint LsaNtStatusToWinError(uint status);
  [DllImport("advapi32.dll")] static extern uint LsaFreeMemory(IntPtr pointer);
  [DllImport("advapi32.dll")] static extern uint LsaClose(IntPtr handle);
  static void Check(uint status) { if(status!=0) throw new Win32Exception((int)LsaNtStatusToWinError(status)); }
  static IntPtr Open(bool write) {
   Attributes a=new Attributes(); a.Length=Marshal.SizeOf(typeof(Attributes)); IntPtr handle;
   Check(LsaOpenPolicy(IntPtr.Zero,ref a,0x800u | (write ? 0x10u : 0u),out handle)); return handle;
  }
  public static string[] Get(byte[] sid) {
   IntPtr handle=Open(false), buffer=IntPtr.Zero;
   try {
    uint count; uint status=LsaEnumerateAccountRights(handle,sid,out buffer,out count);
    if(status!=0 && LsaNtStatusToWinError(status)==2) return new string[0];
    Check(status); string[] result=new string[count]; int size=Marshal.SizeOf(typeof(Text));
    for(int i=0;i<count;i++) { Text t=(Text)Marshal.PtrToStructure(IntPtr.Add(buffer,i*size),typeof(Text)); result[i]=Marshal.PtrToStringUni(t.Buffer,t.Length/2); }
    return result;
   } finally { if(buffer!=IntPtr.Zero) LsaFreeMemory(buffer); LsaClose(handle); }
  }
  public static void GrantBatch(byte[] sid) {
   IntPtr handle=Open(true); Text t=new Text();
   try {
    string right="SeBatchLogonRight"; t.Buffer=Marshal.StringToHGlobalUni(right);
    t.Length=(ushort)(right.Length*2); t.MaximumLength=(ushort)(t.Length+2);
    Check(LsaAddAccountRights(handle,sid,new Text[]{t},1));
   } finally { if(t.Buffer!=IntPtr.Zero) Marshal.FreeHGlobal(t.Buffer); LsaClose(handle); }
  }
 }
}
'@
 -ErrorAction Stop
}
function Invoke-NSPLocalAccountRights {
    [CmdletBinding()]
    param([Parameter(Mandatory)][string]$SID,[ValidateSet('Get','GrantBatch')][string]$Operation='Get')
    Initialize-NSPLocalAccountNative
    $identity=New-Object Security.Principal.SecurityIdentifier($SID)
    $bytes=New-Object byte[] $identity.BinaryLength
    $identity.GetBinaryForm($bytes,0)
    if($Operation -eq 'Get') { [NSP.LocalAccount.NativeRights]::Get($bytes) }
    else { [NSP.LocalAccount.NativeRights]::GrantBatch($bytes) }
}