SQL Service Packs (SP) and Cumulative Updates (CU) Install Server Core 2016

In order to manually install any Service Pack or Cumulative Update to SQL on Server Core all you need to do is download and run the exe with the following parameters:

/q /action=patch /allinstances /IAcceptSQLServerLicenseTerms

Example:
C:TempSQLServer2016-KB3194717-x64.exe /q /action=patch /allinstances /IAcceptSQLServerLicenseTerms
or
C:TempSQLServer2016SP1-KB3182545-x64-ENU.exe /q /action=patch /allinstances /IAcceptSQLServerLicenseTerms

Virtual Machine Manager 2016 on Server Core

You can use remote desktop / command line for most of this. Just copy all the files to c:temp or something.

VMM requires a SQL Database and an account with permission to create databases/tables etc, so I’m going to assume you’ve got one of them handy…if not click here

#1. Download and Install the Microsoft® SQL Server® 2012 Native Client – QFE

#2. Download and Install the Microsoft® Command Line Utilities 13 for SQL Server®

#3. Download and Install the latest Windows ADK for Windows 10 (You only need the Deployment Tools and Windows Preinstallation Environment options selected)

#4. If you want to use Distributed Key Management open up ADSIEdit and create a new container by going into the Default Naming Context, right clicking on your DC=domain,DC=local, clicking New Object, selecting Container and giving it a name “VMMDKM” works.

#5. Make sure you’ve created a service account for VMM and that it’s a member of the server’s local admin group.

#6. Run the VMM Setup, and follow instructions…nothing special here other than setting your DKM location: CN=VMMDKM,DC=domain,DC=local

#7. Run sconfig and download the latest updates!

How to install SQL 2016 on Server Core 2016

#1. Create a service account for SQL in AD.

#2. Add that service account to the computers Local Admin groups.

#3. Create a Silent Install INI file. Lots of details can be found here but here’s an example for a Named Instance, in this case “VMM”:

; ssNoVersion Configuration File
[OPTIONS]

; Specifies a Setup work flow, like INSTALL, UNINSTALL, or UPGRADE. This is a required parameter.
ACTION="Install"

; Specifies features to install, uninstall, or upgrade. The lists of features include SQLEngine, FullText, Replication, AS, IS, and Conn.
FEATURES=SQLENGINE,FullText,Replication,AS,IS,Conn

; Specify a default or named instance. MSSQLSERVER is the default instance for non-Express editions and SQLExpress for Express editions. This parameter is required when installing the ssNoVersion Database Engine (SQL), or Analysis Services (AS).
INSTANCENAME="VMM"

; Specify the Instance ID for the ssNoVersion features you have specified. ssNoVersion directory structure, registry structure, and service names will incorporate the instance ID of the ssNoVersion instance.
INSTANCEID="VMM"

; Account for ssNoVersion service: DomainUser or system account.
SQLSVCACCOUNT="domain.localSQL_SERVICE_ACCOUNT"

; Windows account(s) to provision as ssNoVersion system administrators.
SQLSYSADMINACCOUNTS="domain.localadmin1,domain.localadmin2"

; The name of the account that the Analysis Services service runs under.
ASSVCACCOUNT= "domain.localSQL_SERVICE_ACCOUNT"

; Specifies the list of administrator accounts that need to be provisioned.
ASSYSADMINACCOUNTS="domain.localadmin1,domain.localadmin2"

; Specifies the server mode of the Analysis Services instance. Valid values are MULTIDIMENSIONAL, POWERPIVOT or TABULAR. ASSERVERMODE is case-sensitive. All values must be expressed in upper case.
ASSERVERMODE="MULTIDIMENSIONAL"

; Optional value, which specifies the state of the TCP protocol for the ssNoVersion service. Supported values are: 0 to disable the TCP protocol, and 1 to enable the TCP protocol.
TCPENABLED=1

;Specifies acceptance of License Terms
IAcceptSQLServerLicenseTerms="True"

#4. Copy that file to the server into C:Temp or somewhere similar

#5. Run setup from command line like so:

D:Setup.exe /Q /SQLSVCPASSWORD="SERVICEACCOUNTPASSWORD" /ASSVCPASSWORD="SERVICEACCOUNTPASSWORD" /ConfigurationFile=c:tempSilentInstall.ini

#6. Use SQLCMD.EXE to enable remote access, change the hostname and instance name as needed:

