Modules/WindowsService.ps1
function Stop-WindowsService { <# .SYNOPSIS Stops a Windows Service .DESCRIPTION Stops a Windows Service .PARAMETER Name Name of the Service to stop .PARAMETER Sleep Seconds to wait after the Stop service command was executed .EXAMPLE Stop-WindowsService -Name "MyService" -Sleep 10 #> param( [Parameter(Mandatory=$true, Position=1)] [string]$Name = $null, [Parameter(Mandatory=$false, Position=2)] [string]$Sleep = 5 # seconds ) Begin { } Process { # Verify if the service exists, and if yes stop it and wait for new state. if(Assert-ServiceExists $Name) { $Service = Get-Service $Name | Where-Object {$_.status -eq 'Running'} if ($Service) { Write-Log "Stop Service: $Name`n" $Service | Stop-Service -Pass } else { Write-Log "Service $Name not Running`n" } Start-Sleep -s $Sleep } } End { } } function Remove-WindowsService { <# .SYNOPSIS Removes a Windows Service .DESCRIPTION Removes a Windows Service .PARAMETER ServiceName Name of the Service to remove .EXAMPLE Remove-WindowsService -Name "MyService" #> param( [Parameter(Mandatory=$true, Position=1)] [string]$ServiceName = $null ) Begin { } Process { # Verify if the service already exists, and if yes remove it if(Assert-ServiceExists $ServiceName) { if(-Not (Assert-ServiceStopped)) { Stop-WindowsService -Name $ServiceName } # Remove Service Start-Process -FilePath sc.exe -Args "delete $ServiceName" -Verb runAs -Wait Write-Log "Service removed: $ServiceName" } } End { } } function New-WindowsService { <# .SYNOPSIS Creates a Windows Service .DESCRIPTION Creates a Windows Service .PARAMETER ServiceName Name of the Service to create .PARAMETER DisplayName Name under which the Service will be displayed in the windows servcies list Default: Same as ServiceName .PARAMETER BinaryPath Path to the service binary .PARAMETER Description Desciption of the service .PARAMETER StartUpType Possible values: Automatic Manual Disabled .PARAMETER DelayedStart If StartUpType is Automatic and DelayedStart is true then the service will be set to automatic (delayed start) Default = false .PARAMETER User Specifies the user under which the service should be started Possible values: "NT AUTHORITY\LocalSystem" "NT AUTHORITY\LocalService" "NT AUTHORITY\NetworkService" <.\User> <Domain\User> Default: "NT AUTHORITY\LocalSystem" .PARAMETER Password Specifies teh password of the user under which the service should be started, only needed if a specific local or domain user is used .EXAMPLE New-WindowsService -ServiceName "MyService" -DisplayName "Company.MyService" -BinaryPath "C:\MyService\myservice.exe" .EXAMPLE New-WindowsService -ServiceName "MyService" -BinaryPath "C:\MyService\myservice.exe" -User ".\MyUser" -Password "MyPassword" .EXAMPLE New-WindowsService -ServiceName "MyService" -BinaryPath "C:\MyService\myservice.exe" -Arguments "--foo --bar" -User ".\MyUser" -Password "MyPassword" #> param( [Parameter(Mandatory=$true, Position=1)] [string]$ServiceName = $null, [Parameter(Mandatory=$false, Position=2)] [string]$DisplayName = $null, [Parameter(Mandatory=$true, Position=3)] [string]$BinaryPath = $null, [Parameter(Mandatory=$false, Position=4)] [string]$Description = $null, [Parameter(Mandatory=$false, Position=5)] [string]$StartUpType = $null, [Parameter(Mandatory=$false, Position=6)] [bool]$DelayedStart= $false, [Parameter(Mandatory=$false, Position=7)] [string]$User = $null, # NT AUTHORITY\LocalSystem, NT AUTHORITY\LocalService, NT AUTHORITY\NetworkService, <Domain\User> [Parameter(Mandatory=$false, Position=8)] [string]$Password = $null, [Parameter(Mandatory=$false, Position=9)] [string]$Arguments = $null ) Begin { if ((Test-Path $BinaryPath) -eq $false) { Write-Log "Service binary path not found: $BinaryPath. Service was NOT installed." -LogLevel Error } } Process { Write-Log "Installing service: $serviceName`n" if (-Not($DisplayName)) { $DisplayName = $ServiceName } # Install dotNET Service. New-Service -BinaryPathName "$BinaryPath $Arguments" -Name $ServiceName -DisplayName $DisplayName if($Description) { Set-Service $ServiceName -Description $Description } if($StartUpType) { Set-Service $ServiceName -StartupType $StartupType if($StartUpType -eq "Automatic" -and $DelayedStart) { Start-Process -FilePath sc.exe -Args "config $ServiceName start=delayed-auto" -Verb runAs -Wait } } if($User) { # if password is empty, create a dummy one to allow having credentials for system accounts: # NT AUTHORITY\LocalSystem # NT AUTHORITY\LocalService # NT AUTHORITY\NetworkService if ([string]::IsNullOrEmpty($Password)) { $Password = "dummy" } else { # Add account to logon as a service. Add-AccountToLogonAsService $User } $Service = Get-WmiObject -Class Win32_Service -Filter "name='$ServiceName'" Stop-WindowsService -Name $ServiceName $Service.change($null, $null, $null, $null, $null, $null, $User, $Password, $null, $null, $null) } Write-Log "Installation completed: $ServiceName" } End { } } function Add-AccountToLogonAsService { param( [Parameter(Mandatory=$true, Position=1)] [string]$Username = $null ) Begin { } Process { $sidstr = $null try { # in case somone is using a local account like .\FooBar the .\ will not work and will get replaced $Username = $Username -replace "\.\\", "" $ntprincipal = new-object System.Security.Principal.NTAccount "$Username" $sid = $ntprincipal.Translate([System.Security.Principal.SecurityIdentifier]) $sidstr = $sid.Value.ToString() } catch { $sidstr = $null } Write-Log "Account: $($Username)" if([string]::IsNullOrEmpty($sidstr)) { Write-Log "Account $Username not found!" -LogLevel "Error" exit -1 } Write-Log "Account SID: $($sidstr)" $tmp = [System.IO.Path]::GetTempFileName() Write-Log "Export current Local Security Policy" secedit.exe /export /cfg "$($tmp)" $c = Get-Content -Path $tmp $currentSetting = "" foreach($s in $c) { if( $s -like "SeServiceLogonRight*") { $x = $s.split("=",[System.StringSplitOptions]::RemoveEmptyEntries) $currentSetting = $x[1].Trim() } } if( $currentSetting -notlike "*$($sidstr)*" ) { Write-Log "Modify Setting ""Logon as a Service""" if( [string]::IsNullOrEmpty($currentSetting) ) { $currentSetting = "*$($sidstr)" } else { $currentSetting = "*$($sidstr), $($currentSetting)" } Write-Log "$currentSetting" $outfile = @" [Unicode] Unicode=yes [Version] signature="`$CHICAGO`$" Revision=1 [Privilege Rights] SeServiceLogonRight = $($currentSetting) "@ $tmp2 = [System.IO.Path]::GetTempFileName() Write-Log "Import new settings to Local Security Policy" $outfile | Set-Content -Path $tmp2 -Encoding Unicode -Force #notepad.exe $tmp2 Push-Location (Split-Path $tmp2) try { secedit.exe /configure /db "secedit.sdb" /cfg "$($tmp2)" /areas USER_RIGHTS } finally { Pop-Location } } else { Write-Log "NO ACTIONS REQUIRED! Account already in ""Logon as a Service""" } Write-Log "Done." } end {} } function Assert-ServiceExists { <# .SYNOPSIS Verifies that a Service exists .DESCRIPTION Verifies that a Service exists, this is needed for some functions which will only run if the service is already installed .PARAMETER Name Name of the Service to verify existance of .EXAMPLE Assert-ServiceExists -Name "MyService" #> param( [Parameter(Mandatory=$true, Position=1)] [string] $Name ) # If you use just "Get-Service $Name", it will return an error if # the service didn't exist. Trick Get-Service to return an array of # Services, but only if the name exactly matches the $Name. # This way you can test if the array is empty. if (Get-Service "$Name*" -Include $Name) { return $true } Write-Log "The Service $Name does not Exist" -LogLevel "Error" return $false } function Start-WindowsService { <# .SYNOPSIS Starts a windows service .DESCRIPTION Starts a windows service .PARAMETER Name Name of the Service to be started .EXAMPLE Start-WindowsService -Name "MyService" #> param( [Parameter(Mandatory = $true, Position = 1)] [string] $Name ) Get-Service -Name $Name | Set-Service -Status Running Assert-ServicesStarted } function Assert-ServicesStarted () { <# .SYNOPSIS Verifies that a Service is started .DESCRIPTION Verifies that a Service is started .EXAMPLE Assert-ServicesStarted -Name "MyService" #> Start-Sleep -s 5 $SmokeTestService = Get-Service -Name $serviceName if ($SmokeTestService.Status -ne "Running") { Throw "Smoke test: FAILED. (SERVICE FAILED TO START)" } else { return $true } } function Assert-ServiceStopped { <# .SYNOPSIS Verifies that a Service is stopped .DESCRIPTION Verifies that a Service is stopped .EXAMPLE Assert-ServiceStopped -Name "MyService" #> Start-Sleep -s 5 $SmokeTestService = Get-Service -Name $serviceName if ($SmokeTestService.Status -ne "Stopped") { return $false } else { return $true } } |