SQL Scripts for the Windows server and synthetic check rules (SQLI-WIN-001..013, SQLI-SYN-001..003)

One reviewable file per rule id with a Danish header: what and why, thresholds
with config keys and defaults, severity, required rights (never local
administrator), supported versions, collector file and member, and the
evaluation in plain Danish. The Windows rules use no SQL: their exact CIM/WQL
queries, event log XPath and a read-only PowerShell snippet to verify them are in
a comment block. SQLI-SYN-001/002 carry the exact SQL a SQL check runs (session
settings, transaction that is always rolled back, SELECT 1). A test checks every
server and check rule has its file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Søren Eggert Lundsteen Olsen 2026-10-02 13:32:29 +02:00
parent 6eae719e95
commit 842286d57d
17 changed files with 792 additions and 0 deletions

View file

@ -0,0 +1,65 @@
/*
SQLI-SYN-001 – Et syntetisk tjek fejler
========================================
Hvad og hvorfor
Agenten gør det, en bruger eller applikation ville gøre – forbinder til en port, henter en side, logger på SQL Server
og kører en forespørgsel, slår et navn op, logger på Dynamics AX – og det fejler. Er tjekket knyttet til en tjeneste
på en Windows-server, og kører tjenesten, siges det direkte: "… svarer ikke, selv om tjenesten kører" – den fejl,
en tjenesteovervågning alene ikke ser.
Tærskler (agentens appsettings.json og tjekkets indstillinger)
SQLInsight:SyntheticChecks:FailuresBeforeFinding standard 2 (fejl i træk før et fund: ét tabt svar er ingen alarm)
Pr. tjek: interval (standard 300 s, 60–86400) og timeout (standard 15 s, AX 60 s, højst 120).
Alvor
Høj.
Rettigheder
TCP, HTTP(S), DNS: ingen ud over netværksadgang fra agentens server. HTTP med Windows-godkendelse: agentens konto.
SQL: login'et i tjekket (agentens Windows-konto eller et SQL-login, hvis adgangskode krypteres til agenten som i 1.3)
skal kunne logge ind og køre forespørgslen; least-privilege-login'et fra grant-least-privilege.sql er nok til standardforespørgslen.
AX-logon: agentens Windows-konto skal være AX-bruger (i firmaet), og .NET Business Connector skal være installeret på
agentens server; ellers testes kun AOS-porten (og resultatet siger det).
Tjenestens tilstand: WMI Remote Enable på root\cimv2 + læseret på Service Control Manager på tjenestens server.
Understøttede versioner
SQL: SQL Server 2005 og nyere (SqlClient, ODBC-fallback for 2005). AX-logon: AX 2009 (kræver Windows PowerShell
2.0-motoren) og AX 2012. Windows-tjenester: Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/Synthetic/SyntheticCheckRunner.cs – SyntheticCheckRunner.RunAsync (TcpAsync, HttpAsync, SqlAsync,
DnsAsync, AxAsync), AX-logon: src/SQLInsight.Servers/Synthetic/AxBusinessConnector.cs og ax-logon.ps1. Køres efter
tjekkets interval af src/SQLInsight.Agent/Servers/SyntheticCheckService.cs – RunCheckAsync, som også slår den tilknyttede
tjenestes tilstand op (CimQueries.SingleServiceQuery).
Regel
src/SQLInsight.Rules/Servers/SyntheticCheckRules.cs – SyntheticCheckFailingRule
Evaluering
Har de seneste FailuresBeforeFinding kørsler alle fejlet, er det et fund. Titlen siger, om den tilknyttede tjeneste
kører, er stoppet, eller om tjekket blot fejler, med fejlteksten som måling (følsom).
SQL: Ved et SQL-tjek kører agenten præcis dette på den instans, tjekket peger på (ændrer intet: forespørgslen
valideres som én skrivebeskyttet SELECT, både når den gemmes og før den køres, og køres i en transaktion, der altid
rulles tilbage). Hver GO er en separat kommando på samme forbindelse.
*/
-- Variant: SQL-tjek med standardforespørgslen (alle versioner fra SQL Server 2005)
SET LOCK_TIMEOUT 5000; SET DEADLOCK_PRIORITY LOW; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
GO
-- Kun hvis tjekket har en database (tjekkets "Database", ] fordoblet): USE [<database>];
BEGIN TRANSACTION;
GO
SELECT 1
GO
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
GO
/*
Ikke-SQL-tjek, efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
TCP: Test-NetConnection -ComputerName aos01 -Port 2712
HTTP: Invoke-WebRequest -Uri https://ep.firma.local/ -UseDefaultCredentials -UseBasicParsing | Select-Object StatusCode
DNS: Resolve-DnsName aos01.firma.local
AX: Test-NetConnection -ComputerName aos01 -Port 2712 (logon: se src/SQLInsight.Servers/Synthetic/ax-logon.ps1)
Tjeneste: Get-CimInstance -ComputerName aos01 -Query "SELECT Name, DisplayName, State, StartMode, ExitCode FROM Win32_Service WHERE Name = 'AOS60`$01'"
*/

View file

