使用适用于 Linux 上的 SQL Server 的 SQL 评估 API

适用于:Linux 上的 SQL Server

SQL 评估 API 提供了评估 SQL Server 配置的最佳实践机制。 该 API 附带一个规则集,其中包含 SQL Server 团队建议的最佳做法。 随着新版本的发布,此规则集得到增强。 对于确保 SQL Server 配置符合建议的最佳做法,它非常有用。

Microsoft发布的规则集可在GitHub上获取。 可以在示例存储库中查看完整规则集。

在本文中,使用PowerShell运行Linux 上的 SQL Server及容器的SQL评估API。

先决条件

  1. 请确保在 Linux 上安装 PowerShell。

  2. 安装 PowerShell 库中的 SqlServer PowerShell 模块,并以 mssql 用户身份运行。

    su mssql -c "/usr/bin/pwsh -Command Install-Module SqlServer"
    

设置评估

SQL 评估 API 提供 JSON 格式的输出。 要配置SQL评估API,请完成以下步骤:

  1. 在你想评估的实例中,创建一个使用 SQL 认证的 SQL Server 评估登录名。 请使用以下 Transact-SQL 脚本创建一个登录名和强密码。 密码应遵循 SQL Server 默认密码策略。 默认情况下,密码必须为至少八个字符且包含以下四种字符中的三种:大写字母、小写字母、十进制数字、符号。 密码可最长为 128 个字符。 使用的密码应尽可能长,尽可能复杂。

    USE [master];
    GO
    
    CREATE LOGIN [assessmentLogin]
        WITH PASSWORD = N'<password>';
    
    GRANT CONTROL SERVER TO [assessmentLogin];
    GO
    

    这个 CONTROL SERVER 权限适用于大多数评估。 不过,有些评估可能需要系统 管理员 权限。 如果你不执行这些评估,请使用 CONTROL SERVER 权限。

  2. 存储连接实例的凭据。 用你之前用过的密码替换 <password> 。

    echo "assessmentLogin" > /var/opt/mssql/secrets/assessment
    echo "<password>" >> /var/opt/mssql/secrets/assessment
    
  3. 通过确保只有 mssql 用户可以访问这些凭据,来保护新的评估凭据。

    chmod 600 /var/opt/mssql/secrets/assessment
    chown mssql:mssql /var/opt/mssql/secrets/assessment
    

下载评估脚本

以下示例脚本将调用你在前述步骤中创建的凭证调用 SQL 评估 API。 脚本在 /var/opt/mssql/log/assessments 目录中生成 JSON 输出。

注意

SQL 评估 API 还可以生成 CSV 和 XML 格式的输出。

可从 GitHub 下载该脚本。

可以将此文件另存为 /opt/mssql/bin/runassessment.ps1。

[CmdletBinding()] param ()

$Error.Clear()

# Create output directory if not exists

$outDir = '/var/opt/mssql/log/assessments'
if (-not ( Test-Path $outDir )) { mkdir $outDir }
$outPath = Join-Path $outDir 'assessment-latest'

$errorPath = Join-Path $outDir 'assessment-latest-errors'
if ( Test-Path $errorPath ) { remove-item $errorPath }

function ConvertTo-LogOutput {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        $input
    )
    process {
        switch ($input) {
            { $_ -is [System.Management.Automation.WarningRecord] } {
                $result = @{
                    'TimeStamp' = $(Get-Date).ToString("O");
                    'Warning'   = $_.Message
                }
            }
            default {
                $result = @{
                    'TimeStamp'      = $input.TimeStamp;
                    'Severity'       = $input.Severity;
                    'TargetType'     = $input.TargetType;
                    'ServerName'     = $serverName;
                    'HostName'       = $hostName;
                    'TargetName'     = $input.TargetObject.Name;
                    'TargetPath'     = $input.TargetPath;
                    'CheckId'        = $input.Check.Id;
                    'CheckName'      = $input.Check.DisplayName;
                    'Message'        = $input.Message;
                    'RulesetName'    = $input.Check.OriginName;
                    'RulesetVersion' = $input.Check.OriginVersion.ToString();
                    'HelpLink'       = $input.HelpLink
                }

                if ( $input.TargetType -eq 'Database') {
                    $result['AvailabilityGroup'] = $input.TargetObject.AvailabilityGroupName
                }
            }
        }

        $result
    }
}

