Run Solr 6.0.1 in background on Windows server












1















I'd like to start a solr core (version 6.0.1) on Windows Server 2012 R2(command is: binsolr.cmd start), and keep it running after logging off. I'd also like to run it under an account other than my own, and I'd like it to start up automatically. I came across this post, How to run solr on a windows server so it starts up automatically?, but it was from 6 years ago, so I thought there may be a new way with the new version. Also, the new version I have is not running on Tomcat. It acts as it's own server, which is another difference from the version in the example. Thanks!










share|improve this question





























    1















    I'd like to start a solr core (version 6.0.1) on Windows Server 2012 R2(command is: binsolr.cmd start), and keep it running after logging off. I'd also like to run it under an account other than my own, and I'd like it to start up automatically. I came across this post, How to run solr on a windows server so it starts up automatically?, but it was from 6 years ago, so I thought there may be a new way with the new version. Also, the new version I have is not running on Tomcat. It acts as it's own server, which is another difference from the version in the example. Thanks!










    share|improve this question



























      1












      1








      1








      I'd like to start a solr core (version 6.0.1) on Windows Server 2012 R2(command is: binsolr.cmd start), and keep it running after logging off. I'd also like to run it under an account other than my own, and I'd like it to start up automatically. I came across this post, How to run solr on a windows server so it starts up automatically?, but it was from 6 years ago, so I thought there may be a new way with the new version. Also, the new version I have is not running on Tomcat. It acts as it's own server, which is another difference from the version in the example. Thanks!










      share|improve this question
















      I'd like to start a solr core (version 6.0.1) on Windows Server 2012 R2(command is: binsolr.cmd start), and keep it running after logging off. I'd also like to run it under an account other than my own, and I'd like it to start up automatically. I came across this post, How to run solr on a windows server so it starts up automatically?, but it was from 6 years ago, so I thought there may be a new way with the new version. Also, the new version I have is not running on Tomcat. It acts as it's own server, which is another difference from the version in the example. Thanks!







      solr windows-server-2012-r2 solr6






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited May 23 '17 at 10:27









      Community

      11




      11










      asked Jun 17 '16 at 20:00









      zBombzBomb

      12912




      12912
























          2 Answers
          2






          active

          oldest

          votes


















          1














          You can use NSSM to register Solr as a Windows Service.




          "c:Program Filesnssmwin64nssm" install solr6




          It's important that you use the -f parameter to run Solr in the foreground (which in this case means "run it inside NSSM"). This can be changed in the box that pops up for NSSM ("Arguments"). You can also pick which user to run the service as under "Log on".






          share|improve this answer
























          • Thanks for the tutorial link, that was helpful!

            – zBomb
            Jun 21 '16 at 21:38



















          0














          It can be done in PowerShell without any external components. This is what I'm currently using with Solr 7.5.0 to install and run as a native Windows service. I'm reasonably certain this will work with any version that uses binsolr.cmd on Server 2012 R2.



          You need to create an environment variable called SOLR_HOME and set the value to the full path of your Solr bin directory, and possibly reboot to pick up that new environment variable. Make any necessary changes, especially to memory and port under #region Solr configuration. Then you drop the code below in a file named PSSolrService.ps1 in your Solr root directory, cd to that directory in a PowerShell console, and then run the setup command: ./PSService.ps1 -Setup This should give you a Windows service called Solr which is set to run automatically at system startup. You can also then use NET STOP Solr and NET START Solr to stop and start the service, just like any other Windows service.



          #
          # PSSolrService.ps1
          #
          #Requires -version 2

          <#
          .SYNOPSIS
          A simple Windows service, in a standalone PowerShell script.

          .DESCRIPTION
          This script demonstrates how to write a Windows service in pure PowerShell.
          It dynamically generates a small PSService.exe wrapper, that in turn
          invokes this PowerShell script again for its start and stop events.

          .PARAMETER Start
          Start the service.

          .PARAMETER Stop
          Stop the service.

          .PARAMETER Restart
          Stop then restart the service.

          .PARAMETER Status
          Get the current service status: Not installed / Stopped / Running

          .PARAMETER Setup
          Install the service.

          .PARAMETER Remove
          Uninstall the service.

          .PARAMETER Service
          Run the service in the background. Used internally by the script.
          Do not use, except for test purposes.

          .PARAMETER Control
          Send a control message to the service thread.

          .PARAMETER Version
          Display this script version and exit.

          .EXAMPLE
          # Setup the service and run it for the first time
          C:PS>.PSService.ps1 -Status
          Not installed
          C:PS>.PSService.ps1 -Setup
          C:PS># At this stage, a copy of PSService.ps1 is present in the path
          C:PS>PSService -Status
          Stopped
          C:PS>PSService -Start
          C:PS>PSService -Status
          Running
          C:PS># Load the log file in Notepad.exe for review
          C:PS>notepad ${ENV:windir}LogsPSService.log

          .EXAMPLE
          # Stop the service and uninstall it.
          C:PS>PSService -Stop
          C:PS>PSService -Status
          Stopped
          C:PS>PSService -Remove
          C:PS># At this stage, no copy of PSService.ps1 is present in the path anymore
          C:PS>.PSService.ps1 -Status
          Not installed

          .EXAMPLE
          # Send a control message to the service, and verify that it received it.
          C:PS>PSService -Control Hello
          C:PS>Notepad C:WindowsLogsPSService.log
          # The last lines should contain a trace of the reception of this Hello message
          #>

          [CmdletBinding(DefaultParameterSetName='Status')]
          Param(
          [Parameter(ParameterSetName='Start', Mandatory=$true)]
          [Switch]$Start, # Start the service

          [Parameter(ParameterSetName='Stop', Mandatory=$true)]
          [Switch]$Stop, # Stop the service

          [Parameter(ParameterSetName='Restart', Mandatory=$true)]
          [Switch]$Restart, # Restart the service

          [Parameter(ParameterSetName='Status', Mandatory=$false)]
          [Switch]$Status = $($PSCmdlet.ParameterSetName -eq 'Status'), # Get the current service status

          [Parameter(ParameterSetName='Setup', Mandatory=$true)]
          [Switch]$Setup, # Install the service

          [Parameter(ParameterSetName='Remove', Mandatory=$true)]
          [Switch]$Remove, # Uninstall the service

          [Parameter(ParameterSetName='Service', Mandatory=$true)]
          [Switch]$Service, # Run the service

          [Parameter(ParameterSetName='Control', Mandatory=$true)]
          [String]$Control = $null, # Control message to send to the service

          [Parameter(ParameterSetName='Version', Mandatory=$true)]
          [Switch]$Version # Get this script version
          )

          # modify this to update script version used by Solr Service
          $scriptVersion = "2018-11-07"

          if( -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
          {
          Write-Warning -Message "Please run thisscript with Administrator permissions."
          return
          }

          #region Solr configuration
          $solrPort = '8983'
          $solrMemory = '12g' # eg. 1g 4g
          $solrHome = [environment]::GetEnvironmentVariable("SOLR_HOME",[EnvironmentVariableTarget]::Machine)
          $solrRoot = Split-Path -Path (Split-Path -Path $solrHome -Parent) -Parent
          $sorlStartCmd = Join-Path -Path $solrRoot -ChildPath "binsolr.cmd"

          if( -not (Test-Path -Path $sorlStartCmd) )
          {
          Write-Warning "Solr.cmd not exist: $sorlStartCmd"
          return
          }
          #endregion

          # This script name, with various levels of details
          $argv0 = Get-Item $MyInvocation.MyCommand.Definition
          $script = $argv0.basename # Ex: PSSolrService
          $scriptName = $argv0.name # Ex: PSSolrService.ps1
          $scriptFullName = $argv0.fullname # Ex: C:TempPSSolrService.ps1

          # Global settings
          $serviceName = $script # A one-word name used for net start commands
          $serviceDisplayName = "Solr"
          $ServiceDescription = "Solr"

          $installDir = "${ENV:ProgramFiles}$serviceName" # Where to install the service files

          #$installDir = "${ENV:windir}System32" # Where to install the service files

          $scriptCopy = "$installDir$scriptName"
          $exeName = "$serviceName.exe"
          $exeFullName = "$installDir$exeName"
          $logDir = "${ENV:windir}Logs" # Where to log the service messages
          $logFile = "$logDir$serviceName.log"
          $logName = "Application" # Event Log name (Unrelated to the logFile!)
          # Note: The current implementation only supports "classic" (ie. XP-compatble) event logs.
          # To support new style (Vista and later) "Applications and Services Logs" folder trees, it would
          # be necessary to use the new *WinEvent commands instead of the XP-compatible *EventLog commands.
          # Gotcha: If you change $logName to "NEWLOGNAME", make sure that the registry key below does not exist:
          # HKLMSystemCurrentControlSetserviceseventlogApplicationNEWLOGNAME
          # Else, New-EventLog will fail, saying the log NEWLOGNAME is already registered as a source,
          # even though "Get-WinEvent -ListLog NEWLOGNAME" says this log does not exist!

          # If the -Version switch is specified, display the script version and exit.
          if ($Version) {
          Write-Output $scriptVersion
          return
          }

          Function Now {
          Param (
          [Switch]$ms, # Append milliseconds
          [Switch]$ns # Append nanoseconds
          )
          $Date = Get-Date
          $now = ""
          $now += "{0:0000}-{1:00}-{2:00} " -f $Date.Year, $Date.Month, $Date.Day
          $now += "{0:00}:{1:00}:{2:00}" -f $Date.Hour, $Date.Minute, $Date.Second
          $nsSuffix = ""
          if ($ns) {
          if ("$($Date.TimeOfDay)" -match ".dddddd") {
          $now += $matches[0]
          $ms = $false
          } else {
          $ms = $true
          $nsSuffix = "000"
          }
          }
          if ($ms) {
          $now += ".{0:000}$nsSuffix" -f $Date.MilliSecond
          }
          return $now
          }

          Function Log () {
          Param(
          [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)]
          [String]$string
          )
          if (!(Test-Path $logDir)) {
          New-Item -ItemType directory -Path $logDir | Out-Null
          }
          if ($String.length) {
          $string = "$(Now) $pid $userName $string"
          }
          $string | Out-File -Encoding ASCII -Append "$logFile"
          }

          $scriptCopyCname = $scriptCopy -replace "\", "\" # Double backslashes. (The first \ is a regexp with escaped; The second is a plain string.)
          $source = @"
          using System;
          using System.ServiceProcess;
          using System.Diagnostics;
          using System.Runtime.InteropServices; // SET STATUS
          using System.ComponentModel; // SET STATUS

          public enum ServiceType : int { // SET STATUS [
          SERVICE_WIN32_OWN_PROCESS = 0x00000010,
          SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
          }; // SET STATUS ]

          public enum ServiceState : int { // SET STATUS [
          SERVICE_STOPPED = 0x00000001,
          SERVICE_START_PENDING = 0x00000002,
          SERVICE_STOP_PENDING = 0x00000003,
          SERVICE_RUNNING = 0x00000004,
          SERVICE_CONTINUE_PENDING = 0x00000005,
          SERVICE_PAUSE_PENDING = 0x00000006,
          SERVICE_PAUSED = 0x00000007,
          }; // SET STATUS ]

          [StructLayout(LayoutKind.Sequential)] // SET STATUS [
          public struct ServiceStatus {
          public ServiceType dwServiceType;
          public ServiceState dwCurrentState;
          public int dwControlsAccepted;
          public int dwWin32ExitCode;
          public int dwServiceSpecificExitCode;
          public int dwCheckPoint;
          public int dwWaitHint;
          }; // SET STATUS ]

          public enum Win32Error : int { // WIN32 errors that we may need to use
          NO_ERROR = 0,
          ERROR_APP_INIT_FAILURE = 575,
          ERROR_FATAL_APP_EXIT = 713,
          ERROR_SERVICE_NOT_ACTIVE = 1062,
          ERROR_EXCEPTION_IN_SERVICE = 1064,
          ERROR_SERVICE_SPECIFIC_ERROR = 1066,
          ERROR_PROCESS_ABORTED = 1067,
          };

          public class Service_$serviceName : ServiceBase { // $serviceName may begin with a digit; The class name must begin with a letter
          private System.Diagnostics.EventLog eventLog; // EVENT LOG
          private ServiceStatus serviceStatus; // SET STATUS

          public Service_$serviceName() {
          ServiceName = "$serviceName";
          CanStop = true;
          CanPauseAndContinue = false;
          AutoLog = true;

          eventLog = new System.Diagnostics.EventLog(); // EVENT LOG [
          if (!System.Diagnostics.EventLog.SourceExists(ServiceName)) {
          System.Diagnostics.EventLog.CreateEventSource(ServiceName, "$logName");
          }
          eventLog.Source = ServiceName;
          eventLog.Log = "$logName"; // EVENT LOG ]
          EventLog.WriteEntry(ServiceName, "$exeName $serviceName()"); // EVENT LOG
          }

          [DllImport("advapi32.dll", SetLastError=true)] // SET STATUS
          private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);

          protected override void OnStart(string args) {
          EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Entry. Starting script '$scriptCopyCname' -Start"); // EVENT LOG
          // Set the service state to Start Pending. // SET STATUS [
          // Only useful if the startup time is long. Not really necessary here for a 2s startup time.
          serviceStatus.dwServiceType = ServiceType.SERVICE_WIN32_OWN_PROCESS;
          serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
          serviceStatus.dwWin32ExitCode = 0;
          serviceStatus.dwWaitHint = 2000; // It takes about 2 seconds to start PowerShell
          SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS ]
          // Start a child process with another copy of this script
          try {
          Process p = new Process();
          // Redirect the output stream of the child process.
          p.StartInfo.UseShellExecute = false;
          p.StartInfo.RedirectStandardOutput = true;
          p.StartInfo.FileName = "PowerShell.exe";
          p.StartInfo.Arguments = "-ExecutionPolicy Bypass -c & '$scriptCopyCname' -Start"; // Works if path has spaces, but not if it contains ' quotes.
          p.Start();
          // Read the output stream first and then wait. (To avoid deadlocks says Microsoft!)
          string output = p.StandardOutput.ReadToEnd();
          // Wait for the completion of the script startup code, that launches the -Service instance
          p.WaitForExit();
          if (p.ExitCode != 0) throw new Win32Exception((int)(Win32Error.ERROR_APP_INIT_FAILURE));
          // Success. Set the service state to Running. // SET STATUS
          serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING; // SET STATUS
          } catch (Exception e) {
          EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Failed to start $scriptCopyCname. " + e.Message, EventLogEntryType.Error); // EVENT LOG
          // Change the service state back to Stopped. // SET STATUS [
          serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
          Win32Exception w32ex = e as Win32Exception; // Try getting the WIN32 error code
          if (w32ex == null) { // Not a Win32 exception, but maybe the inner one is...
          w32ex = e.InnerException as Win32Exception;
          }
          if (w32ex != null) { // Report the actual WIN32 error
          serviceStatus.dwWin32ExitCode = w32ex.NativeErrorCode;
          } else { // Make up a reasonable reason
          serviceStatus.dwWin32ExitCode = (int)(Win32Error.ERROR_APP_INIT_FAILURE);
          } // SET STATUS ]
          } finally {
          serviceStatus.dwWaitHint = 0; // SET STATUS
          SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
          EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Exit"); // EVENT LOG
          }
          }

          protected override void OnStop() {
          EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Entry"); // EVENT LOG
          // Start a child process with another copy of ourselves
          Process p = new Process();
          // Redirect the output stream of the child process.
          p.StartInfo.UseShellExecute = false;
          p.StartInfo.RedirectStandardOutput = true;
          p.StartInfo.FileName = "PowerShell.exe";
          p.StartInfo.Arguments = "-c & '$scriptCopyCname' -Stop"; // Works if path has spaces, but not if it contains ' quotes.
          p.Start();
          // Read the output stream first and then wait.
          string output = p.StandardOutput.ReadToEnd();
          // Wait for the PowerShell script to be fully stopped.
          p.WaitForExit();
          // Change the service state back to Stopped. // SET STATUS
          serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED; // SET STATUS
          SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
          EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Exit"); // EVENT LOG
          }

          public static void Main() {
          System.ServiceProcess.ServiceBase.Run(new Service_$serviceName());
          }
          }
          "@

          # Check if we're running as a real user, or as the SYSTEM = As a service
          $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
          $userName = $identity.Name # Ex: "NT AUTHORITYSYSTEM" or "DomainAdministrator"
          $authority,$name = $username -split "\"
          $isSystem = $identity.IsSystem # Do not test ($userName -eq "NT AUTHORITYSYSTEM"), as this fails in non-English systems.
          # Log "# `$userName = `"$userName`" ; `$isSystem = $isSystem"

          if ($Setup) {Log ""} # Insert one blank line to separate test sessions logs
          Log $MyInvocation.Line # The exact command line that was used to start us

          # The following commands write to the event log, but we need to make sure the PSService source is defined.
          New-EventLog -LogName $logName -Source $serviceName -ea SilentlyContinue

          # Workaround for PowerShell v2 bug: $PSCmdlet Not yet defined in Param() block
          $Status = ($PSCmdlet.ParameterSetName -eq 'Status')

          if ($Start) { # Start the service
          if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
          # Do whatever is necessary to start the service script instance
          Log "$scriptName -Start: Starting script '$scriptFullName' -Service"
          Write-EventLog -LogName $logName -Source $serviceName -EventId 1001 -EntryType Information -Message "$scriptName -Start: Starting script '$scriptFullName' -Service"
          Start-Process PowerShell.exe -ArgumentList ("-c & '$scriptFullName' -Service")
          } else {
          Write-Verbose "Starting service $serviceName"
          Write-EventLog -LogName $logName -Source $serviceName -EventId 1002 -EntryType Information -Message "$scriptName -Start: Starting service $serviceName"
          Start-Service $serviceName # Ask Service Control Manager to start it
          }
          return
          }

          if ($Stop) { # Stop the service
          if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
          # Do whatever is necessary to stop the service script instance
          Write-EventLog -LogName $logName -Source $serviceName -EventId 1003 -EntryType Information -Message "$scriptName -Stop: Stopping script $scriptName -Service"
          Log "$scriptName -Stop: Stopping script $scriptName -Service"

          #region Solr stop
          &$sorlStartCmd stop -p $solrPort
          #endregion

          } else {
          Write-Verbose "Stopping service $serviceName"
          Write-EventLog -LogName $logName -Source $serviceName -EventId 1004 -EntryType Information -Message "$scriptName -Stop: Stopping service $serviceName"
          Stop-Service $serviceName # Ask Service Control Manager to stop it
          }
          return
          }

          if ($Restart) { # Restart the service
          & $scriptFullName -Stop
          & $scriptFullName -Start
          return
          }

          if ($Status) { # Get the current service status
          $spid = $null
          $processes = @(Get-WmiObject Win32_Process -filter "Name = 'powershell.exe'" | Where-Object {
          $_.CommandLine -match ".*$scriptCopyCname.*-Service"
          })
          foreach ($process in $processes) { # There should be just one, but be prepared for surprises.
          $spid = $process.ProcessId
          Write-Verbose "$serviceName Process ID = $spid"
          }
          # if (Test-Path "HKLM:SYSTEMCurrentControlSetservices$serviceName") {}
          try {
          $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
          } catch {
          "Not Installed"
          return
          }
          $pss.Status
          if (($pss.Status -eq "Running") -and (!$spid)) { # This happened during the debugging phase
          Write-Error "The Service Control Manager thinks $serviceName is started, but $serviceName.ps1 -Service is not running."
          exit 1
          }
          return
          }

          if ($Setup) { # Install the service
          # Check if it's necessary
          try {
          $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
          # Check if this script is newer than the installed copy.
          if ((Get-Item $scriptCopy -ea SilentlyContinue).LastWriteTime -lt (Get-Item $scriptFullName -ea SilentlyContinue).LastWriteTime) {
          Write-Verbose "Service $serviceName is already Installed, but requires upgrade"
          & $scriptFullName -Remove
          throw "continue"
          } else {
          Write-Verbose "Service $serviceName is already Installed, and up-to-date"
          }
          exit 0
          } catch {
          # This is the normal case here. Do not throw or write any error!
          Write-Debug "Installation is necessary" # Also avoids a ScriptAnalyzer warning
          # And continue with the installation.
          }
          if (!(Test-Path $installDir)) {
          New-Item -ItemType directory -Path $installDir | Out-Null
          }
          # Copy the service script into the installation directory
          if ($ScriptFullName -ne $scriptCopy) {
          Write-Verbose "Installing $scriptCopy"
          Copy-Item $ScriptFullName $scriptCopy
          }
          # Generate the service .EXE from the C# source embedded in this script
          try {
          Write-Verbose "Compiling $exeFullName"
          Add-Type -TypeDefinition $source -Language CSharp -OutputAssembly $exeFullName -OutputType ConsoleApplication -ReferencedAssemblies "System.ServiceProcess" -Debug:$false
          } catch {
          $msg = $_.Exception.Message
          Write-error "Failed to create the $exeFullName service stub. $msg"
          exit 1
          }
          # Register the service
          Write-Verbose "Registering service $serviceName"
          $pss = New-Service $serviceName $exeFullName -DisplayName $serviceDisplayName -Description $ServiceDescription -StartupType Automatic

          return
          }

          if ($Remove) { # Uninstall the service
          # Check if it's necessary
          try {
          $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
          } catch {
          Write-Verbose "Already uninstalled"
          return
          }
          Stop-Service $serviceName # Make sure it's stopped
          # In the absence of a Remove-Service applet, use sc.exe instead.
          Write-Verbose "Removing service $serviceName"
          $msg = sc.exe delete $serviceName
          if ($LastExitCode) {
          Write-Error "Failed to remove the service ${serviceName}: $msg"
          exit 1
          } else {
          Write-Verbose $msg
          }
          # Remove the installed files
          if (Test-Path $installDir) {
          foreach ($ext in ("exe", "pdb", "ps1")) {
          $file = "$installDir$serviceName.$ext"
          if (Test-Path $file) {
          Write-Verbose "Deleting file $file"
          Remove-Item $file
          }
          }
          if (!(@(Get-ChildItem $installDir -ea SilentlyContinue)).Count) {
          Write-Verbose "Removing directory $installDir"
          Remove-Item $installDir
          }
          }
          return
          }

          if ($Service) { # Run the service
          Write-EventLog -LogName $logName -Source $serviceName -EventId 1005 -EntryType Information -Message "$scriptName -Service # Beginning background job"
          # Do the service background job
          try
          {

          #region Solr start
          Log "$scriptName Starting $sorlStartCmd with parameteres start -f -p $solrPort -m $solrMemory"
          &$sorlStartCmd start -f -p $solrPort -m $solrMemory
          #endregion

          }
          catch
          { # An exception occurred while runnning the service
          $msg = $_.Exception.Message
          $line = $_.InvocationInfo.ScriptLineNumber
          Log "$scriptName -Service # Error at line ${line}: $msg"
          }
          finally
          {
          # Invoked in all cases: Exception or normally by -Stop

          # Flush all leftover events (There may be some that arrived after we exited the while event loop, but before we unregistered the events)
          $events = Get-Event | Remove-Event
          # Log a termination event, no matter what the cause is.
          Write-EventLog -LogName $logName -Source $serviceName -EventId 1006 -EntryType Information -Message "$script -Service # Exiting"
          Log "$scriptName -Service # Exiting"
          }
          return
          }





          share|improve this answer

























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f37889521%2frun-solr-6-0-1-in-background-on-windows-server%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            1














            You can use NSSM to register Solr as a Windows Service.




            "c:Program Filesnssmwin64nssm" install solr6




            It's important that you use the -f parameter to run Solr in the foreground (which in this case means "run it inside NSSM"). This can be changed in the box that pops up for NSSM ("Arguments"). You can also pick which user to run the service as under "Log on".






            share|improve this answer
























            • Thanks for the tutorial link, that was helpful!

              – zBomb
              Jun 21 '16 at 21:38
















            1














            You can use NSSM to register Solr as a Windows Service.




            "c:Program Filesnssmwin64nssm" install solr6




            It's important that you use the -f parameter to run Solr in the foreground (which in this case means "run it inside NSSM"). This can be changed in the box that pops up for NSSM ("Arguments"). You can also pick which user to run the service as under "Log on".






            share|improve this answer
























            • Thanks for the tutorial link, that was helpful!

              – zBomb
              Jun 21 '16 at 21:38














            1












            1








            1







            You can use NSSM to register Solr as a Windows Service.




            "c:Program Filesnssmwin64nssm" install solr6




            It's important that you use the -f parameter to run Solr in the foreground (which in this case means "run it inside NSSM"). This can be changed in the box that pops up for NSSM ("Arguments"). You can also pick which user to run the service as under "Log on".






            share|improve this answer













            You can use NSSM to register Solr as a Windows Service.




            "c:Program Filesnssmwin64nssm" install solr6




            It's important that you use the -f parameter to run Solr in the foreground (which in this case means "run it inside NSSM"). This can be changed in the box that pops up for NSSM ("Arguments"). You can also pick which user to run the service as under "Log on".







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Jun 17 '16 at 20:24









            MatsLindhMatsLindh

            25.5k32341




            25.5k32341













            • Thanks for the tutorial link, that was helpful!

              – zBomb
              Jun 21 '16 at 21:38



















            • Thanks for the tutorial link, that was helpful!

              – zBomb
              Jun 21 '16 at 21:38

















            Thanks for the tutorial link, that was helpful!

            – zBomb
            Jun 21 '16 at 21:38





            Thanks for the tutorial link, that was helpful!

            – zBomb
            Jun 21 '16 at 21:38













            0














            It can be done in PowerShell without any external components. This is what I'm currently using with Solr 7.5.0 to install and run as a native Windows service. I'm reasonably certain this will work with any version that uses binsolr.cmd on Server 2012 R2.



            You need to create an environment variable called SOLR_HOME and set the value to the full path of your Solr bin directory, and possibly reboot to pick up that new environment variable. Make any necessary changes, especially to memory and port under #region Solr configuration. Then you drop the code below in a file named PSSolrService.ps1 in your Solr root directory, cd to that directory in a PowerShell console, and then run the setup command: ./PSService.ps1 -Setup This should give you a Windows service called Solr which is set to run automatically at system startup. You can also then use NET STOP Solr and NET START Solr to stop and start the service, just like any other Windows service.



            #
            # PSSolrService.ps1
            #
            #Requires -version 2

            <#
            .SYNOPSIS
            A simple Windows service, in a standalone PowerShell script.

            .DESCRIPTION
            This script demonstrates how to write a Windows service in pure PowerShell.
            It dynamically generates a small PSService.exe wrapper, that in turn
            invokes this PowerShell script again for its start and stop events.

            .PARAMETER Start
            Start the service.

            .PARAMETER Stop
            Stop the service.

            .PARAMETER Restart
            Stop then restart the service.

            .PARAMETER Status
            Get the current service status: Not installed / Stopped / Running

            .PARAMETER Setup
            Install the service.

            .PARAMETER Remove
            Uninstall the service.

            .PARAMETER Service
            Run the service in the background. Used internally by the script.
            Do not use, except for test purposes.

            .PARAMETER Control
            Send a control message to the service thread.

            .PARAMETER Version
            Display this script version and exit.

            .EXAMPLE
            # Setup the service and run it for the first time
            C:PS>.PSService.ps1 -Status
            Not installed
            C:PS>.PSService.ps1 -Setup
            C:PS># At this stage, a copy of PSService.ps1 is present in the path
            C:PS>PSService -Status
            Stopped
            C:PS>PSService -Start
            C:PS>PSService -Status
            Running
            C:PS># Load the log file in Notepad.exe for review
            C:PS>notepad ${ENV:windir}LogsPSService.log

            .EXAMPLE
            # Stop the service and uninstall it.
            C:PS>PSService -Stop
            C:PS>PSService -Status
            Stopped
            C:PS>PSService -Remove
            C:PS># At this stage, no copy of PSService.ps1 is present in the path anymore
            C:PS>.PSService.ps1 -Status
            Not installed

            .EXAMPLE
            # Send a control message to the service, and verify that it received it.
            C:PS>PSService -Control Hello
            C:PS>Notepad C:WindowsLogsPSService.log
            # The last lines should contain a trace of the reception of this Hello message
            #>

            [CmdletBinding(DefaultParameterSetName='Status')]
            Param(
            [Parameter(ParameterSetName='Start', Mandatory=$true)]
            [Switch]$Start, # Start the service

            [Parameter(ParameterSetName='Stop', Mandatory=$true)]
            [Switch]$Stop, # Stop the service

            [Parameter(ParameterSetName='Restart', Mandatory=$true)]
            [Switch]$Restart, # Restart the service

            [Parameter(ParameterSetName='Status', Mandatory=$false)]
            [Switch]$Status = $($PSCmdlet.ParameterSetName -eq 'Status'), # Get the current service status

            [Parameter(ParameterSetName='Setup', Mandatory=$true)]
            [Switch]$Setup, # Install the service

            [Parameter(ParameterSetName='Remove', Mandatory=$true)]
            [Switch]$Remove, # Uninstall the service

            [Parameter(ParameterSetName='Service', Mandatory=$true)]
            [Switch]$Service, # Run the service

            [Parameter(ParameterSetName='Control', Mandatory=$true)]
            [String]$Control = $null, # Control message to send to the service

            [Parameter(ParameterSetName='Version', Mandatory=$true)]
            [Switch]$Version # Get this script version
            )

            # modify this to update script version used by Solr Service
            $scriptVersion = "2018-11-07"

            if( -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
            {
            Write-Warning -Message "Please run thisscript with Administrator permissions."
            return
            }

            #region Solr configuration
            $solrPort = '8983'
            $solrMemory = '12g' # eg. 1g 4g
            $solrHome = [environment]::GetEnvironmentVariable("SOLR_HOME",[EnvironmentVariableTarget]::Machine)
            $solrRoot = Split-Path -Path (Split-Path -Path $solrHome -Parent) -Parent
            $sorlStartCmd = Join-Path -Path $solrRoot -ChildPath "binsolr.cmd"

            if( -not (Test-Path -Path $sorlStartCmd) )
            {
            Write-Warning "Solr.cmd not exist: $sorlStartCmd"
            return
            }
            #endregion

            # This script name, with various levels of details
            $argv0 = Get-Item $MyInvocation.MyCommand.Definition
            $script = $argv0.basename # Ex: PSSolrService
            $scriptName = $argv0.name # Ex: PSSolrService.ps1
            $scriptFullName = $argv0.fullname # Ex: C:TempPSSolrService.ps1

            # Global settings
            $serviceName = $script # A one-word name used for net start commands
            $serviceDisplayName = "Solr"
            $ServiceDescription = "Solr"

            $installDir = "${ENV:ProgramFiles}$serviceName" # Where to install the service files

            #$installDir = "${ENV:windir}System32" # Where to install the service files

            $scriptCopy = "$installDir$scriptName"
            $exeName = "$serviceName.exe"
            $exeFullName = "$installDir$exeName"
            $logDir = "${ENV:windir}Logs" # Where to log the service messages
            $logFile = "$logDir$serviceName.log"
            $logName = "Application" # Event Log name (Unrelated to the logFile!)
            # Note: The current implementation only supports "classic" (ie. XP-compatble) event logs.
            # To support new style (Vista and later) "Applications and Services Logs" folder trees, it would
            # be necessary to use the new *WinEvent commands instead of the XP-compatible *EventLog commands.
            # Gotcha: If you change $logName to "NEWLOGNAME", make sure that the registry key below does not exist:
            # HKLMSystemCurrentControlSetserviceseventlogApplicationNEWLOGNAME
            # Else, New-EventLog will fail, saying the log NEWLOGNAME is already registered as a source,
            # even though "Get-WinEvent -ListLog NEWLOGNAME" says this log does not exist!

            # If the -Version switch is specified, display the script version and exit.
            if ($Version) {
            Write-Output $scriptVersion
            return
            }

            Function Now {
            Param (
            [Switch]$ms, # Append milliseconds
            [Switch]$ns # Append nanoseconds
            )
            $Date = Get-Date
            $now = ""
            $now += "{0:0000}-{1:00}-{2:00} " -f $Date.Year, $Date.Month, $Date.Day
            $now += "{0:00}:{1:00}:{2:00}" -f $Date.Hour, $Date.Minute, $Date.Second
            $nsSuffix = ""
            if ($ns) {
            if ("$($Date.TimeOfDay)" -match ".dddddd") {
            $now += $matches[0]
            $ms = $false
            } else {
            $ms = $true
            $nsSuffix = "000"
            }
            }
            if ($ms) {
            $now += ".{0:000}$nsSuffix" -f $Date.MilliSecond
            }
            return $now
            }

            Function Log () {
            Param(
            [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)]
            [String]$string
            )
            if (!(Test-Path $logDir)) {
            New-Item -ItemType directory -Path $logDir | Out-Null
            }
            if ($String.length) {
            $string = "$(Now) $pid $userName $string"
            }
            $string | Out-File -Encoding ASCII -Append "$logFile"
            }

            $scriptCopyCname = $scriptCopy -replace "\", "\" # Double backslashes. (The first \ is a regexp with escaped; The second is a plain string.)
            $source = @"
            using System;
            using System.ServiceProcess;
            using System.Diagnostics;
            using System.Runtime.InteropServices; // SET STATUS
            using System.ComponentModel; // SET STATUS

            public enum ServiceType : int { // SET STATUS [
            SERVICE_WIN32_OWN_PROCESS = 0x00000010,
            SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
            }; // SET STATUS ]

            public enum ServiceState : int { // SET STATUS [
            SERVICE_STOPPED = 0x00000001,
            SERVICE_START_PENDING = 0x00000002,
            SERVICE_STOP_PENDING = 0x00000003,
            SERVICE_RUNNING = 0x00000004,
            SERVICE_CONTINUE_PENDING = 0x00000005,
            SERVICE_PAUSE_PENDING = 0x00000006,
            SERVICE_PAUSED = 0x00000007,
            }; // SET STATUS ]

            [StructLayout(LayoutKind.Sequential)] // SET STATUS [
            public struct ServiceStatus {
            public ServiceType dwServiceType;
            public ServiceState dwCurrentState;
            public int dwControlsAccepted;
            public int dwWin32ExitCode;
            public int dwServiceSpecificExitCode;
            public int dwCheckPoint;
            public int dwWaitHint;
            }; // SET STATUS ]

            public enum Win32Error : int { // WIN32 errors that we may need to use
            NO_ERROR = 0,
            ERROR_APP_INIT_FAILURE = 575,
            ERROR_FATAL_APP_EXIT = 713,
            ERROR_SERVICE_NOT_ACTIVE = 1062,
            ERROR_EXCEPTION_IN_SERVICE = 1064,
            ERROR_SERVICE_SPECIFIC_ERROR = 1066,
            ERROR_PROCESS_ABORTED = 1067,
            };

            public class Service_$serviceName : ServiceBase { // $serviceName may begin with a digit; The class name must begin with a letter
            private System.Diagnostics.EventLog eventLog; // EVENT LOG
            private ServiceStatus serviceStatus; // SET STATUS

            public Service_$serviceName() {
            ServiceName = "$serviceName";
            CanStop = true;
            CanPauseAndContinue = false;
            AutoLog = true;

            eventLog = new System.Diagnostics.EventLog(); // EVENT LOG [
            if (!System.Diagnostics.EventLog.SourceExists(ServiceName)) {
            System.Diagnostics.EventLog.CreateEventSource(ServiceName, "$logName");
            }
            eventLog.Source = ServiceName;
            eventLog.Log = "$logName"; // EVENT LOG ]
            EventLog.WriteEntry(ServiceName, "$exeName $serviceName()"); // EVENT LOG
            }

            [DllImport("advapi32.dll", SetLastError=true)] // SET STATUS
            private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);

            protected override void OnStart(string args) {
            EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Entry. Starting script '$scriptCopyCname' -Start"); // EVENT LOG
            // Set the service state to Start Pending. // SET STATUS [
            // Only useful if the startup time is long. Not really necessary here for a 2s startup time.
            serviceStatus.dwServiceType = ServiceType.SERVICE_WIN32_OWN_PROCESS;
            serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
            serviceStatus.dwWin32ExitCode = 0;
            serviceStatus.dwWaitHint = 2000; // It takes about 2 seconds to start PowerShell
            SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS ]
            // Start a child process with another copy of this script
            try {
            Process p = new Process();
            // Redirect the output stream of the child process.
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.FileName = "PowerShell.exe";
            p.StartInfo.Arguments = "-ExecutionPolicy Bypass -c & '$scriptCopyCname' -Start"; // Works if path has spaces, but not if it contains ' quotes.
            p.Start();
            // Read the output stream first and then wait. (To avoid deadlocks says Microsoft!)
            string output = p.StandardOutput.ReadToEnd();
            // Wait for the completion of the script startup code, that launches the -Service instance
            p.WaitForExit();
            if (p.ExitCode != 0) throw new Win32Exception((int)(Win32Error.ERROR_APP_INIT_FAILURE));
            // Success. Set the service state to Running. // SET STATUS
            serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING; // SET STATUS
            } catch (Exception e) {
            EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Failed to start $scriptCopyCname. " + e.Message, EventLogEntryType.Error); // EVENT LOG
            // Change the service state back to Stopped. // SET STATUS [
            serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
            Win32Exception w32ex = e as Win32Exception; // Try getting the WIN32 error code
            if (w32ex == null) { // Not a Win32 exception, but maybe the inner one is...
            w32ex = e.InnerException as Win32Exception;
            }
            if (w32ex != null) { // Report the actual WIN32 error
            serviceStatus.dwWin32ExitCode = w32ex.NativeErrorCode;
            } else { // Make up a reasonable reason
            serviceStatus.dwWin32ExitCode = (int)(Win32Error.ERROR_APP_INIT_FAILURE);
            } // SET STATUS ]
            } finally {
            serviceStatus.dwWaitHint = 0; // SET STATUS
            SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
            EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Exit"); // EVENT LOG
            }
            }

            protected override void OnStop() {
            EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Entry"); // EVENT LOG
            // Start a child process with another copy of ourselves
            Process p = new Process();
            // Redirect the output stream of the child process.
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.FileName = "PowerShell.exe";
            p.StartInfo.Arguments = "-c & '$scriptCopyCname' -Stop"; // Works if path has spaces, but not if it contains ' quotes.
            p.Start();
            // Read the output stream first and then wait.
            string output = p.StandardOutput.ReadToEnd();
            // Wait for the PowerShell script to be fully stopped.
            p.WaitForExit();
            // Change the service state back to Stopped. // SET STATUS
            serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED; // SET STATUS
            SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
            EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Exit"); // EVENT LOG
            }

            public static void Main() {
            System.ServiceProcess.ServiceBase.Run(new Service_$serviceName());
            }
            }
            "@

            # Check if we're running as a real user, or as the SYSTEM = As a service
            $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
            $userName = $identity.Name # Ex: "NT AUTHORITYSYSTEM" or "DomainAdministrator"
            $authority,$name = $username -split "\"
            $isSystem = $identity.IsSystem # Do not test ($userName -eq "NT AUTHORITYSYSTEM"), as this fails in non-English systems.
            # Log "# `$userName = `"$userName`" ; `$isSystem = $isSystem"

            if ($Setup) {Log ""} # Insert one blank line to separate test sessions logs
            Log $MyInvocation.Line # The exact command line that was used to start us

            # The following commands write to the event log, but we need to make sure the PSService source is defined.
            New-EventLog -LogName $logName -Source $serviceName -ea SilentlyContinue

            # Workaround for PowerShell v2 bug: $PSCmdlet Not yet defined in Param() block
            $Status = ($PSCmdlet.ParameterSetName -eq 'Status')

            if ($Start) { # Start the service
            if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
            # Do whatever is necessary to start the service script instance
            Log "$scriptName -Start: Starting script '$scriptFullName' -Service"
            Write-EventLog -LogName $logName -Source $serviceName -EventId 1001 -EntryType Information -Message "$scriptName -Start: Starting script '$scriptFullName' -Service"
            Start-Process PowerShell.exe -ArgumentList ("-c & '$scriptFullName' -Service")
            } else {
            Write-Verbose "Starting service $serviceName"
            Write-EventLog -LogName $logName -Source $serviceName -EventId 1002 -EntryType Information -Message "$scriptName -Start: Starting service $serviceName"
            Start-Service $serviceName # Ask Service Control Manager to start it
            }
            return
            }

            if ($Stop) { # Stop the service
            if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
            # Do whatever is necessary to stop the service script instance
            Write-EventLog -LogName $logName -Source $serviceName -EventId 1003 -EntryType Information -Message "$scriptName -Stop: Stopping script $scriptName -Service"
            Log "$scriptName -Stop: Stopping script $scriptName -Service"

            #region Solr stop
            &$sorlStartCmd stop -p $solrPort
            #endregion

            } else {
            Write-Verbose "Stopping service $serviceName"
            Write-EventLog -LogName $logName -Source $serviceName -EventId 1004 -EntryType Information -Message "$scriptName -Stop: Stopping service $serviceName"
            Stop-Service $serviceName # Ask Service Control Manager to stop it
            }
            return
            }

            if ($Restart) { # Restart the service
            & $scriptFullName -Stop
            & $scriptFullName -Start
            return
            }

            if ($Status) { # Get the current service status
            $spid = $null
            $processes = @(Get-WmiObject Win32_Process -filter "Name = 'powershell.exe'" | Where-Object {
            $_.CommandLine -match ".*$scriptCopyCname.*-Service"
            })
            foreach ($process in $processes) { # There should be just one, but be prepared for surprises.
            $spid = $process.ProcessId
            Write-Verbose "$serviceName Process ID = $spid"
            }
            # if (Test-Path "HKLM:SYSTEMCurrentControlSetservices$serviceName") {}
            try {
            $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
            } catch {
            "Not Installed"
            return
            }
            $pss.Status
            if (($pss.Status -eq "Running") -and (!$spid)) { # This happened during the debugging phase
            Write-Error "The Service Control Manager thinks $serviceName is started, but $serviceName.ps1 -Service is not running."
            exit 1
            }
            return
            }

            if ($Setup) { # Install the service
            # Check if it's necessary
            try {
            $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
            # Check if this script is newer than the installed copy.
            if ((Get-Item $scriptCopy -ea SilentlyContinue).LastWriteTime -lt (Get-Item $scriptFullName -ea SilentlyContinue).LastWriteTime) {
            Write-Verbose "Service $serviceName is already Installed, but requires upgrade"
            & $scriptFullName -Remove
            throw "continue"
            } else {
            Write-Verbose "Service $serviceName is already Installed, and up-to-date"
            }
            exit 0
            } catch {
            # This is the normal case here. Do not throw or write any error!
            Write-Debug "Installation is necessary" # Also avoids a ScriptAnalyzer warning
            # And continue with the installation.
            }
            if (!(Test-Path $installDir)) {
            New-Item -ItemType directory -Path $installDir | Out-Null
            }
            # Copy the service script into the installation directory
            if ($ScriptFullName -ne $scriptCopy) {
            Write-Verbose "Installing $scriptCopy"
            Copy-Item $ScriptFullName $scriptCopy
            }
            # Generate the service .EXE from the C# source embedded in this script
            try {
            Write-Verbose "Compiling $exeFullName"
            Add-Type -TypeDefinition $source -Language CSharp -OutputAssembly $exeFullName -OutputType ConsoleApplication -ReferencedAssemblies "System.ServiceProcess" -Debug:$false
            } catch {
            $msg = $_.Exception.Message
            Write-error "Failed to create the $exeFullName service stub. $msg"
            exit 1
            }
            # Register the service
            Write-Verbose "Registering service $serviceName"
            $pss = New-Service $serviceName $exeFullName -DisplayName $serviceDisplayName -Description $ServiceDescription -StartupType Automatic

            return
            }

            if ($Remove) { # Uninstall the service
            # Check if it's necessary
            try {
            $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
            } catch {
            Write-Verbose "Already uninstalled"
            return
            }
            Stop-Service $serviceName # Make sure it's stopped
            # In the absence of a Remove-Service applet, use sc.exe instead.
            Write-Verbose "Removing service $serviceName"
            $msg = sc.exe delete $serviceName
            if ($LastExitCode) {
            Write-Error "Failed to remove the service ${serviceName}: $msg"
            exit 1
            } else {
            Write-Verbose $msg
            }
            # Remove the installed files
            if (Test-Path $installDir) {
            foreach ($ext in ("exe", "pdb", "ps1")) {
            $file = "$installDir$serviceName.$ext"
            if (Test-Path $file) {
            Write-Verbose "Deleting file $file"
            Remove-Item $file
            }
            }
            if (!(@(Get-ChildItem $installDir -ea SilentlyContinue)).Count) {
            Write-Verbose "Removing directory $installDir"
            Remove-Item $installDir
            }
            }
            return
            }

            if ($Service) { # Run the service
            Write-EventLog -LogName $logName -Source $serviceName -EventId 1005 -EntryType Information -Message "$scriptName -Service # Beginning background job"
            # Do the service background job
            try
            {

            #region Solr start
            Log "$scriptName Starting $sorlStartCmd with parameteres start -f -p $solrPort -m $solrMemory"
            &$sorlStartCmd start -f -p $solrPort -m $solrMemory
            #endregion

            }
            catch
            { # An exception occurred while runnning the service
            $msg = $_.Exception.Message
            $line = $_.InvocationInfo.ScriptLineNumber
            Log "$scriptName -Service # Error at line ${line}: $msg"
            }
            finally
            {
            # Invoked in all cases: Exception or normally by -Stop

            # Flush all leftover events (There may be some that arrived after we exited the while event loop, but before we unregistered the events)
            $events = Get-Event | Remove-Event
            # Log a termination event, no matter what the cause is.
            Write-EventLog -LogName $logName -Source $serviceName -EventId 1006 -EntryType Information -Message "$script -Service # Exiting"
            Log "$scriptName -Service # Exiting"
            }
            return
            }





            share|improve this answer






























              0














              It can be done in PowerShell without any external components. This is what I'm currently using with Solr 7.5.0 to install and run as a native Windows service. I'm reasonably certain this will work with any version that uses binsolr.cmd on Server 2012 R2.



              You need to create an environment variable called SOLR_HOME and set the value to the full path of your Solr bin directory, and possibly reboot to pick up that new environment variable. Make any necessary changes, especially to memory and port under #region Solr configuration. Then you drop the code below in a file named PSSolrService.ps1 in your Solr root directory, cd to that directory in a PowerShell console, and then run the setup command: ./PSService.ps1 -Setup This should give you a Windows service called Solr which is set to run automatically at system startup. You can also then use NET STOP Solr and NET START Solr to stop and start the service, just like any other Windows service.



              #
              # PSSolrService.ps1
              #
              #Requires -version 2

              <#
              .SYNOPSIS
              A simple Windows service, in a standalone PowerShell script.

              .DESCRIPTION
              This script demonstrates how to write a Windows service in pure PowerShell.
              It dynamically generates a small PSService.exe wrapper, that in turn
              invokes this PowerShell script again for its start and stop events.

              .PARAMETER Start
              Start the service.

              .PARAMETER Stop
              Stop the service.

              .PARAMETER Restart
              Stop then restart the service.

              .PARAMETER Status
              Get the current service status: Not installed / Stopped / Running

              .PARAMETER Setup
              Install the service.

              .PARAMETER Remove
              Uninstall the service.

              .PARAMETER Service
              Run the service in the background. Used internally by the script.
              Do not use, except for test purposes.

              .PARAMETER Control
              Send a control message to the service thread.

              .PARAMETER Version
              Display this script version and exit.

              .EXAMPLE
              # Setup the service and run it for the first time
              C:PS>.PSService.ps1 -Status
              Not installed
              C:PS>.PSService.ps1 -Setup
              C:PS># At this stage, a copy of PSService.ps1 is present in the path
              C:PS>PSService -Status
              Stopped
              C:PS>PSService -Start
              C:PS>PSService -Status
              Running
              C:PS># Load the log file in Notepad.exe for review
              C:PS>notepad ${ENV:windir}LogsPSService.log

              .EXAMPLE
              # Stop the service and uninstall it.
              C:PS>PSService -Stop
              C:PS>PSService -Status
              Stopped
              C:PS>PSService -Remove
              C:PS># At this stage, no copy of PSService.ps1 is present in the path anymore
              C:PS>.PSService.ps1 -Status
              Not installed

              .EXAMPLE
              # Send a control message to the service, and verify that it received it.
              C:PS>PSService -Control Hello
              C:PS>Notepad C:WindowsLogsPSService.log
              # The last lines should contain a trace of the reception of this Hello message
              #>

              [CmdletBinding(DefaultParameterSetName='Status')]
              Param(
              [Parameter(ParameterSetName='Start', Mandatory=$true)]
              [Switch]$Start, # Start the service

              [Parameter(ParameterSetName='Stop', Mandatory=$true)]
              [Switch]$Stop, # Stop the service

              [Parameter(ParameterSetName='Restart', Mandatory=$true)]
              [Switch]$Restart, # Restart the service

              [Parameter(ParameterSetName='Status', Mandatory=$false)]
              [Switch]$Status = $($PSCmdlet.ParameterSetName -eq 'Status'), # Get the current service status

              [Parameter(ParameterSetName='Setup', Mandatory=$true)]
              [Switch]$Setup, # Install the service

              [Parameter(ParameterSetName='Remove', Mandatory=$true)]
              [Switch]$Remove, # Uninstall the service

              [Parameter(ParameterSetName='Service', Mandatory=$true)]
              [Switch]$Service, # Run the service

              [Parameter(ParameterSetName='Control', Mandatory=$true)]
              [String]$Control = $null, # Control message to send to the service

              [Parameter(ParameterSetName='Version', Mandatory=$true)]
              [Switch]$Version # Get this script version
              )

              # modify this to update script version used by Solr Service
              $scriptVersion = "2018-11-07"

              if( -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
              {
              Write-Warning -Message "Please run thisscript with Administrator permissions."
              return
              }

              #region Solr configuration
              $solrPort = '8983'
              $solrMemory = '12g' # eg. 1g 4g
              $solrHome = [environment]::GetEnvironmentVariable("SOLR_HOME",[EnvironmentVariableTarget]::Machine)
              $solrRoot = Split-Path -Path (Split-Path -Path $solrHome -Parent) -Parent
              $sorlStartCmd = Join-Path -Path $solrRoot -ChildPath "binsolr.cmd"

              if( -not (Test-Path -Path $sorlStartCmd) )
              {
              Write-Warning "Solr.cmd not exist: $sorlStartCmd"
              return
              }
              #endregion

              # This script name, with various levels of details
              $argv0 = Get-Item $MyInvocation.MyCommand.Definition
              $script = $argv0.basename # Ex: PSSolrService
              $scriptName = $argv0.name # Ex: PSSolrService.ps1
              $scriptFullName = $argv0.fullname # Ex: C:TempPSSolrService.ps1

              # Global settings
              $serviceName = $script # A one-word name used for net start commands
              $serviceDisplayName = "Solr"
              $ServiceDescription = "Solr"

              $installDir = "${ENV:ProgramFiles}$serviceName" # Where to install the service files

              #$installDir = "${ENV:windir}System32" # Where to install the service files

              $scriptCopy = "$installDir$scriptName"
              $exeName = "$serviceName.exe"
              $exeFullName = "$installDir$exeName"
              $logDir = "${ENV:windir}Logs" # Where to log the service messages
              $logFile = "$logDir$serviceName.log"
              $logName = "Application" # Event Log name (Unrelated to the logFile!)
              # Note: The current implementation only supports "classic" (ie. XP-compatble) event logs.
              # To support new style (Vista and later) "Applications and Services Logs" folder trees, it would
              # be necessary to use the new *WinEvent commands instead of the XP-compatible *EventLog commands.
              # Gotcha: If you change $logName to "NEWLOGNAME", make sure that the registry key below does not exist:
              # HKLMSystemCurrentControlSetserviceseventlogApplicationNEWLOGNAME
              # Else, New-EventLog will fail, saying the log NEWLOGNAME is already registered as a source,
              # even though "Get-WinEvent -ListLog NEWLOGNAME" says this log does not exist!

              # If the -Version switch is specified, display the script version and exit.
              if ($Version) {
              Write-Output $scriptVersion
              return
              }

              Function Now {
              Param (
              [Switch]$ms, # Append milliseconds
              [Switch]$ns # Append nanoseconds
              )
              $Date = Get-Date
              $now = ""
              $now += "{0:0000}-{1:00}-{2:00} " -f $Date.Year, $Date.Month, $Date.Day
              $now += "{0:00}:{1:00}:{2:00}" -f $Date.Hour, $Date.Minute, $Date.Second
              $nsSuffix = ""
              if ($ns) {
              if ("$($Date.TimeOfDay)" -match ".dddddd") {
              $now += $matches[0]
              $ms = $false
              } else {
              $ms = $true
              $nsSuffix = "000"
              }
              }
              if ($ms) {
              $now += ".{0:000}$nsSuffix" -f $Date.MilliSecond
              }
              return $now
              }

              Function Log () {
              Param(
              [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)]
              [String]$string
              )
              if (!(Test-Path $logDir)) {
              New-Item -ItemType directory -Path $logDir | Out-Null
              }
              if ($String.length) {
              $string = "$(Now) $pid $userName $string"
              }
              $string | Out-File -Encoding ASCII -Append "$logFile"
              }

              $scriptCopyCname = $scriptCopy -replace "\", "\" # Double backslashes. (The first \ is a regexp with escaped; The second is a plain string.)
              $source = @"
              using System;
              using System.ServiceProcess;
              using System.Diagnostics;
              using System.Runtime.InteropServices; // SET STATUS
              using System.ComponentModel; // SET STATUS

              public enum ServiceType : int { // SET STATUS [
              SERVICE_WIN32_OWN_PROCESS = 0x00000010,
              SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
              }; // SET STATUS ]

              public enum ServiceState : int { // SET STATUS [
              SERVICE_STOPPED = 0x00000001,
              SERVICE_START_PENDING = 0x00000002,
              SERVICE_STOP_PENDING = 0x00000003,
              SERVICE_RUNNING = 0x00000004,
              SERVICE_CONTINUE_PENDING = 0x00000005,
              SERVICE_PAUSE_PENDING = 0x00000006,
              SERVICE_PAUSED = 0x00000007,
              }; // SET STATUS ]

              [StructLayout(LayoutKind.Sequential)] // SET STATUS [
              public struct ServiceStatus {
              public ServiceType dwServiceType;
              public ServiceState dwCurrentState;
              public int dwControlsAccepted;
              public int dwWin32ExitCode;
              public int dwServiceSpecificExitCode;
              public int dwCheckPoint;
              public int dwWaitHint;
              }; // SET STATUS ]

              public enum Win32Error : int { // WIN32 errors that we may need to use
              NO_ERROR = 0,
              ERROR_APP_INIT_FAILURE = 575,
              ERROR_FATAL_APP_EXIT = 713,
              ERROR_SERVICE_NOT_ACTIVE = 1062,
              ERROR_EXCEPTION_IN_SERVICE = 1064,
              ERROR_SERVICE_SPECIFIC_ERROR = 1066,
              ERROR_PROCESS_ABORTED = 1067,
              };

              public class Service_$serviceName : ServiceBase { // $serviceName may begin with a digit; The class name must begin with a letter
              private System.Diagnostics.EventLog eventLog; // EVENT LOG
              private ServiceStatus serviceStatus; // SET STATUS

              public Service_$serviceName() {
              ServiceName = "$serviceName";
              CanStop = true;
              CanPauseAndContinue = false;
              AutoLog = true;

              eventLog = new System.Diagnostics.EventLog(); // EVENT LOG [
              if (!System.Diagnostics.EventLog.SourceExists(ServiceName)) {
              System.Diagnostics.EventLog.CreateEventSource(ServiceName, "$logName");
              }
              eventLog.Source = ServiceName;
              eventLog.Log = "$logName"; // EVENT LOG ]
              EventLog.WriteEntry(ServiceName, "$exeName $serviceName()"); // EVENT LOG
              }

              [DllImport("advapi32.dll", SetLastError=true)] // SET STATUS
              private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);

              protected override void OnStart(string args) {
              EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Entry. Starting script '$scriptCopyCname' -Start"); // EVENT LOG
              // Set the service state to Start Pending. // SET STATUS [
              // Only useful if the startup time is long. Not really necessary here for a 2s startup time.
              serviceStatus.dwServiceType = ServiceType.SERVICE_WIN32_OWN_PROCESS;
              serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
              serviceStatus.dwWin32ExitCode = 0;
              serviceStatus.dwWaitHint = 2000; // It takes about 2 seconds to start PowerShell
              SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS ]
              // Start a child process with another copy of this script
              try {
              Process p = new Process();
              // Redirect the output stream of the child process.
              p.StartInfo.UseShellExecute = false;
              p.StartInfo.RedirectStandardOutput = true;
              p.StartInfo.FileName = "PowerShell.exe";
              p.StartInfo.Arguments = "-ExecutionPolicy Bypass -c & '$scriptCopyCname' -Start"; // Works if path has spaces, but not if it contains ' quotes.
              p.Start();
              // Read the output stream first and then wait. (To avoid deadlocks says Microsoft!)
              string output = p.StandardOutput.ReadToEnd();
              // Wait for the completion of the script startup code, that launches the -Service instance
              p.WaitForExit();
              if (p.ExitCode != 0) throw new Win32Exception((int)(Win32Error.ERROR_APP_INIT_FAILURE));
              // Success. Set the service state to Running. // SET STATUS
              serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING; // SET STATUS
              } catch (Exception e) {
              EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Failed to start $scriptCopyCname. " + e.Message, EventLogEntryType.Error); // EVENT LOG
              // Change the service state back to Stopped. // SET STATUS [
              serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
              Win32Exception w32ex = e as Win32Exception; // Try getting the WIN32 error code
              if (w32ex == null) { // Not a Win32 exception, but maybe the inner one is...
              w32ex = e.InnerException as Win32Exception;
              }
              if (w32ex != null) { // Report the actual WIN32 error
              serviceStatus.dwWin32ExitCode = w32ex.NativeErrorCode;
              } else { // Make up a reasonable reason
              serviceStatus.dwWin32ExitCode = (int)(Win32Error.ERROR_APP_INIT_FAILURE);
              } // SET STATUS ]
              } finally {
              serviceStatus.dwWaitHint = 0; // SET STATUS
              SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
              EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Exit"); // EVENT LOG
              }
              }

              protected override void OnStop() {
              EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Entry"); // EVENT LOG
              // Start a child process with another copy of ourselves
              Process p = new Process();
              // Redirect the output stream of the child process.
              p.StartInfo.UseShellExecute = false;
              p.StartInfo.RedirectStandardOutput = true;
              p.StartInfo.FileName = "PowerShell.exe";
              p.StartInfo.Arguments = "-c & '$scriptCopyCname' -Stop"; // Works if path has spaces, but not if it contains ' quotes.
              p.Start();
              // Read the output stream first and then wait.
              string output = p.StandardOutput.ReadToEnd();
              // Wait for the PowerShell script to be fully stopped.
              p.WaitForExit();
              // Change the service state back to Stopped. // SET STATUS
              serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED; // SET STATUS
              SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
              EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Exit"); // EVENT LOG
              }

              public static void Main() {
              System.ServiceProcess.ServiceBase.Run(new Service_$serviceName());
              }
              }
              "@

              # Check if we're running as a real user, or as the SYSTEM = As a service
              $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
              $userName = $identity.Name # Ex: "NT AUTHORITYSYSTEM" or "DomainAdministrator"
              $authority,$name = $username -split "\"
              $isSystem = $identity.IsSystem # Do not test ($userName -eq "NT AUTHORITYSYSTEM"), as this fails in non-English systems.
              # Log "# `$userName = `"$userName`" ; `$isSystem = $isSystem"

              if ($Setup) {Log ""} # Insert one blank line to separate test sessions logs
              Log $MyInvocation.Line # The exact command line that was used to start us

              # The following commands write to the event log, but we need to make sure the PSService source is defined.
              New-EventLog -LogName $logName -Source $serviceName -ea SilentlyContinue

              # Workaround for PowerShell v2 bug: $PSCmdlet Not yet defined in Param() block
              $Status = ($PSCmdlet.ParameterSetName -eq 'Status')

              if ($Start) { # Start the service
              if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
              # Do whatever is necessary to start the service script instance
              Log "$scriptName -Start: Starting script '$scriptFullName' -Service"
              Write-EventLog -LogName $logName -Source $serviceName -EventId 1001 -EntryType Information -Message "$scriptName -Start: Starting script '$scriptFullName' -Service"
              Start-Process PowerShell.exe -ArgumentList ("-c & '$scriptFullName' -Service")
              } else {
              Write-Verbose "Starting service $serviceName"
              Write-EventLog -LogName $logName -Source $serviceName -EventId 1002 -EntryType Information -Message "$scriptName -Start: Starting service $serviceName"
              Start-Service $serviceName # Ask Service Control Manager to start it
              }
              return
              }

              if ($Stop) { # Stop the service
              if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
              # Do whatever is necessary to stop the service script instance
              Write-EventLog -LogName $logName -Source $serviceName -EventId 1003 -EntryType Information -Message "$scriptName -Stop: Stopping script $scriptName -Service"
              Log "$scriptName -Stop: Stopping script $scriptName -Service"

              #region Solr stop
              &$sorlStartCmd stop -p $solrPort
              #endregion

              } else {
              Write-Verbose "Stopping service $serviceName"
              Write-EventLog -LogName $logName -Source $serviceName -EventId 1004 -EntryType Information -Message "$scriptName -Stop: Stopping service $serviceName"
              Stop-Service $serviceName # Ask Service Control Manager to stop it
              }
              return
              }

              if ($Restart) { # Restart the service
              & $scriptFullName -Stop
              & $scriptFullName -Start
              return
              }

              if ($Status) { # Get the current service status
              $spid = $null
              $processes = @(Get-WmiObject Win32_Process -filter "Name = 'powershell.exe'" | Where-Object {
              $_.CommandLine -match ".*$scriptCopyCname.*-Service"
              })
              foreach ($process in $processes) { # There should be just one, but be prepared for surprises.
              $spid = $process.ProcessId
              Write-Verbose "$serviceName Process ID = $spid"
              }
              # if (Test-Path "HKLM:SYSTEMCurrentControlSetservices$serviceName") {}
              try {
              $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
              } catch {
              "Not Installed"
              return
              }
              $pss.Status
              if (($pss.Status -eq "Running") -and (!$spid)) { # This happened during the debugging phase
              Write-Error "The Service Control Manager thinks $serviceName is started, but $serviceName.ps1 -Service is not running."
              exit 1
              }
              return
              }

              if ($Setup) { # Install the service
              # Check if it's necessary
              try {
              $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
              # Check if this script is newer than the installed copy.
              if ((Get-Item $scriptCopy -ea SilentlyContinue).LastWriteTime -lt (Get-Item $scriptFullName -ea SilentlyContinue).LastWriteTime) {
              Write-Verbose "Service $serviceName is already Installed, but requires upgrade"
              & $scriptFullName -Remove
              throw "continue"
              } else {
              Write-Verbose "Service $serviceName is already Installed, and up-to-date"
              }
              exit 0
              } catch {
              # This is the normal case here. Do not throw or write any error!
              Write-Debug "Installation is necessary" # Also avoids a ScriptAnalyzer warning
              # And continue with the installation.
              }
              if (!(Test-Path $installDir)) {
              New-Item -ItemType directory -Path $installDir | Out-Null
              }
              # Copy the service script into the installation directory
              if ($ScriptFullName -ne $scriptCopy) {
              Write-Verbose "Installing $scriptCopy"
              Copy-Item $ScriptFullName $scriptCopy
              }
              # Generate the service .EXE from the C# source embedded in this script
              try {
              Write-Verbose "Compiling $exeFullName"
              Add-Type -TypeDefinition $source -Language CSharp -OutputAssembly $exeFullName -OutputType ConsoleApplication -ReferencedAssemblies "System.ServiceProcess" -Debug:$false
              } catch {
              $msg = $_.Exception.Message
              Write-error "Failed to create the $exeFullName service stub. $msg"
              exit 1
              }
              # Register the service
              Write-Verbose "Registering service $serviceName"
              $pss = New-Service $serviceName $exeFullName -DisplayName $serviceDisplayName -Description $ServiceDescription -StartupType Automatic

              return
              }

              if ($Remove) { # Uninstall the service
              # Check if it's necessary
              try {
              $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
              } catch {
              Write-Verbose "Already uninstalled"
              return
              }
              Stop-Service $serviceName # Make sure it's stopped
              # In the absence of a Remove-Service applet, use sc.exe instead.
              Write-Verbose "Removing service $serviceName"
              $msg = sc.exe delete $serviceName
              if ($LastExitCode) {
              Write-Error "Failed to remove the service ${serviceName}: $msg"
              exit 1
              } else {
              Write-Verbose $msg
              }
              # Remove the installed files
              if (Test-Path $installDir) {
              foreach ($ext in ("exe", "pdb", "ps1")) {
              $file = "$installDir$serviceName.$ext"
              if (Test-Path $file) {
              Write-Verbose "Deleting file $file"
              Remove-Item $file
              }
              }
              if (!(@(Get-ChildItem $installDir -ea SilentlyContinue)).Count) {
              Write-Verbose "Removing directory $installDir"
              Remove-Item $installDir
              }
              }
              return
              }

              if ($Service) { # Run the service
              Write-EventLog -LogName $logName -Source $serviceName -EventId 1005 -EntryType Information -Message "$scriptName -Service # Beginning background job"
              # Do the service background job
              try
              {

              #region Solr start
              Log "$scriptName Starting $sorlStartCmd with parameteres start -f -p $solrPort -m $solrMemory"
              &$sorlStartCmd start -f -p $solrPort -m $solrMemory
              #endregion

              }
              catch
              { # An exception occurred while runnning the service
              $msg = $_.Exception.Message
              $line = $_.InvocationInfo.ScriptLineNumber
              Log "$scriptName -Service # Error at line ${line}: $msg"
              }
              finally
              {
              # Invoked in all cases: Exception or normally by -Stop

              # Flush all leftover events (There may be some that arrived after we exited the while event loop, but before we unregistered the events)
              $events = Get-Event | Remove-Event
              # Log a termination event, no matter what the cause is.
              Write-EventLog -LogName $logName -Source $serviceName -EventId 1006 -EntryType Information -Message "$script -Service # Exiting"
              Log "$scriptName -Service # Exiting"
              }
              return
              }





              share|improve this answer




























                0












                0








                0







                It can be done in PowerShell without any external components. This is what I'm currently using with Solr 7.5.0 to install and run as a native Windows service. I'm reasonably certain this will work with any version that uses binsolr.cmd on Server 2012 R2.



                You need to create an environment variable called SOLR_HOME and set the value to the full path of your Solr bin directory, and possibly reboot to pick up that new environment variable. Make any necessary changes, especially to memory and port under #region Solr configuration. Then you drop the code below in a file named PSSolrService.ps1 in your Solr root directory, cd to that directory in a PowerShell console, and then run the setup command: ./PSService.ps1 -Setup This should give you a Windows service called Solr which is set to run automatically at system startup. You can also then use NET STOP Solr and NET START Solr to stop and start the service, just like any other Windows service.



                #
                # PSSolrService.ps1
                #
                #Requires -version 2

                <#
                .SYNOPSIS
                A simple Windows service, in a standalone PowerShell script.

                .DESCRIPTION
                This script demonstrates how to write a Windows service in pure PowerShell.
                It dynamically generates a small PSService.exe wrapper, that in turn
                invokes this PowerShell script again for its start and stop events.

                .PARAMETER Start
                Start the service.

                .PARAMETER Stop
                Stop the service.

                .PARAMETER Restart
                Stop then restart the service.

                .PARAMETER Status
                Get the current service status: Not installed / Stopped / Running

                .PARAMETER Setup
                Install the service.

                .PARAMETER Remove
                Uninstall the service.

                .PARAMETER Service
                Run the service in the background. Used internally by the script.
                Do not use, except for test purposes.

                .PARAMETER Control
                Send a control message to the service thread.

                .PARAMETER Version
                Display this script version and exit.

                .EXAMPLE
                # Setup the service and run it for the first time
                C:PS>.PSService.ps1 -Status
                Not installed
                C:PS>.PSService.ps1 -Setup
                C:PS># At this stage, a copy of PSService.ps1 is present in the path
                C:PS>PSService -Status
                Stopped
                C:PS>PSService -Start
                C:PS>PSService -Status
                Running
                C:PS># Load the log file in Notepad.exe for review
                C:PS>notepad ${ENV:windir}LogsPSService.log

                .EXAMPLE
                # Stop the service and uninstall it.
                C:PS>PSService -Stop
                C:PS>PSService -Status
                Stopped
                C:PS>PSService -Remove
                C:PS># At this stage, no copy of PSService.ps1 is present in the path anymore
                C:PS>.PSService.ps1 -Status
                Not installed

                .EXAMPLE
                # Send a control message to the service, and verify that it received it.
                C:PS>PSService -Control Hello
                C:PS>Notepad C:WindowsLogsPSService.log
                # The last lines should contain a trace of the reception of this Hello message
                #>

                [CmdletBinding(DefaultParameterSetName='Status')]
                Param(
                [Parameter(ParameterSetName='Start', Mandatory=$true)]
                [Switch]$Start, # Start the service

                [Parameter(ParameterSetName='Stop', Mandatory=$true)]
                [Switch]$Stop, # Stop the service

                [Parameter(ParameterSetName='Restart', Mandatory=$true)]
                [Switch]$Restart, # Restart the service

                [Parameter(ParameterSetName='Status', Mandatory=$false)]
                [Switch]$Status = $($PSCmdlet.ParameterSetName -eq 'Status'), # Get the current service status

                [Parameter(ParameterSetName='Setup', Mandatory=$true)]
                [Switch]$Setup, # Install the service

                [Parameter(ParameterSetName='Remove', Mandatory=$true)]
                [Switch]$Remove, # Uninstall the service

                [Parameter(ParameterSetName='Service', Mandatory=$true)]
                [Switch]$Service, # Run the service

                [Parameter(ParameterSetName='Control', Mandatory=$true)]
                [String]$Control = $null, # Control message to send to the service

                [Parameter(ParameterSetName='Version', Mandatory=$true)]
                [Switch]$Version # Get this script version
                )

                # modify this to update script version used by Solr Service
                $scriptVersion = "2018-11-07"

                if( -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
                {
                Write-Warning -Message "Please run thisscript with Administrator permissions."
                return
                }

                #region Solr configuration
                $solrPort = '8983'
                $solrMemory = '12g' # eg. 1g 4g
                $solrHome = [environment]::GetEnvironmentVariable("SOLR_HOME",[EnvironmentVariableTarget]::Machine)
                $solrRoot = Split-Path -Path (Split-Path -Path $solrHome -Parent) -Parent
                $sorlStartCmd = Join-Path -Path $solrRoot -ChildPath "binsolr.cmd"

                if( -not (Test-Path -Path $sorlStartCmd) )
                {
                Write-Warning "Solr.cmd not exist: $sorlStartCmd"
                return
                }
                #endregion

                # This script name, with various levels of details
                $argv0 = Get-Item $MyInvocation.MyCommand.Definition
                $script = $argv0.basename # Ex: PSSolrService
                $scriptName = $argv0.name # Ex: PSSolrService.ps1
                $scriptFullName = $argv0.fullname # Ex: C:TempPSSolrService.ps1

                # Global settings
                $serviceName = $script # A one-word name used for net start commands
                $serviceDisplayName = "Solr"
                $ServiceDescription = "Solr"

                $installDir = "${ENV:ProgramFiles}$serviceName" # Where to install the service files

                #$installDir = "${ENV:windir}System32" # Where to install the service files

                $scriptCopy = "$installDir$scriptName"
                $exeName = "$serviceName.exe"
                $exeFullName = "$installDir$exeName"
                $logDir = "${ENV:windir}Logs" # Where to log the service messages
                $logFile = "$logDir$serviceName.log"
                $logName = "Application" # Event Log name (Unrelated to the logFile!)
                # Note: The current implementation only supports "classic" (ie. XP-compatble) event logs.
                # To support new style (Vista and later) "Applications and Services Logs" folder trees, it would
                # be necessary to use the new *WinEvent commands instead of the XP-compatible *EventLog commands.
                # Gotcha: If you change $logName to "NEWLOGNAME", make sure that the registry key below does not exist:
                # HKLMSystemCurrentControlSetserviceseventlogApplicationNEWLOGNAME
                # Else, New-EventLog will fail, saying the log NEWLOGNAME is already registered as a source,
                # even though "Get-WinEvent -ListLog NEWLOGNAME" says this log does not exist!

                # If the -Version switch is specified, display the script version and exit.
                if ($Version) {
                Write-Output $scriptVersion
                return
                }

                Function Now {
                Param (
                [Switch]$ms, # Append milliseconds
                [Switch]$ns # Append nanoseconds
                )
                $Date = Get-Date
                $now = ""
                $now += "{0:0000}-{1:00}-{2:00} " -f $Date.Year, $Date.Month, $Date.Day
                $now += "{0:00}:{1:00}:{2:00}" -f $Date.Hour, $Date.Minute, $Date.Second
                $nsSuffix = ""
                if ($ns) {
                if ("$($Date.TimeOfDay)" -match ".dddddd") {
                $now += $matches[0]
                $ms = $false
                } else {
                $ms = $true
                $nsSuffix = "000"
                }
                }
                if ($ms) {
                $now += ".{0:000}$nsSuffix" -f $Date.MilliSecond
                }
                return $now
                }

                Function Log () {
                Param(
                [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)]
                [String]$string
                )
                if (!(Test-Path $logDir)) {
                New-Item -ItemType directory -Path $logDir | Out-Null
                }
                if ($String.length) {
                $string = "$(Now) $pid $userName $string"
                }
                $string | Out-File -Encoding ASCII -Append "$logFile"
                }

                $scriptCopyCname = $scriptCopy -replace "\", "\" # Double backslashes. (The first \ is a regexp with escaped; The second is a plain string.)
                $source = @"
                using System;
                using System.ServiceProcess;
                using System.Diagnostics;
                using System.Runtime.InteropServices; // SET STATUS
                using System.ComponentModel; // SET STATUS

                public enum ServiceType : int { // SET STATUS [
                SERVICE_WIN32_OWN_PROCESS = 0x00000010,
                SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
                }; // SET STATUS ]

                public enum ServiceState : int { // SET STATUS [
                SERVICE_STOPPED = 0x00000001,
                SERVICE_START_PENDING = 0x00000002,
                SERVICE_STOP_PENDING = 0x00000003,
                SERVICE_RUNNING = 0x00000004,
                SERVICE_CONTINUE_PENDING = 0x00000005,
                SERVICE_PAUSE_PENDING = 0x00000006,
                SERVICE_PAUSED = 0x00000007,
                }; // SET STATUS ]

                [StructLayout(LayoutKind.Sequential)] // SET STATUS [
                public struct ServiceStatus {
                public ServiceType dwServiceType;
                public ServiceState dwCurrentState;
                public int dwControlsAccepted;
                public int dwWin32ExitCode;
                public int dwServiceSpecificExitCode;
                public int dwCheckPoint;
                public int dwWaitHint;
                }; // SET STATUS ]

                public enum Win32Error : int { // WIN32 errors that we may need to use
                NO_ERROR = 0,
                ERROR_APP_INIT_FAILURE = 575,
                ERROR_FATAL_APP_EXIT = 713,
                ERROR_SERVICE_NOT_ACTIVE = 1062,
                ERROR_EXCEPTION_IN_SERVICE = 1064,
                ERROR_SERVICE_SPECIFIC_ERROR = 1066,
                ERROR_PROCESS_ABORTED = 1067,
                };

                public class Service_$serviceName : ServiceBase { // $serviceName may begin with a digit; The class name must begin with a letter
                private System.Diagnostics.EventLog eventLog; // EVENT LOG
                private ServiceStatus serviceStatus; // SET STATUS

                public Service_$serviceName() {
                ServiceName = "$serviceName";
                CanStop = true;
                CanPauseAndContinue = false;
                AutoLog = true;

                eventLog = new System.Diagnostics.EventLog(); // EVENT LOG [
                if (!System.Diagnostics.EventLog.SourceExists(ServiceName)) {
                System.Diagnostics.EventLog.CreateEventSource(ServiceName, "$logName");
                }
                eventLog.Source = ServiceName;
                eventLog.Log = "$logName"; // EVENT LOG ]
                EventLog.WriteEntry(ServiceName, "$exeName $serviceName()"); // EVENT LOG
                }

                [DllImport("advapi32.dll", SetLastError=true)] // SET STATUS
                private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);

                protected override void OnStart(string args) {
                EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Entry. Starting script '$scriptCopyCname' -Start"); // EVENT LOG
                // Set the service state to Start Pending. // SET STATUS [
                // Only useful if the startup time is long. Not really necessary here for a 2s startup time.
                serviceStatus.dwServiceType = ServiceType.SERVICE_WIN32_OWN_PROCESS;
                serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
                serviceStatus.dwWin32ExitCode = 0;
                serviceStatus.dwWaitHint = 2000; // It takes about 2 seconds to start PowerShell
                SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS ]
                // Start a child process with another copy of this script
                try {
                Process p = new Process();
                // Redirect the output stream of the child process.
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.FileName = "PowerShell.exe";
                p.StartInfo.Arguments = "-ExecutionPolicy Bypass -c & '$scriptCopyCname' -Start"; // Works if path has spaces, but not if it contains ' quotes.
                p.Start();
                // Read the output stream first and then wait. (To avoid deadlocks says Microsoft!)
                string output = p.StandardOutput.ReadToEnd();
                // Wait for the completion of the script startup code, that launches the -Service instance
                p.WaitForExit();
                if (p.ExitCode != 0) throw new Win32Exception((int)(Win32Error.ERROR_APP_INIT_FAILURE));
                // Success. Set the service state to Running. // SET STATUS
                serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING; // SET STATUS
                } catch (Exception e) {
                EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Failed to start $scriptCopyCname. " + e.Message, EventLogEntryType.Error); // EVENT LOG
                // Change the service state back to Stopped. // SET STATUS [
                serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
                Win32Exception w32ex = e as Win32Exception; // Try getting the WIN32 error code
                if (w32ex == null) { // Not a Win32 exception, but maybe the inner one is...
                w32ex = e.InnerException as Win32Exception;
                }
                if (w32ex != null) { // Report the actual WIN32 error
                serviceStatus.dwWin32ExitCode = w32ex.NativeErrorCode;
                } else { // Make up a reasonable reason
                serviceStatus.dwWin32ExitCode = (int)(Win32Error.ERROR_APP_INIT_FAILURE);
                } // SET STATUS ]
                } finally {
                serviceStatus.dwWaitHint = 0; // SET STATUS
                SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
                EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Exit"); // EVENT LOG
                }
                }

                protected override void OnStop() {
                EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Entry"); // EVENT LOG
                // Start a child process with another copy of ourselves
                Process p = new Process();
                // Redirect the output stream of the child process.
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.FileName = "PowerShell.exe";
                p.StartInfo.Arguments = "-c & '$scriptCopyCname' -Stop"; // Works if path has spaces, but not if it contains ' quotes.
                p.Start();
                // Read the output stream first and then wait.
                string output = p.StandardOutput.ReadToEnd();
                // Wait for the PowerShell script to be fully stopped.
                p.WaitForExit();
                // Change the service state back to Stopped. // SET STATUS
                serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED; // SET STATUS
                SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
                EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Exit"); // EVENT LOG
                }

                public static void Main() {
                System.ServiceProcess.ServiceBase.Run(new Service_$serviceName());
                }
                }
                "@

                # Check if we're running as a real user, or as the SYSTEM = As a service
                $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
                $userName = $identity.Name # Ex: "NT AUTHORITYSYSTEM" or "DomainAdministrator"
                $authority,$name = $username -split "\"
                $isSystem = $identity.IsSystem # Do not test ($userName -eq "NT AUTHORITYSYSTEM"), as this fails in non-English systems.
                # Log "# `$userName = `"$userName`" ; `$isSystem = $isSystem"

                if ($Setup) {Log ""} # Insert one blank line to separate test sessions logs
                Log $MyInvocation.Line # The exact command line that was used to start us

                # The following commands write to the event log, but we need to make sure the PSService source is defined.
                New-EventLog -LogName $logName -Source $serviceName -ea SilentlyContinue

                # Workaround for PowerShell v2 bug: $PSCmdlet Not yet defined in Param() block
                $Status = ($PSCmdlet.ParameterSetName -eq 'Status')

                if ($Start) { # Start the service
                if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
                # Do whatever is necessary to start the service script instance
                Log "$scriptName -Start: Starting script '$scriptFullName' -Service"
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1001 -EntryType Information -Message "$scriptName -Start: Starting script '$scriptFullName' -Service"
                Start-Process PowerShell.exe -ArgumentList ("-c & '$scriptFullName' -Service")
                } else {
                Write-Verbose "Starting service $serviceName"
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1002 -EntryType Information -Message "$scriptName -Start: Starting service $serviceName"
                Start-Service $serviceName # Ask Service Control Manager to start it
                }
                return
                }

                if ($Stop) { # Stop the service
                if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
                # Do whatever is necessary to stop the service script instance
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1003 -EntryType Information -Message "$scriptName -Stop: Stopping script $scriptName -Service"
                Log "$scriptName -Stop: Stopping script $scriptName -Service"

                #region Solr stop
                &$sorlStartCmd stop -p $solrPort
                #endregion

                } else {
                Write-Verbose "Stopping service $serviceName"
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1004 -EntryType Information -Message "$scriptName -Stop: Stopping service $serviceName"
                Stop-Service $serviceName # Ask Service Control Manager to stop it
                }
                return
                }

                if ($Restart) { # Restart the service
                & $scriptFullName -Stop
                & $scriptFullName -Start
                return
                }

                if ($Status) { # Get the current service status
                $spid = $null
                $processes = @(Get-WmiObject Win32_Process -filter "Name = 'powershell.exe'" | Where-Object {
                $_.CommandLine -match ".*$scriptCopyCname.*-Service"
                })
                foreach ($process in $processes) { # There should be just one, but be prepared for surprises.
                $spid = $process.ProcessId
                Write-Verbose "$serviceName Process ID = $spid"
                }
                # if (Test-Path "HKLM:SYSTEMCurrentControlSetservices$serviceName") {}
                try {
                $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
                } catch {
                "Not Installed"
                return
                }
                $pss.Status
                if (($pss.Status -eq "Running") -and (!$spid)) { # This happened during the debugging phase
                Write-Error "The Service Control Manager thinks $serviceName is started, but $serviceName.ps1 -Service is not running."
                exit 1
                }
                return
                }

                if ($Setup) { # Install the service
                # Check if it's necessary
                try {
                $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
                # Check if this script is newer than the installed copy.
                if ((Get-Item $scriptCopy -ea SilentlyContinue).LastWriteTime -lt (Get-Item $scriptFullName -ea SilentlyContinue).LastWriteTime) {
                Write-Verbose "Service $serviceName is already Installed, but requires upgrade"
                & $scriptFullName -Remove
                throw "continue"
                } else {
                Write-Verbose "Service $serviceName is already Installed, and up-to-date"
                }
                exit 0
                } catch {
                # This is the normal case here. Do not throw or write any error!
                Write-Debug "Installation is necessary" # Also avoids a ScriptAnalyzer warning
                # And continue with the installation.
                }
                if (!(Test-Path $installDir)) {
                New-Item -ItemType directory -Path $installDir | Out-Null
                }
                # Copy the service script into the installation directory
                if ($ScriptFullName -ne $scriptCopy) {
                Write-Verbose "Installing $scriptCopy"
                Copy-Item $ScriptFullName $scriptCopy
                }
                # Generate the service .EXE from the C# source embedded in this script
                try {
                Write-Verbose "Compiling $exeFullName"
                Add-Type -TypeDefinition $source -Language CSharp -OutputAssembly $exeFullName -OutputType ConsoleApplication -ReferencedAssemblies "System.ServiceProcess" -Debug:$false
                } catch {
                $msg = $_.Exception.Message
                Write-error "Failed to create the $exeFullName service stub. $msg"
                exit 1
                }
                # Register the service
                Write-Verbose "Registering service $serviceName"
                $pss = New-Service $serviceName $exeFullName -DisplayName $serviceDisplayName -Description $ServiceDescription -StartupType Automatic

                return
                }

                if ($Remove) { # Uninstall the service
                # Check if it's necessary
                try {
                $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
                } catch {
                Write-Verbose "Already uninstalled"
                return
                }
                Stop-Service $serviceName # Make sure it's stopped
                # In the absence of a Remove-Service applet, use sc.exe instead.
                Write-Verbose "Removing service $serviceName"
                $msg = sc.exe delete $serviceName
                if ($LastExitCode) {
                Write-Error "Failed to remove the service ${serviceName}: $msg"
                exit 1
                } else {
                Write-Verbose $msg
                }
                # Remove the installed files
                if (Test-Path $installDir) {
                foreach ($ext in ("exe", "pdb", "ps1")) {
                $file = "$installDir$serviceName.$ext"
                if (Test-Path $file) {
                Write-Verbose "Deleting file $file"
                Remove-Item $file
                }
                }
                if (!(@(Get-ChildItem $installDir -ea SilentlyContinue)).Count) {
                Write-Verbose "Removing directory $installDir"
                Remove-Item $installDir
                }
                }
                return
                }

                if ($Service) { # Run the service
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1005 -EntryType Information -Message "$scriptName -Service # Beginning background job"
                # Do the service background job
                try
                {

                #region Solr start
                Log "$scriptName Starting $sorlStartCmd with parameteres start -f -p $solrPort -m $solrMemory"
                &$sorlStartCmd start -f -p $solrPort -m $solrMemory
                #endregion

                }
                catch
                { # An exception occurred while runnning the service
                $msg = $_.Exception.Message
                $line = $_.InvocationInfo.ScriptLineNumber
                Log "$scriptName -Service # Error at line ${line}: $msg"
                }
                finally
                {
                # Invoked in all cases: Exception or normally by -Stop

                # Flush all leftover events (There may be some that arrived after we exited the while event loop, but before we unregistered the events)
                $events = Get-Event | Remove-Event
                # Log a termination event, no matter what the cause is.
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1006 -EntryType Information -Message "$script -Service # Exiting"
                Log "$scriptName -Service # Exiting"
                }
                return
                }





                share|improve this answer















                It can be done in PowerShell without any external components. This is what I'm currently using with Solr 7.5.0 to install and run as a native Windows service. I'm reasonably certain this will work with any version that uses binsolr.cmd on Server 2012 R2.



                You need to create an environment variable called SOLR_HOME and set the value to the full path of your Solr bin directory, and possibly reboot to pick up that new environment variable. Make any necessary changes, especially to memory and port under #region Solr configuration. Then you drop the code below in a file named PSSolrService.ps1 in your Solr root directory, cd to that directory in a PowerShell console, and then run the setup command: ./PSService.ps1 -Setup This should give you a Windows service called Solr which is set to run automatically at system startup. You can also then use NET STOP Solr and NET START Solr to stop and start the service, just like any other Windows service.



                #
                # PSSolrService.ps1
                #
                #Requires -version 2

                <#
                .SYNOPSIS
                A simple Windows service, in a standalone PowerShell script.

                .DESCRIPTION
                This script demonstrates how to write a Windows service in pure PowerShell.
                It dynamically generates a small PSService.exe wrapper, that in turn
                invokes this PowerShell script again for its start and stop events.

                .PARAMETER Start
                Start the service.

                .PARAMETER Stop
                Stop the service.

                .PARAMETER Restart
                Stop then restart the service.

                .PARAMETER Status
                Get the current service status: Not installed / Stopped / Running

                .PARAMETER Setup
                Install the service.

                .PARAMETER Remove
                Uninstall the service.

                .PARAMETER Service
                Run the service in the background. Used internally by the script.
                Do not use, except for test purposes.

                .PARAMETER Control
                Send a control message to the service thread.

                .PARAMETER Version
                Display this script version and exit.

                .EXAMPLE
                # Setup the service and run it for the first time
                C:PS>.PSService.ps1 -Status
                Not installed
                C:PS>.PSService.ps1 -Setup
                C:PS># At this stage, a copy of PSService.ps1 is present in the path
                C:PS>PSService -Status
                Stopped
                C:PS>PSService -Start
                C:PS>PSService -Status
                Running
                C:PS># Load the log file in Notepad.exe for review
                C:PS>notepad ${ENV:windir}LogsPSService.log

                .EXAMPLE
                # Stop the service and uninstall it.
                C:PS>PSService -Stop
                C:PS>PSService -Status
                Stopped
                C:PS>PSService -Remove
                C:PS># At this stage, no copy of PSService.ps1 is present in the path anymore
                C:PS>.PSService.ps1 -Status
                Not installed

                .EXAMPLE
                # Send a control message to the service, and verify that it received it.
                C:PS>PSService -Control Hello
                C:PS>Notepad C:WindowsLogsPSService.log
                # The last lines should contain a trace of the reception of this Hello message
                #>

                [CmdletBinding(DefaultParameterSetName='Status')]
                Param(
                [Parameter(ParameterSetName='Start', Mandatory=$true)]
                [Switch]$Start, # Start the service

                [Parameter(ParameterSetName='Stop', Mandatory=$true)]
                [Switch]$Stop, # Stop the service

                [Parameter(ParameterSetName='Restart', Mandatory=$true)]
                [Switch]$Restart, # Restart the service

                [Parameter(ParameterSetName='Status', Mandatory=$false)]
                [Switch]$Status = $($PSCmdlet.ParameterSetName -eq 'Status'), # Get the current service status

                [Parameter(ParameterSetName='Setup', Mandatory=$true)]
                [Switch]$Setup, # Install the service

                [Parameter(ParameterSetName='Remove', Mandatory=$true)]
                [Switch]$Remove, # Uninstall the service

                [Parameter(ParameterSetName='Service', Mandatory=$true)]
                [Switch]$Service, # Run the service

                [Parameter(ParameterSetName='Control', Mandatory=$true)]
                [String]$Control = $null, # Control message to send to the service

                [Parameter(ParameterSetName='Version', Mandatory=$true)]
                [Switch]$Version # Get this script version
                )

                # modify this to update script version used by Solr Service
                $scriptVersion = "2018-11-07"

                if( -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
                {
                Write-Warning -Message "Please run thisscript with Administrator permissions."
                return
                }

                #region Solr configuration
                $solrPort = '8983'
                $solrMemory = '12g' # eg. 1g 4g
                $solrHome = [environment]::GetEnvironmentVariable("SOLR_HOME",[EnvironmentVariableTarget]::Machine)
                $solrRoot = Split-Path -Path (Split-Path -Path $solrHome -Parent) -Parent
                $sorlStartCmd = Join-Path -Path $solrRoot -ChildPath "binsolr.cmd"

                if( -not (Test-Path -Path $sorlStartCmd) )
                {
                Write-Warning "Solr.cmd not exist: $sorlStartCmd"
                return
                }
                #endregion

                # This script name, with various levels of details
                $argv0 = Get-Item $MyInvocation.MyCommand.Definition
                $script = $argv0.basename # Ex: PSSolrService
                $scriptName = $argv0.name # Ex: PSSolrService.ps1
                $scriptFullName = $argv0.fullname # Ex: C:TempPSSolrService.ps1

                # Global settings
                $serviceName = $script # A one-word name used for net start commands
                $serviceDisplayName = "Solr"
                $ServiceDescription = "Solr"

                $installDir = "${ENV:ProgramFiles}$serviceName" # Where to install the service files

                #$installDir = "${ENV:windir}System32" # Where to install the service files

                $scriptCopy = "$installDir$scriptName"
                $exeName = "$serviceName.exe"
                $exeFullName = "$installDir$exeName"
                $logDir = "${ENV:windir}Logs" # Where to log the service messages
                $logFile = "$logDir$serviceName.log"
                $logName = "Application" # Event Log name (Unrelated to the logFile!)
                # Note: The current implementation only supports "classic" (ie. XP-compatble) event logs.
                # To support new style (Vista and later) "Applications and Services Logs" folder trees, it would
                # be necessary to use the new *WinEvent commands instead of the XP-compatible *EventLog commands.
                # Gotcha: If you change $logName to "NEWLOGNAME", make sure that the registry key below does not exist:
                # HKLMSystemCurrentControlSetserviceseventlogApplicationNEWLOGNAME
                # Else, New-EventLog will fail, saying the log NEWLOGNAME is already registered as a source,
                # even though "Get-WinEvent -ListLog NEWLOGNAME" says this log does not exist!

                # If the -Version switch is specified, display the script version and exit.
                if ($Version) {
                Write-Output $scriptVersion
                return
                }

                Function Now {
                Param (
                [Switch]$ms, # Append milliseconds
                [Switch]$ns # Append nanoseconds
                )
                $Date = Get-Date
                $now = ""
                $now += "{0:0000}-{1:00}-{2:00} " -f $Date.Year, $Date.Month, $Date.Day
                $now += "{0:00}:{1:00}:{2:00}" -f $Date.Hour, $Date.Minute, $Date.Second
                $nsSuffix = ""
                if ($ns) {
                if ("$($Date.TimeOfDay)" -match ".dddddd") {
                $now += $matches[0]
                $ms = $false
                } else {
                $ms = $true
                $nsSuffix = "000"
                }
                }
                if ($ms) {
                $now += ".{0:000}$nsSuffix" -f $Date.MilliSecond
                }
                return $now
                }

                Function Log () {
                Param(
                [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)]
                [String]$string
                )
                if (!(Test-Path $logDir)) {
                New-Item -ItemType directory -Path $logDir | Out-Null
                }
                if ($String.length) {
                $string = "$(Now) $pid $userName $string"
                }
                $string | Out-File -Encoding ASCII -Append "$logFile"
                }

                $scriptCopyCname = $scriptCopy -replace "\", "\" # Double backslashes. (The first \ is a regexp with escaped; The second is a plain string.)
                $source = @"
                using System;
                using System.ServiceProcess;
                using System.Diagnostics;
                using System.Runtime.InteropServices; // SET STATUS
                using System.ComponentModel; // SET STATUS

                public enum ServiceType : int { // SET STATUS [
                SERVICE_WIN32_OWN_PROCESS = 0x00000010,
                SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
                }; // SET STATUS ]

                public enum ServiceState : int { // SET STATUS [
                SERVICE_STOPPED = 0x00000001,
                SERVICE_START_PENDING = 0x00000002,
                SERVICE_STOP_PENDING = 0x00000003,
                SERVICE_RUNNING = 0x00000004,
                SERVICE_CONTINUE_PENDING = 0x00000005,
                SERVICE_PAUSE_PENDING = 0x00000006,
                SERVICE_PAUSED = 0x00000007,
                }; // SET STATUS ]

                [StructLayout(LayoutKind.Sequential)] // SET STATUS [
                public struct ServiceStatus {
                public ServiceType dwServiceType;
                public ServiceState dwCurrentState;
                public int dwControlsAccepted;
                public int dwWin32ExitCode;
                public int dwServiceSpecificExitCode;
                public int dwCheckPoint;
                public int dwWaitHint;
                }; // SET STATUS ]

                public enum Win32Error : int { // WIN32 errors that we may need to use
                NO_ERROR = 0,
                ERROR_APP_INIT_FAILURE = 575,
                ERROR_FATAL_APP_EXIT = 713,
                ERROR_SERVICE_NOT_ACTIVE = 1062,
                ERROR_EXCEPTION_IN_SERVICE = 1064,
                ERROR_SERVICE_SPECIFIC_ERROR = 1066,
                ERROR_PROCESS_ABORTED = 1067,
                };

                public class Service_$serviceName : ServiceBase { // $serviceName may begin with a digit; The class name must begin with a letter
                private System.Diagnostics.EventLog eventLog; // EVENT LOG
                private ServiceStatus serviceStatus; // SET STATUS

                public Service_$serviceName() {
                ServiceName = "$serviceName";
                CanStop = true;
                CanPauseAndContinue = false;
                AutoLog = true;

                eventLog = new System.Diagnostics.EventLog(); // EVENT LOG [
                if (!System.Diagnostics.EventLog.SourceExists(ServiceName)) {
                System.Diagnostics.EventLog.CreateEventSource(ServiceName, "$logName");
                }
                eventLog.Source = ServiceName;
                eventLog.Log = "$logName"; // EVENT LOG ]
                EventLog.WriteEntry(ServiceName, "$exeName $serviceName()"); // EVENT LOG
                }

                [DllImport("advapi32.dll", SetLastError=true)] // SET STATUS
                private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);

                protected override void OnStart(string args) {
                EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Entry. Starting script '$scriptCopyCname' -Start"); // EVENT LOG
                // Set the service state to Start Pending. // SET STATUS [
                // Only useful if the startup time is long. Not really necessary here for a 2s startup time.
                serviceStatus.dwServiceType = ServiceType.SERVICE_WIN32_OWN_PROCESS;
                serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
                serviceStatus.dwWin32ExitCode = 0;
                serviceStatus.dwWaitHint = 2000; // It takes about 2 seconds to start PowerShell
                SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS ]
                // Start a child process with another copy of this script
                try {
                Process p = new Process();
                // Redirect the output stream of the child process.
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.FileName = "PowerShell.exe";
                p.StartInfo.Arguments = "-ExecutionPolicy Bypass -c & '$scriptCopyCname' -Start"; // Works if path has spaces, but not if it contains ' quotes.
                p.Start();
                // Read the output stream first and then wait. (To avoid deadlocks says Microsoft!)
                string output = p.StandardOutput.ReadToEnd();
                // Wait for the completion of the script startup code, that launches the -Service instance
                p.WaitForExit();
                if (p.ExitCode != 0) throw new Win32Exception((int)(Win32Error.ERROR_APP_INIT_FAILURE));
                // Success. Set the service state to Running. // SET STATUS
                serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING; // SET STATUS
                } catch (Exception e) {
                EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Failed to start $scriptCopyCname. " + e.Message, EventLogEntryType.Error); // EVENT LOG
                // Change the service state back to Stopped. // SET STATUS [
                serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
                Win32Exception w32ex = e as Win32Exception; // Try getting the WIN32 error code
                if (w32ex == null) { // Not a Win32 exception, but maybe the inner one is...
                w32ex = e.InnerException as Win32Exception;
                }
                if (w32ex != null) { // Report the actual WIN32 error
                serviceStatus.dwWin32ExitCode = w32ex.NativeErrorCode;
                } else { // Make up a reasonable reason
                serviceStatus.dwWin32ExitCode = (int)(Win32Error.ERROR_APP_INIT_FAILURE);
                } // SET STATUS ]
                } finally {
                serviceStatus.dwWaitHint = 0; // SET STATUS
                SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
                EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Exit"); // EVENT LOG
                }
                }

                protected override void OnStop() {
                EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Entry"); // EVENT LOG
                // Start a child process with another copy of ourselves
                Process p = new Process();
                // Redirect the output stream of the child process.
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.FileName = "PowerShell.exe";
                p.StartInfo.Arguments = "-c & '$scriptCopyCname' -Stop"; // Works if path has spaces, but not if it contains ' quotes.
                p.Start();
                // Read the output stream first and then wait.
                string output = p.StandardOutput.ReadToEnd();
                // Wait for the PowerShell script to be fully stopped.
                p.WaitForExit();
                // Change the service state back to Stopped. // SET STATUS
                serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED; // SET STATUS
                SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
                EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Exit"); // EVENT LOG
                }

                public static void Main() {
                System.ServiceProcess.ServiceBase.Run(new Service_$serviceName());
                }
                }
                "@

                # Check if we're running as a real user, or as the SYSTEM = As a service
                $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
                $userName = $identity.Name # Ex: "NT AUTHORITYSYSTEM" or "DomainAdministrator"
                $authority,$name = $username -split "\"
                $isSystem = $identity.IsSystem # Do not test ($userName -eq "NT AUTHORITYSYSTEM"), as this fails in non-English systems.
                # Log "# `$userName = `"$userName`" ; `$isSystem = $isSystem"

                if ($Setup) {Log ""} # Insert one blank line to separate test sessions logs
                Log $MyInvocation.Line # The exact command line that was used to start us

                # The following commands write to the event log, but we need to make sure the PSService source is defined.
                New-EventLog -LogName $logName -Source $serviceName -ea SilentlyContinue

                # Workaround for PowerShell v2 bug: $PSCmdlet Not yet defined in Param() block
                $Status = ($PSCmdlet.ParameterSetName -eq 'Status')

                if ($Start) { # Start the service
                if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
                # Do whatever is necessary to start the service script instance
                Log "$scriptName -Start: Starting script '$scriptFullName' -Service"
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1001 -EntryType Information -Message "$scriptName -Start: Starting script '$scriptFullName' -Service"
                Start-Process PowerShell.exe -ArgumentList ("-c & '$scriptFullName' -Service")
                } else {
                Write-Verbose "Starting service $serviceName"
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1002 -EntryType Information -Message "$scriptName -Start: Starting service $serviceName"
                Start-Service $serviceName # Ask Service Control Manager to start it
                }
                return
                }

                if ($Stop) { # Stop the service
                if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
                # Do whatever is necessary to stop the service script instance
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1003 -EntryType Information -Message "$scriptName -Stop: Stopping script $scriptName -Service"
                Log "$scriptName -Stop: Stopping script $scriptName -Service"

                #region Solr stop
                &$sorlStartCmd stop -p $solrPort
                #endregion

                } else {
                Write-Verbose "Stopping service $serviceName"
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1004 -EntryType Information -Message "$scriptName -Stop: Stopping service $serviceName"
                Stop-Service $serviceName # Ask Service Control Manager to stop it
                }
                return
                }

                if ($Restart) { # Restart the service
                & $scriptFullName -Stop
                & $scriptFullName -Start
                return
                }

                if ($Status) { # Get the current service status
                $spid = $null
                $processes = @(Get-WmiObject Win32_Process -filter "Name = 'powershell.exe'" | Where-Object {
                $_.CommandLine -match ".*$scriptCopyCname.*-Service"
                })
                foreach ($process in $processes) { # There should be just one, but be prepared for surprises.
                $spid = $process.ProcessId
                Write-Verbose "$serviceName Process ID = $spid"
                }
                # if (Test-Path "HKLM:SYSTEMCurrentControlSetservices$serviceName") {}
                try {
                $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
                } catch {
                "Not Installed"
                return
                }
                $pss.Status
                if (($pss.Status -eq "Running") -and (!$spid)) { # This happened during the debugging phase
                Write-Error "The Service Control Manager thinks $serviceName is started, but $serviceName.ps1 -Service is not running."
                exit 1
                }
                return
                }

                if ($Setup) { # Install the service
                # Check if it's necessary
                try {
                $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
                # Check if this script is newer than the installed copy.
                if ((Get-Item $scriptCopy -ea SilentlyContinue).LastWriteTime -lt (Get-Item $scriptFullName -ea SilentlyContinue).LastWriteTime) {
                Write-Verbose "Service $serviceName is already Installed, but requires upgrade"
                & $scriptFullName -Remove
                throw "continue"
                } else {
                Write-Verbose "Service $serviceName is already Installed, and up-to-date"
                }
                exit 0
                } catch {
                # This is the normal case here. Do not throw or write any error!
                Write-Debug "Installation is necessary" # Also avoids a ScriptAnalyzer warning
                # And continue with the installation.
                }
                if (!(Test-Path $installDir)) {
                New-Item -ItemType directory -Path $installDir | Out-Null
                }
                # Copy the service script into the installation directory
                if ($ScriptFullName -ne $scriptCopy) {
                Write-Verbose "Installing $scriptCopy"
                Copy-Item $ScriptFullName $scriptCopy
                }
                # Generate the service .EXE from the C# source embedded in this script
                try {
                Write-Verbose "Compiling $exeFullName"
                Add-Type -TypeDefinition $source -Language CSharp -OutputAssembly $exeFullName -OutputType ConsoleApplication -ReferencedAssemblies "System.ServiceProcess" -Debug:$false
                } catch {
                $msg = $_.Exception.Message
                Write-error "Failed to create the $exeFullName service stub. $msg"
                exit 1
                }
                # Register the service
                Write-Verbose "Registering service $serviceName"
                $pss = New-Service $serviceName $exeFullName -DisplayName $serviceDisplayName -Description $ServiceDescription -StartupType Automatic

                return
                }

                if ($Remove) { # Uninstall the service
                # Check if it's necessary
                try {
                $pss = Get-Service $serviceName -ea stop # Will error-out if not installed
                } catch {
                Write-Verbose "Already uninstalled"
                return
                }
                Stop-Service $serviceName # Make sure it's stopped
                # In the absence of a Remove-Service applet, use sc.exe instead.
                Write-Verbose "Removing service $serviceName"
                $msg = sc.exe delete $serviceName
                if ($LastExitCode) {
                Write-Error "Failed to remove the service ${serviceName}: $msg"
                exit 1
                } else {
                Write-Verbose $msg
                }
                # Remove the installed files
                if (Test-Path $installDir) {
                foreach ($ext in ("exe", "pdb", "ps1")) {
                $file = "$installDir$serviceName.$ext"
                if (Test-Path $file) {
                Write-Verbose "Deleting file $file"
                Remove-Item $file
                }
                }
                if (!(@(Get-ChildItem $installDir -ea SilentlyContinue)).Count) {
                Write-Verbose "Removing directory $installDir"
                Remove-Item $installDir
                }
                }
                return
                }

                if ($Service) { # Run the service
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1005 -EntryType Information -Message "$scriptName -Service # Beginning background job"
                # Do the service background job
                try
                {

                #region Solr start
                Log "$scriptName Starting $sorlStartCmd with parameteres start -f -p $solrPort -m $solrMemory"
                &$sorlStartCmd start -f -p $solrPort -m $solrMemory
                #endregion

                }
                catch
                { # An exception occurred while runnning the service
                $msg = $_.Exception.Message
                $line = $_.InvocationInfo.ScriptLineNumber
                Log "$scriptName -Service # Error at line ${line}: $msg"
                }
                finally
                {
                # Invoked in all cases: Exception or normally by -Stop

                # Flush all leftover events (There may be some that arrived after we exited the while event loop, but before we unregistered the events)
                $events = Get-Event | Remove-Event
                # Log a termination event, no matter what the cause is.
                Write-EventLog -LogName $logName -Source $serviceName -EventId 1006 -EntryType Information -Message "$script -Service # Exiting"
                Log "$scriptName -Service # Exiting"
                }
                return
                }






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Nov 14 '18 at 23:49

























                answered Nov 14 '18 at 23:43









                Joshua PerryJoshua Perry

                386




                386






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f37889521%2frun-solr-6-0-1-in-background-on-windows-server%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Florida Star v. B. J. F.

                    Danny Elfman

                    Lugert, Oklahoma