Public/Grant-LogonAsBatchRight.ps1

Function Grant-LogonAsBatchRight {

    <#
    .SYNOPSIS
        Grants a local or domain account the "Log on as a batch job" (SeBatchLogonRight) security policy right.
    .DESCRIPTION
        Needed when a scheduled task that runs one of the Apteco PS modules must run under a service account
        that is not an interactive user -- Windows Task Scheduler refuses to start such a task until the
        account has been granted this right. Must be run elevated (as Administrator). Windows only, no-op
        elsewhere.
    .PARAMETER UserName
        Account to grant the right to, as "domain\account" or just "account" for a local account.
    .EXAMPLE
        Grant-LogonAsBatchRight -UserName "svc-apteco"
    .EXAMPLE
        Grant-LogonAsBatchRight -UserName "CONTOSO\svc-apteco"
    .NOTES
        Adapted from https://github.com/zloeber/Powershell/blob/master/Supplemental/Add-UserToLoginAsBatch.ps1
        which itself credits http://www.morgantechspace.com/2014/03/Set-Logon-as-batch-job-rights-to-User-by-Powershell-CSharp-CMD.html
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [String]$UserName
    )

    Process {

        If ( $Script:os -ne "Windows" ) {
            Write-Verbose "Logon-as-batch-job rights are only relevant on Windows, skipping"
            return
        }

        $CSharpCode = @'
using System;
using System.Text;
using System.Runtime.InteropServices;
public class LsaWrapper
{
    [DllImport("advapi32.dll", PreserveSig = true)]
    private static extern UInt32 LsaOpenPolicy(
        ref LSA_UNICODE_STRING SystemName,
        ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,
        Int32 DesiredAccess,
        out IntPtr PolicyHandle
        );
 
    [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
    private static extern long LsaAddAccountRights(
        IntPtr PolicyHandle,
        IntPtr AccountSid,
        LSA_UNICODE_STRING[] UserRights,
        long CountOfRights);
 
    [DllImport("advapi32")]
    public static extern void FreeSid(IntPtr pSid);
 
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)]
    private static extern bool LookupAccountName(
        string lpSystemName, string lpAccountName,
        IntPtr psid,
        ref int cbsid,
        StringBuilder domainName, ref int cbdomainLength, ref int use);
 
    [DllImport("advapi32.dll")]
    private static extern bool IsValidSid(IntPtr pSid);
 
    [DllImport("advapi32.dll")]
    private static extern long LsaClose(IntPtr ObjectHandle);
 
    [DllImport("kernel32.dll")]
    private static extern int GetLastError();
 
    [DllImport("advapi32.dll")]
    private static extern long LsaNtStatusToWinError(long status);
 
    private enum LSA_AccessPolicy : long
    {
        POLICY_VIEW_LOCAL_INFORMATION = 0x00000001L,
        POLICY_VIEW_AUDIT_INFORMATION = 0x00000002L,
        POLICY_GET_PRIVATE_INFORMATION = 0x00000004L,
        POLICY_TRUST_ADMIN = 0x00000008L,
        POLICY_CREATE_ACCOUNT = 0x00000010L,
        POLICY_CREATE_SECRET = 0x00000020L,
        POLICY_CREATE_PRIVILEGE = 0x00000040L,
        POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080L,
        POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100L,
        POLICY_AUDIT_LOG_ADMIN = 0x00000200L,
        POLICY_SERVER_ADMIN = 0x00000400L,
        POLICY_LOOKUP_NAMES = 0x00000800L,
        POLICY_NOTIFICATION = 0x00001000L
    }
 
    [StructLayout(LayoutKind.Sequential)]
    private struct LSA_OBJECT_ATTRIBUTES
    {
        public int Length;
        public IntPtr RootDirectory;
        public readonly LSA_UNICODE_STRING ObjectName;
        public UInt32 Attributes;
        public IntPtr SecurityDescriptor;
        public IntPtr SecurityQualityOfService;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    private struct LSA_UNICODE_STRING
    {
        public UInt16 Length;
        public UInt16 MaximumLength;
        public IntPtr Buffer;
    }
 
    public long SetRight(String accountName, String privilegeName)
    {
        long winErrorCode = 0;
 
        IntPtr sid = IntPtr.Zero;
        int sidSize = 0;
        var domainName = new StringBuilder();
        int nameSize = 0;
        int accountType = 0;
 
        LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType);
 
        domainName = new StringBuilder(nameSize);
        sid = Marshal.AllocHGlobal(sidSize);
 
        bool result = LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize,
                                        ref accountType);
 
        if (!result)
        {
            winErrorCode = GetLastError();
        }
        else
        {
            var systemName = new LSA_UNICODE_STRING();
            var access = (int) (
                                    LSA_AccessPolicy.POLICY_AUDIT_LOG_ADMIN |
                                    LSA_AccessPolicy.POLICY_CREATE_ACCOUNT |
                                    LSA_AccessPolicy.POLICY_CREATE_PRIVILEGE |
                                    LSA_AccessPolicy.POLICY_CREATE_SECRET |
                                    LSA_AccessPolicy.POLICY_GET_PRIVATE_INFORMATION |
                                    LSA_AccessPolicy.POLICY_LOOKUP_NAMES |
                                    LSA_AccessPolicy.POLICY_NOTIFICATION |
                                    LSA_AccessPolicy.POLICY_SERVER_ADMIN |
                                    LSA_AccessPolicy.POLICY_SET_AUDIT_REQUIREMENTS |
                                    LSA_AccessPolicy.POLICY_SET_DEFAULT_QUOTA_LIMITS |
                                    LSA_AccessPolicy.POLICY_TRUST_ADMIN |
                                    LSA_AccessPolicy.POLICY_VIEW_AUDIT_INFORMATION |
                                    LSA_AccessPolicy.POLICY_VIEW_LOCAL_INFORMATION
                                );
            IntPtr policyHandle = IntPtr.Zero;
 
            var ObjectAttributes = new LSA_OBJECT_ATTRIBUTES();
            ObjectAttributes.Length = 0;
            ObjectAttributes.RootDirectory = IntPtr.Zero;
            ObjectAttributes.Attributes = 0;
            ObjectAttributes.SecurityDescriptor = IntPtr.Zero;
            ObjectAttributes.SecurityQualityOfService = IntPtr.Zero;
 
            uint resultPolicy = LsaOpenPolicy(ref systemName, ref ObjectAttributes, access, out policyHandle);
            winErrorCode = LsaNtStatusToWinError(resultPolicy);
 
            if (winErrorCode == 0)
            {
                var userRights = new LSA_UNICODE_STRING[1];
                userRights[0] = new LSA_UNICODE_STRING();
                userRights[0].Buffer = Marshal.StringToHGlobalUni(privilegeName);
                userRights[0].Length = (UInt16) (privilegeName.Length*UnicodeEncoding.CharSize);
                userRights[0].MaximumLength = (UInt16) ((privilegeName.Length + 1)*UnicodeEncoding.CharSize);
 
                long res = LsaAddAccountRights(policyHandle, sid, userRights, 1);
                winErrorCode = LsaNtStatusToWinError(res);
 
                LsaClose(policyHandle);
            }
            FreeSid(sid);
        }
 
        return winErrorCode;
    }
}
 
public class AddUserToLoginAsBatch
{
    public static long GrantUserLogonAsBatchJob(string userName)
    {
        LsaWrapper lsaUtility = new LsaWrapper();
        return lsaUtility.SetRight(userName, "SeBatchLogonRight");
    }
}
'@


        if ( -not ( [System.Management.Automation.PSTypeName]'AddUserToLoginAsBatch' ).Type ) {
            try {
                Add-Type -ErrorAction Stop -Language:CSharpVersion3 -TypeDefinition $CSharpCode
            } catch {
                Write-Error "Failed to compile the LSA helper type: $( $_.Exception.Message )"
                return
            }
        }

        Write-Verbose "Granting 'Log on as a batch job' right to $UserName"
        $winErrorCode = [AddUserToLoginAsBatch]::GrantUserLogonAsBatchJob($UserName)

        If ( $winErrorCode -ne 0 ) {
            Write-Error "Failed to grant 'Log on as a batch job' right to $( $UserName ): Win32 error code $winErrorCode"
        } else {
            Write-Verbose "'Log on as a batch job' right granted successfully to $UserName"
        }

    }

}