DSCResources/Grani_TopShelf/Grani_TopShelf.psm1
#region Initialize function Initialize { # Enum for Ensure Add-Type -TypeDefinition @" public enum EnsureType { Present, Absent } "@ -ErrorAction SilentlyContinue # Import [System.ServiceProcess.ServiceControllerStatus] and other types..... Get-Service > $null } . Initialize #endregion #region Message Definition $verboseMessages = Data { ConvertFrom-StringData -StringData @" KillingConcoleTopShelf = Console TopShelf running detected. Killing.... StoppedServiceMayBeTopShelf = Stopped Service may be TopShelf Service. StoppedServiceMayNotBeTopShelf = Stopped Service may NOT be TopShelf Service. "@ } $debugMessages = Data { ConvertFrom-StringData -StringData @" CheckingServicePathAndParameterPathMatching = Checking Service Path equals parameter Path PathFound = Successfully found path '{0}'. PathNotExist = Be sure you have download or set target path '{0}' in advance. ProcessNotFound = Process not found. ProcessPathNotDesired = Process path not detected. ProcessPath '{0}', Path '{1}'. ServiceNotExists = Service not exist "@ } $errorMessages = Data { ConvertFrom-StringData -StringData @" "@ } #endregion #region *-TargetResource function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [parameter(Mandatory = $true)] [System.String]$Path, [parameter(Mandatory = $true)] [System.String]$ServiceName, [parameter(Mandatory = $true)] [ValidateSet("Present","Absent")] [System.String]$Ensure ) # validate path is correct. ValidatePathExists -Path $Path # Check existing is TopShelfService or not if (-not (IsServiceExists -Name $ServiceName)) { Write-Debug $debugMessages.ServiceNotExists $ensureResult = [EnsureType]::Absent.ToString() } else { # service exist. Validate if it is TopShelf Service or not if (IsTopShelfServiceValid -Name $ServiceName -Path $Path) { $ensureResult = [EnsureType]::Present.ToString() } else { $ensureResult = [EnsureType]::Absent.ToString() } } $returnValue = @{ Path = $Path ServiceName = $ServiceName Ensure = $ensureResult } return $returnValue } function Set-TargetResource { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String]$Path, [parameter(Mandatory = $true)] [System.String]$ServiceName, [parameter(Mandatory = $true)] [ValidateSet("Present","Absent")] [System.String]$Ensure ) #region Uninstall Service if ($Ensure -eq [EnsureType]::Absent.ToString()) { UninstallTopShelfService -Path $Path; return; } #endregion #region Install Service # Even install process but uninstall is neccesary for in case TopShelf Process path is changed. UninstallTopShelfService -Path $Path; # Detect TopShelf run as Console if ((IsServiceStopped -Name $ServiceName) -and (IsProcessUsing -Path $Path)) { Write-Verbose $verboseMessages.KillingConcoleTopShelf; Get-Process -Name $ServiceName | Stop-Process -Force; } # Install Service InstallTopShelfService -Path $Path; return; #endregion } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String]$Path, [parameter(Mandatory = $true)] [System.String]$ServiceName, [parameter(Mandatory = $true)] [ValidateSet("Present","Absent")] [System.String]$Ensure ) $result = (Get-TargetResource -Path $Path -ServiceName $ServiceName -Ensure $Ensure).Ensure -eq $Ensure return $result } #endregion #region TopShlef helper function InstallTopShelfService { [CmdletBinding()] [OutputType([void])] param ( [parameter(Mandatory = $true)] [System.String]$Path ) try { . $path install | Write-Verbose } catch { throw $_ } } function UninstallTopShelfService { [CmdletBinding()] [OutputType([void])] param ( [parameter(Mandatory = $true)] [System.String]$Path ) try { . $path uninstall | Write-Verbose } catch { throw $_ } } #endregion #region Service helper function IsServiceExists { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String]$Name ) # Name or DisplayName checking return (Get-Service | where {($_.Name -eq $Name) -or ($_.DisplayName -eq $Name)} | measure).Count -ne 0 } function GetServiceStatusSafe { [CmdletBinding()] [OutputType([System.ServiceProcess.ServiceControllerStatus])] param ( [parameter(Mandatory = $true)] [System.String]$Name ) # Name or DisplayName checking return (Get-Service | where {($_.Name -eq $Name) -or ($_.DisplayName -eq $Name)}).Status } function IsServiceRunning { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String]$Name ) return (GetServiceStatusSafe -Name $Name) -eq [System.ServiceProcess.ServiceControllerStatus]::Running } function IsServiceStopped { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String]$Name ) return (GetServiceStatusSafe -Name $Name) -eq [System.ServiceProcess.ServiceControllerStatus]::Stopped } function IsTopShelfServiceValid { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String]$Name, [parameter(Mandatory = $true)] [System.String]$Path ) # make sure : this function NOT expecting to pass invalid service. Be sure to filter invalid input in-advance. # Check Service Path is valid Write-Debug ($debugMessages.CheckingServicePathAndParameterPathMatching); if (-not (IsServicePathValid -Name $Name -Path $Path)) { # Service Path != Request Path immediately return false return $false; } return $true; } function IsServicePathValid { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String]$Name, [parameter(Mandatory = $true)] [System.String]$Path ) # Get Service Instance detail $service = Get-CimInstance -ClassName Win32_Service | where Name -eq $Name; # Service executable Path Check - Pick up exe path. TopShelf Service PathName Format will be : # "xxx.exe" -displayname "xxx" -servicename "xxx" if ($null -ne $service.PathName) { $servicePath = $service.PathName.Split(" ") | select -First 1; return $servicePath.Replace('"',"") -eq $Path; } return $false; } #endregion #region Process helper function IsProcessUsing { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String]$Path ) # pickup process from executable file name $processName = (Get-Item -Path $Path).BaseName $processInfo = Get-Process | where Name -eq $processName # validate Process exist - if service stopped, then process will not shown if (($processInfo | measure).Count -eq 0) { Write-Debug ($debugMessages.ProcessNotFound -f $processName) return $false } # validate service path is as desired if (($processInfo).Path -ne $Path) { Write-Debug ($debugMessages.ProcessPathNotDesired -f $processInfo.Path, $Path) return $false } # Name and Path matches TopShelf execute Path return $true } #endregion #region Path helder function ValidatePathExists { [CmdletBinding()] [OutputType([Void])] param ( [parameter(Mandatory = $true)] [System.String]$Path ) if (-not (Test-Path -Path $Path)) { Write-Debug ($debugMessages.PathNotExist -f $Path) throw New-Object System.IO.FileNotFoundException } Write-Debug ($debugMessages.PathFound -f $Path) } #endregion Export-ModuleMember -Function *-TargetResource |