cEprsCreateWindowsService.psm1

enum Ensure
{
    Absent
    Present
}
<#
This resource manages a particular windows service in a specific path.
[DscResource()] indicates the class is a DSC resource
#>


[DscResource()]
class cEprsCreateWindowsService
{
[DscProperty(Key)]
[string]$ServiceName 

<#
    This parameter takes the name of the windows service.
 
    The [DscProperty(Key)] attribute indicates the property is a key and its value uniquely identifies a resource instance. Defining this attribute also means the property is required and DSC will ensure a value is set before calling the resource.
 
    A DSC resource must define at least one key property.
#>


[DscProperty(Mandatory)]
[string]$BinaryPath 

<#
    This parameter takes the physical path of windows service.
 
    The [DscProperty(Key)] attribute indicates the property is a key and its value uniquely identifies a resource instance. Defining this attribute also means the property is required and DSC will ensure a value is set before calling the resource.
 
    A DSC resource must define at least one key property.
#>


[DscProperty(Key)]
[string]$ExeName 
<#
    This property takes the name of exe that should run the windows service.
 
    The [DscProperty(Mandatory)] attribute indicates the property is required and DSC will guarantee it is set.
 
    If Mandatory is not specified or if it is defined as Mandatory=$false, the value is not guaranteed to be set when DSC calls the resource.
 
    This is appropriate for optional properties.
 
#>


[DscProperty(Mandatory=$false)]
[string]$Description 
<#
    This value gives the descripion about the windows service.
 
    The [DscProperty(Mandatory=$false)] attribute indicates the property is not guaranteed to be set when DSC calls the resource.
 
#>


[DscProperty(Mandatory=$true)]
[string]$DisplayName 
<#
 
    This property would display the value as Name of the service.
 
    The [DscProperty(Mandatory=$false)] attribute indicates the property is not guaranteed to be set when DSC calls the resource.
 
#>

[DscProperty(Mandatory)]
[ValidateSet("Automatic","Disabled","Manual")]
[string]$StartupType
<#
    This property takes startup type of service(Automatic\Disbaled\Manual).
 
    The [DscProperty(Mandatory)] attribute indicates the property is required and DSC will guarantee it is set.
 
    If Mandatory is not specified or if it is defined as Mandatory=$false, the value is not guaranteed to be set when DSC calls the resource.
 
    This is appropriate for optional properties.
#>


[DscProperty(Mandatory=$false)]
[String]$Domain 
<#
    Property takes the value for Account Domain to run the service on. This is an optional property but it should be provided if logon account type is custom.
#>


[DscProperty(Mandatory=$false)]
[String]$UserName 
<#
    Property takes the UserName for the service to run on. This is an optional property but it should be provided if logon account type is custom.
#>


[DscProperty(Mandatory=$false)]
[String]$Password 
<#
    Property takes the Password for the user account given. This is an optional property but it should be provided if logon account type is custom.
#>


[DscProperty(Mandatory)]
[ValidateSet("Custom","LocalSystem")]
[String]$LogOnAccount 
<#
     
    This property takes the value for type of logon account(Custom or LocalSystem) the service would run on. If Custom type is passed then Domain, username and password should be given.
 
    The [DscProperty(Mandatory)] attribute indicates the property is required and DSC will guarantee it is set.
 
    If Mandatory is not specified or if it is defined as Mandatory=$false, the value is not guaranteed to be set when DSC calls the resource.
 
    This is appropriate for optional properties.
 
#>

[DscProperty(Mandatory)]
[ValidateSet("Absent","Present")][Ensure]$Ensure #Ensure value(Give Present for install and Absent for uninstall)


<#
    This method is equivalent of the Get‐TargetResource script function.
    The implementation should use the keys to find appropriate resources.
    This method returns an instance of this class with the updated key properties.
#>


[cEprsCreateWindowsService] Get()
{
    $Service = Get-WmiObject win32_service | where {$_.name -eq $this.ServiceName} -ErrorAction Ignore
    if($service)
    {
        $this.Ensure = [Ensure]::Present
        $this.DisplayName = $Service.DisplayName
        $this.ServiceName = $Service.Name
        $this.UserName = $Service.StartName
    }
    else
    {
        $this.Ensure = [Ensure]::Absent
    }
    return $this
}

<#
    This method is equivalent of the Set‐TargetResource script function.
    It sets the resource to the desired state.
#>


[void] Set()
{
    $filePresent = $this.TestFilePath("$($this.BinaryPath)\$($this.ExeName)")
    if($this.Ensure -eq [Ensure]::Present)
    {
        if($filePresent -eq $true)
        {
            if($this.LogOnAccount -eq "Custom")
            {
                if(!$this.Domain -or !$this.UserName -or !$this.Password)
                {
                    Write-Verbose "pass 'Domain','UserName' and 'Password' parameters for setting up custom account as logon for windows service."
                    exit 1 
                }
            }
            Write-Verbose "Creating windows service '$($this.ServiceName)' $(Get-Date)"
            
            if($this.Description -eq "" -or $null)
            {
                New-Service -Name "$($this.ServiceName)" -BinaryPathName "$($this.BinaryPath)\$($this.ExeName)" -DisplayName "$($this.DisplayName)" -StartupType $this.StartupType
            }
            else
            {
                New-Service -Name "$($this.ServiceName)" -BinaryPathName "$($this.BinaryPath)\$($this.ExeName)" -DisplayName "$($this.DisplayName)" -Description "$($this.Description)" -StartupType $this.StartupType
            }
            if($this.LogOnAccount -eq "Custom")
            {
                $this.UserName=$this.Domain+"\"+ $this.UserName
            }

            $Services = Get-WmiObject win32_service -property name, startname, caption, state | Where-Object { $_.name -eq "$($this.ServiceName)"}
            if($this.UserName -eq "LocalSystem")
            {
                $Services.Change($null,$null,$null,$null,$null,$null, "LocalSystem", $null)
            }
            elseif($this.UserName -eq "NT Authority\Network Service")
            {
                $Services.Change($null,$null,$null,$null,$null,$null, "NT Authority\Network Service", $null)
            }
            else
            {
                $Services.Change($null,$null,$null,$null,$null,$null, $this.UserName, $this.Password)
            }
            Start-Service -Name $($this.ServiceName)
        }
        else
        {
            Write-Error "Path $($this.BinaryPath)\$($this.ExeName) not found."
        }
    }
    else
    {
        Write-Verbose "Ensure Set to Absent... Removing the service"

        $service = Get-WmiObject -Class Win32_Service -Filter "Name='$($this.ServiceName)'"

        $err=Stop-Service $service.Name -force
        if($err -eq $null)
        {
            $service.delete()
                        
        }
        else
        {
            Write-Verbose "$err"
            exit 1
        }
    }
}

<#
    This method is equivalent of the Test‐TargetResource script function.
    It should return True or False, showing whether the resource is in a desired state.
#>


[bool] Test()
{
    $CheckService = Get-WmiObject win32_service | where {$_.name -eq $this.ServiceName} -ErrorAction Ignore
    if($this.Ensure -eq [Ensure]::Present)
    {
        if($CheckService -eq $null)
        {
            Write-Verbose "In if loop 'true'"
            return $false
        }
        else
        {
            Write-Verbose "In if loop 'false'"
            return $true
        }
    }
    else
    {
        if($CheckService -ne $null)
        {
            Write-Verbose "In else loop 'false'"
            return $false
        }
        else
        {
            Write-Verbose "In else loop 'true'"
            return $true
        }
    }
}

<#
    Helper method to check if the file for windows service exists and it is file
#>


[bool] TestFilePath([String]$location)
{
    $present = $true
    $item = Get-ChildItem -Path "$location" -ErrorAction Ignore
    
    if ($item -eq $null)
    {
        $present = $false
    }
    else
    {
        Write-Verbose "Path $($location) found."
    }
    return $present
}
}