Deploy and install .NET Windows services using MSDeploy & Phantom

I recently needed to deploy a .NET Windows service to a remote machine and, if needed, install it.

The first part was to be able to install or uninstall a .NET Windows service using InstallerTool.exe in the following way:

Install   >> InstallerTool.exe /name=MyService /account=user /user=domain\username /password=password ./service.exe
Uninstall >> InstallerTool.exe /u /name=MyService ./service.exe

To do this without needing to manually enter credentials in a popup window I needed to modify my installer class. In order for it to accept credentials and stuff from the command line parameters I added the following to my installer.

protected override void OnBeforeInstall(IDictionary savedState)
{
	base.OnBeforeInstall(savedState);

	this.processInstaller = this.processInstaller ?? new ServiceProcessInstaller();
	this.serviceInstaller = this.serviceInstaller ?? new ServiceInstaller();

	this.serviceInstaller.ServiceName = string.IsNullOrEmpty(GetContextParameter("name")) ? this.serviceInstaller.ServiceName : GetContextParameter("name");
	this.serviceInstaller.Description = string.IsNullOrEmpty(GetContextParameter("desc")) ? this.serviceInstaller.Description : GetContextParameter("desc");

	switch (GetContextParameter("account").ToLower())
	{
		case "user":
			this.processInstaller.Account = ServiceAccount.User;
			this.serviceInstaller.Username = string.IsNullOrEmpty(GetContextParameter("user")) ? this.serviceInstaller.Username : GetContextParameter("user");
			this.serviceInstaller.Password = string.IsNullOrEmpty(GetContextParameter("password")) ? this.serviceInstaller.Password : GetContextParameter("password");
			break;
		case "localservice":
			this.processInstaller.Account = ServiceAccount.LocalService;
			break;
		case "localsystem":
			this.processInstaller.Account = ServiceAccount.LocalSystem;
			break;
		case "networkservice":
			this.processInstaller.Account = ServiceAccount.NetworkService;
			break;
	}
}

protected override void OnBeforeUninstall(IDictionary savedState)
{
	base.OnBeforeUninstall(savedState);
	this.serviceInstaller.ServiceName = string.IsNullOrEmpty(GetContextParameter("name")) ? this.serviceInstaller.ServiceName : GetContextParameter("name");
}

private string GetContextParameter(string key)
{
	return Context.Parameters.ContainsKey(key) ? Context.Parameters[key] : string.Empty;
}

I am using my Phantom fork on Github that adds a wrapper for MSDeploy.exe

In this example InstallUtil.exe is deployed together with my service to the remote machine in the folder “./installer/installUtil.exe”

Example script usage:

deploy             >> pantom.exe build deploy clean
deploy & install   >> pantom.exe build deploy install clean
deploy & uninstall >> pantom.exe build deploy uninstall clean
# file: build.boo
# deploys and install or uninstalls services on a remote machine
solution_file = "./solution.sln"
configuration = "release"
output_path = "./phantom.build"

remote_path="E:/Services/service"
remote_installer = deploy_path+"/installer/installUtil.exe"
remote_server = "127.0.0.1"
remote_username = "admin"
remote_password = "god!secure"
remote_uninstall_cmd = remote_installer_path+" /u /name=MyService "+deploy_path+"/service.exe"
remote_install_cmd = remote_installer_path+" /name=MyService /account=user /user=domain\\username /password=password "+deploy_path+"/service.exe"

target default, (build, clean):
pass

target build:
msbuild(file: solution_file, configuration: configuration, properties: {"OutputPath": output_path })

target deploy:
msdeploy(verb: "sync",source:{"dirPath": output_path}, dest: {"dirPath":remote_path,"wmsvc":remote_server,"authType":"basic","userName":remote_username,"password":remote_password},flags:"-allowUntrusted")

target uninstall:
msdeploy(verb: "sync", source:{"runCommand":remote_uninstall_cmd}, dest: {"auto":"","wmsvc":remote_server,"authType":"basic","userName":deploy_username,"password":deploy_password},flags:"-allowUntrusted")

target install:
msdeploy(verb: "sync", source:{"runCommand":remote_install_cmd}, dest: {"auto":"","wmsvc":remote_server,"authType":"basic","userName":remote_username,"password":remote_password},flags:"-allowUntrusted")

target clean:
rm(output_path)

You may need to add some privileges to the WMSVC service running on the remote machine

c:\> sc privs wmsvc SeChangeNotifyPrivilege/SeImpersonatePrivilege/SeAssignPrimaryTokenPrivilege/SeIncreaseQuotaPrivilege
c:\> sc stop wmsvc
c:\> sc start wmsvc

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>