Document Classification: Operational Procedures - On-Call Reference
Author: Adrian Johnson | Email: adrian207@gmail.com
This Monitoring & Alerting Triage Guide provides on-call engineers with comprehensive incident response procedures, diagnostic workflows, and resolution steps for all Configuration Management infrastructure alerts.
On-call teams using this guide achieve:
- ⚡ Rapid Response: Meet SLA targets (15 min critical, 1 hour high severity)
- 🎯 Effective Triage: Systematic diagnostic procedures eliminate guesswork
- ✅ Consistent Resolution: Proven resolution steps for 95%+ of common alerts
- 📞 Proper Escalation: Clear criteria for when to engage additional expertise
- 📈 Continuous Improvement: Post-incident reviews documented for pattern analysis
Severity-Based Response Times:
|
🔴 Critical
|
🟠 High
|
🟡 Warning
|
🟢 Info
|
📊 Alert Volume Expectations:
- ✅ Normal Operations: 0-2 Critical alerts/month, 5-10 Warning alerts/week
- 🚀 During Deployment: Increased Info/Warning alerts expected (planned changes)
- 🎯 Alert Accuracy: <5% false positive rate (well-tuned thresholds)
🏗️ Monitoring Architecture:
┌─────────────┐ ┌────────────┐ ┌──────────────┐ ┌──────────────┐
│ Managed │────▶│ Node │────▶│ Prometheus │────▶│ Alertmanager │
│ Nodes │ │ Exporters │ │ │ │ │
└─────────────┘ └────────────┘ └──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Grafana │ │ PagerDuty │
│ Dashboards │ │ Slack/Email │
└─────────────┘ └──────────────┘
📊 Key Dashboards:
- 🏥 Control Plane Health: Overall system status, component availability
- 🖥️ Node Fleet Status: Node check-ins, configuration success rates, drift detection
- 📊 Performance Metrics: CPU, memory, disk, network utilization
- 🔐 Security Dashboard: Failed authentication attempts, unauthorized access, audit log review
| Role | Primary Use |
|---|---|
| 🚨 On-Call Engineers | Primary incident responders (24/7 rotation) |
| 🛠️ Operations Team | Day-time support and escalation point |
| 📊 NOC Staff | Initial alert triage and assessment |
| 🏗️ Infrastructure Engineers | Escalation for complex infrastructure issues |
| 👨💼 Management | Understanding of incident response capabilities |
This Triage Guide is the authoritative reference for responding to monitoring alerts from the Configuration Management infrastructure. It provides structured procedures to:
|
🎯 Core Functions
|
📚 Usage Scenarios
|
graph TD
A[🚨 Receive Alert] -->|Acknowledge| B[🔍 Find Alert in Guide]
B -->|Section 3-6| C[📋 Follow Diagnostics]
C -->|Identify Issue| D[🔧 Execute Resolution]
D -->|Verify| E[✅ Close Alert]
E --> F[📝 Document Incident]
style A fill:#ffe1e1
style C fill:#fff4e1
style E fill:#e1ffe1
Every alert follows this standardized workflow (regardless of severity):
1️⃣ ACKNOWLEDGE → 2️⃣ ASSESS → 3️⃣ INVESTIGATE → 4️⃣ ACT → 5️⃣ DOCUMENT → 6️⃣ RESOLVE → 7️⃣ FOLLOW-UP
Step 1: Acknowledge (Within SLA Time)
- 🔐 Log into monitoring system (Prometheus/Grafana/Alertmanager)
- ✅ Acknowledge alert to prevent duplicate pages
- 📝 Note timestamp of acknowledgment
- 🎫 Update incident ticket status to "In Progress"
Step 2: Assess Actual Impact
- ✅ Verify alert is genuine (not false positive)
- 🎯 Determine actual vs. perceived impact
- 🔗 Check if multiple related alerts (correlated incident)
- 📊 Assess number of users/nodes affected
- ⚖️ Re-prioritize severity if needed
Step 3: Investigate Root Cause
- 📋 Follow diagnostic procedures in this guide (Sections 3-6)
- 🔄 Check recent changes (deployments, configuration updates)
- 📝 Review system logs and metrics
- ⏱️ Determine if issue is transient or persistent
Step 4: Act on Findings
- 🔧 Execute resolution steps from this guide
- 📞 If no documented procedure, engage escalation
- 🚨 For Critical issues: Notify management immediately
- 🔄 Implement temporary workaround if full fix not possible
Step 5: Document Actions
- 💻 Log all commands executed
- 📊 Capture output and error messages
- ⏱️ Record timeline of actions
- 📝 Note any deviations from documented procedures
Step 6: Resolve Alert
- ✅ Verify resolution fixed the issue
- 📊 Confirm monitoring shows healthy status
- 🔒 Close alert in monitoring system
- 🎫 Update incident ticket with resolution details
Step 7: Follow-Up (For Critical/High Severity)
- 📅 Schedule post-incident review within 48 hours
- 📖 Update documentation based on lessons learned
- 🔄 Identify preventive measures or automation opportunities
- 👥 Share findings with team
|
📋 Definition Service completely unavailable OR data loss imminent OR security breach suspected 🚨 Examples
|
⏱️ Response Requirements
|
|
📋 Definition Major service degradation OR significant impact to managed nodes OR component failure without redundancy
|
⏱️ Response Requirements
|
|
📋 Definition Potential issue requiring investigation OR resource utilization approaching thresholds
|
⏱️ Response Requirements
|
📋 Definition: Informational notification, no action required
ℹ️ Examples: Successful deployment, Scheduled maintenance completed, Backup successful, Normal system events
⏱️ Response: Not required (auto-acknowledged), Review during next day health check
📞 When to Escalate (engage additional resources):
|
🎯 Escalation Triggers
|
🎯 Additional Triggers 3. ✋ Requires approval for disruptive action
|
📋 Escalation Contacts:
| Role | Responsibility | Business Hours | After Hours |
|---|---|---|---|
| 👨💻 Senior Operations Engineer | Complex troubleshooting, architecture decisions | 📞 Direct call/Slack | 📟 PagerDuty escalation |
| 🗄️ Database Administrator | SQL Server, PostgreSQL issues | 🎫 Ticket assignment | 📟 PagerDuty (Critical only) |
| 🔐 Security Engineer | Security incidents, Vault issues | 📞 Direct call/Slack | 📟 PagerDuty (Critical only) |
| 🏗️ Infrastructure Lead | Infrastructure decisions, vendor escalation | 📞 Direct call/Slack | 📟 PagerDuty escalation |
| 👨💼 Operations Manager | Management decision authority | 📞 Direct call | 📞 On-call phone |
🔄 Escalation Procedure:
- 📝 Document current situation, steps taken, and reason for escalation
- 📟 Use PagerDuty escalation policy OR direct contact (depending on severity/time)
- 💬 Provide brief summary: "What happened, what you've tried, what you need"
- 🤝 Remain available to assist (don't hand off completely unless instructed)
- 🎫 Update incident ticket with escalation details
Alert Name: ConfigMgmt_DSC_PullServer_Down
Severity: Critical
Trigger Condition: DSC Pull Server not responding to health check for 5 consecutive minutes
Business Impact: Windows nodes cannot pull configurations; drift detection stopped; no new node onboarding
- Pull server URL (https://dsc.corp.contoso.com) returns connection refused or timeout
- Nodes reporting errors in event logs: "Unable to contact pull server"
- Monitoring shows pull server target DOWN in Prometheus
- Grafana dashboard shows 0% pull server availability
Step 1: Verify Alert Validity (2 minutes)
# Test pull server endpoint from monitoring server
curl -I https://dsc.corp.contoso.com/PSDSCPullServer.svc
# Expected Output: HTTP/1.1 200 OK
# If Connection Refused/Timeout: Alert is valid, proceed
# If HTTP 200: False alarm, check monitoring configurationStep 2: Check Server Availability (3 minutes)
# Ping pull server
Test-NetConnection dsc-01.corp.contoso.com -Port 443
# Expected: TcpTestSucceeded : True
# If False: Server unreachable, check hypervisor/cloud console
# Check if VM is running (Azure example)
az vm get-instance-view --resource-group RG-ConfigMgmt-Prod --name dsc-01 --query "instanceView.statuses[?starts_with(code, 'PowerState/')].displayStatus" -o tsv
# Expected: VM running
# If "VM stopped" or "VM deallocated": Proceed to Step 3Step 3: Check IIS and DSC Service Status (5 minutes)
# RDP or SSH to pull server (if accessible)
# Check IIS status
Get-Service W3SVC | Select-Object Name, Status, StartType
# Expected: Status = Running, StartType = Automatic
# If Stopped: Proceed to Resolution Step 1
# Check DSC Service
Get-Service DSCService | Select-Object Name, Status, StartType
# Check Application Pool status
Import-Module WebAdministration
Get-WebAppPoolState -Name "PSDSCPullServer"
# Expected: Started
# If Stopped: Check IIS logs for errorsStep 4: Check System Resources (if server accessible) (3 minutes)
# Check disk space
Get-PSDrive C | Select-Object Used, Free
# Expected: >20 GB free
# If low disk: Disk space issue (see Resolution Step 4)
# Check CPU and Memory
Get-Counter '\Processor(_Total)\% Processor Time', '\Memory\Available MBytes'
# Expected: CPU <80%, Memory >1 GB availableResolution 1: Restart IIS and DSC Services (Low-risk, try first)
# Connect to pull server
# Restart IIS
iisreset /restart
# Wait 30 seconds
Start-Sleep -Seconds 30
# Test endpoint
Invoke-WebRequest -Uri "https://dsc.corp.contoso.com/PSDSCPullServer.svc" -UseBasicParsing
# If still down, restart DSC Service
Restart-Service DSCService -Force
# Verify services running
Get-Service W3SVC, DSCService | Select-Object Name, StatusVerification:
- Pull server URL returns HTTP 200
- Prometheus shows target UP
- Test node can pull configuration
Resolution 2: Restart VM (If services won't start)
# Azure example (adapt for your environment)
az vm restart --resource-group RG-ConfigMgmt-Prod --name dsc-01 --no-wait
# Wait 5 minutes for VM to boot
Start-Sleep -Seconds 300
# Verify services auto-started
Test-NetConnection dsc-01.corp.contoso.com -Port 443Verification: Same as Resolution 1
Resolution 3: Failover to Secondary Pull Server (If primary won't recover)
# Update load balancer to remove failed primary from pool
# (Manual step in load balancer UI or via CLI)
# Azure Load Balancer example:
az network lb rule update \
--resource-group RG-ConfigMgmt-Prod \
--lb-name LB-DSC-Prod \
--name DSC-HTTP-Rule \
--backend-pool-name DSC-Backend-Pool \
# Remove dsc-01 from backend pool
# Verify traffic routing to secondary
curl -I https://dsc.corp.contoso.com/PSDSCPullServer.svc
# Should now resolve to dsc-02.corp.contoso.com
# Engage team to restore primary serverVerification:
- Pull server URL returns HTTP 200
- Nodes successfully pulling configurations
- Monitor primary server restoration progress
Resolution 4: Clear Disk Space (If disk full)
# Connect to pull server
# Check IIS logs size
Get-ChildItem C:\inetpub\logs\LogFiles -Recurse | Measure-Object -Property Length -Sum
# If large (>10 GB), archive and delete old logs
$OldLogs = Get-ChildItem C:\inetpub\logs\LogFiles -Recurse -File | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)}
$OldLogs | Compress-Archive -DestinationPath "E:\Backup\IIS-Logs-$(Get-Date -Format 'yyyyMMdd').zip"
$OldLogs | Remove-Item -Force
# Check DSC log directory
Get-ChildItem E:\DSC\Logs -Recurse | Measure-Object -Property Length -Sum
# Clean up old MOF files (if disk space critical)
Get-ChildItem C:\Program Files\WindowsPowerShell\DscService\Configuration -Recurse -File |
Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-90)} | Remove-Item -Force
# Restart IIS
iisreset /restartVerification: Pull server responding, disk space >20 GB free
- Unable to restart services after 2 attempts
- VM won't boot after restart
- Disk space cannot be freed (requires infrastructure expansion)
- Corruption suspected (requires restore from backup)
Escalate To: Senior Operations Engineer (if infrastructure issue) OR Database Administrator (if database connectivity issue)
az vm get-instance-view --name dsc-01-prod --resource-group rg-prod --query instanceView.statuses
**Step 3: Check IIS Service** (if server reachable)
```powershell
Invoke-Command -ComputerName dsc-01 -ScriptBlock {
Get-Service W3SVC, PSWS | Select-Object Name, Status
}
# Expected: Both services Running
Step 4: Check Application Pool
Invoke-Command -ComputerName dsc-01 -ScriptBlock {
Import-Module WebAdministration
Get-WebAppPoolState -Name "DefaultAppPool"
}
# Expected: StartedStep 5: Check Event Logs
Invoke-Command -ComputerName dsc-01 -ScriptBlock {
Get-WinEvent -LogName "Microsoft-IIS-Configuration/Operational" -MaxEvents 20 |
Where-Object {$_.Level -le 3} | # Errors and warnings
Format-Table TimeCreated, Id, Message -AutoSize
}Scenario A: Service Stopped
# Restart IIS
Invoke-Command -ComputerName dsc-01 -ScriptBlock {
iisreset /restart
}
# Wait 30 seconds, verify service restored
Start-Sleep -Seconds 30
curl -I https://dsc.corp.contoso.com/PSDSCPullServer.svcScenario B: Application Pool Stopped
Invoke-Command -ComputerName dsc-01 -ScriptBlock {
Import-Module WebAdministration
Start-WebAppPool -Name "DefaultAppPool"
}Scenario C: Server Down
# Start VM
az vm start --name dsc-01-prod --resource-group rg-prod
# Or via hypervisor console
# Wait for server to boot, verify service comes upScenario D: Load Balancer Issue
# Check load balancer health probe
az network lb probe list --lb-name lb-dsc-prod --resource-group rg-prod
# Check backend pool status
az network lb address-pool list --lb-name lb-dsc-prod --resource-group rg-prod
# Remove and re-add server to pool if stuck in unhealthy state# Test pull server endpoint
curl -I https://dsc.corp.contoso.com/PSDSCPullServer.svc
# Expected: HTTP 200 OK
# Check monitoring (target should be UP)
# Check that nodes start checking in again- Monitor resource utilization (CPU, memory, disk)
- Set up predictive alerts before resource exhaustion
- Implement auto-healing (automatic service restart on failure)
- If not resolved in 30 minutes: Escalate to Infrastructure Lead
- If requires infrastructure changes: Escalate to Infrastructure Manager
Alert Name: ConfigMgmt_AWX_Down
Severity: Critical
Trigger: AWX not responding to health check for 5 minutes
Impact: Cannot deploy configurations, scheduled jobs not running
Step 1: Verify Alert
curl -I https://awx.corp.contoso.com/api/v2/ping/
# Expected: HTTP 200 OKStep 2: Check Server Availability
ssh ubuntu@awx-server "uptime"
# If unreachable, check cloud console for VM statusStep 3: Check Docker Containers
ssh ubuntu@awx-server "docker ps"
# Expected: 4 containers running
# - awx_web
# - awx_task
# - redis
# - awx_receptor
# Check for restarting containers
ssh ubuntu@awx-server "docker ps -a | grep -i restart"Step 4: Check Container Logs
ssh ubuntu@awx-server "docker logs awx_web --tail 50"
ssh ubuntu@awx-server "docker logs awx_task --tail 50"
# Look for errors, exceptions, connection issuesStep 5: Check PostgreSQL Connectivity
ssh ubuntu@awx-server "docker exec awx_web awx-manage check --database default"
# Expected: No errorsScenario A: Container Stopped
ssh ubuntu@awx-server "docker-compose -f /opt/awx/docker-compose.yml up -d"
# Wait 2 minutes for containers to startScenario B: Container Crash Loop
# Check why container crashing
ssh ubuntu@awx-server "docker logs awx_web --tail 100"
# Common causes:
# - Database connection failure → Check PostgreSQL
# - Disk full → Clean up disk space
# - Memory exhaustion → Restart containers, investigate memory leak
# Restart containers
ssh ubuntu@awx-server "docker-compose -f /opt/awx/docker-compose.yml restart"Scenario C: Database Connection Issue
# Test PostgreSQL connectivity
ssh ubuntu@awx-server "pg_isready -h <pgsql-server> -p 5432"
# If PostgreSQL down, see "PostgreSQL Down" section
# If firewall issue, check network rulesScenario D: Server Down
# Start VM
az vm start --name awx-prod --resource-group rg-prod
# Wait for boot, containers should auto-startcurl https://awx.corp.contoso.com/api/v2/ping/
# Expected: {"ha":false,"version":"23.3.0","active_node":"awx"}
# Launch test job
awx job_templates launch "Test - Connectivity Check" --monitor- If database issue: Escalate to DBA
- If not resolved in 30 minutes: Escalate to Application Engineer Lead
Alert Name: ConfigMgmt_Vault_Sealed
Severity: Critical
Trigger: Vault status shows sealed
Impact: Cannot retrieve secrets, automation failing
Step 1: Verify Seal Status
vault status
# Look for:
# Sealed: true
# Unseal Progress: 0/3Step 2: Check Why Sealed
# Check Vault logs
ssh vault-01 "journalctl -u vault -n 100 | grep -i seal"
# Common reasons:
# - Server restart
# - Out of memory (OOM killer)
# - Storage backend issue
# - Manual seal (rare)Step 3: Check Cluster Status
# If one node sealed, check others
for node in vault-01 vault-02 vault-03; do
echo "=== $node ==="
ssh $node "vault status"
doneScenario A: Single Node Sealed (Cluster Otherwise Healthy)
# Unseal the sealed node
ssh vault-01 "vault operator unseal <key1>"
ssh vault-01 "vault operator unseal <key2>"
ssh vault-01 "vault operator unseal <key3>"
# Node should rejoin cluster automaticallyScenario B: All Nodes Sealed
# Unseal primary node first
ssh vault-01 "vault operator unseal <key1>"
ssh vault-01 "vault operator unseal <key2>"
ssh vault-01 "vault operator unseal <key3>"
# Verify unsealed
ssh vault-01 "vault status"
# Unseal remaining nodes
for node in vault-02 vault-03; do
ssh $node "vault operator unseal <key1>"
ssh $node "vault operator unseal <key2>"
ssh $node "vault operator unseal <key3>"
done
# Verify cluster status
vault operator membersScenario C: Auto-Unseal Configured but Failed
# Check auto-unseal key accessible
# For Azure Key Vault auto-unseal:
az keyvault key show --vault-name contoso-vault-unseal --name vault-unseal-key
# If key accessible, restart Vault service
ssh vault-01 "sudo systemctl restart vault"
# Should auto-unseal on restartScenario D: Unseal Keys Not Available
# EMERGENCY PROCEDURE
# Retrieve unseal keys from secure offline storage:
# 1. Physical safe in datacenter
# 2. Password manager (break-glass account)
# 3. HSM (if configured)
# 4. Sealed envelope with CFO/CTO
# Document who accessed keys and why
echo "$(date): Vault unsealed by $(whoami) - Ticket: INC-12345" >> /var/log/vault-unseal.log# Verify unsealed
vault status
# Expected: Sealed: false
# Test secret retrieval
vault kv get secret/production/test
# Check that automation services can retrieve secrets
awx job_templates launch "Test - Vault Integration" --monitor- Implement auto-unseal (Azure Key Vault, AWS KMS)
- Monitor memory usage (OOM can cause seal)
- Monitor storage backend health
- Regular snapshot backups
- If unseal keys unavailable: Escalate IMMEDIATELY to Security Lead and DR Commander
- If underlying infrastructure issue: Escalate to Infrastructure Lead
Alert Name: Database_SQLServer_HighCPU
Severity: Warning → High (if sustained >20 min)
Trigger: CPU utilization >80% for 10 minutes
Impact: Slow query performance, pull server latency
Step 1: Verify CPU Usage
Invoke-Command -ComputerName sql-01 -ScriptBlock {
Get-Counter '\Processor(_Total)\% Processor Time' -SampleInterval 1 -MaxSamples 10
}Step 2: Identify Resource-Intensive Queries
-- Top CPU consuming queries
SELECT TOP 10
qs.total_worker_time / qs.execution_count AS avg_cpu_time,
qs.execution_count,
SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(qt.text)
ELSE qs.statement_end_offset
END - qs.statement_start_offset)/2)+1) AS query_text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
ORDER BY avg_cpu_time DESC;Step 3: Check for Blocking
-- Check for blocking sessions
SELECT
blocking_session_id,
session_id,
wait_type,
wait_time,
wait_resource
FROM sys.dm_exec_requests
WHERE blocking_session_id <> 0;Step 4: Check Database Maintenance
-- Check if index maintenance running
SELECT
session_id,
command,
percent_complete,
estimated_completion_time
FROM sys.dm_exec_requests
WHERE command LIKE '%INDEX%' OR command LIKE '%UPDATE STATISTICS%';Scenario A: Runaway Query
-- Identify session
SELECT session_id, host_name, program_name, login_name, status, command
FROM sys.dm_exec_sessions
WHERE session_id = <high_cpu_session_id>;
-- If appropriate, kill session
KILL <session_id>;Scenario B: Blocking Chain
-- Find head blocker
WITH BlockingTree AS (
SELECT session_id, blocking_session_id
FROM sys.dm_exec_requests
WHERE blocking_session_id <> 0
)
SELECT * FROM BlockingTree;
-- Kill head blocker (with caution)
KILL <head_blocker_session_id>;Scenario C: Index Maintenance Running
-- If scheduled maintenance running during business hours
-- Let it complete or kill if urgent
-- Reschedule maintenance to off-hoursScenario D: Insufficient Resources
# If consistently high CPU, may need to scale up
# Check metrics over last 7 days
Invoke-Sqlcmd -ServerInstance "sql-01" -Query @"
SELECT
AVG(CAST(value AS INT)) as avg_cpu
FROM sys.dm_os_performance_counters
WHERE counter_name = 'CPU usage %'
AND object_name = 'SQLServer:Resource Pool Stats'
"@
# If sustained >70%, recommend scaling up
# Create ticket for infrastructure team# Verify CPU returned to normal
Invoke-Command -ComputerName sql-01 -ScriptBlock {
Get-Counter '\Processor(_Total)\% Processor Time'
}
# Expected: <70%- Query optimization and indexing
- Schedule maintenance during off-hours
- Implement query timeout policies
- Monitor for query plan regressions
- If query optimization needed: Escalate to DBA
- If scaling required: Escalate to Infrastructure Manager
Alert Name: Database_PostgreSQL_MaxConnections
Severity: High
Trigger: Active connections >90% of max_connections
Impact: AWX cannot create new connections, jobs fail
Step 1: Check Current Connections
-- Connect to PostgreSQL
psql -h <pgsql-server> -U postgres
-- Check connection count
SELECT count(*) as total_connections, max_conn
FROM pg_stat_activity, (SELECT setting::int as max_conn FROM pg_settings WHERE name='max_connections') mc
GROUP BY max_conn;
-- List connections by database
SELECT datname, count(*) as connections
FROM pg_stat_activity
GROUP BY datname
ORDER BY connections DESC;Step 2: Identify Connection Sources
-- Who is consuming connections?
SELECT
client_addr,
usename,
datname,
count(*) as connection_count
FROM pg_stat_activity
WHERE state = 'active'
GROUP BY client_addr, usename, datname
ORDER BY connection_count DESC;Step 3: Check for Idle Connections
-- Find long-running idle connections
SELECT
pid,
client_addr,
usename,
state,
state_change,
now() - state_change as idle_time
FROM pg_stat_activity
WHERE state = 'idle'
AND now() - state_change > interval '1 hour'
ORDER BY idle_time DESC;Scenario A: Idle Connections
-- Terminate idle connections (>1 hour idle)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
AND now() - state_change > interval '1 hour'
AND pid <> pg_backend_pid(); -- Don't kill own sessionScenario B: Connection Leak in Application
# Restart AWX to release connections
ssh awx-server "docker-compose restart awx_web awx_task"
# Monitor connection count
watch -n 5 "psql -h <pgsql-server> -U postgres -c \"SELECT count(*) FROM pg_stat_activity;\""Scenario C: Increase Max Connections (temporary)
-- Check current limit
SHOW max_connections;
-- Increase (requires restart)
ALTER SYSTEM SET max_connections = 200; -- Was 100
-- Restart PostgreSQLssh pgsql-server "sudo systemctl restart postgresql"Scenario D: Implement Connection Pooling
# Install PgBouncer (connection pooler)
# Configure AWX to use PgBouncer instead of direct connections
# PgBouncer can maintain 100 client connections with only 20 PostgreSQL connections-- Verify connections reduced
SELECT count(*) as current,
(SELECT setting::int FROM pg_settings WHERE name='max_connections') as max
FROM pg_stat_activity;
# Should be well below limit- Implement connection pooling (PgBouncer)
- Set connection timeout in applications
- Monitor connection trends
- Rightsize max_connections setting
- If application code issue: Escalate to Development Team
- If persistent problem: Escalate to DBA
Alert Name: ConfigMgmt_HighFailureRate
Severity: Warning → High
Trigger: >5% of configuration runs failed in last hour
Impact: Nodes not reaching desired state, compliance issues
Step 1: Identify Failing Nodes
For DSC:
$Query = @"
SELECT
NodeName,
Status,
ErrorMessage,
LastCheckIn
FROM dbo.StatusReport
WHERE Status = 'Failure'
AND LastCheckIn > DATEADD(hour, -1, GETDATE())
ORDER BY LastCheckIn DESC
"@
Invoke-Sqlcmd -ServerInstance "sql-01" -Database "DSC" -Query $Query | Format-TableFor Ansible:
# List failed jobs in last hour
awx jobs list --status failed --created_gt $(date -u -d '1 hour ago' '+%Y-%m-%dT%H:%M:%S') -f humanStep 2: Identify Common Patterns
# Are all failures on same configuration?
# Same error message?
# Same subset of nodes?
# Started after recent change?Step 3: Review Error Messages
# Get detailed error from recent failed job
awx jobs stdout <job-id> | grep -i error -A 5 -B 5
# Or for DSC
$Query = "SELECT TOP 10 ErrorMessage, COUNT(*) as count FROM dbo.StatusReport WHERE Status='Failure' GROUP BY ErrorMessage ORDER BY count DESC"
Invoke-Sqlcmd -ServerInstance "sql-01" -Database "DSC" -Query $QueryStep 4: Test Configuration Manually
# Test on single node
ansible-playbook -i inventory playbook.yml --limit failed-node -vvv
# Or for DSC
Invoke-Command -ComputerName failed-node -ScriptBlock {
Update-DscConfiguration -Wait -Verbose
}Scenario A: Bad Configuration Deployed
# Rollback configuration (see Operations Manual SOP-009)
git revert <bad-commit>
git push origin main
# Or manually remove bad configuration from pull server
# Force nodes to pull corrected versionScenario B: Environmental Issue
# Common issues:
# - DNS resolution failure → Check DNS
# - Network connectivity → Check network/firewall
# - Disk full on targets → Clean up disk space
# - Service dependency down → Investigate dependencyScenario C: Transient Issue
# If errors appear transient (network blip, etc.)
# Re-run failed jobs
awx jobs relaunch <job-id> --monitor
# Monitor failure rate, should return to normalScenario D: Node-Specific Issue
# If failures on specific nodes
# Investigate those nodes individually
ssh failed-node
# Check:
# - Disk space: df -h
# - Memory: free -h
# - Services: systemctl status
# - Logs: journalctl -xe# Check failure rate returned to normal
# Monitor for next 2 hours
# Query metrics
# For Ansible
TOTAL=$(awx jobs list --created_gt $(date -u -d '1 hour ago' '+%Y-%m-%dT%H:%M:%S') -f json | jq '. | length')
FAILED=$(awx jobs list --status failed --created_gt $(date -u -d '1 hour ago' '+%Y-%m-%dT%H:%M:%S') -f json | jq '. | length')
FAIL_RATE=$(echo "scale=2; $FAILED / $TOTAL * 100" | bc)
echo "Failure Rate: $FAIL_RATE%"
# Should be <2%- Thorough testing in dev/test before production
- Gradual rollout (canary deployments)
- Automated rollback on high failure rate
- Comprehensive error handling in configurations
- If configuration bug: Escalate to Configuration Engineer
- If infrastructure issue: Escalate to Infrastructure Lead
Alert Name: ConfigMgmt_DriftRateIncreasing
Severity: Warning
Trigger: >20% of nodes showing drift (up from baseline of <5%)
Impact: Manual changes being made, compliance at risk
Step 1: Identify Drifted Nodes
# For Ansible (check mode runs)
awx jobs list --job_template "Drift Detection" --status successful | \
jq '.results[] | select(.changed==true) | .inventory'
# For DSC
$Query = @"
SELECT NodeName, ConfigurationName, LastCheckIn
FROM dbo.StatusReport
WHERE Status = 'NotCompliant'
ORDER BY LastCheckIn DESC
"@
Invoke-Sqlcmd -ServerInstance "sql-01" -Database "DSC" -Query $QueryStep 2: Analyze Drift Patterns
# What is being changed?
# Who is making changes? (check auth logs)
# When did drift start? (correlate with events)Step 3: Investigate Root Cause
# Check recent logins on drifted nodes
for node in $(cat drifted-nodes.txt); do
echo "=== $node ==="
ssh $node "last -n 20"
done
# Check for unauthorized access
ssh drifted-node "grep sudo /var/log/auth.log | tail -20"
# Check for competing automation
ssh drifted-node "crontab -l"
ssh drifted-node "systemctl list-timers"Scenario A: Authorized Manual Changes
# Update configurations to match new desired state
# Communicate with team about proper change process
# Update runbook/documentationScenario B: Unauthorized Manual Changes
# Allow auto-remediation to correct drift
# Investigate who made changes and why
# Provide training on proper procedures
# Implement stricter access controls if neededScenario C: Competing Automation
# Identify conflicting automation tool
# Disable or reconfigure conflicting tool
# Consolidate automation under single systemScenario D: Bug in Configuration
# If configuration inherently unstable (doesn't stick)
# Investigate why configuration reverts
# Fix underlying issue (dependency, timing, etc.)# Monitor drift rate over next 24 hours
# Should return to baseline (<5%)
# Run drift detection
awx job_templates launch "Drift Detection" --monitor
# Check results- Education on proper change management
- Restrict manual access (least privilege)
- Audit trail review
- Alert on manual changes
- If security concern: Escalate to Security Team
- If process issue: Escalate to Operations Manager
Alert Name: System_DiskSpaceLow
Severity: Warning (>80%) → High (>90%)
Trigger: Disk utilization >80%
Impact: Service failures, log rotation issues, backup failures
Step 1: Identify Disk Usage
Linux:
ssh affected-server "df -h"
# Find largest directories
ssh affected-server "du -h / --max-depth=2 | sort -rh | head -20"Windows:
Invoke-Command -ComputerName affected-server -ScriptBlock {
Get-PSDrive -PSProvider FileSystem |
Select-Object Name,
@{Name="Used(GB)";Expression={[math]::Round($_.Used/1GB,2)}},
@{Name="Free(GB)";Expression={[math]::Round($_.Free/1GB,2)}},
@{Name="PercentFree";Expression={[math]::Round($_.Free/$_.Used*100,2)}}
}Step 2: Identify Large Files
Linux:
# Find largest files
ssh affected-server "find / -type f -size +1G -exec ls -lh {} \; 2>/dev/null"
# Check log files
ssh affected-server "du -sh /var/log/*" | sort -rhWindows:
Invoke-Command -ComputerName affected-server -ScriptBlock {
Get-ChildItem C:\ -Recurse -ErrorAction SilentlyContinue |
Where-Object {$_.Length -gt 1GB} |
Sort-Object Length -Descending |
Select-Object FullName, @{Name="Size(GB)";Expression={[math]::Round($_.Length/1GB,2)}} |
Format-Table
}Scenario A: Old Log Files
# Compress old logs
ssh affected-server "find /var/log -name '*.log' -mtime +30 -exec gzip {} \;"
# Delete very old logs
ssh affected-server "find /var/log -name '*.gz' -mtime +90 -delete"Scenario B: Old Backup Files
# Check backup retention policy
# Delete backups older than retention period
ssh backup-server "find /backup/vault/snapshots/ -mtime +7 -delete"
ssh backup-server "find /backup/sql/ -mtime +30 -delete"Scenario C: Temp Files
# Linux
ssh affected-server "find /tmp -type f -mtime +7 -delete"
# Windows
Invoke-Command -ComputerName affected-server -ScriptBlock {
Remove-Item C:\Windows\Temp\* -Recurse -Force -ErrorAction SilentlyContinue
}Scenario D: Database Files Growing
-- For SQL Server, shrink transaction log if excessive
DBCC SHRINKFILE (DSC_Log, 1000); -- Shrink to 1GB
-- Then implement proper log backup scheduleScenario E: Expand Disk (if cleanup insufficient)
# For cloud VMs, expand disk
az disk update --name osdisk --resource-group rg-prod --size-gb 500
# Then extend filesystem
ssh affected-server "sudo growpart /dev/sda 1 && sudo resize2fs /dev/sda1"# Verify disk space recovered
ssh affected-server "df -h"
# Should be <70% utilization- Implement log rotation
- Automate cleanup of old backups
- Monitor disk growth trends
- Set up alerts at 70% (earlier warning)
- Proactive capacity planning
Alert Name: Security_MultipleFailedAuth
Severity: Warning → Critical (if ongoing)
Trigger: 5 failed auth attempts in 5 minutes from single source
Impact: Potential brute-force attack, unauthorized access attempt
Step 1: Identify Source
# Check authentication logs
ssh vault-01 "grep 'authentication failed' /var/log/vault/audit.log | tail -20"
# AWX failed logins
awx activity_stream list --action login --changes__icontains failed -f human
# SSH failed attempts
ssh affected-server "grep 'Failed password' /var/log/auth.log | tail -20"Step 2: Identify Target
# What usernames being attempted?
ssh affected-server "grep 'Failed password' /var/log/auth.log | awk '{print $9}' | sort | uniq -c | sort -rn"Step 3: Assess Threat
# Is this:
# - Brute force attack (external IP, random usernames)
# - Misconfigured service (internal IP, same username)
# - Legitimate user with wrong password (known IP, real username)Scenario A: External Brute Force Attack
# Block source IP at firewall
ATTACKER_IP="x.x.x.x"
sudo iptables -A INPUT -s $ATTACKER_IP -j DROP
# Or via cloud NSG/security group
az network nsg rule create \
--resource-group rg-prod \
--nsg-name nsg-prod \
--name Block-Attacker \
--priority 100 \
--source-address-prefixes $ATTACKER_IP \
--access Deny
# Report to abuse contact for IP range
whois $ATTACKER_IP | grep -i abuseScenario B: Misconfigured Service
# Identify which service/server making failed attempts
# Fix configuration (update password, certificate, etc.)
# If service account
# Reset password in AD
# Update in Vault
# Update consuming service configurationScenario C: Compromised Credentials Suspected
# IMMEDIATELY:
# 1. Lock affected account
net user <username> /active:no
# 2. Force password reset
# 3. Review all recent activity from that account
# 4. Notify Security Team
# 5. Initiate incident response procedureScenario D: Legitimate User
# Contact user
# Verify it was them
# Help them reset password if needed
# No further action if resolved# Verify no more failed attempts from that source
# Monitor for next hour
ssh affected-server "tail -f /var/log/auth.log | grep 'Failed password'"- Implement rate limiting (fail2ban)
- Strong password policy
- MFA for all accounts
- Network segmentation (limit SSH access)
- Monitor for unusual patterns
- Always escalate to Security Team for investigation
- If suspected compromise: IMMEDIATELY escalate to Security Lead and initiate incident response
| Level | Role | Contact | Response Time |
|---|---|---|---|
| L1 | On-Call Engineer | PagerDuty | Immediate |
| L2 | Operations Lead | [Phone/Email] | 30 minutes |
| L3 | Technical Lead (Infra/App/DB/Security) | [Phone/Email] | 1 hour |
| L4 | Infrastructure Manager | [Phone/Email] | 2 hours |
| L5 | CTO | [Phone/Email] | 4 hours |
Immediate Escalation (L2):
- Unable to diagnose issue within response time
- Issue beyond your technical expertise
- Security incident suspected
- Multiple critical systems affected
- Requires potentially disruptive action (service restart in production)
Escalation to L3:
- Issue requires deep technical expertise
- Architecture or design change needed
- Requires code or configuration changes
- Performance tuning required
Escalation to L4:
- Major incident affecting business operations
- Multiple L3 specialists needed
- Resource allocation decisions required
- Vendor engagement required
Escalation to L5:
- Company-wide impact
- Major security breach
- Data loss
- Regulatory implications
For All Critical/High Alerts:
After resolution, document:
- Timeline: When alert fired, when acknowledged, when resolved
- Root Cause: What caused the issue
- Impact: What was affected, how many users/systems
- Resolution: What actions were taken
- Prevention: How to prevent recurrence
Incident Report Template:
INCIDENT REPORT
Incident ID: INC-2025-XXX
Alert Name: [Alert Name]
Severity: [Critical/High/Warning]
Timeline:
- Alert Fired: [TIMESTAMP]
- Acknowledged: [TIMESTAMP]
- Investigation Started: [TIMESTAMP]
- Resolution Applied: [TIMESTAMP]
- Incident Closed: [TIMESTAMP]
Total Duration: [MINUTES/HOURS]
Root Cause:
[Description of what caused the alert]
Impact:
[What systems/users were affected]
Resolution:
[What actions were taken to resolve]
Prevention:
[What will be done to prevent recurrence]
Lessons Learned:
[What went well, what could be improved]
Resolved By: [Name]
Reviewed By: [Manager Name]
For All Critical Alerts:
- Schedule post-incident review within 48 hours
- Include on-call engineer, technical lead, and affected teams
- Review incident report
- Identify action items
- Assign owners and due dates
- Update runbooks/documentation
Check System Status:
# All control plane services
ansible all -i inventory/production/control-plane -m shell -a "systemctl status <service>"
# Vault status
vault status
# AWX status
curl https://awx.corp.contoso.com/api/v2/ping/
# DSC Pull Server status
curl -I https://dsc.corp.contoso.com/PSDSCPullServer.svcCheck Resource Utilization:
# Linux CPU/Memory
ssh server "top -bn1 | head -20"
# Linux Disk Space
ssh server "df -h"
# Windows
Invoke-Command -ComputerName server -ScriptBlock {
Get-Counter '\Processor(_Total)\% Processor Time', '\Memory\Available MBytes' |
Select-Object -ExpandProperty CounterSamples |
Format-Table
}Check Logs:
# Vault logs
ssh vault-01 "journalctl -u vault -n 50"
# AWX logs
ssh awx-server "docker logs awx_web --tail 50"
# DSC Pull Server logs
Invoke-Command -ComputerName dsc-01 -ScriptBlock {
Get-WinEvent -LogName "Microsoft-IIS-Configuration/Operational" -MaxEvents 20
}Emergency Contacts:
- On-Call: PagerDuty: 1-844-700-XXXX
- DR Commander: Adrian Johnson - [PHONE] - adrian207@gmail.com
- Security Lead: [Name] - [PHONE] - [EMAIL]
- Infrastructure Manager: [Name] - [PHONE] - [EMAIL]
Vendor Support:
- Azure Support: 1-800-XXX-XXXX, Subscription: [ID]
- AWS Support: 1-800-XXX-XXXX, Account: [ID]
- Red Hat Support: [LINK], Case Portal
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2025-10-17 | Adrian Johnson | Initial release |
Document End