Cool Powershell Trick - Stopping and Starting a service on a remote server without having to explicitly load the .NET assemblies
I was chatting with my colleague at Magenic Donn Felker about two of our favorite topics TFS and Powershell. He found a cool way to load scripts into a profile. It reminded me of a cool Powershell Trick i found on the web before I started blogging...
I wrote deployment scripts in Powershell, which the client wanted to run on a single server regardless of where they were deploying to - so I needed to stop and start services on a remote machine. However, the current version of Powershell doesn't include direct support for managing services on a remote server. The solution (as is so often the case in Powershell is to use .NET Framework classes). To do so - you generally need to load the Assemblies explicitly with code that looks like this
#load assemblies
$key = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\VisualStudio\9.0
$dir = [string] (Get-ItemProperty $key.InstallDir)
$dir += "PrivateAssemblies\"
#set to $x so it won't emit the "success" to the output stream
$lib = $dir + "Microsoft.TeamFoundation.Client.dll"
$x=[Reflection.Assembly]::LoadFrom($lib)
$lib = $dir + "Microsoft.TeamFoundation.VersionControl.Client.dll"
$x=[Reflection.Assembly]::LoadFrom($lib)
So with the assemblies loaded you can call methods on the classes they implement like this:
#Set up connection to TFS Server
$tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($tfsServer)
#get version control$versionControlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
$versionControlService = $tfs.GetService($versionControlType)
(This sample above is to load assemblies to use the TFS Version Control API)
So you pretty much need to know where the Assembly is loaded from, and use the Reflection.Assembly class to load it.
Here’s the trick. While the Powershell “Cmdlets” to manipulate services only work on the machine the script is running on, they use the .NET framework classes “behind the scenes” so if you execute the “Get-Services” cmdlet – all the dependent assemblies are automatically loaded in your process space.
So the code below works without having to explicitly load any assemblies!I
# dummy run of Get-Service CMDLet to force .NET Assemblies to be loaded
$dmy = Get-Service
#Get the services on the Remote Machine
$allServices = [System.ServiceProcess.ServiceController]::GetServices($NSServer)
#Get the NS Service
$nsService = $allServices | where {$_.Name -like 'NS$steriworksinstance'}
#Call Stop on the Service if it is Running - if so stop it
if ($nsService.Status -eq "Running")
{
$nsService.Stop()
}
While this might “break” in a future release of Powershell, they are adding support for remote servers, so switching to the cmdlets for that would be preferred anyway…
Oh and in case you are interested - the code to start the service
$nsService.Start()