{ "metadata": { "kernelspec": { "name": "powershell", "display_name": "PowerShell" }, "language_info": { "name": "powershell", "codemirror_mode": "shell", "mimetype": "text/x-sh", "file_extension": ".ps1" } }, "nbformat_minor": 2, "nbformat": 4, "cells": [ { "cell_type": "markdown", "source": [ "# SQL Assessment API Tutorial\n", "You can use this tutorial to understand how to assess your SQL Server configuration for best practices. In this tutorial, you will learn:\n", "\n", "1. How to install PowerShell SqlServer module that includes SQL Assessment API cmdlets.\n", "2. How to assess your SQL Server and databases\n", "3. How to save results in a sql table and graph over results\n", "4. How to customize rules by disabling some rules, adding new ones, and changing thresholds\n", "\n", "Supported products and platforms: SQL Server 2012 and up, both on Windows and Linux. Azure SQL DB Managed Instance. More products to come.\n", "\n", "Microsoft ruleset ([ruleset.json](https://github.com/microsoft/sql-server-samples/blob/master/samples/manage/sql-assessment-api/ruleset.json)) is published on SQL Assessment API GitHub repo and continuously improved.\n", "\n", "Useful links at the bottom of the tutorial." ], "metadata": { "azdata_cell_guid": "0b04834e-defa-4af2-9d95-a8aaa27e5f7b" } }, { "cell_type": "markdown", "source": [ "### Quick primer on cmdlets\r\n", "\r\n", "There are two cmdlets: \r\n", "\r\n", "1. **Get-SqlAssessmentItem** shows a list of available rules for a given object (5 kinds of objects is currently supported as input: Server, RegisteredServer, Database, AvailabilityGroup, Filegroup; the default ruleset contains rules for Server/RegisteredServer and Database only). Every rule has a target that describes what kind of SQL objects this rule applies to: Object Type, Object Name, SQL Server version, SQL Server platform, SQL Server engine edition. So by the availability of a rule, we mean that when you run Get-SqlAssessmentItem or Invoke-SqlAssessment, the API first verifies what rules apply for the given object. \r\n", "\r\n", "2. **Invoke-SqlAssessment** performs an assessment of a passed object and provides the results. It's worthwhile to mention that assessment is invoked for a passed object only, so if you want to assess a SQL Server instance and all its databases, run the cmdlet with the instance object as input and then run it with the databases as input. We'll show you different ways of doing this below." ], "metadata": { "azdata_cell_guid": "424b5be9-3f95-4fa0-b630-b5507a0e5db2" } }, { "cell_type": "markdown", "source": [ "### 1. Setup\r\n", "You need to install PowerShell SqlServer module using the following command. It is a good practice to run Import-Module at the beginning of your session as well. Get-Module will show you the version you have installed. The minimum version you want is 21.1.18206 — it is the version of SqlServer module containing SQL Assessment API GA. You can [read more](https://docs.microsoft.com/sql/powershell/download-sql-server-ps-module) about installing and updating the SqlServer module on docs." ], "metadata": { "azdata_cell_guid": "9575ad0b-daca-48ac-84f1-59431afd1387" } }, { "cell_type": "code", "source": [ "# Uncomment and run Install-Module only the first time \r\n", "# Install-Module -Name SqlServer -AllowClobber -Force\r\n", "Import-Module -Name SqlServer\r\n", "Get-Module" ], "metadata": { "azdata_cell_guid": "bf6a34ad-a006-4b24-858d-5d08b760b231" }, "outputs": [], "execution_count": 2 }, { "cell_type": "markdown", "source": [ "### 2. Invoke an assessment for SQL Server instance\r\n", "There are various ways to run the assessment cmdlet. The following statements give recommendations for a local default instance. Pick whatever style works for your script.\r\n", "\r\n", "Server and RegisteredServer objects are interchangeable, so you can pass any to the SQL Assessment cmdlets to assess a SQL Server instance." ], "metadata": { "azdata_cell_guid": "a34fa4e1-bf4e-44c9-9274-3fca29a6c535" } }, { "cell_type": "code", "source": [ "# Option 1\r\n", "Get-SqlInstance -ServerInstance 'localhost' | Invoke-SqlAssessment" ], "metadata": { "azdata_cell_guid": "07bf073e-9268-4e6b-b142-9c9fa8dc78ca" }, "outputs": [], "execution_count": 6 }, { "cell_type": "code", "source": [ "# Option 2\r\n", "$serverInstance = Get-SqlInstance -ServerInstance 'localhost'\r\n", "Invoke-SqlAssessment $serverInstance" ], "metadata": { "azdata_cell_guid": "5f30b889-22cb-4f15-9729-adc555fafd8f" }, "outputs": [], "execution_count": 8 }, { "cell_type": "code", "source": [ "# Option 3\r\n", "Get-Item SQLSERVER:\\SQL\\localhost\\default | Invoke-SqlAssessment" ], "metadata": { "azdata_cell_guid": "5e69dad3-0501-4501-bb7d-656fbac1d90e" }, "outputs": [], "execution_count": 10 }, { "cell_type": "code", "source": [ "# Option 4\r\n", "Invoke-SqlAssessment SQLSERVER:\\SQL\\localhost\\default" ], "metadata": { "azdata_cell_guid": "f9241df5-8945-453b-90e1-5e724c76cf89" }, "outputs": [], "execution_count": 12 }, { "cell_type": "code", "source": [ "# Option 5\r\n", "cd SQLSERVER:\\SQL\\localhost\\default\r\n", "Invoke-SqlAssessment -Verbose" ], "metadata": { "azdata_cell_guid": "e0e5d1bc-c5ec-4952-a658-e01d9d51f5d6" }, "outputs": [], "execution_count": 14 }, { "cell_type": "code", "source": [ "# Option 6\r\n", "cd SQLSERVER:\\SQL\\localhost\r\n", "Get-Item default | Invoke-SqlAssessment" ], "metadata": { "azdata_cell_guid": "4a5cbed3-b75c-411b-89bf-9cbd06656dd4" }, "outputs": [], "execution_count": 16 }, { "cell_type": "code", "source": [ "# Option 7 - passing registered servers to the cmdlet\r\n", "Get-ChildItem 'SQLSERVER:\\SQLRegistration\\Database Engine Server Group' | WHERE { $_.Mode -ne 'D'} | Invoke-SqlAssessment" ], "metadata": { "azdata_cell_guid": "dc8b69d6-6d20-4115-a68a-f6abd39d6c4a" }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "### 3. Invoke an assessment for SQL Server database\r\n", "You need to run Invoke-SqlAssessment against a database object to get database specific recommendations. Again, there are various ways of accomplishing this. Below are some examples." ], "metadata": { "azdata_cell_guid": "d5253a77-1c77-45d4-9d96-552024662463" } }, { "cell_type": "code", "source": [ "# Option 1\r\n", "$database = Get-SqlDatabase -ServerInstance 'localhost' -Name master\r\n", "Invoke-SqlAssessment $database -Verbose" ], "metadata": { "azdata_cell_guid": "50d4d023-fbda-4b9d-8263-9aeab26759e7" }, "outputs": [], "execution_count": 18 }, { "cell_type": "code", "source": [ "# Option 2\r\n", "Invoke-SqlAssessment SQLSERVER:\\SQL\\localhost\\default\\Databases\\master -Verbose" ], "metadata": { "azdata_cell_guid": "ad2f5c1b-3d94-4939-b2bb-f51e6b140924" }, "outputs": [], "execution_count": 20 }, { "cell_type": "code", "source": [ "# Option 3\r\n", "cd SQLSERVER:\\SQL\\localhost\\default\\Databases\\master\r\n", "Invoke-SqlAssessment" ], "metadata": { "azdata_cell_guid": "4677c0f0-8d75-404f-8add-b484394d16a4" }, "outputs": [], "execution_count": 22 }, { "cell_type": "code", "source": [ "# Get recommendations for all databases on local instance:\r\n", "Get-SqlDatabase -ServerInstance 'localhost' | Invoke-SqlAssessment" ], "metadata": { "azdata_cell_guid": "b374c2ec-8d2e-4c35-a48a-c427dec9c21c", "tags": [] }, "outputs": [], "execution_count": 26 }, { "cell_type": "markdown", "source": [ "### 4. Browse applicable rules\r\n", "\r\n", "The full Microsoft ruleset is in [ruleset.json](https://github.com/microsoft/sql-server-samples/blob/master/samples/manage/sql-assessment-api/ruleset.json) in the GitHub repo. If you want to list the rules that apply to a particular instance or database, you can use Get-SqlAssessmentItem cmdlet. Below are some different ways of listing the rules." ], "metadata": { "azdata_cell_guid": "97414396-2557-44c4-80e2-5afa6a5a5516" } }, { "cell_type": "code", "source": [ "# Get all rules available for an object:\r\n", "$serverInstance = Get-SqlInstance -ServerInstance 'localhost'\r\n", "Get-SqlAssessmentItem $serverInstance | Select Id, Description" ], "metadata": { "azdata_cell_guid": "56b4a035-658b-4bc4-8870-90823b8c2549" }, "outputs": [], "execution_count": 28 }, { "cell_type": "code", "source": [ "# Get all rules by a specific tag\r\n", "$serverInstance = Get-SqlInstance -ServerInstance 'localhost'\r\n", "Get-SqlAssessmentItem $serverInstance -Check TraceFlag" ], "metadata": { "azdata_cell_guid": "b1274ddb-b9f7-4bb0-bb69-4b8c403f0b6a" }, "outputs": [], "execution_count": 30 }, { "cell_type": "markdown", "source": [ "### 5. Run a specific rule\r\n", "If you want to check a particular rule (maybe after you fixed it), you can run it by its name. You can also specify several rules in the -Check parameter, just delimit them by commas.\r\n", "\r\n", "Every rule in the default ruleset has tags to group them into logical sets. In the example below, we look for backup related issues only. Backup value used for the -Check parameter is a tag. You can use both rule names and tags at the same time in a comma delimited list. " ], "metadata": { "azdata_cell_guid": "0267d880-c35e-4fbf-90db-dd9120194da9" } }, { "cell_type": "code", "source": [ "# Run a rule by its name\r\n", "$serverInstance = Get-SqlInstance -ServerInstance 'localhost'\r\n", "Invoke-SqlAssessment $serverInstance -Check TF634" ], "metadata": { "azdata_cell_guid": "02638a85-ca44-4e34-88fa-e8eea85f4304" }, "outputs": [], "execution_count": 32 }, { "cell_type": "code", "source": [ "# Run a group of rules using their tag\r\n", "$database = Get-SqlDatabase -ServerInstance 'localhost' \r\n", "Invoke-SqlAssessment $database -Check Backup" ], "metadata": { "azdata_cell_guid": "09c8991d-1b92-4630-9a87-5361f04e2c36" }, "outputs": [], "execution_count": 36 }, { "cell_type": "markdown", "source": [ "### 6. Store results in a table\r\n", "You probably want to save the results of an assessment to analyze and process later on. You can pipe the results of Invoke-SqlAssessment cmdlet into a table using Write-SqlTableData cmdlet. If the table doesn't exist, it creates the table and then inserts the results. If the table exists (subsequent runs), it appends the results to the table. Just remember to use -FlattenOutput parameter as it makes the Invoke-SqlAssessment output sutiable for Write-SqlTableData." ], "metadata": { "azdata_cell_guid": "289f9e4a-29c4-4da3-a52d-71b95ddaf3df" } }, { "cell_type": "code", "source": [ "Get-SqlInstance -ServerInstance 'localhost' | Invoke-SqlAssessment -FlattenOutput |\r\n", "Write-SqlTableData -ServerInstance 'localhost' -DatabaseName SQLAssessmentDemo -SchemaName Assessment -TableName Results -Force" ], "metadata": { "azdata_cell_guid": "695f924d-0729-4ebb-ba83-bba43ed8a9c7" }, "outputs": [], "execution_count": 38 }, { "cell_type": "markdown", "source": [ "### 7. Scaling up your Checks\r\n", "Running checks across multiple machines and writng their results back to the same table in SQL Server. You can keep you list of SQL Servers to check anywhere you want, in a text file, in an Excel spreadsheet, in a table you maintain yourself, the options are endless.\r\n", "\r\n", "Two options are shown below show features availabile that can be managed from SSMS or Azure Data Studio, and can accessed via the `SQLSERVER:\\` Provider, which is part of the `SqlServer` module.\r\n", "\r\n", "1. Registered Servers is feature of SSMS and stores a list of SQL Server instances in a local XML file.\r\n", "2. Central Management Server relise on a SQL Server to maintain the list of SQL Server instances (instead of a _local XML file_) and is available in both SSMS & Azure Data Studio." ], "metadata": { "azdata_cell_guid": "0ad9518f-e770-4819-b4d1-7f5608c793b3" } }, { "cell_type": "code", "source": [ "# This approach leverages the Registered Servers feature of SSMS to obtain a list of SQL Servers, and run checks against them.\r\n", "Get-ChildItem 'SQLSERVER:\\SQLRegistration\\Database Engine Server Group' | \r\n", "Where-Object { $_.mode -ne 'd'} | \r\n", "Invoke-SqlAssessment -FlattenOutput |\r\n", "Write-SqlTableData -ServerInstance localhost -DatabaseName SQLAssessmentDemo -SchemaName Assessment -TableName Results -Force" ], "metadata": { "azdata_cell_guid": "3a0c510d-07c7-464b-8b0a-2152df24e237" }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "<# This approach leverages the Central Management Server feature to obtain a list of SQL Servers #>\r\n", "Get-ChildItem 'SQLSERVER:\\SQLRegistration\\Central Management Server Group' -Recurse | \r\n", "Where-Object { $_.mode -ne 'd'} | \r\n", "Invoke-SqlAssessment -FlattenOutput |\r\n", "Write-SqlTableData -ServerInstance localhost -DatabaseName SQLAssessmentDemo -SchemaName Assessment -TableName Results -Force" ], "metadata": { "azdata_cell_guid": "c6ca3196-b661-46b4-9818-502c779742ab" }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Customization\r\n", "In this section you will learn how to customize existing rules and create new ones.\r\n", "\r\n", "As a prerequisite, make sure to grab the JSON files in the CustomizationSamples folder and place them in an accessible path and then edit the first script below to point at the right path and server instance for your environment. By default, we use in this notebook the following parameters:\r\n", "- SQL Instance to assess is \"localhost\"\r\n", "- JSON samples and DLLs are available by path \"C:\\SQLAsmnt\\CustomizationSamples\\\"\r\n", "\r\n", "The final code block in this notebook has its own prerequisites, please complete them prior to running it:\r\n", "- There are 2 dlls in CustomizationSamples folder. They both should be unblocked: https://stackoverflow.com/questions/34400546/could-not-load-file-or-assembly-operation-is-not-supported-exception-from-hres/45221477\r\n", "- Then open CustomRuleCLRProbe.json and make sure that assembly key contains the right path to TestsProbeLibrary.dll, double backslashes are required.\r\n", "\r\n", "We encourage you to look into every JSON sample so you can understand better the making of customizations for SQL Assessment.\r\n", "" ], "metadata": { "azdata_cell_guid": "3cfbe027-21f0-40be-b6f0-b871360aff5d" } }, { "cell_type": "markdown", "source": [ "### Disabling/Enabling rules" ], "metadata": { "azdata_cell_guid": "88318608-c678-4692-8315-c205933331cf" } }, { "cell_type": "code", "source": [ "#Setup three parameters that are used in all the customization examples below\r\n", "#$samplesPath='' \r\n", "$samplesPath='C:\\SQLAsmnt\\CustomizationSamples'\r\n", "$samplesPath\r\n", "\r\n", "#$serverInstance = Get-SqlInstance -ServerInstance 'localhost'\r\n", "$serverInstance = Get-SqlInstance -ServerInstance '.\\sql2017express'\r\n", "$serverInstance\r\n", "\r\n", "$sqlDbMaster = $serverInstance | Get-SqlDatabase -Name master\r\n", "$sqlDbMaster" ], "metadata": { "azdata_cell_guid": "5df878e7-8204-45c8-8bb2-4861131f3588", "tags": [] }, "outputs": [], "execution_count": 2 }, { "cell_type": "code", "source": [ "# Disable a single rule using its ID (TF634)\r\n", "# To see this in action, make sure you have trace flag 634 turned on in the instance you are testing. Otherwise this rule will not fire even when enabled.\r\n", "# You will see that TF634 is not enabled (On=False)\r\n", "Get-SqlAssessmentItem $serverInstance -Configuration $(join-path $samplesPath \"DisableTF634.json\")" ], "metadata": { "azdata_cell_guid": "7a2a90f0-8341-4cd8-81a3-1fdedbd1adb6" }, "outputs": [], "execution_count": 4 }, { "cell_type": "code", "source": [ "# Disable all Trace Flag rules using a tag\r\n", "# You will see that all TF rules are set to False (disabled)\r\n", "Get-SqlAssessmentItem $serverInstance -Configuration $(join-path $samplesPath \"DisableAllTF.json\")" ], "metadata": { "azdata_cell_guid": "35ee6731-70dd-42b9-b041-f3b09ac02a4d", "tags": [] }, "outputs": [], "execution_count": 6 }, { "cell_type": "code", "source": [ "# Combine configurations\r\n", "# This example disables all trace flag rules except for performance-related ones using tags. \r\n", "# The order of json files is important. First we disable all TF rules, then enable performance rules which re-enables performance-related TF rules.\r\n", "Get-SqlAssessmentItem $serverInstance -Configuration $(join-path $samplesPath \"DisableAllTF.json\"), $(join-path $samplesPath \"EnablePerformance.json\")" ], "metadata": { "azdata_cell_guid": "4f79d729-4a3b-47a6-8cb3-d4a5b0c2db05" }, "outputs": [], "execution_count": 8 }, { "cell_type": "markdown", "source": [ "### Creating a new rule\r\n", "The rules are defined in json files. In this example, we are creating a rule that checks for available database space. Go ahead and examine CustomRuleTSQLProbe.json.\r\n", "\r\n", "A rule has many components such as which ruleset it belongs to, what type of objects, editions, versions, platforms it targets as well as more obvious components such as id, name, description, etc. \r\n", "\r\n", "Condition is what gets evaluated. When an expression in Condition returns false, it means that the rule is violated and the user gets a recommendation from this rule. \r\n", "\r\n", "Probe is what gets the data to be evaluated in the condition. Probes can be SQL or CLR. SQL probe is a T-SQL query to pull the required data right out of SQL Server. \r\n", "CLR probe is a reference to a .NET or Core assembly with a call to a method inside the library. " ], "metadata": { "azdata_cell_guid": "0d79fec1-d64d-4580-abfa-0c367a6f20f8" } }, { "cell_type": "code", "source": [ "# Create a new rule with TSQL probe\r\n", "# This rule applies to databases and uses a TSQL statement to get the data for the rule. \r\n", "Invoke-SqlAssessment $sqlDbMaster -configuration $(join-path $samplesPath \"CustomRuleTSQLProbe.json\")" ], "metadata": { "azdata_cell_guid": "1df3379f-e5fe-47ea-a43e-920265f3d0e2", "tags": [] }, "outputs": [], "execution_count": 10 }, { "cell_type": "code", "source": [ "# Override threshold parameter\r\n", "# CustomRuleThresholdChange.json defines a new threshold value for DBSpaceAvailable rule created above\r\n", "Invoke-SqlAssessment $sqlDbMaster -configuration $(join-path $samplesPath \"CustomRuleTSQLProbe.json\"),$(join-path $samplesPath \"CustomRuleThresholdChange.json\")" ], "metadata": { "azdata_cell_guid": "953cd87b-4311-4553-9170-e219c231642a" }, "outputs": [], "execution_count": 13 }, { "cell_type": "markdown", "source": [ "## Probe types\r\n", "### CmdShell\r\n", "Create a new rule with CmdShell probe. CmdShell probe executes a CMD.EXE shell command and returns lines of text in variable @stdout. Use 'CMDSHELL' instead of 'SQL' in probe definition to load a .cmd file. Use Regex parser transformation to extract data from @stdout\r\n", "" ], "metadata": { "azdata_cell_guid": "86aed978-b91a-47f0-8a78-f8254c5ec6f1" } }, { "cell_type": "code", "source": [ "#Create new rule with cmd probe type. It runs 'dir' cmd command and checks that resulted list is'n empty.\r\n", "#Make sure that xp_cmdshell is enabled\r\n", "Invoke-SqlAssessment $serverInstance -configuration $(join-path $samplesPath \"CustomRuleCmdShellProbe.json\")" ], "metadata": { "azdata_cell_guid": "a75ebf02-e27e-4d19-b306-ce4e04481acd" }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "### PowerShell\r\n", "\r\n", "PowerShell probe executes a command in PowerShell on target machine and returns pipeline output in @Output variable.\r\n", "Use $ (dollar) sign to access probe parameters passed from checks.\r\n", "Use . (dot) to access properties of the output object. For example, if a returned object is string, then @Output.Length returns its length." ], "metadata": { "azdata_cell_guid": "4450f005-b008-4464-8b83-059b5d3fa8f6" } }, { "cell_type": "code", "source": [ "#Create new rule with Powershell probe type.\r\n", "#It runs query to get major PS version\r\n", "#Make sure that xp_cmdshell is enabled and PS execution policy is RemoteSigned or Unrestricted.\r\n", "Invoke-SqlAssessment $serverInstance -configuration $(join-path $samplesPath \"CustomRulePowerShellProbe.json\")" ], "metadata": { "azdata_cell_guid": "0be17782-0051-4d49-931e-a29aaca34d02" }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "### Registry\r\n", "\r\n", "Registry probe obtains data from target machine's registry. The key name will be returned in @RegistryKeyName. Use * (asterisk) symbol to enumerate all keys." ], "metadata": { "azdata_cell_guid": "9277b49b-7f19-4d9b-8e37-d3592962c03f" } }, { "cell_type": "code", "source": [ "#Create new rule with Registry probe\r\n", "#Make sure that xp_cmdshell is enabled\r\n", "Invoke-SqlAssessment $serverInstance -configuration $(join-path $samplesPath \"CustomRuleRegistryProbe.json\")" ], "metadata": { "azdata_cell_guid": "6a100496-4356-4e15-b857-4cdf44177010", "tags": [] }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "### WMI\r\n", "\r\n", "WMI probe runs a WMI query and returns results in @Output variable in the same way as a PowerShell probe does.\r\n", "Use $ (dollar) sign to access probe parameters passed from checks." ], "metadata": { "azdata_cell_guid": "3a03fb80-361d-4b41-9a52-a2eedd1e85a0" } }, { "cell_type": "code", "source": [ "#Create new rule with WMI probe\r\n", "#Make sure that xp_cmdshell is enabled.\r\n", "Invoke-SqlAssessment $serverInstance -configuration $(join-path $samplesPath \"CustomRuleWmiProbe.json\")" ], "metadata": { "azdata_cell_guid": "143eea26-62d1-40e8-a97a-9f1ed8ac2532" }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "### Managed code probe\r\n", "\r\n", "For CLR probe use \"External\" probe type." ], "metadata": { "azdata_cell_guid": "815584d6-ceac-4160-ba9a-020c59e14221" } }, { "cell_type": "code", "source": [ "# Create a new rule with CLR probe. CustomRuleCLRProbe.json, in addition to a check with a CLR probe, contains an ovveride to disable all the rules of the DefaultRuleset.\r\n", "# !!! Complete the prerequisites below before running this block.\r\n", "# !!! There are 2 dlls in CustomizationSamples folder. Make sure that they both are not blocked: https://stackoverflow.com/questions/34400546/could-not-load-file-or-assembly-operation-is-not-supported-exception-from-hres/45221477\r\n", "# !!! Then open CustomRuleCLRProbe.json in the same folder and make sure that assembly key contains the right path to TestsProbeLibrary.dll, double slashes are required.\r\n", "# !!! You're all set. Run this block\r\n", "Invoke-SqlAssessment $serverInstance -configuration $(join-path $samplesPath \"CustomRuleCLRProbe.json\")" ], "metadata": { "azdata_cell_guid": "bcff0e69-65e8-4b44-90b8-83f1241589bd" }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Useful links about SQL Assessment API\r\n", "\r\n", "- [Docs online page](https://docs.microsoft.com/sql/sql-assessment-api/sql-assessment-api-overview)\r\n", "- [GitHub repo](http://aka.ms/sql-assessment-api)\r\n", "- [SQL Server blog with release announcements and other useful information](https://techcommunity.microsoft.com/t5/SQL-Server/bg-p/SQLServer) " ], "metadata": { "azdata_cell_guid": "2c572e58-0281-432f-b400-70b55b719b7e" } } ] }