function Get-TargetsRecursive {

    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Smo.Server] $server
    )

    $server
    $server.Databases
}

function Get-ConfSetting {
    [CmdletBinding()]
    param (
        $confFile,
        $section,
        $name,
        $defaultValue = $null
    )

    $inSection = $false

    switch -regex -file $confFile {
        "^\s*\[\s*(.+?)\s*\]" {
            $inSection = $matches[1] -eq $section
        }
        "^\s*$($name)\s*=\s*(.+?)\s*$" {
            if ($inSection) {
                return $matches[1]
            }
        }
    }

    return $defaultValue
}

try {
    Write-Verbose "Acquiring credentials"

    $login, $pwd = Get-Content '/var/opt/mssql/secrets/assessment' -Encoding UTF8NoBOM -TotalCount 2
    $securePassword = ConvertTo-SecureString $pwd -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential ($login, $securePassword)
    $securePassword.MakeReadOnly()

    Write-Verbose "Acquired credentials"

    $serverInstance = '.'

    if (Test-Path /var/opt/mssql/mssql.conf) {
        $port = Get-ConfSetting /var/opt/mssql/mssql.conf network tcpport

        if (-not [string]::IsNullOrWhiteSpace($port)) {
            Write-Verbose "Using port $($port)"
            $serverInstance = "$($serverInstance),$($port)"
        }
    }

    # IMPORTANT: If the script is run in trusted environments and there is a prelogin handshake error,
    # add -TrustServerCertificate flag in the commands for $serverName, $hostName and Get-SqlInstance lines below.
    $serverName = (Invoke-SqlCmd -ServerInstance $serverInstance -Credential $credential -Query "SELECT @@SERVERNAME")[0]
    $hostName = (Invoke-SqlCmd -ServerInstance $serverInstance -Credential $credential -Query "SELECT HOST_NAME()")[0]

    # Invoke assessment and store results.
    # Replace 'ConvertTo-Json' with 'ConvertTo-Csv' to change output format.
    # Available output formats: JSON, CSV, XML.
    # Encoding parameter is optional.

    Get-SqlInstance -ServerInstance $serverInstance -Credential $credential -ErrorAction Stop
    | Get-TargetsRecursive
    | ForEach-Object { Write-Verbose "Invoke assessment on $($_.Urn)"; $_ }
    | Invoke-SqlAssessment 3>&1
    | ConvertTo-LogOutput
    | ConvertTo-Json -AsArray
    | Set-Content $outPath -Encoding UTF8NoBOM
}
finally {
    Write-Verbose "Error count: $($Error.Count)"

    if ($Error) {
        $Error
        | ForEach-Object { @{ 'TimeStamp' = $(Get-Date).ToString("O"); 'Message' = $_.ToString() } }
        | ConvertTo-Json -AsArray
        | Set-Content $errorPath -Encoding UTF8NoBOM
    }
}

注意

当你在可信环境中运行此脚本时,如果遇到预登录握手错误,请在代码中 -TrustServerCertificate、$hostName 和 Get-SqlInstance 行的命令中添加 $serverName 标志。

执行评估

  1. 确保 mssql 用户拥有脚本并能执行。

    chown mssql:mssql /opt/mssql/bin/runassessment.ps1
    chmod 700 /opt/mssql/bin/runassessment.ps1
    
  2. 创建一个日志文件夹,并赋予 mssql 用户该文件夹的权限。

    mkdir /var/opt/mssql/log/assessments/
    chown mssql:mssql /var/opt/mssql/log/assessments/
    chmod 0700 /var/opt/mssql/log/assessments/
    
  3. 你现在可以以 mssql 用户身份创建你的第一个评估。 以该用户身份运行评估会更安全,尤其是在你之后通过 cron 或 systemd 自动运行评估时。

    su mssql -c "pwsh -File /opt/mssql/bin/runassessment.ps1"
    
  4. 命令完成后,它会生成 JSON 格式的输出。 你可以将这个输出与任何支持解析JSON文件的工具一起使用,包括Visual Studio Code。