@ -0,0 +1,48 @@
/*
SQLI-SYN-002 – Et syntetisk tjek svarer langsomt
=================================================
Hvad og hvorfor
Tjekket lykkes, men svaret har de seneste gange taget længere end tjekkets grænse. Brugerne oplever det som en langsom
applikation, længe før den holder op med at svare.
Tærskler (agentens appsettings.json og tjekkets indstillinger)
Pr. tjek: "Langsomt svar over (ms)" (insight.SyntheticChecks.SettingsJson: warnMilliseconds). Uden grænse: "ikke relevant".
SQLInsight:SyntheticChecks:SlowRunsBeforeFinding standard 3 (langsomme svar i træk før et fund)
Alvor
Middel.
Rettigheder
Som SQLI-SYN-001.
Understøttede versioner
Som SQLI-SYN-001.
Collector
src/SQLInsight.Servers/Synthetic/SyntheticCheckRunner.cs – SyntheticCheckRunner.RunAsync (DurationMs: hele tjekket, for
SQL login + forespørgsel), fra src/SQLInsight.Agent/Servers/SyntheticCheckService.cs – RunCheckAsync.
Regel
src/SQLInsight.Rules/Servers/SyntheticCheckRules.cs – SyntheticCheckSlowRule
Evaluering
Er de seneste SlowRunsBeforeFinding kørsler alle lykkedes og alle langsommere end grænsen, er det et fund.
SQL: Et SQL-tjek kører præcis det samme som i SQLI-SYN-001 (svartiden er login plus forespørgsel):
*/
-- Variant: SQL-tjek med standardforespørgslen (alle versioner fra SQL Server 2005)
SET LOCK_TIMEOUT 5000; SET DEADLOCK_PRIORITY LOW; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
GO
BEGIN TRANSACTION;
GO
SELECT 1
GO
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
GO
/*
Ikke-SQL-tjek, efterprøv svartiden med PowerShell (skrivebeskyttet):
Measure-Command { Invoke-WebRequest -Uri https://ep.firma.local/ -UseDefaultCredentials -UseBasicParsing } | Select-Object TotalMilliseconds
Measure-Command { Test-NetConnection -ComputerName aos01 -Port 2712 } | Select-Object TotalMilliseconds
*/

View file

@ -0,0 +1,39 @@
/*
SQLI-SYN-003 – TLS-certifikatet udløber snart
==============================================
Hvad og hvorfor
Når certifikatet på en HTTPS-adresse udløber, afviser browsere og integrationer forbindelsen (eller viser en
sikkerhedsadvarsel), og siden er i praksis nede. Udløbsdatoen læses ved hvert HTTPS-tjek – også når certifikatet
afvises (fx selvsigneret).
Tærskler (agentens appsettings.json)
SQLInsight:SyntheticChecks:CertificateWarningDays standard 21 (fund når certifikatet udløber inden for så mange dage)
SQLInsight:SyntheticChecks:CertificateCriticalDays standard 7 (Høj inden for så mange dage)
Alvor
Middel; Høj inden for CertificateCriticalDays; Kritisk når det er udløbet.
Rettigheder
Ingen ud over netværksadgang fra agentens server til adressen.
Understøttede versioner
Alle HTTPS-servere (TLS 1.2 og nyere fra agentens server).
Collector
src/SQLInsight.Servers/Synthetic/SyntheticCheckRunner.cs – HttpAsync (RemoteCertificateValidationCallback gemmer
certifikatets NotAfter og Subject), fra src/SQLInsight.Agent/Servers/SyntheticCheckService.cs – RunCheckAsync.
Regel
src/SQLInsight.Rules/Servers/SyntheticCheckRules.cs – CertificateExpiryRule
Evaluering
Kun HTTP-tjek med https://. Den nyeste kørsel, der har set et certifikat, bruges: dage til udløb = NotAfter − nu.
Uden set certifikat endnu: "ikke målbar". Andre tjek: "ikke relevant".
Reglen bruger ikke SQL. Tjekket er en HTTPS GET; filen indeholder kun kommentarer og ændrer intet.
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server):
$c = New-Object System.Net.Sockets.TcpClient('ep.firma.local', 443)
$s = New-Object System.Net.Security.SslStream($c.GetStream(), $false, { $true })
$s.AuthenticateAsClient('ep.firma.local'); $s.RemoteCertificate.GetExpirationDateString(); $c.Close()
*/

View file

@ -0,0 +1,48 @@
/*
SQLI-WIN-001 – CPU-forbruget er højt i længere tid
===================================================
Hvad og hvorfor
Serverens processorer har i gennemsnit været næsten fuldt belastet i en længere periode. Alt på serveren bliver
langsommere (forespørgsler, AOS-kald, fildeling, logon). Korte toppe er normale og giver intet fund.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:Cpu:HighPercent standard 90 (gennemsnitlig CPU i procent)
SQLInsight:Servers:Cpu:SustainedMinutes standard 15 (perioden gennemsnittet tages over)
Målingerne skal dække mindst 80 % af perioden (en kort top efter et hul tæller ikke).
Alvor
Høj; Kritisk når gennemsnittet er 98 % eller derover.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
Performance Monitor Users (ydelsestællere) + WMI Enable Account/Remote Enable på root\cimv2
WS-Man: Remote Management Users · DCOM: Distributed COM Users
Understøttede versioner
Windows Server 2008 R2 og nyere (WS-Man fra Windows Server 2012 / WinRM 3.0; DCOM på alle).
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetPerformanceAsync (CimQueries.ProcessorQuery),
hvert minut (SQLInsight:Servers:Intervals:performance) fra src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectPerformanceAsync.
Beregning: src/SQLInsight.Core/Servers/ServerPerformance.cs – ServerPerformanceMath.Cpu.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – ServerCpuRule
Evaluering
Hvert minut læses tælleren Processor(_Total)\% Processor Time som rå værdi. CPU for minuttet er
100 × (1 − Δ tomgangstid / Δ tid) mellem to læsninger (PERF_100NSEC_TIMER_INV), altså gennemsnittet over hele
minuttet, ikke et øjebliksbillede. Er gennemsnittet af minutterne i de seneste SustainedMinutes minutter mindst
HighPercent, er det et fund. Uden to læsninger endnu: "ikke målbar".
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\cimv2):
SELECT PercentProcessorTime, Timestamp_Sys100NS FROM Win32_PerfRawData_PerfOS_Processor WHERE Name = '_Total'
SELECT NumberOfLogicalProcessors FROM Win32_ComputerSystem
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
$s = New-CimSession -ComputerName SERVER # DCOM: -SessionOption (New-CimSessionOption -Protocol Dcom)
$q = "SELECT PercentProcessorTime, Timestamp_Sys100NS FROM Win32_PerfRawData_PerfOS_Processor WHERE Name = '_Total'"
$a = Get-CimInstance -CimSession $s -Query $q; Start-Sleep -Seconds 60; $b = Get-CimInstance -CimSession $s -Query $q
100 * (1 - ($b.PercentProcessorTime - $a.PercentProcessorTime) / ($b.Timestamp_Sys100NS - $a.Timestamp_Sys100NS))
*/

