{"id":86235,"date":"2019-08-21T03:00:51","date_gmt":"2019-08-21T11:00:51","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/scripting\/?p=86235"},"modified":"2019-08-22T05:55:39","modified_gmt":"2019-08-22T13:55:39","slug":"executing-sql-server-assessments-from-powershell","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/executing-sql-server-assessments-from-powershell\/","title":{"rendered":"Executing SQL Server Assessments from PowerShell"},"content":{"rendered":"<p><strong>Summary<\/strong>: Using the SQLServer module cmdlets to review and monitor SQL Server instance and database configuration<\/p>\n<p>Q: Hey, Doctor Scripto!<\/p>\n<p>I have loads of SQL Servers in my area of responsibility and I know they all need certain configuration settings but I&#8217;m never confident that they are all set just right so I spend a long time every month visiting them all over RDP to give reassure myself. How can I automate this work?<\/p>\n<p>\u2014AB<\/p>\n<p>A: Hello AB, I know the very person that can answer that question for you. It&#8217;s my good friend Jonathan Allen. Let me introduce him to you.<\/p>\n<p>He&#8217;s a SQL Server PFE from the UK. He also speaks at and attends PowerShell and Data and AI conferences in the UK and Europe.<\/p>\n<p>As a side note \u2013 He help runs the SQLBits conference once a year. Take it away Jonathan!<\/p>\n<p>Thanks, Doctor Scripto!<\/p>\n<p>To solve this challenge you need to take a look at SQL Server Assessments, the latest thing in the SQL Server PowerShell module \u2013 available <a href=\"https:\/\/www.powershellgallery.com\/packages\/SqlServer\">here on the PowerShell Gallery<\/a>.<\/p>\n<h4>How do we get the module?<\/h4>\n<p>As the SQLServer module is published on the PowerShell Gallery it is available right at your command line. Check if you have SQLServer already available with this command and check the Version information in the results<\/p>\n<p><strong>Get-Module SQLServer <\/strong><\/p>\n<p>If you don\u2019t see any results or you see any version older than 21.1.18147 then you need to run<\/p>\n<p><strong># get the latest (possibly prerelease) version of SQLServer module\nUpdate-Module sqlserver -RequiredVersion 21.1.18147 -Scope CurrentUser<\/strong><\/p>\n<p>(We use -Scope CurrentUser to avoid having to run this as Administrator)<\/p>\n<p>Let&#8217;s check that we have the two commands we need by listing the commands in the module with<\/p>\n<p><strong>Get-Command -Module SqlServer -Name *sqlassessment*<\/strong><\/p>\n<p>This should give us the results of:<\/p>\n<table>\n<tbody>\n<tr>\n<td>CommandType<\/td>\n<td>Name<\/td>\n<td>Version<\/td>\n<td>Source<\/td>\n<\/tr>\n<tr>\n<td>&#8212;&#8212;&#8212;-<\/td>\n<td>&#8212;-<\/td>\n<td>&#8212;&#8212;-<\/td>\n<td>&#8212;&#8212;<\/td>\n<\/tr>\n<tr>\n<td>Cmdlet<\/td>\n<td>Get-SqlAssessmentItem<\/td>\n<td>21.1.18147<\/td>\n<td>SqlServer<\/td>\n<\/tr>\n<tr>\n<td>Cmdlet<\/td>\n<td>Invoke-SqlAssessment<\/td>\n<td>21.1.18147<\/td>\n<td>SqlServer<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Now that we have the cmdlets we need, let&#8217;s take a look at what they do, first of all let&#8217;s see the what Get-SQLAssessmentItem does. I have a SQL Server 2014 test instance on my laptop so I&#8217;ll use that<\/p>\n<p><strong># see what can be in the instance assessment\nset-location SQLSERVER:\\SQL\\localhost\\sql2014\nGet-SqlAssessmentItem <\/strong><\/p>\n<p>We get 45 results that point to instance level features such as TraceFlags usage, Deprecated Features, Extended Events sessions, update status, plan use and more.<\/p>\n<p>We can see some of the assessments done on databases by running<\/p>\n<p><strong># see what can be in the database assessment\nSet-Location SQLSERVER:\\SQL\\localhost\\sql2014\\databases\nGet-SqlDatabase | Get-SqlAssessmentItem | Group-Object<\/strong><\/p>\n<p>Here we see properties relating to Statistics, Query Store, Indexes, Constraints etc.<\/p>\n<h4>So, how do we actually run an assessment then?<\/h4>\n<p>Still working against the test instance on my laptop, we can run an assessment with this command<\/p>\n<p><strong># running an assessment\n$SQLInstance = &#8220;$ENV:COMPUTERNAME\\SQL2014&#8221;\nGet-SqlInstance -ServerInstance $SQLInstance | Invoke-SqlAssessment<\/strong><\/p>\n<p>This will give us a lot of results (at least my test instance did!) but they all share the same properties -we have CheckId, CheckName, HelpLink, Message, Severity, TargetPath, TargetType. Here&#8217;s one result with example values for each property<\/p>\n<table>\n<tbody>\n<tr>\n<td>CheckId<\/td>\n<td>SqlServer.Server.PlansUseRatio<\/td>\n<\/tr>\n<tr>\n<td>CheckName<\/td>\n<td>Amount of single use plans in cache is high<\/td>\n<\/tr>\n<tr>\n<td>HelpLink<\/td>\n<td>https\/\/docs.microsoft.com\/sql\/database-engine\/configure-windows\/optimize-for-ad-hoc-workloads-server-configuration-option<\/td>\n<\/tr>\n<tr>\n<td>Message<\/td>\n<td>Amount of single use plans in cache is high (66%). Consider enabling the Optimize for ad hoc workloads setting on heavy OLTP ad-hoc workloads to conserve resources.<\/td>\n<\/tr>\n<tr>\n<td>Severity<\/td>\n<td>Warning<\/td>\n<\/tr>\n<tr>\n<td>TargetPath<\/td>\n<td>Server[@Name=<em>[ComputerName]<\/em>\\SQL2014&#8242;]<\/td>\n<\/tr>\n<tr>\n<td>TargetType<\/td>\n<td>Server<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Now, getting a stream of results on your console from one computer is hard to analyse, let alone if you run this against multiple servers so let\u2019s look at collecting information into a management database from multiple instances. Let&#8217;s create a database on our test server<\/p>\n<p><strong># step 1 &#8211; we need a database\n$qry =\n@&#8217;\ncreate database SQLAssessmentDemo\ngo\n&#8216;@<\/strong><\/p>\n<p><strong>#step 2 &#8211; what instance are we storing the data in\n$SQLManagementInstance = &#8220;$ENV:COMPUTERNAME\\SQL2019&#8221;\nInvoke-Sqlcmd -ServerInstance $SQLManagementInstance -Database &#8216;master&#8217; -Query $qry<\/strong><\/p>\n<p>Now we can use that database to store the output from Write-SQLTableData like this<\/p>\n<p><strong>$SQLManagementInstance = &#8220;$ENV:COMPUTERNAME\\SQL2019&#8221;\nGet-SqlInstance -ServerInstance $SQLInstance |\nInvoke-SqlAssessment |\nWrite-SqlTableData -ServerInstance $SQLManagementInstance -DatabaseName SQLAssessmentDemo -SchemaName Assessment -TableName Results -Force<\/strong><\/p>\n<p><strong>Get-SqlDatabase -ServerInstance $SQLInstance |\nWhere-Object status -eq &#8216;normal&#8217; |\nInvoke-SqlAssessment |\nWrite-SqlTableData -ServerInstance $SQLManagementInstance -DatabaseName SQLAssessmentDemo -SchemaName Assessment -TableName Results -Force<\/strong><\/p>\n<p>This creates a table called Results in the Assessment schema in our SQLAssessmentDemo database and places the results of the Instance assessment and the database assessment in there.<\/p>\n<p>We need to alter the Results table just a little to make it easier to analyse the collected data and keep it as a live record of the SQL Server assessments that we run by adding a CollectionDate column.<\/p>\n<p><strong>$SQLManagementInstance = &#8220;$ENV:COMPUTERNAME\\SQL2019&#8221;\n$qry =\n@&#8217;\nALTER TABLE ASSESSMENT.Results\nADD CollectionDate datetime not null default cast(getdate() as varchar(17))\n&#8216;@<\/strong><\/p>\n<p><strong>Invoke-Sqlcmd -ServerInstance $SQLManagementInstance -Database &#8216;SQLAssessmentDemo&#8217; -Query $qry<\/strong><\/p>\n<p>This makes sure that each time we run an assessment the results are stamped with the date and time that the collection was run and we can then track changes in our servers or make sure that remediation work is progressing as we want it to.<\/p>\n<p>Once that column is added then we can run the assessment against multiple servers as and when we want to with this<\/p>\n<p><strong># multiple server collection\n$SQLManagementInstance = &#8220;$ENV:COMPUTERNAME\\SQL2019&#8221;\n$Instances = @(&#8220;$ENV:COMPUTERNAME\\SQL2014&#8221;, &#8220;$ENV:COMPUTERNAME\\SQL2016&#8221;, &#8220;$ENV:COMPUTERNAME\\SQL2019&#8221;)\nforeach ($SQLInstance in $Instances) {\n#instances\nWrite-Output &#8220;Assessing $SQLInstance&#8221;\nGet-SqlInstance -ServerInstance $SQLInstance |\nInvoke-SqlAssessment |\nWrite-SqlTableData -ServerInstance $SQLManagementInstance -DatabaseName SQLAssessmentDemo -SchemaName Assessment -TableName Results -Force<\/p>\n<p>#databases\nGet-SqlDatabase -ServerInstance $SQLInstance |\nWhere-Object status -eq &#8216;normal&#8217; |\nInvoke-SqlAssessment |\nWrite-SqlTableData -ServerInstance $SQLManagementInstance -DatabaseName SQLAssessmentDemo -SchemaName Assessment -TableName Results -Force\n}<\/strong><\/p>\n<p>Once this runs successfully then we can step into SQL Server Management Studio or PowerBI and start to review \/ analyse our Results table to see where we need to take action first.<\/p>\n<h4>Where can I get more information?<\/h4>\n<p>Introducing SQL Assessment API (Public Preview)\n<a href=\"https:\/\/techcommunity.microsoft.com\/t5\/SQL-Server\/Introducing-SQL-Assessment-API-Public-Preview\/ba-p\/778570\">https:\/\/techcommunity.microsoft.com\/t5\/SQL-Server\/Introducing-SQL-Assessment-API-Public-Preview\/ba-p\/778570<\/a><\/p>\n<p>Github API\n<a href=\"https:\/\/github.com\/microsoft\/sql-server-samples\/tree\/master\/samples\/manage\/sql-assessment-api\">https:\/\/github.com\/microsoft\/sql-server-samples\/tree\/master\/samples\/manage\/sql-assessment-api<\/a><\/p>\n<p>Install SQL Server PowerShell module\n<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/powershell\/download-sql-server-ps-module?view=sql-server-2017\">https:\/\/docs.microsoft.com\/en-us\/sql\/powershell\/download-sql-server-ps-module?view=sql-server-2017<\/a><\/p>\n<p>So, AB that is all there is to starting to use SQL Server Assessment cmdlets to monitor your SQL Server estate.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\">Official Scripting Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p>Your good friend, Doctor Scripto<\/p>\n<p>PowerShell, Doctor Scripto, Jonathan Allen, SQL Server<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Using the SQLServer module cmdlets to review and monitor SQL Server instance and database configuration Q: Hey, Doctor Scripto! I have loads of SQL Servers in my area of responsibility and I know they all need certain configuration settings but I&#8217;m never confident that they are all set just right so I spend a [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1739,1738],"tags":[1740,1854,377,176],"class_list":["post-86235","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-doctor-scripto","category-powershell","tag-doctor-scripto","tag-jonathan-allen","tag-powershell","tag-sql-server"],"acf":[],"blog_post_summary":"<p>Summary: Using the SQLServer module cmdlets to review and monitor SQL Server instance and database configuration Q: Hey, Doctor Scripto! I have loads of SQL Servers in my area of responsibility and I know they all need certain configuration settings but I&#8217;m never confident that they are all set just right so I spend a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/86235","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=86235"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/86235\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=86235"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=86235"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=86235"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}