SQL Server on Linux용 SQL 평가 API 사용

적용 대상:SQL Server on Linux

SQL 평가 API는 SQL Server 구성을 평가하여 모범 사례를 평가할 수 있는 메커니즘을 제공합니다. API는 SQL Server 팀에서 권장하는 모범 사례가 포함된 규칙 집합과 함께 제공됩니다. 이 규칙 집합은 새 버전의 출시로 향상되었습니다. SQL Server 구성이 권장 모범 사례와 일치하는지 확인하는 것이 유용합니다.

Microsoft에서 제공하는 규칙 세트는 GitHub에서 확인할 수 있습니다. 샘플 리포지토리에서 전체 규칙 세트를 볼 수 있습니다.

이 글에서는 PowerShell을 사용해 Linux와 컨테이너에서 SQL Server on Linux용 SQL Assessment API를 실행해 보세요.

필수 조건

  1. Linux에 Azure PowerShell을 설치했는지 확인합니다.

  2. SqlServer 사용자로 실행하여 PowerShell 갤러리에서 mssql PowerShell 모듈을 설치합니다.

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

평가 설정

SQL 평가 API는 JSON 형식으로 출력을 제공합니다. SQL 평가 API를 구성하려면 다음 단계를 완료하세요:

  1. 평가하려는 인스턴스에서 SQL 인증을 사용해 SQL Server 평가를 위해 로그인 계정을 생성하세요. 다음 Transact-SQL 스크립트를 사용해 로그인 및 강력한 비밀번호를 설정하세요. 암호는 SQL Server 기본 암호 정책을 따라야 합니다. 기본적으로 암호는 8자 이상이어야 하며 대문자, 소문자, 0~9까지의 숫자 및 기호 네 가지 집합 중 세 집합의 문자를 포함해야 합니다. 암호 길이는 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
    }
}

참고

이 스크립트를 신뢰할 수 있는 환경에서 실행할 때 사전 로그인 핸드셰이크 오류가 발생하면, 코드에서 Get-SqlInstance, $serverName, -TrustServerCertificate 줄의 명령에 $hostName 플래그를 추가하세요.

평가 실행

  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 형식으로 생성됩니다. 이 출력은 Visual Studio Code를 포함한 JSON 파일 파싱을 지원하는 모든 도구와 함께 사용할 수 있습니다.