View file

@ -0,0 +1,47 @@
/*
SQLI-WIN-002 – Serveren har næsten ingen ledig hukommelse
==========================================================
Hvad og hvorfor
Når Windows løber tør for ledig hukommelse, flyttes data til sidefilen, alt bliver langsomt, og processer kan fejle
med "out of memory". SQL Server kan blive tvunget til at give hukommelse tilbage.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:Memory:AvailablePercent standard 5 (andel ledig af den samlede hukommelse)
SQLInsight:Servers:Memory:AvailableMegabytes standard 1024 (ledig hukommelse i MB)
Begge skal være under grænsen (gennemsnit over 5 minutter), så store SQL Server-hosts, der bevidst har lidt
tilbage, men stadig flere GB, ikke giver fund.
Alvor
Middel; Høj under 2 % og under 256 MB.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
Performance Monitor Users + WMI Enable Account/Remote Enable på root\cimv2
WS-Man: Remote Management Users · DCOM: Distributed COM Users
Understøttede versioner
Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetPerformanceAsync (CimQueries.MemoryQuery) og
Session.GetOperatingSystemAsync (CimQueries.OperatingSystemQuery), hvert minut fra
src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectPerformanceAsync.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – ServerMemoryRule
Evaluering
Ledig hukommelse er Memory\Available MBytes; andelen er den i forhold til Win32_OperatingSystem.TotalVisibleMemorySize.
Gennemsnittet over de seneste 5 minutter (mindst 80 % dækket) sammenlignes med begge grænser.
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\cimv2):
SELECT AvailableMBytes, PagesPersec, CommittedBytes, CommitLimit, Timestamp_PerfTime, Frequency_PerfTime FROM Win32_PerfRawData_PerfOS_Memory
SELECT Caption, Version, BuildNumber, LastBootUpTime, TotalVisibleMemorySize, CSName FROM Win32_OperatingSystem
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
$s = New-CimSession -ComputerName SERVER
$m = Get-CimInstance -CimSession $s -Query "SELECT AvailableMBytes FROM Win32_PerfRawData_PerfOS_Memory"
$o = Get-CimInstance -CimSession $s -Query "SELECT TotalVisibleMemorySize FROM Win32_OperatingSystem"
"{0} MB ledig ({1:N1} %)" -f $m.AvailableMBytes, (100 * $m.AvailableMBytes * 1024 / $o.TotalVisibleMemorySize)
*/

View file

@ -0,0 +1,45 @@
/*
SQLI-WIN-003 – Serveren pager hukommelse til disk
==================================================
Hvad og hvorfor
Windows læser og skriver hukommelsessider til sidefilen, fordi der ikke er plads i RAM. Det er mange gange
langsommere end hukommelse og belaster disken, som andre også bruger.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:Memory:PagesPerSecond standard 1000 (Memory\Pages/sec, gennemsnit over 15 minutter)
Kun sammen med lidt ledig hukommelse (under 10 % i samme periode): Pages/sec alene er også fil-I/O gennem
memory-mapped filer og er ikke i sig selv et problem.
Alvor
Middel.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
Performance Monitor Users + WMI Enable Account/Remote Enable på root\cimv2
WS-Man: Remote Management Users · DCOM: Distributed COM Users
Understøttede versioner
Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetPerformanceAsync (CimQueries.MemoryQuery), hvert minut
fra src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectPerformanceAsync.
Beregning: src/SQLInsight.Core/Servers/ServerPerformance.cs – ServerPerformanceMath.PagesPerSecond.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – ServerPagingRule
Evaluering
Pages/sec er en PERF_COUNTER_COUNTER: (Δ PagesPersec) / (Δ Timestamp_PerfTime / Frequency_PerfTime) mellem to
læsninger. Er gennemsnittet over 15 minutter mindst grænsen, og den ledige andel samtidig under 10 %, er det et fund.
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\cimv2):
SELECT AvailableMBytes, PagesPersec, CommittedBytes, CommitLimit, Timestamp_PerfTime, Frequency_PerfTime FROM Win32_PerfRawData_PerfOS_Memory
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
$s = New-CimSession -ComputerName SERVER
$q = "SELECT PagesPersec, Timestamp_PerfTime, Frequency_PerfTime FROM Win32_PerfRawData_PerfOS_Memory"
$a = Get-CimInstance -CimSession $s -Query $q; Start-Sleep -Seconds 60; $b = Get-CimInstance -CimSession $s -Query $q
($b.PagesPersec - $a.PagesPersec) / (($b.Timestamp_PerfTime - $a.Timestamp_PerfTime) / $b.Frequency_PerfTime)
*/

View file

@ -0,0 +1,40 @@
/*
SQLI-WIN-004 – Den reserverede hukommelse (commit) er tæt på grænsen
=====================================================================
Hvad og hvorfor
Commit charge er den hukommelse, programmerne har reserveret (RAM plus sidefil). Når grænsen nås, kan Windows ikke
give mere hukommelse ud, og programmer og tjenester fejler, selv om der måske er ledig RAM.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:Memory:CommitPercent standard 90 (CommittedBytes / CommitLimit, gennemsnit over 5 minutter)
Alvor
Middel; Høj ved 97 % eller derover.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
Performance Monitor Users + WMI Enable Account/Remote Enable på root\cimv2
WS-Man: Remote Management Users · DCOM: Distributed COM Users
Understøttede versioner
Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetPerformanceAsync (CimQueries.MemoryQuery), hvert minut
fra src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectPerformanceAsync.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – ServerCommitRule
Evaluering
100 × CommittedBytes / CommitLimit pr. minut; gennemsnittet over 5 minutter (mindst 80 % dækket) mod grænsen.
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\cimv2):
SELECT AvailableMBytes, PagesPersec, CommittedBytes, CommitLimit, Timestamp_PerfTime, Frequency_PerfTime FROM Win32_PerfRawData_PerfOS_Memory
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
$s = New-CimSession -ComputerName SERVER
$m = Get-CimInstance -CimSession $s -Query "SELECT CommittedBytes, CommitLimit FROM Win32_PerfRawData_PerfOS_Memory"
100 * $m.CommittedBytes / $m.CommitLimit
*/

View file

@ -0,0 +1,48 @@
/*
SQLI-WIN-005 – Lidt ledig plads på et drev
===========================================
Hvad og hvorfor
Når et drev løber fuldt, stopper alt, der skriver til det: SQL Server kan ikke vokse data- og logfiler, backup
fejler, AOS og Windows kan ikke skrive logfiler og midlertidige filer, og brugere kan ikke gemme.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:Disks:FreePercent standard 10 (ledig andel, under: Middel)
SQLInsight:Servers:Disks:CriticalFreePercent standard 5 (under: Høj)
SQLInsight:Servers:Disks:EnoughFreeGigabytes standard 50 (er der mindst så mange GB ledige, er der intet fund,
uanset andelen: store drev med lille andel ledig kan være fine)
Alvor
Middel under FreePercent, Høj under CriticalFreePercent, Kritisk under 1 GB eller 1 %.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
WMI Enable Account/Remote Enable på root\cimv2
WS-Man: Remote Management Users · DCOM: Distributed COM Users
Understøttede versioner
Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetDisksAsync (CimQueries.VolumeQuery, ellers
CimQueries.LogicalDiskQuery), hvert 15. minut (SQLInsight:Servers:Intervals:disks) fra
src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectDisksAsync.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – DiskFreeSpaceRule
Evaluering
Lokale diske (DriveType 3) med drevbogstav eller monteringssti; partitioner uden (system/recovery, navn \\?\Volume{…})
springes over. Ledig andel = FreeSpace / Capacity. Et fund pr. drev, der er under FreePercent og samtidig har
mindre end EnoughFreeGigabytes ledig.
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\cimv2):
SELECT Name, Label, FileSystem, Capacity, FreeSpace FROM Win32_Volume WHERE DriveType = 3
SELECT DeviceID, VolumeName, FileSystem, Size, FreeSpace FROM Win32_LogicalDisk WHERE DriveType = 3 (fallback)
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
$s = New-CimSession -ComputerName SERVER
Get-CimInstance -CimSession $s -Query "SELECT Name, Label, FileSystem, Capacity, FreeSpace FROM Win32_Volume WHERE DriveType = 3" |
Where-Object { $_.Name -notlike '\\?\*' } |
Select-Object Name, Label, @{n='LedigGB';e={[math]::Round($_.FreeSpace / 1GB, 1)}}, @{n='Ledig%';e={[math]::Round(100 * $_.FreeSpace / $_.Capacity, 1)}}
*/

View file

@ -0,0 +1,55 @@
/*
SQLI-WIN-006 – Et drev bliver fuldt om få dage
===============================================
Hvad og hvorfor
Varsler, at disken løber fuld om et antal dage, før det sker: pladsen på drevet er de seneste uger brugt i et jævnt
tempo, og fortsætter det, er drevet fuldt inden for prognosegrænsen.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:Disks:ForecastDays standard 30 (fund når drevet forventes fuldt inden for så mange dage)
SQLInsight:Servers:Disks:ForecastHistoryDays standard 14 (dage med SQLInsights egne målinger, linjen beregnes over)
SQLInsight:Servers:DiskHistoryDays standard 35 (hvor længe målingerne gemmes i insight.ServerDiskSamples)
Faste fornuftsgrænser (src/SQLInsight.Core/Servers/DiskForecast.cs – DiskForecastSettings): mindst 2 dages historik og
12 målinger, R² mindst 0,5, vækst mindst 0,01 GB pr. dag, højst 365 dage frem.
Alvor
Middel; Høj inden for 7 dage; Kritisk inden for 2 dage.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
WMI Enable Account/Remote Enable på root\cimv2
WS-Man: Remote Management Users · DCOM: Distributed COM Users
Understøttede versioner
Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetDisksAsync (CimQueries.VolumeQuery), hvert 15. minut fra
src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectDisksAsync, som gemmer målingen og beregner prognosen.
Prognose: src/SQLInsight.Core/Servers/DiskForecast.cs – DiskForecaster.Forecast.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – DiskForecastRule
Evaluering
Ledig plads pr. drev hver 15. minut gemmes i repositoriet. Prognosen tager timegennemsnit af de seneste
ForecastHistoryDays dage – kun siden drevet sidst skiftede størrelse (et udvidet drev starter en ny tendens) – og
lægger en ret linje (mindste kvadraters metode) gennem den ledige plads. Hælder linjen nedad, passer den rimeligt
(R²), og er der nok historik, er "fuld om ca. N dage" = ledig plads nu / forbrug pr. dag. Svinger pladsen op og ned
(backup skrevet og slettet), er der ingen prognose frem for et skræmmende gæt.
Reglen bruger ikke SQL mod den overvågede server. Forespørgslen mod Windows står herunder, og den forespørgsel, agenten
bruger mod sit eget repository (ikke den overvågede server), står til sidst. Filen indeholder kun kommentarer.
CIM/WQL (namespace root\cimv2):
SELECT Name, Label, FileSystem, Capacity, FreeSpace FROM Win32_Volume WHERE DriveType = 3
Historikken i SQLInsight-repositoriet (src/SQLInsight.Repository/InsightRepository.WindowsServers.cs – GetServerDiskHistoryAsync):
SELECT Volume, DATEADD(hour, DATEDIFF(hour, 0, SampledAt), 0) AS [Hour], MAX(SizeGb), AVG(FreeGb)
FROM insight.ServerDiskSamples WHERE ServerId = @id AND SampledAt >= @from
GROUP BY Volume, DATEADD(hour, DATEDIFF(hour, 0, SampledAt), 0)
ORDER BY Volume, [Hour];
Efterprøv med PowerShell (skrivebeskyttet; ledig plads nu):
Get-CimInstance -ComputerName SERVER -Query "SELECT Name, Capacity, FreeSpace FROM Win32_Volume WHERE DriveType = 3" |
Select-Object Name, @{n='LedigGB';e={[math]::Round($_.FreeSpace / 1GB, 1)}}
*/

View file

@ -0,0 +1,50 @@
/*
SQLI-WIN-007 – En tjeneste, der skal starte automatisk, kører ikke
===================================================================
Hvad og hvorfor
En tjeneste med starttypen Automatisk står ikke som kørende. Det, tjenesten gør, sker ikke, og en automatisk
tjeneste, der er stoppet, er oftest gået ned. SQLInsight starter aldrig selv tjenester.
Tærskler (agentens appsettings.json og serverens indstillinger i SQLInsight Web)
SQLInsight:Servers:Services:Ignore ekstra tjenester, der må være stoppet (navn eller præfiks*)
SQLInsight:Servers:Services:StartupGraceMinutes standard 15 (ingen fund de første minutter efter en genstart)
Indbygget liste over automatiske tjenester, der stopper af sig selv (StoppedAutomaticServiceRule.StopByDesign),
fx sppsvc, gupdate, edgeupdate, MapsBroker, RemoteRegistry, clr_optimization_*, CDPUserSvc_*.
Pr. server: "Tjenester der ignoreres" (insight.WindowsServers.IgnoredServices). Overvågede tjenester (SQLI-WIN-008)
rapporteres dér og ikke her.
Alvor
Middel.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
WMI Enable Account/Remote Enable på root\cimv2 + læseret (CC LC RP RC) på Service Control Manager
(over netværket må ellers kun administratorer liste tjenester).
WS-Man: Remote Management Users · DCOM: Distributed COM Users
Understøttede versioner
Windows Server 2008 R2 og nyere (DelayedAutoStart læses fra Windows Server 2012; før det uden).
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetServicesAsync (CimQueries.ServiceQuery, ellers
CimQueries.ServiceQueryWithoutDelayed), hvert 5. minut (SQLInsight:Servers:Intervals:services) fra
src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectServicesAsync.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – StoppedAutomaticServiceRule
Evaluering
Et fund pr. tjeneste med StartMode = 'Auto' og State <> 'Running', som hverken står på den indbyggede liste, i
Ignore, i serverens ignoreringsliste eller i serverens liste over overvågede tjenester. Ingen fund inden for
StartupGraceMinutes efter LastBootUpTime. Viser serveren slet ingen tjenester, er området "ikke målbar"
(manglende læseret på Service Control Manager).
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\cimv2):
SELECT Name, DisplayName, State, StartMode, DelayedAutoStart, ExitCode FROM Win32_Service
SELECT Name, DisplayName, State, StartMode, ExitCode FROM Win32_Service (før Windows Server 2012)
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
Get-CimInstance -ComputerName SERVER -Query "SELECT Name, DisplayName, State, StartMode, DelayedAutoStart, ExitCode FROM Win32_Service" |
Where-Object { $_.StartMode -eq 'Auto' -and $_.State -ne 'Running' } | Select-Object Name, DisplayName, State, ExitCode
*/

View file

@ -0,0 +1,41 @@
/*
SQLI-WIN-008 – En overvåget tjeneste kører ikke
================================================
Hvad og hvorfor
Tjenester, der er valgt for serveren i SQLInsight Web (fx AOS60$01 for Dynamics AX, W3SVC for IIS, MSSQLSERVER),
skal køre uanset starttype. Når de ikke gør, er det, de leverer, ikke tilgængeligt. SQLInsight starter aldrig selv tjenester.
Tærskler
Listen pr. server: "Overvågede tjenester" (insight.WindowsServers.WatchedServices), sat i SQLInsight Web eller API v1.
SQLInsight:Servers:Services:StartupGraceMinutes standard 15 (ingen fund de første minutter efter en genstart)
Alvor
Høj når tjenesten ikke kører; Middel når den ikke findes (eller ikke kan ses).
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
WMI Enable Account/Remote Enable på root\cimv2 + læseret (CC LC RP RC) på Service Control Manager
WS-Man: Remote Management Users · DCOM: Distributed COM Users
Understøttede versioner
Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetServicesAsync (CimQueries.ServiceQuery), hvert 5. minut fra
src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectServicesAsync.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – WatchedServiceRule
Evaluering
For hvert navn på listen findes tjenesten på navn eller visningsnavn. Findes den ikke: fund (Middel). Er State ikke
'Running': fund (Høj). Uden liste: "ikke relevant".
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\cimv2):
SELECT Name, DisplayName, State, StartMode, DelayedAutoStart, ExitCode FROM Win32_Service
SELECT Name, DisplayName, State, StartMode, ExitCode FROM Win32_Service WHERE Name = '<tjeneste>' (syntetiske tjek)
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
Get-CimInstance -ComputerName SERVER -Query "SELECT Name, DisplayName, State, StartMode, ExitCode FROM Win32_Service WHERE Name = 'AOS60`$01'"
*/

View file

@ -0,0 +1,54 @@
/*
SQLI-WIN-009 – Fejl i Windows' hændelseslog
============================================
Hvad og hvorfor
Fejl og kritiske hændelser i System- og Application-loggen: disk- og filsystemfejl, opbrugte ressourcer, tjenester
der stopper eller ikke starter, programmer der går ned, fejl fra Dynamics AX' AOS – og almindelige fejl, der gentager sig.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:EventLog:RepeatThreshold standard 5 (en almindelig fejl skal ses så mange gange på 24 timer)
SQLInsight:Servers:EventLog:Ignore ekstra støj: "Kilde:id", "Kilde" eller "Kilde*"
SQLInsight:Servers:EventLog:MaxFindings standard 15 (højst så mange fund pr. server, vigtigste først)
SQLInsight:Servers:EventLog:MaxEventsPerCollection standard 2000 (hændelser pr. log pr. indsamling)
Indbygget støjliste (ServerEventAnalysis.DefaultIgnore): DCOM 10016/10010, Schannel 36874/36887/36888, TPM-WMI 1796/1801,
WMI 10, Security-SPP 8198, og SQL Server's egne kilder (MSSQLSERVER, MSSQL$*, SQLSERVERAGENT, SQLAgent$*,
SQLISPackage*), som SQL Server-fejlloggen allerede dækker. Et fund holdes åbent 24 timer efter hændelsen sidst blev set.
Alvor
Høj: disk/filsystem, ressourcer, niveau Kritisk, tjeneste stoppet 3+ gange. Middel: tjeneste stoppet, program gik ned,
Dynamics AX. Lav: almindelige fejl der gentager sig. (Uventet nedlukning: se SQLI-WIN-010.)
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
Event Log Readers (System- og Application-loggen; aldrig Security-loggen).
Over RPC (MS-EVEN6): firewallreglerne "Remote Event Log Management". Fallback over CIM: WMI Remote Enable på root\cimv2.
Understøttede versioner
Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetEventsAsync: EventLogReader med CimQueries.EventXPath,
ellers CimQueries.EventQuery; hvert 5. minut (SQLInsight:Servers:Intervals:events) fra
src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectEventsAsync. Gruppering og støjfilter:
src/SQLInsight.Core/Servers/ServerEvents.cs – ServerEventAnalysis.Group/Recent/Categorize.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – WindowsEventLogRule
Evaluering
Hændelser med niveau 1 (kritisk) og 2 (fejl) siden sidste indsamling læses fra System og Application. Støj tælles,
men grupperes ikke. Resten grupperes pr. log, kilde og hændelses-id med antal, første og seneste tidspunkt og den
nyeste tekst som eksempel (følsom: vises kun lokalt, [udeladt] i API'et). Grupperne fra de seneste 24 timer lægges
sammen. Vigtige kategorier giver fund med det samme, almindelige fejl først ved RepeatThreshold gentagelser.
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
Event Log (MS-EVEN6), XPath pr. log ('System' og 'Application'):
*[System[(Level=1 or Level=2) and TimeCreated[@SystemTime>'2026-10-02T12:00:00.000Z']]]
CIM/WQL fallback (namespace root\cimv2; EventType 1 = fejl, kritiske hændelser rapporteres som fejl):
SELECT Logfile, SourceName, EventCode, EventType, TimeGenerated, RecordNumber, Message FROM Win32_NTLogEvent
WHERE Logfile = 'System' AND EventType = 1 AND TimeGenerated > '20261002120000.000000+000'
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
Get-WinEvent -ComputerName SERVER -LogName System -FilterXPath "*[System[(Level=1 or Level=2) and TimeCreated[timediff(@SystemTime) <= 86400000]]]" |
Group-Object ProviderName, Id | Sort-Object Count -Descending | Select-Object Count, Name
*/

