DBA Scripts: Get Services Information

🔧Part of the DBA-Tools Project, copy/paste SQL Server scripts and health checks.In: Server & ConfigurationServer Inventory

A SQL Server instance is really a handful of Windows services working together, and it is surprisingly common for one of them to be quietly misconfigured. The SQL Server Agent service set to Manual startup instead of Automatic. A service running under an account nobody can account for. The Full-Text service left Disabled that nobody remembers switching off. None of it shows up as an error until the day the server reboots and something does not come back with it.

This script inventories every SQL Server service on the instance with its startup type, status and account, and adds three plain risk columns on top. It is deliberately a visibility script rather than a verdict: it shows you the state and the obvious risks, and leaves the judgement to you. Correlating findings across a whole instance is what the health check and its AI assessment are for.


Why Services Information Matters

The services layer sits underneath everything else a DBA monitors. If SQL Server Agent is not running, scheduled jobs stop firing, and backups, index maintenance and every collector job go quiet with no immediate symptom. If the service account is wrong you inherit either a security exposure, such as LocalSystem and its unrestricted local access, or an operational fragility, such as a named user account whose password will eventually expire and take the service down with it.

This is one of the first things worth checking on a server you did not build yourself:

  • Confirms the Engine and Agent are both set to start automatically
  • Surfaces high-privilege or shared service accounts that should be dedicated instead
  • Separates services that must be running from features that are allowed to sit idle
  • Confirms cluster node ownership on a Failover Cluster Instance

When to Run This Script

  • Routine SQL Server health checks
  • Reviewing a server you have just inherited or migrated
  • After a Windows patch cycle or reboot, to confirm every service came back
  • Auditing service account usage across an estate
  • Troubleshooting a job that “just stopped running”, where the Agent service is the first thing to check

The Script

Run the following against your SQL Server instance.

✓ Verified
  • Tested on: SQL Server 2025 (RTM CU8, 17.0.4075.5), Windows lab instance
  • Last verified: 2026-08-31 (run against a live instance, and each account branch tested against sample values)
  • Permissions: VIEW SERVER STATE
  • Safety: read-only, impact low
Account type definitions follow Microsoft’s service accounts reference, linked beside the claims they support.
/*
Script Name : Get-ServicesInformation
Category    : monitoring
Purpose     : SQL Server services — startup type, running status, and service account with
              risk flags. Surfaces manual/disabled startup on critical services and
              high-privilege service accounts (LocalSystem, SYSTEM, NetworkService).
Author      : Peter Whyte (https://sqldba.blog/dba-scripts-get-services-information/)
Requires    : VIEW SERVER STATE
*/
-- SAFE:ReadOnly
-- IMPACT:Low
SET NOCOUNT ON;

SELECT
    servicename,
    startup_type_desc,
    status_desc,
    process_id,
    -- datetimeoffset carries 7 fractional digits; style 120 needs VARCHAR(19), and a
    -- shorter target silently truncates to minutes
    CONVERT(VARCHAR(19), last_startup_time, 120) AS last_startup_time,
    service_account,
    is_clustered,
    cluster_nodename,
    -- Service account risk: built-in high-privilege accounts are a security concern
    CASE
        WHEN service_account IN ('LocalSystem', 'NT AUTHORITY\SYSTEM')
            THEN 'CRITICAL - LocalSystem has unrestricted local access; use a dedicated service account'
        WHEN service_account = 'NT AUTHORITY\NETWORK SERVICE'
            THEN 'WARN - NetworkService shares identity with other services; prefer a dedicated account'
        WHEN service_account LIKE 'NT Service\%'
            THEN 'OK - Managed Service Account (virtual account)'
        WHEN service_account LIKE '%$'
            THEN 'OK - Group Managed Service Account (gMSA)'
        WHEN service_account IS NULL OR service_account = ''
            THEN 'INFO - service account not visible (insufficient permissions or service not running)'
        ELSE 'OK - dedicated service account'
    END AS account_risk,
    -- Startup type: SQL Engine and Agent should be Automatic
    CASE
        WHEN startup_type_desc = 'Disabled'
            THEN 'CRITICAL - service is disabled; will not start after reboot'
        WHEN startup_type_desc = 'Manual'
             AND servicename NOT LIKE '%Browser%'
             AND servicename NOT LIKE '%Full-text%'
            THEN 'WARN - Manual startup; service will not auto-recover after reboot'
        WHEN startup_type_desc = 'Manual'
            THEN 'INFO - Manual startup (acceptable for Browser/Full-text if not required)'
        ELSE 'OK'
    END AS startup_risk,
    -- Running status
    CASE
        WHEN status_desc = 'Running'  THEN 'OK'
        WHEN status_desc = 'Stopped' AND servicename NOT LIKE '%Browser%'
            THEN 'WARN - service is stopped'
        ELSE 'INFO - ' + status_desc
    END AS running_status
FROM sys.dm_server_services
ORDER BY
    CASE
        WHEN servicename LIKE '%SQL Server (%' AND servicename NOT LIKE '%Agent%' THEN 1
        WHEN servicename LIKE '%SQL Server Agent%'                                 THEN 2
        ELSE 3
    END,
    servicename;

It queries sys.dm_server_services and returns one row per service with three risk columns: account_risk, startup_risk and running_status.

TipIf this script tells you to change an account, do it in SQL Server Configuration Manager, not the Windows Services applet. Microsoft are explicit that swapping it in the Services applet leaves Agent job steps using CmdExec, replication or SSIS failing afterwards. Budget for an outage either way: changing the Engine or Agent account needs a service restart, so every database on that instance is unavailable until it comes back.

How To Run From The Repo

Clone DBA Tools, initialize and run the script:

# Clone dba-tools repo:
git clone https://github.com/peterwhyte-lgtm/dba-tools

# Initialize environment:
cd dba-tools
.\Initialize-Environment.ps1

# Services, startup types, accounts and risk flags:
.\run.ps1 Get-ServicesInformation

This script lives in the repo at:


Example Output

SSMS results grid listing three SQL Server services on one instance, each Automatic and Running, with their process IDs, NT Service virtual accounts, is_clustered N, and an account_risk column reading OK for all three.

Which services come back depends on what is installed. On a default instance with Machine Learning Services you get the Engine, the Agent and Launchpad; add Browser and Full-Text on a named instance that uses them.

The grid scrolls further right than the window: startup_risk and running_status sit past account_risk and are covered in the panel below. Note last_startup_time here, populated for the Engine and NULL for the Agent and Launchpad. That is Windows, not a fault.


Understanding the Results

Read the three risk columns first and the raw values only when one of them says something.

servicename
One row per SQL Server service the instance knows about. Which ones appear depends on what is installed, so a service missing from this list is usually a feature you never installed rather than a fault.
startup_type_desc
status_desc
How the service starts at boot, and whether it is running right now. These are the raw values; the three risk columns below are the ones to read first.
service_account
The identity the service runs under. Microsoft define three kinds that are easy to confuse, and the service accounts reference sets them out: a virtual account is per service and local (NT Service\MSSQLSERVER), a managed service account is a domain account tied to one computer, and a group managed service account is the same idea across many. Both MSA and gMSA end in $.
account_risk
The verdict on that identity. Anything under NT Service\ is the Microsoft default and needs no action, and an account ending in $ is managed for you. Everything else is reported OK, so a plain domain account passes here whether or not it is a real service account.Act when this reads CRITICAL. LocalSystem has unrestricted access to the whole machine, far beyond what the database engine needs, and it is the account an attacker most wants to land on.
startup_risk
Whether the service comes back on its own. Browser and Full-text are exempt from the Manual warning because they are not always required. Other optional services are not exempt, so a Manual or stopped Launchpad on an instance that never runs R or Python will show a WARN you can read past.Act when this reads CRITICAL. A disabled Engine or Agent will not start after the next reboot, and nothing tells you until the reboot happens.
running_status
Whether it is up now. Only Browser is exempt from the stopped warning.Act when the Agent is stopped. Every scheduled job stops with it, silently, and backups are usually the first casualty.
process_id
last_startup_time
The Windows process and when it last started. Both come straight from Windows, and last_startup_time is not populated for every service: on the lab instance the Engine reports one while Agent and Launchpad return NULL. A NULL here is not a finding.
is_clustered
cluster_nodename
Whether this is a failover cluster instance and, if so, which node currently owns it. On a standalone instance is_clustered reads N and cluster_nodename is NULL, which is the normal, healthy answer.Act when you expected a cluster and see N. Somebody is looking at a standalone instance, or at the wrong server.

Anything CRITICAL is worth acting on directly. WARN deserves a deliberate decision, either fix it or write down why it is intentional, rather than being left as an accident of how the instance was built. INFO is the script telling you it cannot judge something for you.


Frequently Asked Questions

Why is SQL Server Agent set to Manual instead of Automatic?

Usually an oversight from the original build rather than a deliberate choice. Automatic is correct for almost every production instance, since Agent-dependent jobs (backups, maintenance, collectors) need it running after every reboot without anyone intervening.

Is NT Service\MSSQLSERVER a managed service account?

No, and the distinction matters if anyone ever audits you on it. That is a virtual account: local to the machine, created per service, with its password managed by Windows. A managed service account is a domain account named DOMAIN\ACCOUNTNAME$, and a group managed service account is the same thing shared across several servers. Microsoft’s reference keeps all three separate.

Virtual accounts are the Microsoft default and are the right answer for a standalone instance that never reaches off the box. You want an MSA or gMSA when the service needs to authenticate somewhere else, such as a backup share or a linked server.

Can this tell a proper service account from someone’s personal login?

No, and it does not pretend to. DOMAIN\svc_sql and DOMAIN\peter are indistinguishable from the account name alone, so both are reported the same way: OK - dedicated service account. That OK only means the account is not LocalSystem or NetworkService. It is not a check that anyone has confirmed the account is a real service account.

It is worth confirming, because a personal account is the one that takes the service down at the next password expiry, and it usually happens while that person is on leave.

Why is Launchpad flagged when Browser and Full-text are not?

Browser and Full-text are exempt from the Manual warning, and Browser from the stopped warning, because they are not always required. Anything else optional is not exempt: a stopped Launchpad on an instance that never runs R or Python will show a WARN.

That is a judgement call the script leaves to you rather than encoding. Read the risk columns as prompts, not verdicts.

Is running SQL Server as LocalSystem actually dangerous?

Yes, on a shared or multi-role server. LocalSystem has unrestricted access to the local machine, well beyond what the SQL Server process needs. A virtual account, managed service account or gMSA keeps the blast radius contained if the account is ever compromised.


Related Scripts

You may also find these scripts useful:


Summary

Service configuration is one of those things nobody checks until something has already gone wrong: a job that stopped running, a service that did not come back after a patch, an audit asking who has access to what. It takes seconds to review and rarely changes once it is set correctly.

Run it when you onboard a server you did not build, and again after any Windows patch cycle or cluster failover, to confirm every service is running under the account and startup type you expect.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *