Chapter 12Lesson 02~150 minutes

Services and Long-Running Workloads

Understand OS-managed services and daemons, Windows-only PowerShell service cmdlets, native Linux/macOS managers, dependencies, privileges, and bounded state polling.

Learning objectives

  • Define services/daemons and distinguish managed workload lifecycle from an interactive process.
  • Use Get-Service safely on Windows and inspect service objects/dependencies.
  • Recognize that PowerShell 7.6 service cmdlets are Windows-only.
  • Treat systemctl and launchctl as native platform adapters.
  • Poll for desired service state with a timeout.
  • Produce a read-only platform-aware workload snapshot.

1. A service or daemon is a long-running workload managed by the operating system

A service (Windows terminology) or daemon (common Unix terminology) is a background workload whose lifecycle is managed by the operating system rather than by a person keeping a terminal open. Service managers can start workloads at boot, restart them, track dependencies, apply identities/permissions, and expose status.

The critical PowerShell 7.6 platform fact is that the built-in service cmdlets such as Get-Service are Windows-only. On Linux, systemd is common but not universal; on macOS, launchd is the native service manager. A cross-platform script must detect capabilities instead of pretending these systems share one API.

2. Windows: Get-Service returns ServiceController objects

On Windows, Get-Service returns System.ServiceProcess.ServiceController objects. Important properties include Name, DisplayName, Status, and dependency information. PowerShell 6+ also exposes additional properties such as startup type through the Windows implementation.

if ($IsWindows -and (Get-Command Get-Service -ErrorAction SilentlyContinue)) {
    Get-Service |
        Select-Object -First 10 Name,DisplayName,Status,StartupType |
        Format-Table
} else {
    'Get-Service is not available as a PowerShell 7 service abstraction on this platform.'
}

3. Dependencies explain why service changes have blast radius

A service may require other services, and other services may depend on it. That relationship matters before a restart: stopping a dependency can cascade into application downtime.

if ($IsWindows) {
    $sample = Get-Service | Where-Object RequiredServices | Select-Object -First 1
    if ($sample) {
        $sample | Select-Object Name,Status
        $sample.RequiredServices | Select-Object Name,Status
        $sample.DependentServices | Select-Object Name,Status
    }
}

4. Start/Stop/Restart/Set operations are privileged state changes

Start-Service, Stop-Service, Restart-Service, and Set-Service modify Windows service state or configuration and often require elevation. Do not use an arbitrary system service as a lab target. Service changes should be tied to a runbook, maintenance window, dependency review, and recovery plan.

This lesson keeps the hands-on path read-only. Mutation syntax is shown only as a review pattern:

# Windows-only examples — do not run against production services as practice.
# Get-Service -Name 'YourDisposableService' | Stop-Service -WhatIf
# Restart-Service -Name 'YourDisposableService' -WhatIf
# Set-Service -Name 'YourDisposableService' -StartupType Manual -WhatIf

5. Linux: systemd is a native boundary, not a PowerShell service cmdlet

On a systemd-based Linux host, PowerShell can call systemctl like any other native executable. Chapter 10 rules apply: inspect output, check $LASTEXITCODE, and model the result as a PowerShell object when downstream automation needs structure.

if (-not $IsWindows -and (Get-Command systemctl -ErrorAction SilentlyContinue)) {
    systemctl is-system-running --quiet
    $systemStateCode = $LASTEXITCODE

    $lines = systemctl --no-pager --type=service --state=running --no-legend 2>$null
    [pscustomobject]@{
        Manager          = 'systemd'
        SystemStateCode  = $systemStateCode
        RunningUnitCount = @($lines).Count
    }
}

6. macOS: launchd/launchctl is another distinct contract

macOS uses launchd, commonly queried with launchctl. Its concepts and output are not identical to Windows services or systemd units. Isolate native parsing inside a platform adapter rather than leaking command-specific text throughout the rest of your automation.

if ($IsMacOS -and (Get-Command launchctl -ErrorAction SilentlyContinue)) {
    $rows = launchctl list
    [pscustomobject]@{
        Manager   = 'launchd'
        RowCount  = @($rows).Count
        ExitCode  = $LASTEXITCODE
    }
}

7. Poll for state with a deadline instead of sleeping blindly

A fixed Start-Sleep -Seconds 30 always waits 30 seconds and still cannot prove the desired state was reached. Polling combines repeated observation with a timeout. That gives fast success when the state changes quickly and bounded failure when it does not.

function Wait-WindowsServiceState {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Name,
        [Parameter(Mandatory)][ValidateSet('Running','Stopped')][string]$DesiredState,
        [ValidateRange(1,120)][int]$TimeoutSeconds = 15
    )

    if (-not $IsWindows) { throw 'This helper uses Windows Get-Service.' }
    $timer = [System.Diagnostics.Stopwatch]::StartNew()
    do {
        $service = Get-Service -Name $Name -ErrorAction Stop
        if ($service.Status.ToString() -eq $DesiredState) { return $service }
        Start-Sleep -Milliseconds 250
    } while ($timer.Elapsed.TotalSeconds -lt $TimeoutSeconds)

    throw "Service '$Name' did not reach $DesiredState within $TimeoutSeconds seconds."
}

8. A safe polling lab waits for a state that already exists

On Windows, choose a service that is already running and wait for Running. The lab verifies the polling algorithm without changing any service.

if ($IsWindows) {
    $running = Get-Service | Where-Object Status -eq Running | Select-Object -First 1
    if (-not $running) { throw 'No running service found for the read-only lab.' }

    $result = Wait-WindowsServiceState -Name $running.Name -DesiredState Running -TimeoutSeconds 5
    $result | Select-Object Name,DisplayName,Status
} else {
    'Windows service lab skipped: use the native service manager on this platform.'
}

9. Startup configuration is separate from current state

“Running now” and “starts automatically after reboot” are different questions. On Windows, startup configuration is part of service configuration. systemd uses enablement/unit policy, and launchd uses its own job definitions. A reliable audit records both current state and configured startup behavior where the platform exposes it.

10. Build a platform-aware workload snapshot

function Get-WorkloadManagerSnapshot {
    [CmdletBinding()]
    param()

    if ($IsWindows -and (Get-Command Get-Service -ErrorAction SilentlyContinue)) {
        return Get-Service | Select-Object -First 20 @{
            Name='Manager';Expression={'Windows Service Control Manager'}
        }, Name, DisplayName, Status
    }

    if ($IsLinux -and (Get-Command systemctl -ErrorAction SilentlyContinue)) {
        $units = systemctl --no-pager --type=service --state=running --no-legend 2>$null
        return [pscustomobject]@{ Manager='systemd'; RunningRows=@($units).Count }
    }

    if ($IsMacOS -and (Get-Command launchctl -ErrorAction SilentlyContinue)) {
        $jobs = launchctl list
        return [pscustomobject]@{ Manager='launchd'; ListedRows=@($jobs).Count }
    }

    [pscustomobject]@{ Manager='Unknown'; Note='No supported service-manager adapter detected.' }
}

Get-WorkloadManagerSnapshot

11. Why this matters in DevOps

Deployment systems often need to prove that a workload reached a healthy operational state. The correct abstraction is not “sleep and hope.” It is: invoke the platform-specific state transition through an approved adapter, poll observable state with a deadline, preserve the failure evidence, and return a stable object to the higher-level workflow.

12. Common mistakes

  • Assuming Get-Service is cross-platform in PowerShell 7.6.
  • Restarting a service without checking dependencies or privileges.
  • Using fixed sleeps instead of bounded polling.
  • Ignoring native exit codes from systemctl/launchctl.
  • Mixing native-service text parsing throughout business logic instead of isolating an adapter.

13. Knowledge check

Question 1. Is Get-Service a cross-platform PowerShell 7.6 cmdlet?

Question 2. What is the difference between service state and startup configuration?

Question 3. Why poll with a timeout?

Question 4. What should PowerShell do with systemctl on Linux?

Question 5. Why isolate platform-specific service code?

14. Summary and next bridge

Services are OS-managed long-running workloads, but their management APIs are platform-specific. PowerShell 7.6 service cmdlets are Windows-only; Linux and macOS require native service-manager adapters. Keep changes privileged and deliberate, poll with deadlines, and normalize evidence into objects. Next we examine CIM—the structured Windows management model that replaced many older WMI scripting patterns.

15. Authoritative references

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.