View file

@ -0,0 +1,42 @@
/*
SQLI-WIN-010 – Serveren er genstartet
======================================
Hvad og hvorfor
Serveren er genstartet det seneste døgn. Uventet (strøm, blå skærm, hård nulstilling) afbryder alt midt i det, og
databaser og AX skal gendanne sig selv; en planlagt genstart (fx Windows-opdateringer) er kun til orientering.
Tærskler
Fast: 24 timer efter LastBootUpTime (ServerEventAnalysis.RecentWindow), derefter forsvinder fundet af sig selv.
Alvor
Høj ved uventet genstart; Info ved genstart uden tegn på at den var uventet.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
WMI Enable Account/Remote Enable på root\cimv2 (starttidspunkt); Event Log Readers (hvorfor).
Understøttede versioner
Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetOperatingSystemAsync (CimQueries.OperatingSystemQuery) og
Session.GetEventsAsync, fra src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectPerformanceAsync og CollectEventsAsync.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – ServerRestartRule
Evaluering
Er LastBootUpTime inden for 24 timer, er serveren genstartet. Er der i hændelsesloggen (seneste 24 timer) en hændelse i
kategorien "uventet nedlukning" – Microsoft-Windows-Kernel-Power 41, EventLog 6008, BugCheck/WER-SystemErrorReporting 1001 –
senest set tidligst en time før starttidspunktet, er genstarten uventet (Høj), ellers Info.
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\cimv2):
SELECT Caption, Version, BuildNumber, LastBootUpTime, TotalVisibleMemorySize, CSName FROM Win32_OperatingSystem
Event Log (MS-EVEN6), XPath for System:
*[System[(Level=1 or Level=2) and TimeCreated[@SystemTime>'2026-10-02T12:00:00.000Z']]]
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
(Get-CimInstance -ComputerName SERVER -Query "SELECT LastBootUpTime FROM Win32_OperatingSystem").LastBootUpTime
Get-WinEvent -ComputerName SERVER -FilterHashtable @{ LogName = 'System'; Id = 41, 6008, 1001, 1074; StartTime = (Get-Date).AddDays(-1) }
*/

View file

@ -0,0 +1,39 @@
/*
SQLI-WIN-011 – Serveren er ikke genstartet i lang tid
======================================================
Hvad og hvorfor
De fleste Windows-opdateringer kræver en genstart. En server, der ikke er genstartet i flere måneder, mangler derfor
næsten sikkert sikkerhedsopdateringer.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:UptimeDays standard 0 = slået fra (fx 120 for et fund efter 120 dage uden genstart)
Alvor
Lav.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
WMI Enable Account/Remote Enable på root\cimv2
WS-Man: Remote Management Users · DCOM: Distributed COM Users
Understøttede versioner
Windows Server 2008 R2 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetOperatingSystemAsync (CimQueries.OperatingSystemQuery),
fra src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectPerformanceAsync.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – ServerUptimeRule
Evaluering
Oppetid = nu − LastBootUpTime. Er UptimeDays større end 0, og oppetiden mindst så mange dage, er det et fund.
Med UptimeDays = 0 er reglen "ikke relevant".
Reglen bruger ikke SQL. Forespørgslen mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\cimv2):
SELECT Caption, Version, BuildNumber, LastBootUpTime, TotalVisibleMemorySize, CSName FROM Win32_OperatingSystem
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
(Get-Date) - (Get-CimInstance -ComputerName SERVER -Query "SELECT LastBootUpTime FROM Win32_OperatingSystem").LastBootUpTime
*/

View file

@ -0,0 +1,50 @@
/*
SQLI-WIN-012 – En planlagt opgave fejlede sidste gang, den kørte
=================================================================
Hvad og hvorfor
En opgave i Opgavestyring (Task Scheduler) sluttede med en fejlkode. Planlagte opgaver er ofte oprydning, eksport,
integrationer eller filbackup: når de fejler, opdager ingen det, før noget mangler.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:Tasks:IncludeMicrosoftTasks standard false (Microsofts egne opgaver under \Microsoft\ overvåges ikke)
SQLInsight:Servers:Tasks:Ignore opgaver der aldrig overvåges (sti\navn eller præfiks*)
SQLInsight:Servers:Tasks:MaxWatched standard 50 (højst så mange opgaver pr. server)
Resultater, der ikke er fejl: 0x0, 0x41300 (klar), 0x41301 (kører), 0x41302 (deaktiveret), 0x41303 (har ikke kørt),
0x41304 (ingen flere kørsler), 0x41325 (i kø), 0x8004131F (kørte allerede) – src/SQLInsight.Core/Servers/ServerTasks.cs.
Alvor
Middel.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
WMI Enable Account/Remote Enable på root\Microsoft\Windows\TaskScheduler. Agenten ser kun opgaver, hvis egne
sikkerhedsindstillinger tillader kontoen at læse dem. Seneste resultat læses med Task Schedulers GetInfo (et
skrivebeskyttet CIM-metodekald, som Get-ScheduledTaskInfo); over WinRM kræver metodekald, at kontoen også har
Execute-ret i WinRM – ellers bruges DCOM.
Understøttede versioner
Windows Server 2012 og nyere (navnerummet TaskScheduler findes ikke på 2008 R2: "ikke målbar").
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetTasksAsync (CimQueries.TaskQuery og
PS_ScheduledTask.GetInfoByName), hvert 15. minut (SQLInsight:Servers:Intervals:tasks) fra
src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectTasksAsync.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – FailedScheduledTaskRule
Evaluering
Et fund pr. aktiveret, overvåget opgave, hvis LastTaskResult er en fejl.
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\Microsoft\Windows\TaskScheduler):
SELECT TaskName, TaskPath, State FROM MSFT_ScheduledTask
CIM-metode (skrivebeskyttet) pr. overvåget opgave:
PS_ScheduledTask.GetInfoByName(TaskName, TaskPath) -> cmdletOutput: MSFT_TaskDynamicInfo
(LastRunTime, LastTaskResult, NextRunTime, NumberOfMissedRuns)
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
$s = New-CimSession -ComputerName SERVER
Get-ScheduledTask -CimSession $s | Where-Object TaskPath -notlike '\Microsoft\*' | Get-ScheduledTaskInfo -CimSession $s |
Select-Object TaskPath, TaskName, LastRunTime, @{n='Resultat';e={'0x{0:X}' -f $_.LastTaskResult}}, NextRunTime, NumberOfMissedRuns
*/

