{"id":2111,"date":"2015-11-16T00:00:00","date_gmt":"2015-11-16T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/doing-a-sql-server-healthcheck-via-powershell\/"},"modified":"2021-08-24T13:39:35","modified_gmt":"2021-08-24T13:39:35","slug":"doing-a-sql-server-healthcheck-via-powershell","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/doing-a-sql-server-healthcheck-via-powershell\/","title":{"rendered":"Doing a SQL Server Healthcheck via PowerShell"},"content":{"rendered":"<div id=\"pretty\">\n<h1>Introduction<\/h1>\n<p>This article describes a way of checking the health of  any number of SQL Server instances and servers. It quickly collates server  performance statistics and highlights potential problems such as missing  indexes, untrusted foreign keys and database options that aren&#8217;t set to the  default. Whether you are an accidental or deliberate DBA, this tool will either  save you paying a consultant to run a health check for you or it will save you  writing and executing all of the necessary scripts and formatting the report.  This tool is open-source, <a href=\"https:\/\/github.com\/SpeedySQL\/HealthCheck\">on GitHub<\/a> and is free for use. It.is rather different  from SQLBlitz in that it is PowerShell-driven and does not attempt to be  prescriptive. It is also designed to be easy for the user to customize.  <\/p>\n<p> To get started, all you need to do is to:<\/p>\n<ol>\n<li>Download and extract <code>HealthCheck.zip<\/code> to the desired location (it is in a link at the bottom of the article).<\/li>\n<li>Open DBList.txt and replace server and database names as required.  \t\tAlternatively, execute the PowerShell script passing the name of the  \t\tserver and the database as parameters and skip the third step.<br \/>&#160;For  \t\texample: <code>.\/HealthCheck.ps1 Server1\\InstanceA DB1<\/code><\/li>\n<li>Execute HealthCheck.bat.<\/li>\n<\/ol>\n<p> \t The report can be found in the<code>\\HealthCheck\\Reports<\/code> directory.  <\/p>\n<h2>The  Report<\/h2>\n<p> The report contains one worksheet for each of the T-SQL scripts. I have included  what I think is most important when evaluating a SQL Server from a performance  perspective.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2307-img180.jpg\" alt=\"2307-img180.jpg\" \/><\/p>\n<dl>\n<dt>CacheHitRatios<\/dt>\n<dd>shows the hit ratios and sizes for each plan cache store. &#160;The closer the hit ratios are to 100%,  the better. Lower values could indicate memory pressure or plan cache bloat.<\/dd>\n<dt>ColumnTypesVarchar255<\/dt>\n<dd>lists tables that have more than one column defined as varchar(255). These  tables should be evaluated to see whether other data types would be more  appropriate.<\/dd>\n<dt>CPU_Pressure<\/dt>\n<dd>Calculates signal waits and resource waits as a percentage of the overall wait  time. As a guideline, signal waits should usually be below 20%. If you see  values higher than this, try tuning the queries highlighted by the  <code>MissingIndexesProcsByCPU <\/code>report.<\/dd>\n<dt>CursorsWithoutCloseDeallocate<\/dt>\n<dd>lists objects with cursors that have not been closed or deallocated. Static  cursors use the most memory and could therefore have the greatest impact if not  closed correctly.<\/dd>\n<dt>DatabaseOptions<\/dt>\n<dd>returns database options that are enabled as well assome other information  such as compatibility leveland recovery model.<\/dd>\n<dt>DataTypes_Deprecated<\/dt>\n<dd>lists tables with columns that use deprecated types text, ntext and image.  These should be replaced by the new types <code>(n)varchar(max) <\/code>and  \t<code>varbinary(max) <\/code>to  avoid your code breaking after upgrading SQL Server.<\/dd>\n<dt>FillFactor<\/dt>\n<dd>lists indexes with fill factors of less than 90. You may want to review these  and decided if space is being wasted.&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <\/dd>\n<dt>ForeignKeysUntrusted<\/dt>\n<dd>lists untrusted foreign keys.These constraints will not be considered by the  query optimizer. See  <a href=\"http:\/\/sqlblog.com\/blogs\/hugo_kornelis\/archive\/2007\/03\/29\/can-you-trust-your-constraints.aspx\">  http:\/\/sqlblog.com\/blogs\/hugo_kornelis\/archive\/2007\/03\/29\/can-you-trust-your-constraints.aspx<\/a>  for more details.<\/dd>\n<dt>HeapsNoIndexes<\/dt>\n<dd>lists tables that have no indexes. It is usually a good idea to have at least  a clustered index on a table.<\/dd>\n<dt>HeapsWithNCIndex<\/dt>\n<dd>lists tables that have a non-clustered index but do not have a clustered  index. There are exceptions where this might be acceptable. See the following  article for more information: <a href=\"https:\/\/msdn.microsoft.com\/en-GB\/library\/hh213609.aspx\"> https:\/\/msdn.microsoft.com\/en-GB\/library\/hh213609.aspx<\/a><\/dd>\n<dt>HypotheticalIndexes<\/dt>\n<dd>lists hypothetical indexes. They are usually created by the Database Tuning  Advisor but can also be created using T-SQL. It is best to ensure these are  removed to avoid clutter.&#160; Also,  there have been bugs related to these such as the following:   <a href=\"http:\/\/blogs.msdn.com\/b\/sqlserverfaq\/archive\/2009\/06\/25\/exception-when-selecting-from-sysindexes-in-sql-server-2005.aspx\">  http:\/\/blogs.msdn.com\/b\/sqlserverfaq\/archive\/2009\/06\/25\/exception-when-selecting-from-sysindexes-in-sql-server-2005.aspx<\/a><\/dd>\n<dt>IndexesIgnoreDupKey<\/dt>\n<dd>lists tables with non-clustered indexes that have the<code>ignore_dup_key \t<\/code>option  enabled. This option harms performance of inserts and updates as describedin  Craig Freedman&#8217;s article on MSDN:<a href=\"http:\/\/blogs.msdn.com\/b\/craigfr\/archive\/2008\/01\/30\/maintaining-unique-indexes-with-ignore-dup-key.aspx\">http:\/\/blogs.msdn.com\/b\/craigfr\/archive\/2008\/01\/30\/maintaining-unique-indexes-with-ignore-dup-key.aspx<\/a><\/dd>\n<dt>IndexesUnusedSizes<\/dt>\n<dd>lists tables that have unused indexes. Before removing indexes, remember that  the index usage stats DMV is cleared upon a server restart.<\/dd>\n<dt>IndexKeysOver900Bytes<\/dt>\n<dd>lists indexes that have a maximum key width over 900 bytes which could result in  error 1946 during an insert or update operation.<\/dd>\n<dt>IndexKeyWidth<\/dt>\n<dd>lists tables with wide cluster keys. These should be reviewed as cluster keys  are also present in all non-clustered indexes and can therefore consume a lot of  space, possibly unnecessarily.<\/dd>\n<dt>InMemoryTables  (2014 only) <\/dt>\n<dd> lists In-Memory OLTP tables along with their memory usage.<\/dd>\n<dt>IO_Stats<\/dt>\n<dd>returns IO stall statistics for all databases with the target database listed  first.<\/dd>\n<dt>LatchStats<\/dt>\n<dd>returns latch wait statistics. The latch stats DMV is also cleared upon a  server restart.<\/dd>\n<dt>MemoryClerks<\/dt>\n<dd>lists top ten memory clerks and total memory usage.<\/dd>\n<dt>MissingIndexes&#194;&#173;<\/dt>\n<dd>  lists missing indexes for the target database with an estimated impact above 70  and index advantage above1,000.<\/dd>\n<dt>MissingIndexesForeignKeys<\/dt>\n<dd>lists unindexed columns referenced by foreign keys.&#160; Deletes on the referenced tables will result in scans on the parent  tables. Evaluate whether indexes on the parent tables are required.<\/dd>\n<dt>MissingIndexesProcsByCPU  (2008-2014 only) <\/dt>\n<dd> returns missing index information for the ten stored  procedures that consume most CPU resource. Filtered on indexes with an impact  above 70.<\/dd>\n<dt>MissingIndexesProcsByReads  (2008-2014 only) <\/dt>\n<dd> as above but for the highest consumers of I\/O.<\/dd>\n<dt>PlanCacheByType<\/dt>\n<dd>returns plan cache size broken down by type. Look out for a bloated ad hoc  cache store, which is a common problem.<\/dd>\n<dt>ServerAudits<\/dt>\n<dd>lists active server audits. Watch out for any synchronous server audits, with  a queue delay of 0. These can really hurt performance.<\/dd>\n<dt>ServerConfig<\/dt>\n<dd> returns non-default server configurations. Look out for a non-default  fill factor. I prefer to leave this setting as the default of zero and assign  each index its own fill factor, if required. Check that the &#8220;max degree of  parallelism&#8221; has been set according to the official guidelines from Microsoft <a href=\"https:\/\/support.microsoft.com\/en-gb\/kb\/2806535\"> https:\/\/support.microsoft.com\/en-gb\/kb\/2806535<\/a>. A common oversight is to  leave the maximum server memory as the default 2 TB. This is one setting where  you will definitely want to have a non-default configuration.<\/dd>\n<dt> ServerInformation.ps1<\/dt>\n<dd>PowerShell script that returns information on the target server&#8217;s  CPUs, BIOS, and memory, as well as SQL Server-specific details.<\/dd>\n<dt>ServerTraces<\/dt>\n<dd>lists active traces on the server. Beware of traces where is_rowset =  1 as this means somebody is running SQL Profiler against your server, which has  a big overhead.<\/dd>\n<dt>sp_xml_removedocument<\/dt>\n<dd>lists objects that prepare XML documents but do not remove them.  Microsoft recommends alwaysincluding this to avoid running out of memory.<\/dd>\n<dt>StatisticsDuplicates<\/dt>\n<dd>lists duplicate statistics with their sizes.This report uses the key_ordinal  column to identify the first column of an index statistic due to the following  bug\\unexpected behavior with the stats_column_id: <a href=\"https:\/\/connect.microsoft.com\/SQLServer\/Feedback\/Details\/1163126\">https:\/\/connect.microsoft.com\/SQLServer\/Feedback\/Details\/1163126<\/a><\/dd>\n<dt>TablesAnsiNulls<\/dt>\n<dd>lists tables created with <code>ANSI_NULLS<\/code> off. This setting is deprecated and  should no longer be used. Also, various features in SQL Server will generate an  error when this setting is off, such as indexed views and filtered indexes.<\/dd>\n<dt>TablesManyIndexes<\/dt>\n<dd>lists tables with more than five indexesand an index count greater than half the  column count. Having five indexes or more isn&#8217;t necessarily bad, it&#8217;s just a  starting point for finding tables that possibly have an unnecessarily large  amount of indexes. They can often be consolidated into fewer indexes, saving  space and improving write latencies. <\/dd>\n<dt>TablesOver10GB<\/dt>\n<dd>lists tables larger than 10 GB. Good candidates for data compression, assuming  you have SQL Server 2008 or a later version and the Enterprise edition.<\/dd>\n<dt>TablesWithMoreIndexesThanColumns<\/dt>\n<dd>lists tables that have more indexes than columns. Not usually a very good sign!<\/dd>\n<dt> UniqueIdentifierClusterKey<\/dt>\n<dd>lists tables that have a unique identifier as the first column of the  clustered index. This is rarely a good idea, although there are exceptions to  this such as latch contention on increasing keys. This MSDN article explains in  more detail.  <a href=\"http:\/\/blogs.msdn.com\/b\/sqlserverfaq\/archive\/2010\/05\/27\/monotonically-increasing-clustered-index-keys-can-cause-latch-contention.aspx\">  http:\/\/blogs.msdn.com\/b\/sqlserverfaq\/archive\/2010\/05\/27\/monotonically-increasing-clustered-index-keys-can-cause-latch-contention.aspx<\/a><\/dd>\n<dt>WaitStats<\/dt>\n<dd>Returns server wait statistics. These give a good indication as to where you  should focus your performance tuning efforts.<\/dd>\n<\/dl>\n<p>  I deliberately didn&#8217;t include missing indexes resulting from ad hoc queries in  the report due to the slow performance of the <code>sys.dm_exec_query_stats <\/code>DMV on  servers with a lot of memory. If you find that the DMV returns results quickly  on your servers, feel free to add a script to report on this.<\/p>\n<p>  I also decided not to includeindex fragmentation statistics for the same reason.<br \/>The performance of the <code>sys.dm_db_index_physical_stats<\/code> DMV can be really  slow with large tables. If you do want to include this information, there are  plenty of ready-made scripts out there on the various SQL Server forums.<\/p>\n<p>  The scripts for the stored procedures missing index  reports &#8230;<br \/>(<code>MissingIndexesProcsByCPU.sql <\/code>and <code>MissingIndexesProcsByReads.sql<\/code>)  <br \/>&#8230;are not  available for SQL Server 2005 instances as the <code>sys.dm_exec_procedure_stats <\/code>DMV  was only introduced in SQL&#160; Server  2008.<\/p>\n<h2>How Does It Work?<\/h2>\n<p>The batch file reads a list of SQL Server instance and  database names from a text file and passes them as parameter values to a  PowerShell script, which it executes in a loop, once for each database (see the  &#8220;Permissions Required&#8221; section below for security issues). The PowerShell script  is intended to do a health check on a single instance, putting the result in an  excel file of the same name as the instance. It does this health check by  executing a set of diagnostic T-SQL and PowerShell scripts, exporting metadata  to CSV files.&#160; Finally, a VBA Excel  macro is executed from PowerShell to merge the CSV files into an Excel  spreadsheet and then formats the report. I could have implemented this part in  PowerShell too, but as I had written a formatting macro a few years ago, I  decided to reuse it.<\/p>\n<h2>Software Required<\/h2>\n<p>  The following software is required to run the health check:<\/p>\n<ul>\n<li>PowerShell version 2.0 or higher (comes with Windows 7 and Windows  \t\tServer 2008 R2)<\/li>\n<li>&#160;Microsoft Excel 2007 or higher.<\/li>\n<li>&#160;SQLPS PowerShell module, which is installed with SQL  \t\tManagement Tools.<\/li>\n<\/ul>\n<h2>Permissions Required<\/h2>\n<p>A windows account must be used with the following access to the target  \tserver and database:<\/p>\n<h4>Server Scope&#160;&#160;&#160;&#160;&#160;&#160;&#160; <\/h4>\n<div class=\"indent\">\n<ul>\n<li>View Server State<\/li>\n<\/ul>\n<\/div>\n<h4>Database Scope&#160;&#160;&#160;&#160;&#160; <\/h4>\n<div class=\"indent\">\n<ul>\n<li>View Definition in target database.<\/li>\n<li>ddl_admin to run DBCC  \tSHOW_STATISTICS if the target server is pre-SQL 2012 SP1, otherwise  \tdb_datareader is enough.<\/li>\n<li>Remote WMI access is required to run the ServerInformation.ps1 script.&#160;  \tThe following article has details on how to grant this access to non-admin  \tusers.<br \/><a href=\"http:\/\/www.adilhindistan.com\/2013\/10\/permissions-to-access-wmi-remotely.html\">http:\/\/www.adilhindistan.com\/2013\/10\/permissions-to-access-wmi-remotely.html<\/a><br \/>If the user does not have the necessary access, the report will still run  \tbut will not include the server information worksheet.<\/li>\n<\/ul>\n<\/div>\n<h2>Customising the report<\/h2>\n<p>You can adjust the report by deleting or adding  scripts, or by changing them as you would any other T-SQL.<\/p>\n<ul>\n<li>To expand the range of the health check to meet your requirements  \t\tsimply add the T-SQL scripts that you need to the relevant scripts  \t\tdirectory and they will be automatically executed the next time you run  \t\tthe health check. A corresponding worksheet will be added to the Excel  \t\treport. <\/li>\n<li>Similarly, to remove a worksheet, you just need to remove the  \t\tunwanted T-SQL script from its directory or, if it&#8217;s a temporary change,  \t\trename the extension to something like &#8220;old&#8221;. <\/li>\n<li>You can add or remove a column in an existing script and this will  \t\tbe reflected in the final report too.<\/li>\n<\/ul>\n<p> The scripts are maintained at four different levels of  compatibility, so you should repeat these operations for each directory,  Scripts_2005, Scripts_2008, Scripts_2012 &amp; Scripts_2014.<\/p>\n<h2>The  Code<\/h2>\n<p>  HealthCheck.zip contains the following five files:<\/p>\n<p> <b> HealthCheck.ps1<\/b> &#8211; This PowerShell script controls the flow of the program.  <\/p>\n<p>  After initializing some variables the script determines the SQL Server version  and database compatibility level (see Listing 1 below).&#160; Each major version has a directory with its own scripts, from SQL Server  2005 to SQL Server 2014.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t\r\n#Get SQL Server version and compatibility level of target database.\r\n\t&#160;\r\n\t[int]$compLevel=invoke-sqlcmd-ServerInstance$args[0]-Query\"SELECT compatibility_level FROM sys.databases WHERE name = '$DatabaseName';\"|%{'{0}'-f$_[0]}\r\n\t&#160;\r\n\t[int]$ver=invoke-sqlcmd-ServerInstance$args[0]-Query\"SELECT REPLACE(LEFT(CONVERT(varchar, SERVERPROPERTY ('ProductVersion')),2), '.', '');\"|%{'{0}'-f$_[0]}\r\n\t&#160;\r\n\t#Set folder path depending upon SQL version.\r\n\tif ($ver-eq 9) {$FolderPath=\"$dir\\Scripts_2005\\\"}&#160;&#160;&#160;&#160;&#160;&#160; \r\n\telseif ($ver-eq 10) {$FolderPath=\"$dir\\Scripts_2008\\\"}&#160; \r\n\telseif ($ver-eq 11) {$FolderPath=\"$dir\\Scripts_2012\\\"}&#160; \r\n\telseif ($ver-eq 12) {$FolderPath=\"$dir\\Scripts_2014\\\"}&#160; \r\n\t&#160;\r\n<\/pre>\n<p class=\"caption\">Listing 1<\/p>\n<p>  Next, another PowerShell script is run to gather information on the target  server before executing the SQL scripts from the relevant directory (see Listing  2 below). The output is temporarily stored in CSV files in the \\Reports\\Temp  directory.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t&#160;\r\n\tpowershell.exe-file\"$FolderPath\\ServerInformation.ps1\"$args[0],$dir\r\n\t&#160;\r\n\t#Loop through the .sql files and run them.\r\n\tforeach ($filenamein$files|sort-object)\r\n\t&#160;&#160;&#160;&#160;&#160; { \r\n\t&#160;&#160;&#160;&#160;&#160;&#160; $i++\r\n\t&#160;&#160;&#160;&#160;&#160;&#160; $outfile=\"$dir\\Reports\\Temp\\\"+$filename.Name.Replace(\".sql\",\"\")  +\".csv\"\r\n\t&#160;\r\n\t&#160;&#160;&#160;&#160;&#160;&#160; invoke-sqlcmd-ServerInstance$args[0]-Database$args[1]-InputFile$filename.Fullname  |Export-Csv-Path$outfile-NoTypeInformation\r\n\t&#160;&#160;&#160;&#160;&#160;&#160; \r\n\tWrite-Progress-activity\"Exporting $filename to CSV. File $i\/$count.\"-status\"Completed: \"-PercentComplete (($i\/$count)*100)&#160;&#160;&#160;&#160;&#160;&#160; \r\n\t}\r\n<\/pre>\n<p class=\"caption\">Listing 2<\/p>\n<p>  Databases which have compatibility level 80 (SQL Server 2000) will not be  compatible with all of the scripts due to the presence of T-SQL operators such  as <code>APPLY<\/code> and <code>UNPIVOT<\/code>. The affected scripts are, therefore, stored in a  subdirectory called &#8220;80&#8221; and are executed in the context of the master database  as shown in Listing 3.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t&#160;\r\n\t#For SQL 2005\/2008 compatibility 80 databases, run scripts that use CROSS APPLY or UNPIVOT against the master database.\r\n\tif ($ver-eq 9-OR$ver-eq 10 )\r\n\t&#160;&#160;&#160; {$FolderPath=\"$FolderPath\\80\\\"\r\n\t$files=get-childitem-path$FolderPath-filter\"*.sql\"\r\n\tforeach ($filenamein$files|sort-object)\r\n\t&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\r\n\t&#160;&#160;&#160;&#160;&#160;&#160; $outfile=\"$dir\\Reports\\Temp\\\"+$filename.Name.Replace(\".sql\",\"\")  +\".csv\"\r\n\t&#160;&#160;&#160;&#160;&#160;&#160; invoke-sqlcmd-ServerInstance$args[0]-Database\"master\"-InputFile$filename.Fullname  -Variable\"DB='${DatabaseName}'\"|Export-Csv-Path$outfile-NoTypeInformation\r\n\t}\r\n\t&#160;&#160;&#160; }\r\n<\/pre>\n<p class=\"caption\">Listing 3<\/p>\n<p>  Finally, the CSV files are imported into an Excel spreadsheet using a VBA macro,  which is stored in <code>\\Reports\\Master.xlsm.<\/code><\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">\t&#160;\r\n\t#Create report file.\r\n\tcopy-item\"$dir\\Reports\\Master.xlsm\"-Destination$report-force\r\n\t&#160;\r\n\t#Open report file.\r\n\t$excel=new-object-comobjectexcel.application\r\n\t$excel.Visible=$False\r\n\t$workbook=$excel.workbooks.open($report)&#160;&#160; \r\n\t$worksheet=$workbook.worksheets.item(1)\r\n\t&#160;\r\n\t#Run import macro.\r\n\t$excel.Run(\"loader\")\r\n\t&#160;\r\n\t#Run formatting macro.\r\n\t$excel.Run(\"FormatWorksheets\")\r\n<\/pre>\n<p class=\"caption\">Listing 4<\/p>\n<p class=\"filelist\"><strong>Master.xlsm<\/strong><code>-<\/code> This contains the VBA code that imports  the CSV files and formats the report. It contains five subroutines:<\/p>\n<ul>\n<li><code>Loader <\/code>&#8211; contains a loop that calls the CSV import subroutine.<\/li>\n<li><code>ImportCSV<\/code> &#8211; imports the CSV files and renames the worksheets.<\/li>\n<li><code>FormatWorkSheets<\/code> &#8211; adjusts column widths and aligns the text.<\/li>\n<li><code>SelectAllMakeTable<\/code> &#8211; formats the data as tables. <\/li>\n<li><code>SortWorksheets<\/code> &#8211; sorts worksheets into alphabetical order.  \t\t<\/li>\n<\/ul>\n<p><b>HealthCheck.bat<\/b> &#8211; This is the executable that creates the report. It loops through the list of  servers and databases in DBlist.txt and passes these names as parameters to the  PowerShell script.<\/p>\n<p> <b> DBList.txt<\/b> &#8211; Put the names of the target servers and databases in this file. Use a  semi-colon as a delimiter, for example:<\/p>\n<ul>\n<li> \t\tFor servers that don&#8217;t have the SQL Browser service  enabled, you can specify the port number delimited by a back tick and a comma,  as follows:\n<ul>\n<li><code>Server1`,1433; DB1<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p> \t<b>ErrorLog.txt<\/b> &#8211; The PowerShell script appends errors to this file.<\/p>\n<h2>Conclusion<\/h2>\n<p> I hope you find this tool useful in evaluating and  troubleshooting the performance of your SQL Servers. I tried to create something  that would quickly provide a detailed health check, while also being  customizable. There are hundreds of more checks that you could include in the  report but my work here is done: What I&#8217;ve done works for me. Now it&#8217;s your  turn!<\/p>\n<div class=\"note\">\n<p class=\"note\">Note: The latest source to the <a href=\"https:\/\/github.com\/SpeedySQL\/HealthCheck\">SQL Healthcheck utility<\/a> is on GitHub.<\/p>\n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>PowerShell is an ideal tool for doing health checks of a collection of SQL Server instances, and there are several examples around, but few acknowledge the fact that individual DBAs have their own priorities for tests, and need something easily changed to suit circumstances. Omid&#8217;s health check allows tests to be SQL or PowerShell and requires only adding, altering or deleting files in directories. &hellip;<\/p>\n","protected":false},"author":147763,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527],"tags":[4170,4635,4150,4151],"coauthors":[11307],"class_list":["post-2111","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","tag-database-administration","tag-powershell","tag-sql","tag-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2111","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/147763"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=2111"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2111\/revisions"}],"predecessor-version":[{"id":92208,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2111\/revisions\/92208"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=2111"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=2111"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=2111"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=2111"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}