System stored procedures (practical)

Status: Reviewed
Stack: PowerShell → SQL / stored procs → Blazor
Depends on: SQL-CLI-01 (sqlcmd/Invoke-Sqlcmd to call them)
Goal: Know which built-in procs to call from CLI/scripts for inventory, config, security, Agent, and maintenance — and which to avoid or replace with DMVs.

Sources: Microsoft Learn (sql-server-ver17 / 2026 docs). Curated for Rick’s ops loop — not an encyclopedia of every sp_* / xp_*.


1. How to call from Rick’s stack

Pointer only — flags, auth, and exit codes live in SQL-CLI-01. Patterns for staging → Blazor live in SQL-CLI-07.

Caller Pattern When
sqlcmd / go-sqlcmd sqlcmd -S $srv -E -d master -b -Q "EXEC sp_…" Agent jobs, loops, $LASTEXITCODE, CSV export (-s,-W)
Invoke-Sqlcmd Invoke-Sqlcmd -ServerInstance $srv -Database master -Query "EXEC …" Objects / DataRows → JSON / Blazor staging
Query file sqlcmd -i .\inventory.sql -b Multi-statement batches with GO

Parameters

Permissions (high level)

Action Typical need
Read most sp_help* Often public + some permission on the object / VIEW DEFINITION
Change sp_configure / RECONFIGURE ALTER SETTINGS (sysadmin / serveradmin hold it)
Session / wait DMVs VIEW SERVER STATE (SQL 2022+: often VIEW SERVER PERFORMANCE STATE)
Agent job start / history SQLAgentOperatorRole / SQLAgentUserRole / sysadmin (see §6)
xp_cmdshell Disabled by default; CONTROL SERVER or explicit GRANT EXEC + proxy — treat as toxic

2. Decision: system procs vs DMVs / catalog views

Need Prefer Why
Scripted inventory → staging → Blazor Catalog views / DMVs One result set, filterable, joinable, stable columns
One-off interactive peek in SSMS / sqlcmd sp_help* / sp_who2 Fast, zero typing of joins
Change server config sp_configure + RECONFIGURE Still the supported write path; read via sys.configurations
Rename table/column/index sp_rename (with caveats) Official rename API — never for modules (proc/view/fn/trigger)
Orphaned users / login map (new work) ALTER USER / catalog sp_change_users_login deprecated
“What’s running / blocking?” automation DMVs sp_who/sp_who2 are snapshotted text; not pipeline-friendly
Agent: list / start / history msdb Agent procs or msdb.dbo.sysjobs* Procs are fine for ops; tables/views better for dashboards
Dependencies sys.dm_sql_*_entities / sys.sql_expression_dependencies sp_depends deprecated
OS shell from SQL Don’t — use PowerShell outside the engine xp_cmdshell is a security incident waiting to happen

Rule for this stack: collect with DMVs/catalog in .sql files; reserve system procs for (a) config changes, (b) Agent control, (c) rare interactive triage.


3. Curated catalog (high-value only)

Columns: Proc · Purpose · Typical args · Perm note · Prefer instead?

3.1 Instance / config

Proc Purpose Typical args Perm Prefer instead?
sp_configure List or set server config options @configname, @configvalue Read: public; write + RECONFIGURE: ALTER SETTINGS Read: sys.configurations (value, value_in_use, is_dynamic). Write: still sp_configure.
RECONFIGURE / RECONFIGURE WITH OVERRIDE Install pending config_valuerun_value (statement, not a proc) ALTER SETTINGS Check is_dynamic; non-dynamic needs service restart. WITH OVERRIDE skips sanity checks — use rarely.
sp_helpserver Linked / remote servers summary @server, @optname, @show_topology public (limited) sys.servers, sys.linked_logins for scripts
sp_who Sessions / blocking (documented) @loginame (ACTIVE or login) public / VIEW SERVER STATE for all sys.dm_exec_sessions + sys.dm_exec_requests + sys.dm_exec_connections
sp_who2 Undocumented richer who (CPU, IO, BlkBy) same spirit as sp_who same Same DMVs; or community sp_WhoIsActive for interactive. Do not build Blazor on sp_who2.

sp_configure caveats (ops)

  1. Show advanced: EXEC sp_configure 'show advanced options', 1; RECONFIGURE; — then set option — then ideally flip advanced back to 0.
  2. After set, config_valuerun_value until RECONFIGURE. Confirm with sys.configurations.
  3. is_dynamic = 0 → restart required even after successful RECONFIGURE.
  4. Surface-area options (xp_cmdshell, Ole Automation Procedures, clr enabled, etc.) are policy decisions, not casual toggles.
  5. Database-scoped options → ALTER DATABASE SCOPED CONFIGURATION, not sp_configure.