View file

@ -0,0 +1,44 @@
/*
SQLI-WIN-013 – En planlagt opgave kørte ikke, da den skulle
============================================================
Hvad og hvorfor
Opgaven skulle have kørt, men gjorde det ikke: Opgavestyring kørte ikke, betingelserne var ikke opfyldt (fx "kun når
brugeren er logget på"), eller serveren var slukket. Det, opgaven gør, er ikke sket.
Tærskler (agentens appsettings.json)
SQLInsight:Servers:Tasks:LateMinutes standard 60 (næste kørsel mere end så mange minutter i fortiden)
SQLInsight:Servers:Tasks:IncludeMicrosoftTasks standard false
SQLInsight:Servers:Tasks:Ignore opgaver der aldrig overvåges
Alvor
Middel.
Rettigheder på den overvågede server (db/permissions/grant-windows-server.ps1, aldrig lokal administrator)
WMI Enable Account/Remote Enable på root\Microsoft\Windows\TaskScheduler (se SQLI-WIN-012 om GetInfo over WinRM).
Understøttede versioner
Windows Server 2012 og nyere.
Collector
src/SQLInsight.Servers/CimWindowsServerSource.cs – Session.GetTasksAsync (CimQueries.TaskQuery og
PS_ScheduledTask.GetInfoByName), hvert 15. minut fra src/SQLInsight.Agent/Servers/ServerMonitor.cs – CollectTasksAsync.
Regel
src/SQLInsight.Rules/Servers/WindowsServerRules.cs – MissedScheduledTaskRule
Evaluering
Et fund pr. aktiveret, overvåget opgave, der ikke kører nu, og hvor enten NumberOfMissedRuns > 0, eller NextRunTime
ligger mere end LateMinutes i fortiden, uden at LastRunTime er efter det tidspunkt.
Reglen bruger ikke SQL. Forespørgslerne mod Windows står herunder; filen indeholder kun kommentarer og ændrer intet.
CIM/WQL (namespace root\Microsoft\Windows\TaskScheduler):
SELECT TaskName, TaskPath, State FROM MSFT_ScheduledTask
CIM-metode (skrivebeskyttet) pr. overvåget opgave:
PS_ScheduledTask.GetInfoByName(TaskName, TaskPath) -> cmdletOutput: MSFT_TaskDynamicInfo
Efterprøv med PowerShell (skrivebeskyttet; kør på agentens server som agentens konto):
$s = New-CimSession -ComputerName SERVER
Get-ScheduledTask -CimSession $s | Where-Object { $_.TaskPath -notlike '\Microsoft\*' -and $_.State -ne 'Disabled' } |
Get-ScheduledTaskInfo -CimSession $s | Where-Object { $_.NumberOfMissedRuns -gt 0 -or ($_.NextRunTime -and $_.NextRunTime -lt (Get-Date).AddMinutes(-60)) }
*/

View file

@ -0,0 +1,37 @@
using SQLInsight.Rules.Servers;
namespace SQLInsight.Rules.Tests;
/// <summary>Every Windows server and synthetic check rule has its reviewable script in SQL Scripts/ (named by its id).</summary>
public class ServerRuleScriptTests
{
private static DirectoryInfo Scripts()
{
for (var directory = new DirectoryInfo(AppContext.BaseDirectory); directory is not null; directory = directory.Parent)
{
var candidate = new DirectoryInfo(Path.Combine(directory.FullName, "SQL Scripts"));
if (candidate.Exists)
{
return candidate;
}
}
throw new DirectoryNotFoundException("SQL Scripts/ blev ikke fundet over testens mappe.");
}
[Fact]
public void Every_server_and_check_rule_has_a_script_with_its_id_and_title()
{
var folder = Scripts();
Assert.All(ServerRuleCatalog.All, rule =>
{
var file = Path.Combine(folder.FullName, rule.Id + ".sql");
Assert.True(File.Exists(file), $"{rule.Id}.sql mangler i SQL Scripts/.");
var text = File.ReadAllText(file);
Assert.Contains($"{rule.Id} – {rule.Title}", text, StringComparison.Ordinal);
Assert.Contains("Evaluering", text, StringComparison.Ordinal);
Assert.DoesNotContain("sysadmin", text, StringComparison.OrdinalIgnoreCase);
});
}
}