Private/Invoke-ADTServiceAndDependencyOperation.ps1

#-----------------------------------------------------------------------------
#
# MARK: Invoke-ADTServiceAndDependencyOperation
#
#-----------------------------------------------------------------------------

function Invoke-ADTServiceAndDependencyOperation
{
    <#
 
    .SYNOPSIS
    Process Windows service and its dependencies.
 
    .DESCRIPTION
    Process Windows service and its dependencies.
 
    .PARAMETER Service
    Specify the name of the service.
 
    .PARAMETER SkipDependentServices
    Choose to skip checking for dependent services. Default is: $false.
 
    .PARAMETER PendingStatusWait
    The amount of time to wait for a service to get out of a pending state before continuing. Default is 60 seconds.
 
    .PARAMETER PassThru
    Return the System.ServiceProcess.ServiceController service object.
 
    .INPUTS
    None. You cannot pipe objects to this function.
 
    .OUTPUTS
    System.ServiceProcess.ServiceController. Returns the service object.
 
    .EXAMPLE
    Invoke-ADTServiceAndDependencyOperation -Service wuauserv -Operation Start
 
    .EXAMPLE
    Invoke-ADTServiceAndDependencyOperation -Service wuauserv -Operation Stop
 
    .LINK
    https://psappdeploytoolkit.com
 
    #>


    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateScript({
                if (!$_.Name)
                {
                    $PSCmdlet.ThrowTerminatingError((New-ADTValidateScriptErrorRecord -ParameterName Service -ProvidedValue $_ -ExceptionMessage 'The specified service does not exist.'))
                }
                return !!$_
            })]
        [System.ServiceProcess.ServiceController]$Service,

        [Parameter(Mandatory = $true)]
        [ValidateSet('Start', 'Stop')]
        [System.String]$Operation,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]$SkipDependentServices,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [System.TimeSpan]$PendingStatusWait = [System.TimeSpan]::FromSeconds(60),

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]$PassThru
    )

    # Internal worker function.
    function Invoke-DependentServiceOperation
    {
        # Discover all dependent services.
        Write-ADTLogEntry -Message "Discovering all dependent service(s) for service [$Name] which are not '$($status = if ($Operation -eq 'Start') {'Running'} else {'Stopped'})'."
        if (!($dependentServices = Get-Service -Name $Service.ServiceName -DependentServices | & { process { if ($_.Status -ne $status) { return $_ } } }))
        {
            Write-ADTLogEntry -Message "Dependent service(s) were not discovered for service [$Name]."
            return
        }

        # Action each found dependent service.
        foreach ($dependent in $dependentServices)
        {
            Write-ADTLogEntry -Message "$(('Starting', 'Stopping')[$Operation -eq 'Start']) dependent service [$($dependent.ServiceName)] with display name [$($dependent.DisplayName)] and a status of [$($dependent.Status)]."
            try
            {
                $dependent | & "$($Operation)-Service" -Force -WarningAction Ignore
            }
            catch
            {
                Write-ADTLogEntry -Message "Failed to $($Operation.ToLower()) dependent service [$($dependent.ServiceName)] with display name [$($dependent.DisplayName)] and a status of [$($dependent.Status)]. Continue..." -Severity 2
            }
        }
    }

    # Wait up to 60 seconds if service is in a pending state.
    if (([System.ServiceProcess.ServiceControllerStatus]$desiredStatus = @{ ContinuePending = 'Running'; PausePending = 'Paused'; StartPending = 'Running'; StopPending = 'Stopped' }[$Service.Status]))
    {
        Write-ADTLogEntry -Message "Waiting for up to [$($PendingStatusWait.TotalSeconds)] seconds to allow service pending status [$($Service.Status)] to reach desired status [$DesiredStatus]."
        $Service.WaitForStatus($desiredStatus, $PendingStatusWait)
        $Service.Refresh()
    }

    # Discover if the service is currently running.
    Write-ADTLogEntry -Message "Service [$($Service.ServiceName)] with display name [$($Service.DisplayName)] has a status of [$($Service.Status)]."
    if (($Operation -eq 'Stop') -and ($Service.Status -ne 'Stopped'))
    {
        # Process all dependent services.
        if (!$SkipDependentServices)
        {
            Invoke-DependentServiceOperation
        }

        # Stop the parent service.
        Write-ADTLogEntry -Message "Stopping parent service [$($Service.ServiceName)] with display name [$($Service.DisplayName)]."
        $Service = $Service | Stop-Service -PassThru -WarningAction Ignore -Force
    }
    elseif (($Operation -eq 'Start') -and ($Service.Status -ne 'Running'))
    {
        # Start the parent service.
        Write-ADTLogEntry -Message "Starting parent service [$($Service.ServiceName)] with display name [$($Service.DisplayName)]."
        $Service = $Service | Start-Service -PassThru -WarningAction Ignore

        # Process all dependent services.
        if (!$SkipDependentServices)
        {
            Invoke-DependentServiceOperation
        }
    }

    # Return the service object if option selected.
    if ($PassThru)
    {
        return $Service
    }
}