3.2 Database / objects

Proc Purpose Typical args Perm Prefer instead?
sp_help Object metadata (columns, indexes, …) multi-set @objname or none (list all) public + object perms; VIEW DEFINITION for constraints sys.objects / sys.columns / sys.indexes / sys.types
sp_helpdb DB size/status/owner snapshot @dbname or none public sys.databases + sys.master_files (+ sp_spaceused / DMVs for space)
sp_helptext Print module definition as rows @objname, @columnname VIEW DEFINITION sys.sql_modules.definition / OBJECT_DEFINITION() — single column, scriptable
sp_spaceused Row counts / reserved / used space @objname, @updateusage, @oneresultset=1 public; @updateusagedb_owner For fleets: sys.dm_db_partition_stats + sys.allocation_units. Use @oneresultset = 1 if you must call the proc from PS.
sp_rename Rename table/column/index/constraint/type/db @objname, @newname, @objtype ALTER on object; db rename → sysadmin/dbcreator Do not rename procs/views/fns/triggers (definition text stays old). Refs do not auto-update — check sys.sql_expression_dependencies first. Prefer drop/create for modules.

3.3 Security / principals

Proc Purpose Typical args Perm Prefer instead?
sp_helplogins Logins + DB users/roles map @LoginNamePattern securityadmin / sysadmin for full sys.server_principals, sys.database_principals, sys.server_role_members
sp_helpuser Users / roles in current DB @name_in_db public (limited) Deprecated. Use catalog views.
sp_helpsrvrole / sp_helpsrvrolemember Fixed server roles role name optional public sys.server_principals + sys.server_role_members
sp_helprole / sp_helprolemember DB roles role name optional public sys.database_principals + sys.database_role_members
sp_validatelogins Windows logins whose SIDs no longer resolve none sysadmin / securityadmin Still useful for orphaned Windows logins after AD cleanup
sp_change_users_login Map orphaned DB user ↔ login @Action (Report/Update_One/Auto_Fix) securityadmin / dbo Deprecated. Use ALTER USER [u] WITH LOGIN = [l]. Prefer Report-style query via SID compare on catalog views. Avoid Auto_Fix in prod.
sp_migrate_user_to_contained Move user toward contained DB model user / rename / password args CONTROL on DB Only when adopting contained DBs

Modern alternatives (bookmark)

Old habit New work
sp_addlogin / sp_grantdbaccess / sp_addrolemember CREATE LOGIN / CREATE USER / ALTER ROLE … ADD MEMBER
sp_password ALTER LOGIN … WITH PASSWORD
sp_changedbowner ALTER AUTHORIZATION ON DATABASE::db TO …
sp_helpuser / sp_change_users_login catalog + ALTER USER

3.4 SQL Server Agent (msdb)

Proc Purpose Typical args Perm Prefer instead?
sp_help_job Job metadata (+ steps/schedules if filtered) @job_name / @job_id, filters (@enabled, @execution_status, …) SQLAgent roles / sysadmin Dashboard: msdb.dbo.sysjobs, sysjobsteps, sysjobschedules
sp_start_job Start a job now @job_name or @job_id; optional @step_name Operator / owner / sysadmin Keep the proc for control plane; don’t reinvent
sp_help_jobhistory Run history @job_name, @start_run_date, @outcome_result, … same family msdb.dbo.sysjobhistory (+ agent_datetime()) for Blazor grids
sp_stop_job Stop running job @job_name / @job_id same
sp_update_job Enable/disable / change properties @job_name, @enabled, … same Prefer explicit enable/disable over ad-hoc SSMS clicks in scripts

Call as EXEC msdb.dbo.sp_start_job @job_name = N'Nightly Inventory';

3.5 Replication / Database Mail / niche

Skip deep coverage unless you operate them daily.

Area Touch points Note
Database Mail sysmail_* procs in msdb; profiles/accounts Prefer catalog sysmail_* views for inventory; send via sp_send_dbmail only from controlled jobs
Replication sp_helppublication, sp_helpdistributor, … Whole subsystem — own runbook; not CLI inventory core
Log shipping / mirroring dedicated procs + DMVs Prefer DMVs/AG DMVs for modern HA
Extended Events sp_xe_* Prefer SSMS / scripts against sys.dm_xe_*

See niche: if Rick hits a specific subsystem, spin a one-pager — don’t bloat this sheet.

3.6 Extended procedures xp_* — security warning

