web-dev-qa-db-ja.com

PowerShellを使用してSQL Serverの実行中のすべてのインスタンスを検出する最も効果的な方法は何ですか?

私は、ドメイン内で実行されているSQL Serverのすべてのインスタンスを発見する任務を負っています。サーバーごとに複数のインスタンスがある場合があります。これらのインスタンスを検出するための2つの異なるPowerShellメソッドを確認しましたが、どちらもすべてのインスタンスを検出するようには見えません。

1)WMIを使用する

        $srvr = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer $computerName
    $instances = $srvr | ForEach-Object {$_.ServerInstances} | Select @{Name="fullName";Expression={$computerName +"\"+ $_.Name}}   
    return $instances

2)リモートレジストリを使用する(Get-SQLInstance 1 と同様)

私が遭遇している最大の問題は、私が知っているすべてのサーバーがSQL Server WMIプロバイダーで実行されているわけではなく、リモートレジストリを許可しているサーバーもすべてないということです。 3番目の方法はありますか?リモートデスクトップを使用してすべてのサーバーにアクセスできますが、約30台のマシンを調べており、可能であれば手動での手順を避けたいと考えています。これはSQL Server 2008以降でのみ機能する必要があり、他のSQL Serverサービス(SSIS/SSAS/SSRS)について知っておくと便利ですが、私の主な焦点はSQL Server自体です。

13
Elsimer

将来的に役立つ何かが必要な場合は、レジストリを検索することを避けます。 SQL Serverのじんましんは、長年の間に少し変わっており、それに追いつくのは面倒な場合があります。

SqlDataSourceEnumeratorを使用した方法は不安定な場合がありますが、使用しますが、インスタンスがネットワーク上にあるという具体的な証拠はありません。 SQLブラウザサービスにも依存していると思います。ほとんどの場合、無効になっています。

WMIクラスwin32_Serviceを使用します。 Get-Serviceコマンドレットよりもサービスに関する詳細情報を提供するため、これを使用します。

トラブルシューティングのためにサービスの毎日のチェックや検証を実際に行うためにこれを使用できるため、すべてを関数として一般的に記述します。

function Get-ServiceStatus ([string[]]$server)
{
 foreach ($s in $server)
 {
   if(Test-Connection $s -Count 2 -Quiet)
   {
    Get-WmiObject win32_Service -Computer $s |
     where {$_.DisplayName -match "SQL Server"} | 
     select SystemName, DisplayName, Name, State, Status, StartMode, StartName
   }
 }
}

これは、私が通常使用するものより少し多いですが、誰かがそれに遭遇してそれを使用したい場合に備えて。 Test-ConnectionはDOSプロンプトのping myserverに相当し、-Quietフラグは単にtrueまたはfalseを返します。これはデフォルトで4つのpingになるため、-Count 2を設定すると、代わりに2回実行されます。

変数[string[]]$serverは、$serverがサーバー名の配列を受け入れることを示すために使用されるメソッドです。したがって、この関数の呼び出し例は次のようになります。

Get-ServiceStatus -server (Get-Content C:\temp\MyServerList.txt)

または

$servers = 'MyServer1','MyServer2','MyServer3'
Get-ServiceStatus -server $servers

[〜#〜]編集[〜#〜]

上記のコメントは、提供されているサーバーのリストに依存することに注意してください。そのリストが提供されていない場合は、他にいくつかのオプションがあります。

  • Active Directory環境にいる場合は、PowerShellで ActiveDirectory モジュールを使用して、Get-ADComputerコマンドレットでドメイン上のすべてのサーバーのリストをプルできます。ただし、大規模なドメインでは適切な-Filterを使用してください。

  • また、ポート1433が開いていることが判明したIPアドレスを提供するネットワークのIPスキャンを(承認付きで)単純に実行しました。そのIPリストを取得し、Get-ADComputerを使用してドメインコンピュータ名を見つけ、それを上記の関数に渡します

例:

Import-Module ActiveDirectory
$sList = $ipList | Select -ExpandProperty IP
$results = foreach ($i in $sList) { 
 Get-ADComputer -Filter 'IPv4Address -eq $i' -Properties * | Select Name}
Get-ServiceStatus -server $results

[〜#〜]編集[〜#〜]

Write-Verboseを利用し、try/catchブロックを追加することをお勧めしますが、これは便利な場合があります。ほとんどの場合、コードの練習では、この関数を使用して追加する人に任せます。追加のコードまたは機能。続行するための基本的な例を提供しようとしています。実際のサーバー名を返す情報を含めるためにSystemNameプロパティを出力に追加しましたが、他の関数でこれを行うと、通常、一度に複数のサーバーでこれを使用しないため、気になりませんでした。

12
user507

可能性のあるすべての所有サーバーとその特定の名前を知らなくても、環境全体でインスタンスを検出する唯一の方法は、 System.Data.Sql.SqlDataSourceEnumerator.GetDataSources() を呼び出すことです。 =ただし、このメソッドには多数の脚注が付属しています。以下は、そのMSDNリソースから直接プルされたスニペットです。

SqlDataSourceEnumeratorがネットワーク上のデータソースを見つけるために使用するメカニズムの性質上、このメソッドは常にa利用可能なサーバーの完全なリスト。リストはすべての呼び出しで同じであるとは限りません。この関数を使用してユーザーがリストからサーバーを選択できるようにする場合は、サーバーの列挙で使用可能なすべてのサーバーが返されない場合に備えて、リストにない名前を入力するオプションも必ず指定してください。さらに、このメソッドの実行にはかなりの時間がかかる場合があるため、パフォーマンスが重要な場合は呼び出しに注意してください。

呼び出しはPowerShellから簡単です。

[System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources()

そのメソッドは、それに応じて処理できるDataTableオブジェクトを返します。

5
Thomas Stringer

SQL Browserサービスがアクティブな場合、以下のPowerShellコードを使用して、SQLインスタンスのサービスをクエリできます。クエリを実行する次のコマンドレットを実装します。

  • Get-SqlBrowserInstanceList
  • Get-SqlBrowserInstanceInfo
  • Get-SqlBrowserInstanceDac

    function Parse-ServerResponse([byte[]] $responseData)
    {
        [PSObject[]] $instances = @()
    
        if (($responseData -ne $null) -and ($responseData[0] -eq 0x05))
        {
            $responseSize = [System.BitConverter]::ToInt16($responseData, 1)
    
            if ($responseSize -le $responseData.Length - 3)
            {
                # Discard any bytes beyond the received response size. An oversized response is usually the result of receiving multiple replies to a broadcast request.
                $responseString = [System.Text.Encoding]::Default.GetString(($responseData | Select -Skip 3 -First $responseSize))
                $instanceResponses = $responseString.Split(@(";;"), [System.StringSplitOptions]::RemoveEmptyEntries)
    
                $instances = foreach ($instanceResponse in $instanceResponses)
                {
                    $instanceResponseValues = $instanceResponse.Split(";")
                    $instanceResponseHash = @{}
                    for ($index = 0; $index -lt $instanceResponseValues.Length; $index += 2)
                    {
                        $instanceResponseHash[$instanceResponseValues[$index]] = $instanceResponseValues[$index + 1]
                    }
    
                    New-Object PSObject -Property $instanceResponseHash
                }
            }
            else
            {
                Write-Warning "The response was too short. Expected $($responseSize) bytes but got $($responseData.Length - 3)."
            }
        }
    
        return ,$instances
    }
    
    function Parse-ServerResponseDac([byte[]] $responseData)
    {
        $dacPort = 0
    
        if (($responseData -ne $null) -and ($responseData[0] -eq 0x05))
        {
            $responseSize = [System.BitConverter]::ToUInt16($responseData, 1)
    
            if (($responseData.Length -eq 6) -and ($responseSize -eq 6))
            {
                if ($responseData[3] -eq 0x01)
                {
                    $dacPort = [System.BitConverter]::ToUInt16($responseData, 4)
                }
                else
                {
                    Write-Error "An unexpected protocol version was returned. Expected 0x01 but got $($requestData[3])."
                }
            }
            else
            {
                Write-Error "The response size was incorrect."
            }
        }
    
        return $dacPort
    }
    
    function Get-SqlBrowserInstanceList
    {
        <#
        .SYNOPSIS
        Gets the list of available SQL Instances on the server.
        .DESCRIPTION
        Gets the list of available SQL Instances on the server by querying the SQL Browser Service on port 1434.
        .EXAMPLE
        Get-SqlBrowserInstanceList servername
        .EXAMPLE
        Get-SqlBrowserInstanceList servername.dnsdomain.tld
        .EXAMPLE
        Get-SqlBrowserInstanceList $env:COMPUTERNAME
        .EXAMPLE
        Get-SqlBrowserInstanceList 192.168.1.255 -Broadcast
        .EXAMPLE
        Get-SqlBrowserInstanceList 255.255.255.255 -Broadcast
        .PARAMETER $ServerName
        The name or IP Address of the server.
        .PARAMETER $Broadcast
        If the broadcast switch is specified, the query will be sent as a broadcast and may receive replies from multiple hosts; otherwise, the query is sent to a single server.
        #>
        [CmdletBinding(SupportsShouldProcess = $False)]
        param
        (
            [Parameter(Mandatory = $True, ValueFromPipeLine = $True)]
            [string] $ServerName,
            [switch] $Broadcast
        )
    
        process
        {   
            [System.Net.IPAddress] $ipAddress = [System.Net.Dns]::GetHostAddresses($serverName) | Select -First 1
            $parsedResponses = @()
    
            if ($ipAddress -ne $null)
            {
                [System.Net.IPEndPoint] $localIPEndPoint = New-Object System.Net.IPEndPoint([System.Net.IPAddress]::Any, 0)
                [System.Net.IPEndPoint] $remoteIPEndPoint = New-Object System.Net.IPEndPoint($ipAddress, 1434)
    
                if ($ipAddress -eq [System.Net.IPAddress]::Broadcast)
                {
                    $Broadcast = $true
                }
    
                [System.Net.Sockets.UdpClient] $receiver = New-Object System.Net.Sockets.UdpClient
                $receiver.Client.ReceiveTimeout = 30000
    
                [byte] $queryMode = 0x03
                $sleepDuration = 1
                [System.Net.Sockets.UdpClient] $sender = $null
    
                if ($Broadcast -eq $true)
                {
                    Write-Verbose "Using broadcast mode."
                    $queryMode = 0x02
                    $sleepDuration = 30
    
                    # Set the receiver to allow another client on the same socket.
                    $receiver.Client.SetSocketOption([System.Net.Sockets.SocketOptionLevel]::Socket, [System.Net.Sockets.SocketOptionName]::ReuseAddress, $true)
                    $receiver.Client.Bind($localIPEndPoint)
    
                    # Because broadcasting from this UdpClient instance causes the underlying socket to be unable to receive normally, a separate sender must be bound to the same socket as the receiver.
                    # NOTE: Windows Firewall does not view a reused socket as being part of the same conversation. If Windows Firewall is active, this requires special firewall rules to work.
                    $sender = New-Object System.Net.Sockets.UdpClient
                    $sender.EnableBroadcast = $Broadcast
                    $sender.Client.SetSocketOption([System.Net.Sockets.SocketOptionLevel]::Socket, [System.Net.Sockets.SocketOptionName]::ReuseAddress, $true)
                    $sender.Client.Bind($receiver.Client.LocalEndPoint);
                }
                else
                {
                    $sender = $receiver
                    $receiver.Client.Bind($localIPEndPoint)
                }
    
    
                $responses = @{}
    
                try
                {
                    # Send the broadcast.
                    Write-Verbose "Sending request to $($ipAddress)..."
                    $sender.Connect($remoteIPEndPoint)
                    $bytesSent = $sender.Send(@($queryMode), 1)
    
                    # Wait to give responses time to arrive.
                    Sleep $sleepDuration
    
                    do
                    {
                        [System.Net.IPEndPoint] $responderIPEndPoint = $null
                        $response = $receiver.Receive([ref] $responderIPEndPoint)
                        $responder = $responderIPEndPoint.ToString()
    
                        if ($responses.Contains($responder))
                        {
                            $responses[$responder] += $response
                        }
                        else
                        {
                            $responses.Add($responder, $response)
                        }
                    } while ($receiver.Available -gt 0)
                }
                finally
                {
                    if ($sender -ne $receiver)
                    {
                        $sender.Close()
                        $sender.Dispose()
                    }
    
                    $receiver.Close()
                    $receiver.Dispose()
                }
    
                foreach ($responseItem in $responses.GetEnumerator())
                {
                    Write-Verbose "Parsing the response from $($responseItem.Name)..."
                    $parsedResponse = Parse-ServerResponse $responseItem.Value
                    $parsedResponses += $parsedResponse
                    Write-Verbose ($parsedResponse | ft ServerName, InstanceName, tcp, np, Version, IsClustered -AutoSize |Out-String)
                }
            }
    
            return $parsedResponses
        }
    }
    
    function Get-SqlBrowserInstanceInfo
    {
        <#
        .SYNOPSIS
        Gets information about the specified SQL Instance from the server.
        .DESCRIPTION
        Gets information about the specified SQL Instance from the server by querying the SQL Browser Service on port 1434.
        .EXAMPLE
        Get-SqlBrowserInstanceInfo servername instancename
        .EXAMPLE
        Get-SqlBrowserInstanceInfo servername.dnsdomain.tld instancename
        .EXAMPLE
        Get-SqlBrowserInstanceInfo $env:COMPUTERNAME
        .PARAMETER $ServerName
        The name or IP Address of the server.
        .PARAMETER $InstanceName
        The name of the SQL Instance.    #>
        [CmdletBinding(SupportsShouldProcess = $False)]
        param
        (
            [Parameter(Mandatory = $True, ValueFromPipeLine = $True)]
            [string] $ServerName,
            [Parameter(Mandatory = $True, ValueFromPipeLine = $False)]
            [string] $InstanceName
        )
    
        process
        {   
            $instances = @()
            [System.Net.IPAddress] $ipAddress = $null
    
            $ipAddress = [System.Net.Dns]::GetHostAddresses($serverName) | Select -First 1
    
            if ($ipAddress -ne $null)
            {
                [System.Net.IPEndPoint] $ipEndPoint = New-Object System.Net.IPEndPoint($ipAddress, 1434)
                [System.Net.Sockets.UdpClient] $udpClient = New-Object System.Net.Sockets.UdpClient
                $udpClient.Client.ReceiveTimeout = 10000
    
                $instanceNameData = [System.Text.Encoding]::Default.GetBytes($instanceName)
                [byte[]] $requestData = @(0x04) + $instanceNameData + 0x00
                [byte[]] $responseData = $null
    
                try
                {
                    $udpClient.Connect($ipEndPoint)
    
                    $bytesSent = $udpClient.Send($requestData, $requestData.Length)
    
                    $responseData = do
                    {
                        $udpClient.Receive([ref] $ipEndPoint)
                    } while ($udpClient.Available -gt 0)
                }
                finally
                {
                    $udpClient.Close()
                    $udpClient.Dispose()
                }
    
                $instances = Parse-ServerResponse $responseData
            }
    
            return $instances
        }
    }
    
    function Get-SqlBrowserInstanceDac
    {
        <#
        .SYNOPSIS
        Gets the Dedicated Administrator Connection port number for the specified SQL Instance on the server.
        .DESCRIPTION
        Gets the Dedicated Administrator Connection port number for the specified SQL Instance on the server by querying the SQL Browser Service on port 1434.
        .EXAMPLE
        Get-SqlBrowserInstanceDac servername instancename
        .EXAMPLE
        Get-SqlBrowserInstanceDac servername.dnsdomain.tld instancename
        .EXAMPLE
        Get-SqlBrowserInstanceDac $env:COMPUTERNAME instancename
        .PARAMETER $ServerName
        The name or IP Address of the server.
        .PARAMETER $InstanceName
        The name of the SQL Instance.
        #>
        [CmdletBinding(SupportsShouldProcess = $False)]
        param
        (
            [Parameter(Mandatory = $True, ValueFromPipeLine = $True)]
            [string] $ServerName,
            [Parameter(Mandatory = $True, ValueFromPipeLine = $False)]
            [string] $InstanceName
        )
    
        process
        {   
            [System.UInt16] $dacPort = 0
            [System.Net.IPAddress] $ipAddress = $null
    
            $ipAddress = [System.Net.Dns]::GetHostAddresses($serverName) | Select -First 1
    
            if ($ipAddress -ne $null)
            {
                [System.Net.IPEndPoint] $ipEndPoint = New-Object System.Net.IPEndPoint($ipAddress, 1434)
                [System.Net.Sockets.UdpClient] $udpClient = New-Object System.Net.Sockets.UdpClient
                $udpClient.Client.ReceiveTimeout = 30000
    
                $instanceNameData = [System.Text.Encoding]::Default.GetBytes($instanceName)
                [byte[]] $requestData = @(0x0F) + 0x01 + $instanceNameData + 0x00
                [byte[]] $responseData = $null
    
                try
                {
                    $udpClient.Connect($ipEndPoint)
    
                    $bytesSent = $udpClient.Send($requestData, $requestData.Length)
    
                    $responseData = do
                    {
                        $udpClient.Receive([ref] $ipEndPoint)
                    } while ($udpClient.Available -gt 0)
                }
                finally
                {
                    $udpClient.Close()
                    $udpClient.Dispose()
                }
    
                $dacPort = Parse-ServerResponseDac($responseData)
            }
    
            return $dacPort
        }
    }
    
3
JamieSee

考えられるSQLインスタンスを識別する別の方法は、Active Directoryにリストされているサービスプリンシパル名(SPN)を調べることです。 Windows認証を使用してリモートでSQL Serverに接続すると、認証プロセスでSPNが使用されます。 SPNの存在は、サーバー/インスタンスが確実にそこにあり、実行されていることを意味するものではありませんが、他のいくつかのアプローチより包括的であることがわかった、可能なインスタンスのリストを提供します。

人生を楽にするために、次のGet-SPNコマンドレットを使用します。 https://gallery.technet.Microsoft.com/scriptcenter/Get-SPN-Get-Service-3bd5524a

Get-SPN.ps1スクリプトをダウンロードして、C:\ powershell_scripts\Get-SPN.ps1に保存し、PowerShellで次のコマンドを実行します。

. "C:\powershell_scripts\Get-SPN.ps1"
Get-SPN -ServiceClass MSSQLSvc

(もちろん、スクリプトを別の場所に保存できます。必要に応じて、最初の行を更新してください。)

これにより、サービスのポート/インスタンスに関連する「仕様」を含む、現在のドメイン上のすべてのSQL Server SPNが一覧表示されます。

2
Matt

Get-Service -ComputerName * MSSQL * | Where-Object {$ _。status -eq "Running"}

名前付きインスタンスとデフォルトインスタンスが取得されます。マシンのリストなどを反復するだけです。

0
user41207