c:Program FilesMicrosoft SQL ServerClient SDKODBC130ToolsBinn>SQLCMD.EXE -S SQL01VMM
1> EXEC sys.sp_configure N'remote access', N'1'
2> GO
Configuration option 'remote access' changed from 1 to 1. Run the RECONFIGURE statement to install.
1> RECONFIGURE WITH OVERRIDE
2> GO
1> quit

#7. Optionally set the SQL Browser to Autostart:

sc config SQLBROWSER start= auto
net start SQLBROWSER

#7. Use PowerShell to create basic Firewall Rules:

powershell.exe
New-NetFirewallRule -DisplayName “SQL Default Instance” -Direction Inbound –Protocol TCP –LocalPort 1433 -Action allow
New-NetFirewallRule -DisplayName “SQL Admin Connection” -Direction Inbound –Protocol TCP –LocalPort 1434 -Action allow
New-NetFirewallRule -DisplayName “SQL Server Browser Service” -Direction Inbound –Protocol UDP –LocalPort 1434 -Action allow
New-NetFirewallRule -DisplayName “SQL Server Browse Button Service” -Direction Inbound –Protocol UDP –LocalPort 1433 -Action allow
New-NetFirewallRule -DisplayName “SQL Service Broker” -Direction Inbound –Protocol TCP –LocalPort 4022 -Action allow
New-NetFirewallRule -DisplayName “SQL Debugger/RPC” -Direction Inbound –Protocol TCP –LocalPort 135 -Action allow
New-NetFirewallRule -DisplayName “SQL Analysis Services” -Direction Inbound –Protocol TCP –LocalPort 2383 -Action allow
New-NetFirewallRule -DisplayName “SQL Browser” -Direction Inbound –Protocol TCP –LocalPort 2382 -Action allow
New-NetFirewallRule -DisplayName “HTTP” -Direction Inbound –Protocol TCP –LocalPort 80 -Action allow
New-NetFirewallRule -DisplayName “SSL” -Direction Inbound –Protocol TCP –LocalPort 443 -Action allow

#8. Use PowerShell to create the SQL instance specific firewall rule that allows for dynamic ports of non-default SQL Instances, notice that the instance name (VMM) is used twice, once for the name of the rule and once in the program path:

powershell.exe
New-NetFirewallRule -DisplayName “SQL Instance (VMM)” -Direction Inbound -Program "C:Program FilesMicrosoft SQL ServerMSSQL13.VMMMSSQLBinnsqlservr.exe" -Action allow

#9. This isn’t needed if you enabled TCP in the INI file but I found Microsoft’s own documentation to be incorrect and it made me angry, so here’s how to actually enable TCP, change the ServerInstance Name as needed:

powershell.exe
Add-Type -Path 'C:Program FilesMicrosoft SQL Server130SDKAssembliesMicrosoft.SqlServer.SqlWmiManagement.dll'
$mc = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer
$uri = "ManagedComputer[@Name='" + (get-item env:computername).Value + "']/ServerInstance[@Name='VMM']/ServerProtocol[@Name='Tcp']"
$Tcp = $mc.GetSmoObject($uri)
$Tcp.IsEnabled = $true
$Tcp.Alter()
$Tcp

Execute Program as Logged in user from Session 0 with C# and Powershell

I’ve got some PowerShell scripts that are executed by a service running as “SYSTEM” which runs everything in Session 0 isolation and I needed to be able to launch a program as the logged in user. After some research I came across this awesome C# code: Launch Program from Session 0

Well in PowerShell we can write and execute C# code directly as described here: Using C# code in PowerShell

So if we copy the entire C# source code from the first link’s answer into the Source variable like such:

$Source = @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;

namespace Session0
{
    /// 

    /// Class that allows running applications with full admin rights. In
    /// addition the application launched will bypass the Vista UAC prompt.
    /// 

    public class AppLaunch
    {
        #region Structures

        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public int Length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public int cb;
            public String lpReserved;
            public String lpDesktop;
            public String lpTitle;
            public uint dwX;
            public uint dwY;
            public uint dwXSize;
            public uint dwYSize;
            public uint dwXCountChars;
            public uint dwYCountChars;
            public uint dwFillAttribute;
            public uint dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }

        #endregion

        #region Enumerations

        enum TOKEN_TYPE : int
        {
            TokenPrimary = 1,
            TokenImpersonation = 2
        }

        enum SECURITY_IMPERSONATION_LEVEL : int
        {
            SecurityAnonymous = 0,
            SecurityIdentification = 1,
            SecurityImpersonation = 2,
            SecurityDelegation = 3,
        }