Proc Purpose Stance
xp_cmdshell Run OS command as SQL service account (or proxy) Danger. Disabled by default. Enabling expands blast radius to the host. Prefer PowerShell / Agent CmdExec / external worker outside the engine. Never enable for Blazor app pool convenience.
xp_fileexist Check path existence on server filesystem Useful in Agent pre-checks; still server-side IO. Prefer PS Test-Path on a jump box when possible.
xp_dirtree List directories/files under a path Same host-trust issues; limit use
xp_fixeddrives Free space per drive letter Quick interactive; fleet → PS CIM / Get-PSDrive / monitoring
sp_xp_cmdshell_proxy_account Set non-sysadmin proxy for cmdshell Only if cmdshell is an accepted risk with least-privilege proxy

Heavy warning: any xp_* that touches the OS runs in the engine’s trust boundary. CIS / auditors flag xp_cmdshell = 1. For Rick’s AD → PS → SQL → Blazor path, OS work stays in PowerShell; SQL stores and transforms data.


4. Deprecated / avoid (short)

Avoid Replacement
sp_depends sys.dm_sql_referencing_entities / sys.dm_sql_referenced_entities
sp_change_users_login ALTER USER … WITH LOGIN
sp_helpuser sys.database_principals (+ role member views)
sp_addlogin, sp_droplogin, sp_grantlogin, sp_revokelogin, sp_denylogin CREATE/ALTER/DROP LOGIN, CREATE USER
sp_addrole, sp_addrolemember, sp_adduser, sp_grantdbaccess, … CREATE ROLE / ALTER ROLE / CREATE USER
sp_password ALTER LOGIN
sp_changedbowner, sp_changeobjectowner ALTER AUTHORIZATION / ALTER SCHEMA
sp_attach_db / sp_detach_db (legacy habits) CREATE DATABASE … FOR ATTACH / modern admin scripts
SQL Trace procs (sp_trace_*) Extended Events
Building automation on sp_who2 Documented DMVs (or WhoIsActive for humans only)
Renaming modules with sp_rename Drop + create

Still OK when you know why: sp_configure, Agent sp_*_job*, sp_spaceused (with @oneresultset), sp_rename for tables/columns/indexes, sp_validatelogins, interactive sp_help*.


5. Copy-paste recipes (inventory → Blazor staging)

Assume Windows auth (-E). Swap to CLI-06 patterns for Entra/SQL auth. Always -b / check exit codes (CLI-01, CLI-07).

5.1 Config snapshot via catalog (preferred) + optional proc cross-check

$sql = @"
SET NOCOUNT ON;
SELECT
  name,
  value AS config_value,
  value_in_use AS run_value,
  is_dynamic,
  is_advanced
FROM sys.configurations
ORDER BY name;
"@
Invoke-Sqlcmd -ServerInstance $srv -Database master -Query $sql |
  Export-Csv ".\staging\config_$($srv -replace '\\','_').csv" -NoTypeInformation
# Interactive / one-off only
sqlcmd -S $srv -E -d master -b -Q "EXEC sp_configure;"

5.2 Database inventory (catalog, not sp_helpdb)

$sql = @"
SET NOCOUNT ON;
SELECT
  d.name, d.database_id, d.state_desc, d.recovery_model_desc,
  d.compatibility_level, d.is_read_only, d.is_encrypted,
  SUSER_SNAME(d.owner_sid) AS owner_name,
  d.create_date
FROM sys.databases AS d
ORDER BY d.name;
"@
Invoke-Sqlcmd -ServerInstance $srv -Database master -Query $sql |
  Export-Csv ".\staging\databases_$env:COMPUTERNAME.csv" -NoTypeInformation

5.3 Space used — proc with one result set (acceptable) vs DMV (preferred)

# Acceptable: single result set for PS
sqlcmd -S $srv -E -d AdventureWorks -b -Q "EXEC sp_spaceused @oneresultset = 1;" -s"," -W -o ".\staging\space_aw.csv"
-- Preferred fleet query (run per DB or via cursor/PS loop)
SET NOCOUNT ON;
SELECT
  DB_NAME() AS database_name,
  OBJECT_SCHEMA_NAME(p.object_id) AS [schema_name],
  OBJECT_NAME(p.object_id) AS table_name,
  SUM(p.rows) AS row_count,
  SUM(a.total_pages) * 8 AS total_kb,
  SUM(a.used_pages) * 8 AS used_kb
FROM sys.partitions AS p
JOIN sys.allocation_units AS a ON p.partition_id = a.container_id
WHERE p.index_id IN (0, 1)
GROUP BY p.object_id
ORDER BY total_kb DESC;

5.4 Session / blocking snapshot (DMV — feed Blazor)

