Skip to content

Instantly share code, notes, and snippets.

@mrchief
Last active June 26, 2024 09:37
Show Gist options
  • Save mrchief/4726433 to your computer and use it in GitHub Desktop.
Save mrchief/4726433 to your computer and use it in GitHub Desktop.
MSBuild Script to deploy Windows Service to remote or local machine
<Project DefaultTargets="CopyOutputs;DeployService" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<!-- These settings control what the service's name, description etc appear in services.msc task panel. -->
<PropertyGroup Label="ServiceMetaData">
<ServiceName>ShinyNewService</ServiceName>
<ServiceDisplayName>Shiny New Service</ServiceDisplayName>
<ServiceDescription>A shiny new service, that changes the world for the greater good.</ServiceDescription>
</PropertyGroup>
<Choose>
<When Condition="'$(DeploymentServerName)' == ''">
<PropertyGroup>
<!-- You can choose any path here. For convenience, I'm using C: -->
<DeploymentFolder>C:\$(ServiceName)</DeploymentFolder>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<!-- should be in \\serverName format-->
<DeploymentServer Condition="'$(DeploymentServerName)' != ''">$(DeploymentServerName)</DeploymentServer>
<DeploymentFolder>$(DeploymentServer)\C$\$(ServiceName)</DeploymentFolder>
<!-- 4:5:4 => Planned: Application: Upgrade. For more reason codes, run "sc stop" -->
<DeploymentReason>4:5:4</DeploymentReason>
</PropertyGroup>
</Otherwise>
</Choose>
<PropertyGroup>
<ProjectName>My.Services.ShinyNewService</ProjectName>
<ProjectFile>$(MSBuildProjectDirectory)\$(ProjectName).csproj</ProjectFile>
<ServiceExecutablePath>C:\$(ServiceName)\$(ProjectName).exe</ServiceExecutablePath>
</PropertyGroup>
<Target Name="DeployService">
<Exec Command="safeServiceStop $(ServiceName) $(DeploymentServer) $(DeploymentReason)" />
<Exec Command="safeServiceDelete $(ServiceName) $(DeploymentServer)" ContinueOnError="true" />
<Exec Command="sc $(DeploymentServer) create $(ServiceName) binPath= &quot;$(ServiceExecutablePath)&quot; start= delayed-auto displayName= &quot;$(ServiceDisplayName)&quot;" />
<Exec Command="sc $(DeploymentServer) description $(ServiceName) &quot;$(ServiceDescription)&quot;" />
<Exec Command="safeServiceStart $(ServiceName) $(DeploymentServer) " ContinueOnError="true" />
</Target>
<Target Name="CopyOutputs">
<MSBuild Projects="$(MSBuildProjectFullPath)"
Properties="ImportProjectFile=true" Targets="Rebuild">
<Output ItemName="ProjectOutputs" TaskParameter="TargetOutputs"/>
</MSBuild>
<!-- This is just for debugging purposes -->
<Message Text="%0a%0dProjectOutputs:%0a%0d @(ProjectOutputs,'%0a%0d ')" Importance="low" />
<Message Text="Stopping Service..." />
<!-- 4:5:4 => Planned: Application: Upgrade -->
<Exec Command="safeServiceStop $(ServiceName) $(DeploymentServer) $(DeploymentReason)" ContinueOnError="true" />
<Message Text="Copying files..." />
<Copy SourceFiles="@(ProjectOutputs)"
DestinationFolder="$(DeploymentFolder)"
SkipUnchangedFiles="true"
OverwriteReadOnlyFiles="true" />
</Target>
<!--
These elements will only be processed when in the context of the
above Target. This is because of the Condition constraint on these items
-->
<Import Project="$(ProjectFile)" Condition="'$(ImportProjectFile)'=='true'" />
<!--
Here we need to override the Build target with our own that will
generate the correct results.
-->
<Target Name="Rebuild"
Condition="'$(ImportProjectFile)'=='true'"
DependsOnTargets="$(BuildDependsOn)"
Outputs="@(AllOutputs->'%(FullPath)')" >
<CreateItem Include="$(OutputPath)\**\*">
<Output ItemName="AllOutputs" TaskParameter="Include"/>
</CreateItem>
<Message Text="Custom build invoked!" Importance="high"/>
</Target>
</Project>
@echo off
:: Script authored by Hrusikesh Panda (inspired from safeServiceStop script by Erik Falksen)
IF [%1]==[] GOTO usage
IF NOT "%2"=="" SET server=%2
SC %server% query %1 >NUL
IF errorlevel 1060 GOTO ServiceNotFound
IF errorlevel 1722 GOTO SystemOffline
IF errorlevel 1001 GOTO DeletingServiceDelay
:ResolveInitialState
SC %server% query %1 | FIND "STATE" | FIND "RUNNING" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService
SC %server% query %1 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StoppedService
SC %server% query %1 | FIND "STATE" | FIND "PAUSED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
echo Service State is changing, waiting for service to resolve its state before making changes
sc %server% query %1 | Find "STATE" >NUL
ping -n 2 127.0.0.1 > NUL
GOTO ResolveInitialState
:StopService
echo Stopping %1 on %server%
sc %server% stop %1 %3 >NUL
GOTO StoppingService
:StoppingServiceDelay
echo Waiting for %1 to stop
ping -n 2 127.0.0.1 > NUL
:StoppingService
SC %server% query %1 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 1 GOTO StoppingServiceDelay
:StoppedService
echo %1 on %server% is stopped
GOTO DeleteService
:DeleteService
SC %server% delete %1 >NUL
:DeletingServiceDelay
echo Waiting for %1 to get deleted
ping -n 2 127.0.0.1 > NUL
:DeletingService
SC %server% query %1 >NUL
IF NOT errorlevel 1060 GOTO DeletingServiceDelay
:DeletedService
echo %1 on %server% is deleted
GOTO:eof
:SystemOffline
echo Server %server% is not accessible or is offline
GOTO:eof
:ServiceNotFound
echo Service %1 is not installed on Server %server%
exit /b 0
:usage
echo Will cause a local/remote service to START (if not already started).
echo This script will waiting for the service to enter the started state if necessary.
echo.
echo %0 [service name] [system name]
echo Example: %0 MyService server1
echo Example: %0 MyService (for local PC)
echo.
GOTO:eof
@echo off
:: Tweaked version of the original script authored by Eric Falsken
IF [%1]==[] GOTO usage
IF NOT "%2"=="" SET server=%2
SC %server% query %1 >NUL
IF errorlevel 1060 GOTO ServiceNotFound
IF errorlevel 1722 GOTO SystemOffline
:ResolveInitialState
SC %server% query %1 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StartService
SC %server% query %1 | FIND "STATE" | FIND "RUNNING" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StartedService
SC %server% query %1 | FIND "STATE" | FIND "PAUSED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
echo Service State is changing, waiting for service to resolve its state before making changes
sc %server% query %1 | Find "STATE" >NUL
ping -n 2 127.0.0.1 > NUL
GOTO ResolveInitialState
:StartService
echo Starting %1 on %server%
sc %server% start %1 >NUL
GOTO StartingService
:StartingServiceDelay
echo Waiting for %1 to start
ping -n 2 127.0.0.1 > NUL
:StartingService
SC %server% query %1 | FIND "STATE" | FIND "RUNNING" >NUL
IF errorlevel 1 GOTO StartingServiceDelay
:StartedService
echo %1 on %server% is started
GOTO:eof
:SystemOffline
echo Server %server% is not accessible or is offline
GOTO:eof
:ServiceNotFound
echo Service %1 is not installed on Server %server%
exit /b 0
:usage
echo Will cause a local/remote service to START (if not already started).
echo This script will waiting for the service to enter the started state if necessary.
echo.
echo %0 [service name] [system name]
echo Example: %0 MyService server1
echo Example: %0 MyService (for local PC)
echo.
GOTO:eof
@echo off
:: Tweaked version of the original script authored by Eric Falsken
IF [%1]==[] GOTO usage
IF NOT "%2"=="" SET server=%2
SC %server% query %1 >NUL
IF errorlevel 1060 GOTO ServiceNotFound
IF errorlevel 1722 GOTO SystemOffline
:ResolveInitialState
SC %server% query %1 | FIND "STATE" | FIND "RUNNING" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService
SC %server% query %1 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StoppedService
SC %server% query %1 | FIND "STATE" | FIND "PAUSED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
echo Service State is changing, waiting for service to resolve its state before making changes
sc %server% query %1 | Find "STATE" >NUL
ping -n 2 127.0.0.1 > NUL
GOTO ResolveInitialState
:StopService
echo Stopping %1 on %server%
sc %server% stop %1 %3 >NUL
GOTO StoppingService
:StoppingServiceDelay
echo Waiting for %1 to stop
ping -n 2 127.0.0.1 > NUL
:StoppingService
SC %server% query %1 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 1 GOTO StoppingServiceDelay
:StoppedService
echo %1 on %server% is stopped
GOTO:eof
:SystemOffline
echo Server %server% is not accessible or is offline
GOTO:eof
:ServiceNotFound
echo Service %1 is not installed on Server %server%
exit /b 0
:usage
echo Will cause a local/remote service to STOP (if not already stopped).
echo This script will waiting for the service to enter the stopped state if necessary.
echo.
echo %0 [service name] [system name] {reason}
echo Example: %0 MyService server1 {reason}
echo Example: %0 MyService (for local PC, DO NOT specify reason)
echo.
echo For reason codes, run "sc stop"
GOTO:eof
@gintsgints
Copy link

Could not it be better instead

<When Condition="'$(DeploymentServerName)' == ''">

use

<PropertyGroup Condition="'$(DeploymentServerName)' == ''">

@mattumotu
Copy link

@mattumotu
Copy link

mattumotu commented Aug 15, 2017

I got the following error:

[SC] ChangeServiceConfig2 (delayed autostart flag) FAILED 124:
The system call level is not correct.

which was resolved by changing start= delayed-auto to start= auto

@digrizzz
Copy link

@mattumotu thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment