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_*.
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
EXEC dbo.sp_helpdb @dbname = N'AdventureWorks';master (or msdb for Agent). Qualify when the session DB is a user DB: EXEC master.dbo.sp_configure; / EXEC msdb.dbo.sp_help_job;sp_help* return multiple result sets — sqlcmd prints them all; Invoke-Sqlcmd returns the last set unless you use lower-level ADO.NET / SqlCommand. For Blazor inventory, prefer catalog views / DMVs (single, queryable result set) over multi-set help procs.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 |
| 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.
Columns: Proc · Purpose · Typical args · Perm note · Prefer instead?
| 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_value → run_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)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE; — then set option — then ideally flip advanced back to 0.config_value ≠ run_value until RECONFIGURE. Confirm with sys.configurations.is_dynamic = 0 → restart required even after successful RECONFIGURE.xp_cmdshell, Ole Automation Procedures, clr enabled, etc.) are policy decisions, not casual toggles.ALTER DATABASE SCOPED CONFIGURATION, not sp_configure.| 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; @updateusage → db_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. |
| 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 |
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';
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.
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.
| 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*.
Assume Windows auth (-E). Swap to CLI-06 patterns for Entra/SQL auth. Always -b / check exit codes (CLI-01, CLI-07).
$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;"
$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
# 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;
$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
-- 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];
# 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" }
$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
sqlcmd -S $srv -E -d master -b -Q "EXEC sp_validatelogins;" -s"," -W -o ".\staging\invalid_windows_logins.csv"
| 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.
sp_configure → RECONFIGURE → verify value vs value_in_use → restart if is_dynamic = 0ALTER USER, not sp_change_users_loginsysjobs* tables; Agent control → sp_start_job / sp_stop_jobxp_cmdshell → leave off; do OS work in PowerShellsp_help* → don’t pipe into Blazor staging without shapingDocumented for Rick in the meta companion — decide numbering (CLI-09 vs Dude), WhoIsActive as optional cite, and Azure SQL gaps (no Agent / limited xp_*).