        #endregion

        #region Constants

        public const int TOKEN_DUPLICATE = 0x0002;
        public const uint MAXIMUM_ALLOWED = 0x2000000;
        public const int CREATE_NEW_CONSOLE = 0x00000010;

        public const int IDLE_PRIORITY_CLASS = 0x40;
        public const int NORMAL_PRIORITY_CLASS = 0x20;
        public const int HIGH_PRIORITY_CLASS = 0x80;
        public const int REALTIME_PRIORITY_CLASS = 0x100;

        #endregion

        #region Win32 API Imports

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool CloseHandle(IntPtr hSnapshot);

        [DllImport("kernel32.dll")]
        static extern uint WTSGetActiveConsoleSessionId();

        [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
            ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
            String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("kernel32.dll")]
        static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);

        [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
        public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
            ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
            int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);

        [DllImport("kernel32.dll")]
        static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

        [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurity]
        static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);

        #endregion

        /// 

        /// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt
        /// 

        /// The name of the application to launch
        /// Process information regarding the launched application that gets returned to the caller
        /// 
        public static bool Start(String applicationName, string startingDir, out PROCESS_INFORMATION procInfo)
        {
            uint winlogonPid = 0;
            IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
            procInfo = new PROCESS_INFORMATION();

            // obtain the currently active session id; every logged on user in the system has a unique session id
            uint dwSessionId = WTSGetActiveConsoleSessionId();

            // obtain the process id of the winlogon process that is running within the currently active session
            // -- chaged by ty 
            // Process[] processes = Process.GetProcessesByName("winlogon");
            Process[] processes = Process.GetProcessesByName("explorer");
            foreach (Process p in processes)
            {
                if ((uint)p.SessionId == dwSessionId)
                {
                    winlogonPid = (uint)p.Id;
                }
            }

            // obtain a handle to the winlogon process
            hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

            // obtain a handle to the access token of the winlogon process
            if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
            {
                CloseHandle(hProcess);
                return false;
            }

            // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
            // I would prefer to not have to use a security attribute variable and to just 
            // simply pass null and inherit (by default) the security attributes
            // of the existing token. However, in C# structures are value types and therefore
            // cannot be assigned the null value.
            SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
            sa.Length = Marshal.SizeOf(sa);

            // copy the access token of the winlogon process; the newly created token will be a primary token
            if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
            {
                CloseHandle(hProcess);
                CloseHandle(hPToken);
                return false;
            }

            // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
            // the window station has a desktop that is invisible and the process is incapable of receiving
            // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user 
            // interaction with the new process.
            STARTUPINFO si = new STARTUPINFO();
            si.cb = (int)Marshal.SizeOf(si);
            si.lpDesktop = @"winsta0default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop

            // flags that specify the priority and creation method of the process
            int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

            // create a new process in the current user's logon session
            bool result = CreateProcessAsUser(hUserTokenDup,        // client's access token
                                            null,                   // file to execute
                                            applicationName,        // command line
                                            ref sa,                 // pointer to process SECURITY_ATTRIBUTES
                                            ref sa,                 // pointer to thread SECURITY_ATTRIBUTES
                                            false,                  // handles are not inheritable
                                            dwCreationFlags,        // creation flags
                                            IntPtr.Zero,            // pointer to new environment block 
                                            startingDir,                   // name of current directory 
                                            ref si,                 // pointer to STARTUPINFO structure
                                            out procInfo            // receives information about new process
                                            );

            // invalidate the handles
            CloseHandle(hProcess);
            CloseHandle(hPToken);
            CloseHandle(hUserTokenDup);

            return result; // return the result
        }

    }
}
'@

For brevity I renamed the namespace, class, and function:
SuperAwesomeNameSpaceOfJustice = Session0
ApplicationLoader = AppLaunch
StartProcessAndBypassUAC = Start

Now we can then do this:

Add-Type -TypeDefinition $Source -Language CSharp
$procInfo = New-Object Session0.AppLaunch+PROCESS_INFORMATION

Now we can run whatever we want like this:
[Session0.AppLaunch]::Start("cmd.exe","c:\windows\system32",[ref]$procInfo)

“CreateProcessAsUser” will return either True or False depending on if it was successful or not, which is the return of the Start function.

This just launches cmd.exe as the logged in user but theoretically you could use it to launch anything…

Enjoy!