Update SQL Assessment APi documentation

- Add descriptions for custom rule set concepts
- Addd reference docs for custom rule sets
- Update existing docs for 4-level severity
This commit is contained in:
Aleksei Guzev
2022-09-21 21:40:10 +03:00
parent f87a898006
commit 22b2374fc4
46 changed files with 2216 additions and 0 deletions
@@ -0,0 +1,28 @@
# Data Transformation
Sometimes data needs to go through series of transformations.
When dta comes in an unconvenient format, a transformation can be applied in the probe implementation. For example, the following T-SQL query returns `host_release` as a string like '10.0.19044.2006'.
```SQL
SELECT host_platform, host_release
FROM sys.dm_os_host_info
```
While it is possible to parse the string in T-SQL, a transformation make the code more readable and easier to create and maintain:
```json
"implementation": {
"query": "SELECT host_platform, host_release FROM sys.dm_os_host_info",
"transform": {
"type": "parse",
"map": {
"host_release": "/^(?<major>\\d+)\\.(?<minor>\\d+)"
}
}
}
```
Transformations can be applied in [probe references](./ProbeReference.md) to increase probe reuse. When one check needs an average value for some metric while another best practice involves maximum for the same metric. Both checks can have references to the same probe but each applies its own specific transformation. In this case not only the probe code is reused, but the data is reused as well, because the probe will be called only once.
See [Data transformation reference](../Reference/DataTransformation/README.md) for more details.
@@ -0,0 +1,17 @@
# Local Variables
A check can define local variables available for conditions and message templates. Local variables are any expressions involving literals, probe data, and transformation results.
```json
{
"probes": ["SysDmOsSysInfo"],
"locals": {
"workers": {"sub": [ "@max_workers_count", 1 ] }
},
"message": "Workers = @{workers}.",
"condition": {
"lt": [ 0, "@workers" ],
"lt": [ "@workers", 4 ]
}
}
```
@@ -0,0 +1,42 @@
# Probe
A *Probe* is a JSON property. The property name is the probe ID used in checks. The property value is an array of [probe implementations](#probe-implementation). The SQL Assessment API engine selects the first implementation with matching target pattern. This means that the order of the implementations in the array is important. A probe can be comprised by a mix of CLR and SQL implementations.
Another ruleset may add probe implementations on top of the list.
A probe should be designed as a function with no side effects. The order of calling probes is not determined. The engine may reorder probe calls to optimize the target SQL Server load. When no check needs data from a probe, that probe will not be called.
Probes from the default rule set read metadata only, e.g. update logs or server properties. They do not read user data from tables or write anything to databases or instances. Probes do not set any flags or properties.
## Probe implementation
Probe implementation is represented by a JSON object. Its properties define procedures for getting data and selecting appropriate implementation.
![Probe structure](img\ProbeStructure.svg)
## Probe properties
### implementation
The `implementation` property contains probe parameters and [data transformations](DataTransformation.md) affecting the probe output. For example, for a T-SQL probe the main parameter is the query for selecting data.
Probe parameters are specific for [probe type](#type).
### target
Target object [pattern](TargetPattern.md).
### type
Probe `type` determines the mechanism used to get data. it may be a T-SQL or a WMI. Available probe types are listed in the following table.
|Type|Description|
|---|---|
|[AzGraph](../Reference/Probes/AzGraphProbe.md)|Kusto query to Azure resource graph|
|[AzMetadata](../Reference/Probes/AzMetadataProbe.md)|JSONPath for the object returned by Azure Instance Metadata Service|
|[CMD](../Reference/Probes/CMDShellProbes.md)|Command shell script run on the target machine|
|[External](../Reference//Probes/ExternalProbe.md)|Arbitrary .NET code|
|[PowerShell](../Reference/Probes/PowerShellProbes.md)|PowerShell script|
|[Registry](../Reference/Probes/RegistryProbes.md)|Data from registry|
|[SQL](../Reference/Probes/TSQLProbes.md)|T-SQl query|
|[WMI](../Reference/Probes/WMIProbes.md)|WMI query|
@@ -0,0 +1,102 @@
# Probe reference
Data for checks comes from probes. Probe reference is a JSON object that describes a probe needed by the check. When a probe is referenced with no additional options or parameters, a probe reference may be abbreviated to a string containing probe ID.
![Probe reference structure](img\ProbeRefStructure.svg)
## Properties
### alias
An alternative name used for this probe.
Alias is an alternative name for the probe which can be used in expressions in this check. It is useful in two scenarios: [calling a probe multiple times](#calling-probe-multiple-times) and [passing data from one probe to another one](#using-data-from-another-probe).
### id
Referenced probe ID.
### params
Parameters passed to the probe called for this check. They are represented as properties of a JSON object. Property name is the parameter name while property value can be any [expression](Expression.md). Expression can include constants, global and local variables, and data returned by other probes (see [alias](#alias)).
### transform
Data transformation applied to the data before calculation the condition value, see [Data transformation](DataTransformation.md).
## Calling probe multiple times
For some checks you may need to call a probe multiple times with different parameters. In this case add multiple probe references with the same `id`. To distinguish results returned for different parameters prepend the output variable name with probe alias and double colon '::'.
The following check compares f4ree space on disks C: and D:.
```json
"probes": [
{
"type": "DiskInfo",
"alias": "FirstDisk",
"params": {
"DiskName": "C:"
}
},
{
"type": "DiskInfo",
"alias": "SecondDisk",
"params": {
"DiskName": "D:"
}
}
],
"condition": {
"greater": [
"@FirstDisk::FreeSpace",
"@SecondDisk::FreeSpace"
]
}
```
## Using data from another probe
Data returned by one probe can be passed to another one as a parameter. The expression for the parameter should contain the output variable name prefixed with the previous probe id or alias.
In the following example *DatabaseMasterFiles* probe finds volume IDs for all disks used by the target database. The *AzStorage* probe is called for each volume ID and adds Azure related storage properties along with volume ID.
```json
"probes": [
{
"id": "DatabaseMasterFiles",
"alias": "db_files",
"params": {
"dbId": null,
"type": null
},
"transform": [
{
"type": "aggregate",
"group": [
"volume_mount_point",
"volume_id"
]
},
{
"type": "aggregate",
"group": "volume_id",
"map": {
"volume_mount_point": "join"
}
}
]
},
{
"id": "AzStorage",
"params": {
"path": "@db_files::volume_id"
}
}
]
```
@@ -0,0 +1,12 @@
# Customization
The SQL Assessment API is a comprehensive solution that allows you not only to use the functionality that comes out of the box, but also customize the assessment workflows, depending on your needs.
## In This Section
- [Understanding rules and probes](RulesandProbes.md)
- [Rule](Rule.md)
- [Probe reference](ProbeReference.md)
- [Probe](Probe.md)
- [Data Transformation](DataTransformation.md)
- [Local Variables](LocalVariables.md)
@@ -0,0 +1,65 @@
# SQL Assessment API Rule
A rule is represented by a JSON object. A rule applies its properties to a new or existing check.
![Rule structure](img\RuleStructure.svg)
## Rule properties
### itemType
__Allowed values:__ *definition*, *override*
This property specifies whether this rule defines a new check or modifies an existing one. A check can be modified by more than one rule.
### tags
An array of short strings used to categorize checks. For example, checks marked with the "Memory" tag are coming from memory-related best practices. Single-word tags work best.
### id
An ID of the check to be defined or modified by this rule. A check can be modified by more than one rule.
An array of IDs or tags of checks to be modified by this rule. A rule can modify multiple checks at once. Checks are selected either by ID or by tag. If multiple tags are specified, every check having _any_ listed tag will be affected by this rule.
### target
Target pattern is a JSON object used to select applicable rules. A rule will be applied if the assessed object matches the `target` pattern. For more information, see [Target pattern]().
### targetFilter
Target pattern with the same syntax as the target property, but used exclusively in overrides. For more information on the usage and object format, see [Overrides]() and [Target pattern](), respectively.
### displayName
Short display names are shown in checklists. You may think of a display name as of a file name if a rule was stored in a separate file. Usually, the display name tells what the check is looking for.
### message
A string which is used as a template for generating messages to the user. Such a message appears when the check detects configurations that are not in compliance with the best practice recommendation. For more information, see [Message template]().
### description
Long description that explains why the check looks for this particular issue. It briefly discusses the impact of the detected issue.
### helpLink
Hyperlink to an article that explains the best practice recommendations and remediation suggestions.
### level
__Allowed values:__ `Information`, `Low`, `Medium`, 'High'
The severity level of the issue detected by this check.
### probes
The `probes` array contains references to probes to be used to get data required by this check. For more information, see [Probe References]().
### condition
Condition is an expression in form of JSON object tree. It uses check parameters and data from probes This expression should return _true_ when the best practice found implemented. If condition evaluates to _false_, the message is displayed to the user.
### parameters
Arbitrary properties passed to conditions, message templates, probe parameters, and transforms along with the data from probes.
@@ -0,0 +1,95 @@
# Understanding rules and probes
The SQL Assessment API uses sets of best practice recommendations to check if a SQL Server environment or configuration could be improved. Best practices are not universal. Guidelines depend on SQL Server version, edition, and configuration, hosting platform, and even usage pattern. For example, some best practices are applicable only to cloud environments, whereas others work for SQL Server instances installed on a physical machine. Databases may also be of different types. For example, a set of best practices for the `msdb` database might be different from that used for user databases.
SQL Assessment API makes recommendations more specific by implementing a two-step process:
1. Build a checklist for the given target
Such a checklist contains a complete set of checks, depending on the specified target.
2. Go through the checklist and report every best practice violation
At this step, the SQL Assessment API goes through the checklist to validate whether the given target satisfies the given best practice.
The checklist construction process is based on rules. Each rule either defines a new check or modifies one or more of the existing ones. Rules are stored in rulesets. Each ruleset has its own name and version. Users can add multiple rulesets to the SQL Assessment API engine one by one. While building the checklist, the engine will apply rules in the order they were added.
A single check can be affected by multiple rules. The first rule must define the check and assign a unique string ID. Checks may have tags that allow managing them in groups. The following rules override checks referred by IDs or tags, which gives the override the ability to modify multiple checks at once.
For example, the following rule makes a check with the `MaxMemory` ID appear in the checklist for all SQL Server 2012 instances and higher. Note that the rule sets the `limit` parameter to 2147483647.
```json
{
"id": "MaxMemory",
"itemType": "definition",
"target":
{
"type": "Server",
"version": "[11.0,)"
},
"limit": 2147483647
}
```
The rule below modifies the limit parameter for the `MaxMemory` check defined above.
```json
{
"id": "MaxMemory",
"itemType": "override",
"limit": 2000000000
}
```
Note that the rule override can modify checks for selected targets if needed. The following rules modify the limit of the `MaxMemory` check for specific SQL Server editions.
```json
{
"id": "MaxMemory",
"itemType": "override",
"targetFilter":
{
"engineEdition": "Standard"
},
"limit": 131072
},
{
"id": "MaxMemory",
"itemType": "override",
"targetFilter":
{
"engineEdition": "Express"
},
"limit": 1410
}
```
Having a check in the checklist does not mean this check will be run by the assessment engine. Each check has the `enabled` property which is set to *true* by default. If the property becomes false after all rules are applied, the engine will skip the check.
The following rule disables the `MaxMemory` check and all performance-related checks for all instances running on Linux.
```json
{
"id": [ "MaxMemory", "Performance" ],
"itemType": "override",
"targetFilter": {
"platform": "Linux"
},
"enabled": false
}
```
Each check needs data to analyze. Checks do not retrieve any data from the target server or database but refer to *probes* instead. Most of the probes use T-SQL queries, but the SQL Assessment API also supports probes for WMI, Windows registry, Azure Instance Metadata Service, as well as custom probes implemented as .NET classes.
Each probe returns zero or more data rows containing named items. If a check gets data from multiple probes, the resulting data set is constructed as *all* combinations of rows. This behavior is similar to `CROSS JOIN` in T-SQL.
A check has a condition expression which formally describes best practice as an arithmetic expression referring to data from a data row. Condition is calculated and checked if it holds for each row separately. For example, when a database uses 3 disks for storing its files, then a free space best practice may apply to each disk independently. If a check uses data from two probes, and one probe produces 2 rows while another one produces 3, the check condition will be evaluated 2×3=6 times. If condition gives *false* 2 times out of 6, the check will produce 2 messages.
At the same time, probe implementation may depend on the SQL Server version. For example, dynamic management views may vary between SQL Server releases. This is why a probe may have multiple implementations. Each implementation has a target specification, the same way the rules do. The assessment engine uses the most appropriate implementation for each target.
## Related topics
- [Probes](../Reference/Probes/README.md)
- [Tutorials](../Tutorials/README.md)
- [How-To](../How-To/README.md)
@@ -0,0 +1,53 @@
# Ruleset file structure
Rule set is stored in a text file that contains a JSON object. The rule set object has three simple mandatory properties: `name`, `version`, and `schemaVersion`. The latter specifies the file format version. As of the time of writing this document, the file format version is 1.0. The `name` and `version` properties comprise the identity of the set.
An essential part of a ruleset is the optional `rules` property that stores an array of SQL Assessment API rules. Rules are used to build a checklist. For more information, see [Understanding Rules and Probes](.\RulesandProbes.md).
The optional `probes` property contains definitions for probes. Probes are used to get actual data from target SQL Server instances, hosting machines, and other sources. Each probe definition is a JSON array of one or more implementations. For more information, see [Probes](..\Reference\Probes\README.md).
The `rules` and `probes` properties are optional because rules from one ruleset can use probes from another one.
![Ruleset Structure](img\RulesetStructure.svg)
```json
{
"name": "Ruleset name",
"version": "Ruleset version",
"schemaVersion": "Schema version",
"rules":[
{
rule A
},
{
rule B
},
],
"probes":{
"probe1": [
{
implementation 1
},
{
implementation 2
},
],
"probe2": [
],
}
}
```
Ruleset examples:
- [DisablingBuiltInChecks_sample.json](https://github.com/microsoft/sql-server-samples/blob/master/samples/manage/sql-assessment-api/DisablingBuiltInChecks_sample.json)
- [MakingCustomChecks_sample.json](https://github.com/microsoft/sql-server-samples/blob/master/samples/manage/sql-assessment-api/MakingCustomChecks_sample.json)
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

@@ -0,0 +1,16 @@
@startjson
{
"id": "<i>Probe ID</i>",
"alias?": "<i>Alternative local ID</i>",
"params?": {
"param_1": "<i>Expression for param 1</i>",
"param_2": "<i>Expression for param 2</i>",
"…": "…"
},
"transform?": [
"Transformation 1",
"Transformation 2",
"…"
]
}
@endjson
@@ -0,0 +1,18 @@
@startjson
{
"type": "<i>Probe type</i>",
"target?": {
"type": "<b>Server</b> | <b>Database</b>",
"version": "<i>Version pattern</i>",
"…": "…"
},
"implementation": {
"probe_param_1": "<i>Expression</i>",
"probe_param_2": "<i>Expression</i>",
"…": "…",
"transform?": "<i>Data transform</i>"
},
"requires?": "<i>Characteristics</i>",
"runFor?": "<i>Characteristics</i>"
}
@endjson
@@ -0,0 +1,29 @@
@startjson
{
"itemType": "<b>definition</b> | <b>override</b>",
"id": "<i>Check ID(s) or tags</i>",
"target?": {
"type": "<b>Server</b> | <b>Database</b>",
"version": "<i>Version pattern</i>",
"…": "…"
},
"targetFilter?": {
"type": "<b>Server</b> | <b>Database</b>",
"version": "<i>Version pattern</i>",
"…": "…"
},
"tags?": ["Tag 1", "Tag 2", "…"],
"displayName?": "<i>Short descriptive name</i>",
"description?": "<i>Long description</i>",
"message?": "<i>Message template</i>",
"level?": "<b>Information</b> | <b>Warning</b> | <b>Critical</b>",
"helpLink?": "<i>Hyperlink to an article</i>",
"probes?": [
"<i>Probe ref 1</i>",
"<i>Probe ref 2</i>",
"…"
],
"condition?": "<i>Condition expression</i>",
"<i>parameter 1</i>": "<i>param 1 value</i>"
}
@endjson
@@ -0,0 +1,23 @@
@startjson
{
"name": "<i>Ruleset name</i>",
"version": "<i>Ruleset version</i>",
"schemaVersion": "<i>Schema version</i>",
"rules?":[
"<i>rule A</i>",
"<i>rule B</i>",
"<i>rule C</i>",
"…"
],
"probes?":{
"<i>probe 1</i>": [
"<i>implementation 1</i>",
"<i>implementation 2</i>",
"…"
],
"<i>probe 2</i>": "…",
"…": "…"
}
}
@endjson
@@ -0,0 +1,64 @@
# SQL Assessment API Quick Start Guide
Assess your SQL Server configuration in 2 simple steps.
## 1. Setup
Install the [PowerShell SqlServer module](https://www.powershellgallery.com/packages/SqlServer) using the following command.
```PowerShell
Install-Module -Name SqlServer -AllowClobber
```
## 2. Invoke assessment
To invoke an assessment against a local SQL Server instance, run the following command.
```PowerShell
Get-SqlInstance -ServerInstance 'localhost' | Invoke-SqlAssessment
```
Sample result:
```PowerShell
PS:> Get-SqlInstance -ServerInstance localhost | Invoke-SqlAssessment
TargetPath: Server[@Name='LOCAL']
Sev. Message Check ID Origin
---- ------- -------- ------
Info Enable trace flag 834 to use large-page allocations to improve TF834 Microsoft Ruleset 0.1.202
analytical and data warehousing workloads.
Low Detected deprecated or discontinued feature uses: String literals DeprecatedFeatures Microsoft Ruleset 0.1.202
as column aliases, syscolumns, sysusers, SET FMTONLY ON, XP_API,
Table hint without WITH, More than two-part column name. We
recommend to replace them with features actual for SQL Server
version 14.0.1000.
Medi Amount of single use plans in cache is high (100%). Consider PlansUseRatio Microsoft Ruleset 0.1.202
enabling the Optimize for ad hoc workloads setting on heavy OLTP
ad-hoc workloads to conserve resources.
...
```
In the results, you will see that each rule has some properties (not the full list):
- Severity (info, low, medium, high)
- Message property explains the recommendation but if you need more info, there is a HelpLink property that points at documentation on the subject.
- Origin shows which ruleset and version the recommendation is coming from
See [ruleset.json](./ruleset.json) for a full list of rules and properties.
If you want to get recommendations for all databases on the local instance, run this command.
```PowerShell
Get-SqlDatabase -ServerInstance 'localhost' | Invoke-SqlAssessment
```
## Learn more about SQL Assessment API
To learn more about the SQL Assessment API such as customizing and extending rulesets, saving results to a table, etc., visit:
- Docs online page for SQL Assessment API PowerShell cmdlets: https://docs.microsoft.com/sql/sql-assessment-api/sql-assessment-api-overview
- [SQL Assessment User Guide](UserGuide/README.md)
- SQL Assessment API Tutorial notebook: [SQLAssessmentAPITutorialNotebook.ipynb](./notebooks/SQLAssessmentAPITutorialNotebook.ipynb)
- Azure Data Studio extension: https://techcommunity.microsoft.com/t5/sql-server/released-sql-server-assessment-extension-for-azure-data-studio/ba-p/1470603
@@ -0,0 +1,9 @@
# How-To
This section explains how to install and invoke assessment, use probes that are specific to VMs running in Azure environments, and how to enable probes that use non-SQL based queries to get data for assessment.
## In This Section
- [SQL Assessment API Quick Start Guide](QuickStart.md)
- [Assessment of SQL Server on Azure Virtual Machines](UsingAzureRules.md)
- [Retrieving Data from Operating System](UsingNonSQLProbes.md)
@@ -0,0 +1,65 @@
# Assessment of SQL Server on Azure Virtual Machines
With SQL Assessment cmdlets, you can assess an instance of SQL Server on an Azure VM not only as on-premises SQL Server, but also with rules that are specific to Azure environments.
To use such rules, do the following:
1. Make sure that both the [Azure PowerShell module](https://aka.ms/AAbdhwk) and the [Az.ResourceGraph module](https://www.powershellgallery.com/packages/Az.ResourceGraph) are installed.
2. [Sign in with Azure PowerShell](https://aka.ms/AAbdogm) before invoking SQL Assessment against SQL Server on an Azure VM.
**NOTE:** It is possible to use Azure account connection persisted between PowerShell sessions, i.e. invoke **Connect-AzAccount** in one session and omit this command later. However, in such a scenario, SQL Assessment cmdlets need the **Az.ResourceGraph** module to be imported explicitly by running **Import-Module Az.ResourceGraph**.
## Performing Assessment
The following example shows how to invoke assessment for SQL Server on an Azure VM instance. Active Azure subscription connection enables rules that are specific to SQL Server on Azure VMs&mdash;**AzSqlVmSize** in this example:
1. Invoke the [Connect-AzAccount](https://docs.microsoft.com/powershell/module/az.accounts/connect-azaccount) cmdlet that establishes connection with the Azure account to get data from Azure Resource Graph.
```PowerShell
Connect-AzAccount
```
2. [Optional step] After invoking **Connect-AzAccount**, you can run the [Set-AzContext](https://docs.microsoft.com/powershell/module/az.accounts/set-azcontext) cmdlet.
```PowerShell
Set-AzContext My-Pay-As-You-Go
```
3. [Optional step] Invoke the [Get-Credential](https://docs.microsoft.com/powershell/module/microsoft.powershell.security/get-credential) cmdlet that creates a credential object for the specified user name and password.
```PowerShell
$cred = Get-Credential
```
4. Select SQL Server objects to assess. For example, the following command gets a SQL Server instance.
```PowerShell
$target = Get-SqlInstance -ServerInstance "Computer002\InstanceName" -Credential $cred
```
`-Credential` is an optional parameter and can be omitted.
5. Run the [Invoke-SqlAssessment](https://docs.microsoft.com/powershell/module/sqlserver/invoke-sqlassessment) cmdlet that builds a check list for each input object, runs through the list, and returns best practice recommendations.
```PowerShell
Invoke-SqlAssessment $target
```
As a result, you would get an output similar to the following one.
```
TargetPath : Server[@Name='ContosoAzureSQL']
Sev. Message Check ID Origin
---- ------- -------- ------
Medi Amount of single use plans in cache is high (100%). Consider PlansUseRatio Microsoft Ruleset 0.1.202
enabling the Optimize for ad hoc workloads setting on heavy OLTP
ad-hoc workloads to conserve resources
Low Use memory optimized virtual machine sizes for the best AzSqlVmSize Microsoft Ruleset 0.1.202
performance of SQL Server workloads
```
Here, **Server[@Name='ContosoAzureSQL']** shows the server name that hosts the assessed SQL Server instance, the **Sev.** column shows the severity level, which can be *Information*, *Medium*, *Low*, or *High*, in the **Message** column, the actual best practice recommendations are shown, the **Check ID** column shows the rule name, and the **Origin** column displays the ruleset name and version.
In this example, the **AzSqlVmSize** rule is applicable solely to the SQL Server deployed on an Azure VM; it checks whether the size of the VM is [memory-optimized](https://docs.microsoft.com/azure/virtual-machines/sizes-memory) or not.
@@ -0,0 +1,22 @@
# Retrieving Data from Operating System
In the SQL Assessment API, most of the probes use T-SQL to get data for assessment. However, there are probes that obtain data from the operating system not presented in SQL Server dynamic management views. In order for these probes to get data, the [xp_cmdshell](https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/xp-cmdshell-transact-sql) stored procedure and PowerShell access should be enabled on the target SQL Server. While these facilities are disabled, some checks may be ckipped.
Keep in mind that the [xp_cmdshell](https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/xp-cmdshell-transact-sql) stored procedure should be enabled temporarily as it is not recommended by the best practices; once you complete the assessment, make sure to disable it.
The following steps are required to enable [xp_cmdshell](https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/xp-cmdshell-transact-sql) and PowerShell:
1. Enable the **xp_cmdshell** stored procedure to work with T-SQL queries, as described in [xp_cmdshell configuration option](https://docs.microsoft.com/sql/database-engine/configure-windows/xp-cmdshell-server-configuration-option).
2. Enable SQL Server PowerShell on the target SQL server, as described in [SQL Server PowerShell](https://docs.microsoft.com/sql/powershell/sql-server-powershell).
3. Make sure that the SQL Server user has access to the **xp_cmdshell** stored procedure. For more information, see [xp_cmdshell (Transact-SQL)](https://docs.microsoft.com/sql/relational-databases/system-stored-procedures/xp-cmdshell-transact-sql).
4. [Perform assessment](https://docs.microsoft.com/sql/tools/sql-assessment-api/sql-assessment-api-overview?view=sql-server-ver15#get-started-using-sql-assessment-cmdlets).
5. Disable the **xp_cmdshell** stored procedure by executing the following T-SQL query on the target SQL Server.
``` sql
EXECUTE sp_configure 'xp_cmdshell', 0;
RECONFIGURE
```
@@ -0,0 +1,12 @@
# SQL Assessment API User Guide
The SQL Assessment API User Guide helps you better understand the SQL Assessment API functionality, how to use built-in features, and how to customize workflows to make your assessment even more precise.
**NOTE:** Some features described in these documents are available only in the [SqlServer PowerShell module](https://www.powershellgallery.com/packages/SqlServer) version **22.0.30-preview** or later.
## In This Section
- [How-To](How-To/README.md)
- [Customization](Customization/README.md)
- [Tutorials](Tutorials/README.md)
- [Reference](Reference/README.md)
@@ -0,0 +1,12 @@
# Data transformation
## In This Section
- [aggregate](aggregate.md)
- [defaultValue](defaultValue.md)
- [expandData](expandData.md)
- [nameValuePairs](nameValuePairs.md)
- [noData](noData.md)
- [parse](parse.md)
- [rename](rename.md)
- [toString](toString.md)
@@ -0,0 +1,100 @@
# Data transformation: **aggregate**
Takes all data rows returned from probes or previous transformations and calculates aggregate values for specified columns. If [grouping](#grouping) was specified, returns an aggregate row for every group. Returns one row otherwise.
## Paramters
|Parameter|Required|Type|Description|
|-|-|-|-|
|map|Required|Map|Maps column names to [aggregate functions](#aggregate-functions)|
|group|Optional|String or Array of strings|Names of columns used for [grouping](#grouping)|
## Grouping
By default aggregate functions are applied to all data records returned by probes or previous transformations. Optionally, aggregates may be applied to groups of rows having the same value(s) in selected column(s). it works much like `GROUP BY` T-SQL clause.
## Example 1
In this example **blocked_spid** becomes the number of unique non-null **blocked_spid**s, **block_time_min** becomes the minimum of all **block_time_min**s. Other columns are removed.
```json
"transform": {
"type": "aggregate",
"map": {
"blocked_spid": "count",
"block_time_min": "min"
}
}
```
## Example 2
In this example the transformation count non-unique non-null **RecoveryUnitId**s.
```json
"transform": {
"type": "aggregate",
"map": {
"RecoveryUnitId": {
"type": "count",
"distinct": false
},
"blocked_spid": "count",
"block_time_min": "min"
}
}
```
## Example 3
In the following example the transform constructs a string containing a comma-separated list of all publications for each **publisher_db**.
```json
"transform": {
"type": "aggregate",
"group": "publisher_db",
"map": {
"publication": "join"
}
}
```
## Aggregate Functions
### and/or
Calculates 'and'/'or' aggregate value for boolean values. Null, DBNull, empty string, empty array, and string *'false'* are converted to *false*. Non-empty string, non-empty array, and string *'true'* are converted to *true*.
### array
Collects all values into an array.
### count
Count all values.
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|distinct|Optional|Bool|*true*|If *true* count only distinct values|
|notNull|Optional|Bool|*true*|if *true* count only non-null values|
### join
Converts values to string and joins them into string separated by comma by default.
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|trim|Optional|Bool|*true*|If *true*, trim white space from the beginning and the end of each value|
|separator|Optional|String|*', '*|A string used to separate values|
|limit|Optional|Int|0|When `limit` > 0, `join` returns a string containing not more than first `limit` items with ellipsis replacing the rest. E.g. when `limit`=3, for [1,2,3,4,5] `join` returns "1, 2, 3, ..."|
|distinct|Optional|Bool|*true*|Skips repeating values. E.g. for [*cat*,*lion*,*cat*,*cat*,*tiger*,*lion*] `join` returns *"cat, lion, tiger"*|
|comparison|Optional|Comparison|*CurrentCulture*|Specifies the culture, case, and sort rules to be used for selecting *distinct* values. See [StringComparison Enum](https://docs.microsoft.com/dotnet/api/system.stringcomparison) for allowed values and more details.|
### max/min
Calculates max/min value. For numeric values only.
### sum
Calculates sum of all the values. For numeric values only.
@@ -0,0 +1,24 @@
# Data transformation: **defaultValue**
Seta default values for data columns. The default value is used when the column does not contain any data.
## Paramters
|Parameter|Required|Type|Description|
|-|-|-|-|
|[map](#map)|Required|Map|Maps names to default values|
## map
Default value map is a JSON objects setting a default value for each data column. In the following example column **automatic_soft_NUMA_disabled** has default value *0*.
## Example 1
```json
"transform": {
"type": "defaultValue",
"map": {
"automatic_soft_NUMA_disabled": 0
}
}
```
@@ -0,0 +1,45 @@
# Data transformation: **nameValuePairs**
From all data rows returned from probes or previous transformations takes two columns and transposes this two-column table.
## Parameters
|Parameter|Required|Type|Description|
|-|-|-|-|
|keyColumn|Required|String|Key column name used for [transformation](#transformation)|
|valueColumn|Required|String|Value column name used for [transformation](#transformation)|
|map|Required|Map|Maps **keyColumn** values to column names|
## Remarks
This transform takes a name from the **keyColumn** and uses it to create a new column with the corresponding value from the **valueColumn**. If **map** is present, then the value from the **keyColumn** is used as a key to find the new column name in the **map**.
In the following example the source table is transformed to result table. The **Description** column is omitted. **CPU Utilization** and **Disk Free Space** columns are renamed according to the **map**.
## Example 1
Source table:
|Metric|MeasuredValue|Description|
|---|---|---|
|CPU Utilization|0.65|Average CPU utilization|
|NOPM|120|Number of new orders per minute|
|Disk Free Space|15|Disk free space|
|TPM|11236|Number of transactions per minute|
```json
"transform": {
"type": "nameValuePairs",
"keyColumn": "Metric",
"valueColumn": "MeasuredValue",
"map": {
"CPU Utilization": "cpu_utilization",
"Disk Free Space": "disk_space"
}
}
```
Result table:
|cpu_utilization|NOPM|disk_space|TPM|
|---|---|---|---|
|0.65|120|15|11236|
@@ -0,0 +1,41 @@
# Data transformation: **noData**
Defines a record returned when the probe(s) or previous transformations produced no data.
When no data comes from probes, the check passes without calculating the value for condition. this transformation allows processing empty data sets. You can use optional [presenceFlag](#presenceFlag) parameter to apply special logics in the condition.
## Parameters
|Parameter|Required|Type|Description|
|-|-|-|-|
|[define](#define)|Required|Map|Defines default columns and their values|
|[presenceFlag](#presenceFlag)|Optional|String|Indicates if data was present|
## define
The record definition is a JSON objects setting a default value for each data column. In the following example, in case of missing data **InUse** = *2*, **Name** = *'master'*.
## Example 1
```json
"transform": {
"define": {
"InUse": 0,
"Name": "master"
}
```
## presenceFlag
Presence flag is a boolean variable indicating if data was present. Use `presenceFlag` to specify variable name, which will be set to *true* when data was present and *false* otherwise. In the following example, in case when some data was produced by probes or previous transformations **dataFound** is *true*, otherwise it's *false*.
## Example 2
```json
"transform": {
"presenceFlag": "dataFound",
"define": {
"InUse": 0,
"Name": "master"
}
```
@@ -0,0 +1,137 @@
# Parse <!-- omit in toc -->
The regular expression parser extracts data from substrings of variable values that correspond to the named groups specified in regular expressions.
## Parameters
|Parameter|Required|Type|Description|
|-|-|-|-|
|map|Required|[Map](#map)|Sets regular expressions for variables.|
|flatten|Optional|bool|Enables [combining fields](#flattening) from subsequent rows.|
|join|Optional|string|Enables [joining](#joining-strings) of strings from all rows and sets the separator.|
## Map
**Map** is a JSON object setting regular expression. Each map property defines a regular expression for a variable of the same name. Named groups in regular expressions define substrings to be returned as new variables. A new variable name consists of **original.group** where **original** is the name of the parsed variable and **group** is the regular expression group name.
When a regular expression has more than one match, a new row is generated for every match. To pack partial matches into a single row, use [flattening](#flattening) or [joining](#joining-strings).
**NOTE**: The [Explicit captures](https://docs.microsoft.com/dotnet/standard/base-types/regular-expression-options#explicit-captures-only) option improves the morph memory footprint and performance. Use 'x' after the closing slash to enable this option: `/(?<int>\d+)/x`.
The following examples demonstrate transformation results:
### Example 1
Input:
|@a|@b|
|-|-|
|"There are 12 chairs"|"The chairs are blue"|
|"Found 5 files"|"Red status"|
|"Number of Napoleons: 6"|"Black pearl"|
Morph:
````json
{
"type": "parse",
"map" : {
"a": "/(?<amount>\\d+)(\\s*(?<kind>\\w+?)s?\\b)?/x",
"b": "/(?<color>red|green|blue|yellow)/ix"
}
}
````
Output:
|@a|@a.amount|@a.kind|@b|@b.color|
|-|-|-|-|-|
|"There are 12 chairs"|"12"|"chair"|"The chairs are blue"|"blue"|
|"Found 5 files"|"5"|"file"|"Red status"|"Red"|
|"Number of Napoleons: 6"|"15"|-|"Black pearl"||
### Example 2
Input:
|@a|@b|
|-|-|
|"12 chairs and 1 priest"|"The chairs are blue"|
|"40 pages in 5 files"|"Red status"|
|"Number of Napoleons: 6"|"Black pearl"|
Morph:
````json
{
"type": "parse",
"map" : {
"a": "/(?<amount>\\d+)(\\s*(?<kind>\\w+?)s?\b)?/x",
"b": "/(?<color>red|green|blue|yellow)/ix"
}
}
````
Output:
|@a|@a.amount|@a.kind|@b|@b.color|
|-|-|-|-|-|
|"12 chairs and 1 priest"|"12"|"chair"|"The chairs are blue"|"blue"|
|"12 chairs and 1 priest"|"1"|"priest"|"The chairs are blue"|"blue"|
|"40 pages in 5 files"|"40"|"page"|"Red status"|"Red"|
|"40 pages in 5 files"|"5"|"file"|"Red status"|"Red"|
|"Number of Napoleons: 6"|"6"||"Black pearl"|-|
## Flattening
Flattening enables combining fields from sequential rows into one in cases when a data row occupies more than one line.
Flattening works only for fields generated by the parsing process; other data is removed.
### Example 3
Raw output data:
| @a.f1 | @a.f2 | @a.f3 | @other |
| :-: | :-: | :-: | :-: |
| 11 | - | - | "extra" |
| - | - | 13 | - |
| - | 12 | - | - |
| 21 | 22 | 23 | "data" |
| - | 32 | 33 | "will" |
| 31 | - | - | - |
| 41 | - | - | "be" |
| - | 42 | - | - |
| 51 | - | - | "ignored" |
After flattening:
| @a.f1 | @a.f2 | @a.f3 | @other |
| :-: | :-: | :-: | :-: |
| 11 | 12 | 13 | - |
| 21 | 22 | 23 | - |
| 31 | 32 | 33 | - |
| 41 | 42 | - | - |
| 51 | - | - | - |
## Joining strings
This option joins all rows into one. Such a new row contains string equivalents of field values joined with the specified delimiter.
Joining works only for variables mentioned in [Map](#map); other data is removed.
In the following example, **join** = **", "**.
Input:
| @a | @b | @c | @notMentioned |
| :-: | :-: | :-: | :-: |
| 42 | "Lorem" | - | "extra" |
| 13 | "ipsum" | true | "data" |
| 0 | "dolor" | false | "ignored" |
Joined:
| @a | @b | @c | @notMentioned |
| :-- | :-- | :-- | :-: |
| "42, 13, 0" | "Lorem, ipsum, dolor" | "true, false" | - |
@@ -0,0 +1,26 @@
# Data transformation: **rename**
Renames any data column.
## Parameters
|Parameter|Required|Type|Description|
|-|-|-|-|
|[map](#map)|Required|Map|Maps new names to old ones|
## Map
Rename map is a JSON objects setting correspondence between new names and old ones. Each property name is the *new* column name. The value sets the name of the old column to be renamed. In the following example column **Log File Size (Byte)** will be renamed to *size*.
## Example 1
```json
"transform": {
"type": "rename",
"map": {
"id": "Archive #",
"size": "Log File Size (Byte)",
"date": "Date"
}
}
```
@@ -0,0 +1,33 @@
# Data transformation: **defaultValue**
Replaces values with strings.
## Parameters
|Parameter|Required|Type|Description|
|-|-|-|-|
|[map](#map)|Required|MapOfMaps|Maps columns to value maps|
## map
String map is a JSON objects defining a string-to-value mapping for one or more columns. In the following example value *2* in column **a** will be replaced with the word *'two'*. The same word *two* will be assigned to column **b** if it's value is *'w'*.
## Example 1
```json
"transform": {
"type": "defaultValue",
"map": {
"a":{
"one" : 1,
"two" : 2,
"three": 3
},
"b":{
"one" : "o",
"two" : "w",
"three": "r"
}
}
}
```
@@ -0,0 +1,180 @@
# JSON configuration file format
This section explains the structure of the JSON file with rules and probes.
## Engine configuration
|Property|Property Type|Description|
|-|-|-|
|version|string|Configuration file version. The current version is "0.3".|
|checks|array|Each item is a [Check](#check) object.|
|probes|object|Each property represents a [Probe Family](#probe).|
## Check
|Property|Property Type|Description|
|-|-|-|
|name|string|Short single name. Must be unique.|
|tags|array of strings|A set of tags for the check. Tags represent check groups and categories. A tag can be used instead of the check name in API calls.|
|displayName|string|Long name that is shown to the user.|
|description|string|Long description that explains the purpose of the check.|
|enabled?|bool|Indicates if the check is enabled. Disabled checks are not run for any object. True by default.|
|messge|string|Recommendation text displayed to the user when the check fails. Recommendation text may contain variable references in the `@{variableName}` form. Such references are replaced by string representation of the variable value. The optional format string can be specified for the variable in the `@{variableName:formatString}` form.|
|target?|[SQL Object Pattern](#sql-object-pattern)|Check can be applied to objects that satisfy the pattern. By default matches any object.|
|probes|array of strings|IDs of the [Probe Families](#probe) required by the check.|
|condition?|[Condition Expression](#condition-expression)|A condition expression that must be satisfied by the target object. If the condition expression returns false, the recommendation is generated. True by default.|
|level|string|Severity level: **Information**, **Warning**, or **Critical**.|
|any|[Expression](#expression)|Other properties are treated as check parameters and accessible from check expressions as variables by the name prefixed with the at-sign @. Parameters may refer to variables or other parameters.|
## Probe
A *Probe* is a JSON property. The property name is the probe family ID used in [Checks](#check). The property value is an array of [probe implementations](#probe-implementation). Probe implementations are checked in the order if their target patterns match the target object. The first matching probe will be used to get data. This means that the order of the probes is important. A probe can be comprised by a mix of CLR and SQL implementations.
### Probe implementation
A JSON object. Security is the user responsibility. Probes only read metadata like update logs or server properties; they do not read user data from tables or write anything to databases or instances. Probes do not set any flags or properties.
|Property|Property Type|Description|
|-|-|-|
|type|string|Probe type. Supported values: **SQL**, **WMI**, **External**, **PowerShell**, **CmdShell**, **AzGraph**, **AzMetadata**, **Registry**.|
|target?|[SQL Object Pattern](#sql-object-pattern)|Probe may be applied to objects satisfying the pattern.|
|implementation|object|Any parameters required by the probe. Probes with type="SQL" need a query property containing the SQL command executed to get the probe data. Probes with type="CLR" need a class property specifying the class name and assembly property specifying assembly. Other properties are used to populate a new class instance.|
#### SQL probe implementation
|Property|Property Type|Description|
|-|-|-|
|query|string|T-SQL query. The query can refer to parameters by the name.|
#### External probe implementation
External probe implementation refers to an arbitrary .NET class in the given assembly. The class must implement `IProbeImplementation` interface.
|Property|Property Type|Description|
|-|-|-|
|assembly|string|String specifying assembly to load.|
|class|string|Full class name.|
### SQL Object Pattern
#### Regular expression
Regular expressions are enclosed in slashes: `"/Win*./"`. Regular expression options can be specified after the closing slash. For example, this is the case insensitive search: `"/win.*/i"`. If the string does not start with a slash, it is treated as an exact string match. The string "Linux" is equivalent to `"/^Linux$/"`.
#### Version range list
The version range list is a single [version range](#version-range) or an array of version ranges. A single version range is equivalent to an array containing the only element. The version range list matches any version matching any of its ranges.
```json
"version": [
"[10.0.4326,10.0.4371]",
"[10.0.5794,10.50)",
"[10.50.2806,11.0)",
"[11.0.2316,)"
]
```
#### Version range
A range of versions is encoded by a string. The string contains one or two versions delimited by the comma and enclosed into optional parenthesis or brackets. If there are two versions, the former version must be less than or equal to the latter. They represent the range boundaries. The version must be specified at least by two numbers separated by the period: major and minor version numbers.
|Version Range|Matches|
|-|-|
|"10.0"|Version 10.0 exactly.|
|"[10.0, 13.0]"|Anything between 10.0 and 13.0, inclusive: 10.0, 10.50, 11.0.345, 13.0.|
|"(10.0, 13.0)"|Anything between 10.0 and 13.0, exclusive: 10.50, 11.0.345. Does not match 10.0 or 13.0.|
|"[10.0, 13.1)"|Anything between 10.0 and 13.1, excluding the right boundary: 10.0, 10.50, 11.0.345, 13.0, 13.0.234. Does not match 13.1.|
|"(10.0, 13.1]"|Anything between 10.0 and 13.1, excluding 10.0.|
|"[10.0,)"|10.0 and abo|
|"(10.0,)"|Above 10.0, but not itself|
|"(,10.0]"|10.0 or below|
|"(,10.0)"|Below 10.0, but not 10.0.|
## Expression
Expressions define calculations made to decide if a recommendation should be given to the user.
### Boolean literals
JSON values true and false are treated as Boolean constants.
### Numeric literals
JSON numbers are treated as decimal constants.
### String literals
String literals represent strings except those starting with the "@" character denoting a reference to a variable.
### Variables
Variables are represented by strings containing their names preceded by the "@" character. Variable values are set by probes and consumed by expressions or recommendation texts.
### Expression JSON object
JSON object with a single property is an expression represented by the property. See [Property Expressions](#property-expressions). JSON object with more than one property is a short version for the AND operation. Its arguments are the object properties. The results are converted into the Boolean type if possible. So these two expressions are equivalent:
```json
expression1: {
"@version": "10.50.0",
"@memroySize": 4096
}
expression2: {
"and": [
{"@version": "10.50.0"},
{"@memroySize": 4096}
]
}
```
### Property Expressions
Any expression can be represented in a form of a JSON property. The property name is the operation name while the property value is an array of operation arguments. Arguments are any expressions except condition expressions.
When the property name starts with the "@" character, it is not the operation name. The expression is a short variant of the equal operation with the variable as the first argument and the only property value as the second. The following expressions are equivalent:
```json
expression1: {"@memorySize": 4096}
expression2: {
"equal": [
"@memorySize",
4096
]
}
```
### Condition Expression
Condition expressions can be any expressions or an array of expressions. An array of expressions is a short version for OR operations. An array of items holds arguments for the operation. The following expressions are equivalent:
```json
"condition": [
{"@version": "10.50.0"},
{"@memroySize": 4096}
]
"condition": {
"or": [
{"@version": "10.50.0"},
{"@memroySize": 4096}
]
}
```
Note: The short version for "OR" works only as condition expressions while the short version for "AND" works everywhere. The following expressions are equivalent:
```json
"condition": {
"@version": "10.50.0",
"@memroySize": 4096
}
"condition": {
"and": [
{"@version": "10.50.0"},
{"@memroySize": 4096}
]
}
```
@@ -0,0 +1,17 @@
# Local Variables
A rule can define local variables available for conditions and message templates. Local variables are any expressions involving literals, probe data, and transformation results.
```json
{
"probes": ["SysDmOsSysInfo"],
"locals": {
"workers": {"sub": [ "@max_workers_count", 1 ] }
},
"message": "Workers = @{workers}.",
"condition": {
"lt": [ 0, "@workers" ],
"lt": [ "@workers", 4 ]
}
}
```
@@ -0,0 +1,70 @@
# Operators <!-- omit in toc -->
With the SQL Assessment API, you can use different kinds of operators to make your assessment even more precise.
## In This Section <!-- omit in toc -->
- [Logical](#logical)
- [String](#string)
- [Math](#math)
- [Set](#set)
- [Comparison](#comparison)
## Logical
|Operator|Arguments|Description|
|-|:-:|-|
|not|(*x*)|Logical not.|
|and|(*a*..\*)|Logical AND. Returns `false` without arguments.|
|or|(*a*..\*)|Logical OR. Returns `true` without arguments.|
## String
|Operator|Arguments|Description|
|-|:-:|-|
|indexof|(*str_a*, *str_b*)|Finds a zero-based index of the first *str_b* occurrence in the string *str_a*. Case-sensitive.|
|iindexof|(*str_a*, *str_b*)|Finds a zero-based index of the first *str_b* occurrence in the string *str_a*. Case-insensitive.|
|startswith|(*str_a*, *str_b*)|Returns `true` if the string *str_a* starts with the string *str_b*. Case-sensitive.|
|istartswith|(*str_a*, *str_b*)|Returns `true` if the string *str_a* starts with the string *str_b*. Case-insensitive.|
|endswith|(*str_a*, *str_b*)|Returns `true` if the string *str_a* ends with the string *str_b*. Case-sensitive.|
|iendswith|(*str_a*, *str_b*)|Returns `true` if the string *str_a* ends with the string *str_b*. Case-insensitive.|
## Math
|Operator|Arguments|Description|
| - |:-:| - |
|ceiling|*x*|Rounds *x* to the nearest greater or equal value.|
|floor|*x*|Rounds *x* to the nearest lower or equal value.|
|max|(*a*, *b*)|Returns maximum of *a* and *b*.|
|min|(*a*, *b*)|Returns minimum of *a* and *b*.|
|mul|(*a*..\*)|Arithmetic multiplication.|
|div|(*a*, *b*)|Arithmetic division of *a* by *b*.|
|mod|(*a*, *b*)|Arithmetic remainder of *a* divided by *b*.|
|add|(*a*..\*)|Arithmetic sum.|
|sub|(*a*, *b*)|Arithmetic difference of *a* and *b*.|
|bitand|(*a*..\*)|Bitwise AND.|
|bitor|(*a*..\*)|Bitwise OR.|
|bitxor|(*a*..\*)|Bitwise XOR.|
## Set
|Operator|Arguments|Description|
| - | :-: |-|
|intersect|(*a*, *b*)|Sets intersection of *a* and *b* arguments. Case-sensitive.|
|in|(*a*, *b*)|Checks if the argument *a* was found in *b*. The *b* argument represents a collection. Case-sensitive.|
|iin|(*a*, *b*)|Checks if the argument *a* was found in *b*. The *b* argument represents a collection. Case-insensitive.|
## Comparison
|Operator|Synonyms|Arguments| Description|
| - | - | :-: | - |
|lt|less|(*a*, *b*)|Checks if the argument *a* is less than the argument *b*.|
|gt|greater|(*a*, *b*)|Checks if the argument *a* is greater than the argument *b*.|
|eq|equal|(*a*, *b*)|Checks if both arguments are equal. Case-sensitive.|
|ieq||(*a*, *b*)|Checks if both arguments are equal. Case-insensitive.|
|ge|greaterequal|(*a*, *b*)|Checks if the argument *a* is greater than or equals to the argument *b*.|
|le|lessequal|(*a*, *b*)|Checks if the argument *a* is less than or equals to the argument *b*.|
|ne|notequal|(*a*, *b*)|Checks if the argument *a* is not equal to the argument *b*. Case-sensitive.|
|ine||(*a*, *b*)|Checks if the argument *a* is not equal to the argument *b*. Case-insensitive.|
|match||(*a*, *b*)|Regular expression match. The second argument is treated as a regular expression. Case-sensitive.|
|imatch||(*a*, *b*)|Regular expression match. The second argument is treated as a regular expression. Case-insensitive.|
|interval||(*a*,*v<sub>1</sub>*,*t<sub>1</sub>*,...,*v<sub>n</sub>*,*t<sub>n</sub>*,*d*)|Finds the first *t<sub>i</sub>* that is greater than or equal to *a* and returns the corresponding *v<sub>i</sub>*. Returns *d* if all *t* are less than *a*.|
@@ -0,0 +1,28 @@
# Overview
**Type code:** *AzGraph*
This probe runs a Kusto query on Azure resource graph.
## Implementation properties
Implementation part of the probe definition contains the following parameters.
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|query|Required|String||Kusto query|
Use at-sign '@' to ember parameters into the query string.
## Example
The following qprobe return `@name` for a resource with resource id equal to parameter `@resId`.
```json
...
"implementation": {
"query": "Resources | where id =~ @resId | project name"
}
...
```
@@ -0,0 +1,16 @@
# Overview
**Type code:** *AzMetadata*
Azure metadata probe returns data which can be retrieved by [Azure Instance Metadata Service](https://learn.microsoft.com/azure/virtual-machines/linux/instance-metadata-service). IMDS returns data as a JSON object. `AzMetadata` probe retrieves data by JSONPath.
When assessment is running remotely and Azure Graph is available, IMDS is emulated with a Kusto query.
## Implementation properties
Implementation part of the probe definition contains the following parameters.
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|query|Required|String|JSONPath to the data to retrieve|
@@ -0,0 +1,36 @@
# Overview
**Type code:** *CMD*
CmdShell probes execute CMD.EXE shell commands and return text lines in the `@stdout` variable.
## How to use
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|command|Required|String||Shell command to execute|
Each line returned by the command it returned as separate data row with a single `@stdout` column. Use the Regex parser transformation to extract data from the `@stdout` variable.
## Example
The following probe lists file system entries in the current directory:
```json
{
"type": "CmdShell",
"target": …,
"implementation": {
"command": "dir"
}
}
```
Output:
|Row|@stdout|
|-|-|
|0|"03/18/2017 06:52 AM\<DIR\>UV"|
|1|"03/18/2017 06:52 AM\<DIR\>UV-FOGRA"|
|2|"03/16/2017 03:21 PM6,419x3dom.css"|
|3|"03/16/2017 03:21 PM926,910x3dom.js"|
@@ -0,0 +1,28 @@
# Overview
**Type code:** *External*
External probe type allows running external code as probe. This probe can call any external .NET class implementing `Microsoft.SqlServer.Management.Assessment.IProbeImplementation` interface.
## Implementation properties
Implementation part of the probe definition contains the following parameters.
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|class|Required|String||The name of the class implementing `IProbeImplementation`|
|assembly|Optional|String|SQL Assessment API engine assembly|Path to an assembly file containing `class`. If not specified the engine will look for an internal class with the specified name.|
### IProbeImplmenetation interface
The `IProbeImplementation` interface has the following properties. The engine sets their values so the probe implementation could have optional parameters and information about the assessed SQL Server instance or database.
|Property|Type|Description|
|---|:-:|---|
|ServerName|string|The name of the target server.|
|TargetName|string|The name of the assessed instance or the database.|
|Urn|string|A string identifier of the target object.|
|Parameters|dictionary|A dictionary containing named parameters passed to the probe from the calling check.|
After setting all the `IProbeImplementation` properties the engine calls `GetDataAsync` method to retrieve all the data.
@@ -0,0 +1,42 @@
# Overview
**Type code:** *PowerShell*
PowerShell probes execute commands in PowerShell (fx) on the target machine and return the pipeline output in the `@Output` variable.
## Implementation parameters
PowerShell probe implementation has the following parameters:
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|command|Required|string||PowerShell command to execute|
Use the `$` (dollar) sign in the `command` text to access probe parameters passed by checks.
Use the `.` (dot) notation to access the object properties. For example, if the returned object is a string, then `@Output.Length` returns its length.
NOTE: Pipeline output is not shown on the screen. For example, the `Write-Host` output is ignored.
## Example
The following probe enumerates file system items in the current dirrectory:
```json
{
"type": "PowerShell",
"target": …,
"implementation": {
"command": 'Get-ChildItem'
}
}
```
Output:
|@Output|'@Output.FullName'|'@Output.Parent.Name'|
|-|-|-|
|"UV"|"C:\Windows\system32\UV"|"system32"|
|"UV-FOGRA"|"C:\Windows\system32\UV-FOGRA"|"system32"|
|"x3dom.css"|"C:\Windows\system32\x3dom.css"|"system32"|
|"x3dom.js"|"C:\Windows\system32\x3dom.js"|"system32"|
@@ -0,0 +1,14 @@
# Probes
The SQL Assessment API provides different types of probes to get data for assessment. Data can be taken from the SQL Server dynamic management views and from the operating system, for example, from the Windows registry.
## In This Section
- [AzGraph](./AzGraphProbe.md)
- [AzMetadata](./AzMetadataProbe.md)
- [CMD](CMDShellProbes.md)
- [External](CLRProbes.md)
- [PowerShell](PowerShellProbes.md)
- [Registry](RegistryProbes.md)
- [T-SQL](TSQLProbes.md)
- [WMI](WMIProbes.md)
@@ -0,0 +1,173 @@
# Overview
**Type code:** *Registry*
Registry probes obtain data from the target machine registry.
## Implementation parameters
Registry probe implementation has the following parameters.
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|query|Required|Object||Tree-like structure specifying Registry keys and values to collect see [Registry query](#registry-query)|
|instance|Optional|Bool|*false*|Indicates whether the probe should return data specific to this SQL Server instance see [Instance specific data](#instance-specific-data)|
### Registry query
Registry query is a complex JSON object.
Top-level properties represent registry hives including *HKEY_LOCAL_MACHINE* and *HKEY_CURRENT_USER*.
Each top-level property value is another JSON object. Its properties represent registry keys. The property name is the full path from the hive to the key.
Use the `*` (asterisk) symbol at any level to enumerate all keys. The key name replacing * will be returned in `@RegistryKeyName` variable.
The value for each key property is an array of registry value names to read. Multi-string values are read as multiple string values with the same name.
### Instance specific data
When `instance` property is *true*, the probe replaces **MSSQLSERVER** in paths with registry path specific for this SQL Server instance. E.g. the following registry path:
```
Software\Microsoft\MSSQLSERVER\SQLServerAgent
```
will be replace with the following instance-specific one:
```
Software\Microsoft\Microsoft SQL Server\MSSQL12.SQL2014\SQLServerAgent
```
where **MSSQL12.SQL2014** is the SQL Server instance name.
### Example 1
The following registry query returns 2 values for for system hardware. Please, note how multi-string value is handled as multiple strings. Use [join aggregate function](..\DataMorphs\aggregate.md#join) to merge them when needed.
```json
"probes": {
"MuRegistryProbe": [
{
"type": "Registry",
"target": { "type": "Server" },
"implementation": {
"query": {
"HKEY_LOCAL_MACHINE":{
"HARDWARE\\DESCRIPTION\\System": [
"Identifier",
"SystemBiosVersion"
]
}
}
}
}
]
}
```
Output:
|@Identifier|@SystemBiosVersion|
|-|-|
|"AT/AT COMPATIBLE"|"ALASKA - 1072009"|
|"AT/AT COMPATIBLE"|"3802"|
|"AT/AT COMPATIBLE"|"American Megatrends - 5000C"|
### Example 2
Use asterisk '*' to enumerate all subkeys of the given key. The following registry query returns processor id and an identifier of its vendor:
```json
{
"type": "Registry",
"target": { "type": "Server" },
"implementation": {
"query": {
"HKEY_LOCAL_MACHINE":{
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\*": [
"VendorIdentifier"
]
}
}
}
}
```
Output:
| @RegistryKeyName | @VendorIdentifier |
|---|---|
| 0 | "Genuine Intel" |
| 1 | "Genuine Intel" |
| 2 | "Genuine Intel" |
| 3 | "Genuine Intel" |
### Example 3
The following registry query gets an identifier for each hardware item.
```json
{
"HKEY_LOCAL_MACHINE": {
"HARDWARE\\DESCRIPTION\\*": [
"Identifier"
]
}
}
```
### Example 4
Use at-sign '@' to insert probe parameters int the registry query. The following check gets two registry keys `HKEY_LOCAL_MACHINE\SOFTWARE\Policies\test` and `HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\some\test`, and compares each to the 'result' string.
```json
"rules": [
{
"id": "MyKeys",
"itemType": "definition",
"target": {
"type": "Server",
"platform": "Windows"
},
"displayName": "My custom keys",
"message": "The current key is @{test}",
"testParam": "test",
"resultParam": "result",
"hiveParam": "Policies",
"condition": {
"@test": "@resultParam"
},
"probes": [
{
"id": "myprobe",
"params": {
"hiveParam": "@hiveParam",
"testParam": "@testParam"
}
}
]
}
],
"probes": {
"myprobe": [
{
"type": "Registry",
"target": {
"type": "Server",
"platform": "Windows",
"instance": true
},
"implementation": {
"query": {
"HKEY_LOCAL_MACHINE": {
"SOFTWARE\\@{hiveParam}": [ "@{testParam}" ],
"SOFTWARE\\@{hiveParam}\\Microsoft\\some": [ "@{testParam}" ]
}
}
}
}
]
}
```
@@ -0,0 +1,37 @@
# Overview
**Type code:** *SQL*
T-SQL probes are based on T-SQL queries used to retrieve data from the specified databases for further assessment of your environment.
## Implementation properties
Implementation part of the probe definition contains the following parameters.
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|query|Required|String||T-SQL query|
|UseDatabase|Optional|Bool|*false*|Indicates whether `USE DATABASE` statement should be issued before running the query. use for probes targeted to databases.|
## Example
```json
{
"type": "SQL",
"target": {
"type": "Server",
"engineEdition": "OnPremises",
"platform": "Linux",
"version": "[11.0,)"
},
"implementation": {
"query": "SELECT [host_platform] AS [host_platform] ,[host_release] AS [host_release] ,64 AS [host_architecture] FROM sys.dm_os_host_info(NOLOCK)",
"transform": {
"type": "parse",
"map": {
"host_release": "/^(?<major>\\d+)\\.(?<minor>\\d+)(?:\\.(?<build>\\d+))?(?:\\.(?<revision>\\d+))?$/x"
}
}
}
}
```
@@ -0,0 +1,89 @@
# Overview
**Type code:** *WMI*
WMI probes run WMI queries.
A WMI query returns a number of WMI objects. Each object is processed as a separate row. `@Output` variable represents the object. Use dot notation to access object's properties: `@Output.BlockSize`.
## Implementation properties
Implementation part has the following properties.
|Parameter|Required|Type|Default|Description|
|---|:-:|:-:|:-:|---|
|query|Required|String||WMI query returning probe data|
|methods|Optional|Map||List of WMI methods to be called on selected WMI objects|
### WMI methods
To call a WMI method mention its name as a property *value* of the `methods` JSON object. The property's name is one for the variable where the result will be stored. In [Example 3](#example-3) the `@da` variable stores the value returned from the **DefragAnalysis** method.
## Examples
### Example 1
Simple WMI probe returns name and block size for volumes.
```JSON
{
"type": "WMI",
"target": …,
"implementation": {
"query": "SELECT Name, BlockSize FROM Win32_Volume WHERE Capacity <> NULL"
}
}
```
Output:
|@Output.Name|@Output.BlockSize|
|-|-|
|E:\\|4096|
|C:\\|4096|
|\\?\Volume{b9878a89-2e73-4790-8576-9c8217b2fba7}\\|4096|
|M:\\|4096|
|G:\\|4096|
|R:\\|4096|
|\\?\Volume{41c4c545-aad7-4a8c-86e5-11a52611a81d}\\|1024|
### Example 2
Use the `$` (dollar) sign to access probe parameters passed by checks.
```JSON
{
"type": "WMI",
"target": {
"type": "Server",
"platform": "Windows",
"engineEdition": "OnPremises",
"version": "[11.0,)"
},
"implementation": {
"query": "SELECT Name, StartingOffset FROM Win32_DiskPartition WHERE StartingOffset < $threshold"
}
}
```
### Example 3
Call a WMI method opn each WMI object returned by a WMI query.
```json
{
"type": "WMI",
"target": {
"type": "Server",
"platform": "Windows",
"engineEdition": "OnPremises",
"version": "[11.0,)"
},
"implementation": {
"query": "SELECT DeviceID, Name FROM Win32_Volume WHERE DriveType=3 AND Name LIKE '_:\\\\'",
"methods": {
"da": "DefragAnalysis"
}
}
}
```
@@ -0,0 +1,10 @@
# Reference
This reference is intended to guide you through some of the most commonly used scenarios in the SQL Assessment API.
## In This Section
- [JSON configuration file format](JSONConfiguration.md)
- [Probes](Probes/README.md)
- [Data morphs](DataMorphs/README.md)
- [Operators](Operators.md)
@@ -0,0 +1,142 @@
# Creating Custom Rules
Sometimes rulesets that come right out of the box after installing the SQL Assessment API might not be enough to validate your environment as required. In such cases, you can simply write your own rulesets.
A typical [ruleset](../Customization/RulesandProbes.md) &mdash; a JSON file &mdash; consists of two blocks: [rules](#rules) and [probes](#probes).
## Rules
The **Rules** block defines the rule structure. It says which objects in your environment should be assessed, which versions of these objects should be considered during the assessment, the platform that hosts the objects being assessed, and so on.
Usually, rules are best practices or company internal policies that should be applied to SQL Server configurations. If any of these configurations violates conditions specified in the rule, the rule throws alerts, pointing at certain areas within your environment that should be changed in order to comply with the best practices. These alerts are represented as messages that give a short and straightforward advice on how to mitigate the issue. Alerts also contain help links to the Microsoft pages when you can get comprehensive instructions on how to configure certain things to avoid potential security and performance issues.
The following is an example of a rule:
```json
{
//Target describes a SQL Server object the check is supposed to run against
"target":
{
//This check targets an object of the Database type
"type": "Database",
//Applies to SQL Server 2016 and higher
//Another example: "[12.0,13.0)" reads as "any SQL Server version >= 12.0 and < 13.0"
"version": "[13.0,)",
//Applies to SQL Server on Windows and Linux
"platform": "Windows, Linux",
//Applies to SQL on Premises and Azure SQL Managed Instance. Here you can also filter specific editions of SQL Server
"engineEdition": "OnPremises, ManagedInstance",
//Applies to any database excluding master, tempdb, and msdb
"name": { "not": "/^(master|tempdb|model)$/" }
},
//Rule ID
"id": "QueryStoreOn",
//Can be "definition" or "override". The former is to declare a rule, the latter is to override/customize an existing rule. See also 'DisablingBuiltInChecks_sample.json'
"itemType": "definition",
//Tags combine rules in different subsets
"tags": [ "CustomRuleset", "Performance", "QueryStore", "Statistics" ],
//Short name for the rule
"displayName": "Query Store should be active",
//A more detailed explanation of a best practice or policy that the rule check
"description": "The Query Store feature provides you with insight on query plan choice and performance. It simplifies performance troubleshooting by helping you quickly find performance differences caused by query plan changes. Query Store automatically captures a history of queries, plans, and runtime statistics, and retains these for your review. It separates data by time windows so you can see database usage patterns and understand when query plan changes happened on the server. While Query Store collects queries, execution plans and statistics, its size in the database grows until this limit is reached. When that happens, Query Store automatically changes the operation mode to read-only and stops collecting new data, which means that your performance analysis is no longer accurate.",
//Usually, it's for recommendation what user should do if the rule raises up an alert
"message": "Make sure Query Store actual operation mode is 'Read Write' to keep your performance analysis accurate",
//Reference material
"helpLink": "https://docs.microsoft.com/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store",
//List of probes that are used to get the required data for this check. See below to know more about probes.
"probes": [ "Custom_DatabaseConfiguration" ],
//Condition object is to define "good" and "bad" state, the latter is when the rule should raise an alert. When the condition is true, it means that the checked object complies with the best practice or policy. Otherwise, the rule raises an alert (it actually adds its message to the resulting set of recommendations)
"condition":
{
//It means that the variable came from the probe should be equal to 2
"equal": [ "@query_store_state", 2 ]
}
}
```
## Probes
The **Probes** block defines the way how to get the required data to check compliance with the rule. Probes are invoked during the assessment. They use queries to obtain data from your environment. This data is then evaluated and based on this evaluation, the rule can tell whether your environment is in compliance with the best practices or not. If not, the rule fires, telling you exactly what should be changed in order to avoid both performance degradation and security risks.
The following is an example of a probe:
```json
//Probe name is used to reference the probe from a rule
//Probe can have a few implementations that will be used for different targets
//This probe has two implementations for different version of SQL Server
"Custom_DatabaseConfiguration":
[
{
//Probe uses a T-SQL query to get the required data. Use 'CLR' for assemblies.
"type": "SQL",
//Probes have their own target, usually to separate implementation for different versions, editions, or platforms. Probe targets work the same way as rule targets do.
"target":
{
"type": "Database",
//This target is for SQL Server of versions prior to 2014
"version": "(,12.0)",
"platform": "Windows, Linux",
"engineEdition": "OnPremises, ManagedInstance"
},
//Implementation object with a T-SQL query. This probe is used in many rules, that's why the query return so many fields
"implementation":
{
"query": "SELECT db.is_auto_create_stats_on, db.is_auto_update_stats_on, 0 AS query_store_state, db.collation_name, (SELECT collation_name FROM master.sys.databases (NOLOCK) WHERE database_id = 1) AS master_collation, db.is_auto_close_on, db.is_auto_shrink_on, db.page_verify_option, db.is_db_chaining_on, NULL AS is_auto_create_stats_incremental_on, db.is_trustworthy_on, db.is_parameterization_forced FROM [sys].[databases] (NOLOCK) AS db WHERE db.[name]=@TargetName"
}
},
//This implementation object is to get the required data from SQL Server 2014 (look at target.version)
{
"type": "SQL",
"target":
{
"type": "Database",
"version": "[12.0, 13.0)",
"platform": "Windows, Linux",
"engineEdition": "OnPremises, ManagedInstance"
},
"implementation":
{
"query": "SELECT db.is_auto_create_stats_on, db.is_auto_update_stats_on, 0 AS query_store_state, db.collation_name, (SELECT collation_name FROM master.sys.databases (NOLOCK) WHERE database_id = 1) AS master_collation, db.is_auto_close_on, db.is_auto_shrink_on, db.page_verify_option, db.is_db_chaining_on, db.is_auto_create_stats_incremental_on, db.is_trustworthy_on, db.is_parameterization_forced FROM [sys].[databases] (NOLOCK) AS db WHERE db.[name]=@TargetName"
}
},
//This implementation object is to get the required data from SQL Server 2016 and up (look at target.version)
{
"type": "SQL",
"target":
{
"type": "Database",
"version": "[13.0,)",
"platform": "Windows, Linux",
"engineEdition": "OnPremises, ManagedInstance"
},
"implementation":
{
//Use this key if your query requires to run on a database that is being assessed (it's a replacement for 'USE <DATABASENAME>;')
"useDatabase": true,
"query": "SELECT db.is_auto_create_stats_on, db.is_auto_update_stats_on, (SELECT CAST(actual_state AS DECIMAL) FROM [sys].[database_query_store_options]) AS query_store_state, db.collation_name, (SELECT collation_name FROM master.sys.databases (NOLOCK) WHERE database_id = 1) AS master_collation, db.is_auto_close_on, db.is_auto_shrink_on, db.page_verify_option, db.is_db_chaining_on, db.is_auto_create_stats_incremental_on, db.is_trustworthy_on, db.is_parameterization_forced FROM [sys].[databases] (NOLOCK) AS db WHERE db.[name]=@TargetName"
}
}
]
```
The complete example is available in the [MakingCustomChecks_sample](MakingCustomChecks_sample.json) file.
You can also build more complex rules by applying certain conditions and using different kinds of operators, as described in [Customization](../Customization)
@@ -0,0 +1,36 @@
# Disabling Built-in Rules
You can silence specific rules when they aren't applied to your environment or until the scheduled work is done to rectify the issue.
The following is an example consisting of three rules. The first rule shows how to disable the check by specifying its ID, the second example disables all checks that have the **TraceFlag** tag, and the third example disables all checks from the default ruleset using the **DefaultRuleset** tag for databases **DBName1** and **DBName2**.
```json
{
"schemaVersion": "1.0",
"version": "0.2",
"name": "Custom Overrides",
"rules":
[
{
"id": "LatestCU",
"itemType": "override",
"enabled": false
},
{
"id": ["TraceFlag"],
"itemType": "override",
"enabled": false
},
{
"id": ["DefaultRuleset"],
"itemType": "override",
"targetFilter":
{
"type": "Database",
"name": [ "DBName1", "DBName2" ]
},
"enabled": false
}
]
}
```
@@ -0,0 +1,73 @@
# Overriding Default Thresholds
Some of the default thresholds provided by the SQL Assessment API might not fit your current infrastructure at the moment of the assessment. In such scenarios, you can easily override them with your own values.
Let's consider an example of using the **FullBackup** rule that validates whether the date your backup was created goes beyond the threshold specified in the rule.
To invoke the assessment, you can run the following command:
```PowerShell
Invoke-SqlAssessment -Check FullBackup | Select TagerObject, Message
```
Depending on the number of objects, the output will be similar to the following one:
```
TargetObject Message
------------- --------
[DevTest] Create full backup. Last full backup is over 7 days old
[Prototype] Create full backup. Last full backup is over 7 days old
```
As you can see, the output tells us that there are two databases that have their backups created more than 7 days ago, which is the default threshold.
Let's say you need to change this rule to validate backups that were created more than 3 days ago instead of 7. You can do this by creating a new json file. Such a file would then be acting as an additional configuration that overrides the default thresholds. As per this example, we will create a file called `BackupPolicy.json` and add the following contents to this file:
```json
{
//Sets the schema version
"schemaVersion": "1.0",
//Sets the version
"version": "1.0",
//Sets the rule name
"name": "Backup Policy",
//Sets the override for the specified rule
"rules":
[
{
//Sets the type, which is 'override'
"itemType": "override",
//Sets the name of the rule that should be overridden
"id": "FullBackup",
//Sets the new threshold
"threshold": 3
}
]
}
```
Then you can save this file to whatever place you need and invoke the assessment, as the following example demonstrates.
```PowerShell
Invoke-SqlAssessment -Check FullBackup -Configuration .\BackupPolicy.json | Select TagerObject, Message
```
Here, you are basically using the same command, as shown in the example above, but extended with `-Configuration .\BackupPolicy.json`. This command instructs the assessment to use a custom configuration file that keeps your overrides.
The output in this case would be similar to the following one:
```
TargetObject Message
------------- --------
[DevProd] Create full backup. Last full backup is over 3 days old
[TestDB] Create full backup. Last full backup is over 3 days old
[DevTest] Create full backup. Last full backup is over 3 days old
[Prototype] Create full backup. Last full backup is over 3 days old
```
As you can see, the output is now different from the first one; now it shows all the databases, the backup date of which is over 3 days.
@@ -0,0 +1,9 @@
# Tutorial
This section is intended to guide you through some of the most frequently used customization workflows in the SQL Assessment API. You can easily create your own rules, disable built-in rules if do not want to use them, or you can override default thresholds with your own values to assess your configuration in a particular manner.
## In This Section
- [Creating Custom Rules](CreatingCustomRules.md)
- [Disabling Built-in Rules](DisablingBuiltInRules.md)
- [Overriding Default Thresholds](OverridingDefaultThresholds.md)