$sql = @"
SET NOCOUNT ON;
SELECT
  s.session_id, s.login_name, s.host_name, s.program_name,
  s.status AS session_status, r.status AS request_status,
  r.blocking_session_id, r.wait_type, r.wait_time,
  r.cpu_time, r.total_elapsed_time,
  DB_NAME(r.database_id) AS database_name,
  SUBSTRING(t.text, 1, 400) AS statement_text
FROM sys.dm_exec_sessions AS s
LEFT JOIN sys.dm_exec_requests AS r ON s.session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) AS t
WHERE s.is_user_process = 1
ORDER BY r.total_elapsed_time DESC;
"@
Invoke-Sqlcmd -ServerInstance $srv -Database master -Query $sql |
  Export-Csv ".\staging\sessions.csv" -NoTypeInformation

5.5 Orphaned users report (modern — no sp_change_users_login)

-- Run in each user database
SET NOCOUNT ON;
SELECT
  dp.name AS db_user,
  dp.type_desc,
  dp.sid
FROM sys.database_principals AS dp
LEFT JOIN sys.server_principals AS sp ON dp.sid = sp.sid
WHERE dp.type IN ('S', 'U', 'G')
  AND dp.sid IS NOT NULL
  AND sp.sid IS NULL
  AND dp.name NOT IN (N'guest', N'INFORMATION_SCHEMA', N'sys');
-- Fix: ALTER USER [db_user] WITH LOGIN = [login_name];

5.6 Agent job inventory + start (control vs dashboard)

# Inventory → staging
$sql = @"
SET NOCOUNT ON;
SELECT
  j.name, j.enabled, j.date_created, j.date_modified,
  SUSER_SNAME(j.owner_sid) AS owner_name,
  c.name AS category
FROM msdb.dbo.sysjobs AS j
LEFT JOIN msdb.dbo.syscategories AS c ON j.category_id = c.category_id
ORDER BY j.name;
"@
Invoke-Sqlcmd -ServerInstance $srv -Database msdb -Query $sql |
  Export-Csv ".\staging\agent_jobs.csv" -NoTypeInformation

# Control plane — start a known job
sqlcmd -S $srv -E -d msdb -b -Q "EXEC msdb.dbo.sp_start_job @job_name = N'Nightly Inventory';"
if ($LASTEXITCODE -ne 0) { throw "sp_start_job failed: $LASTEXITCODE" }

5.7 Module definition pull (catalog, not sp_helptext)

$sql = @"
SET NOCOUNT ON;
SELECT
  OBJECT_SCHEMA_NAME(m.object_id) AS schema_name,
  o.name AS object_name,
  o.type_desc,
  m.definition
FROM sys.sql_modules AS m
JOIN sys.objects AS o ON m.object_id = o.object_id
WHERE o.type IN ('P', 'V', 'FN', 'IF', 'TF', 'TR')
ORDER BY schema_name, object_name;
"@
Invoke-Sqlcmd -ServerInstance $srv -Database $db -Query $sql |
  Export-Csv ".\staging\modules_$db.csv" -NoTypeInformation

5.8 Validate Windows logins after AD changes

sqlcmd -S $srv -E -d master -b -Q "EXEC sp_validatelogins;" -s"," -W -o ".\staging\invalid_windows_logins.csv"

6. Permissions cheat

Permission / role Unlocks
public Many read-only sp_help* (object still must be visible)
VIEW DEFINITION Module text, constraint details via help / catalogs
VIEW ANY DATABASE See rows in sys.databases beyond current DB
VIEW SERVER STATE Classic DMV access (sessions, requests, waits, IO)
VIEW SERVER PERFORMANCE STATE SQL 2022+ finer replacement for many perf DMVs
VIEW SERVER SECURITY STATE SQL 2022+ security-related DMV slice
ALTER SETTINGS sp_configure writes + RECONFIGURE (sysadmin, serveradmin)
ALTER ANY LOGIN / securityadmin Login management; parts of sp_helplogins / validate
CONTROL SERVER / sysadmin Everything; required for raw xp_cmdshell without proxy grants
SQLAgentUserRole Local jobs you own
SQLAgentReaderRole Read multi-job / history
SQLAgentOperatorRole Start/stop/enable broadly
db_owner sp_spaceused @updateusage = 'true'; most DB DDL

Blazor note (CLI-07): ops inventory identity ≠ app-pool identity. Grant the collector VIEW SERVER STATE (or 2022 performance state) + read on staging; grant Blazor EXEC on page procs / SELECT on views only.


Quick ops checklist


Open questions / follow-ups

Documented for Rick in the meta companion — decide numbering (CLI-09 vs Dude), WhoIsActive as optional cite, and Azure SQL gaps (no Agent / limited xp_*).