web-dev-qa-db-ja.com

SQL ServerエージェントでPowershellスクリプトを実行している実行ポリシーエラー

認証情報を介して私のADアカウントを使用して2014年にSQL ServerエージェントからPowerShellスクリプトを実行します。次のエラーが発生します。

ジョブステップは、PowerShellスクリプトの1行目でエラーを受け取りました。対応する行は「set-executionpolicy RemoteSigned -scope process -Force」です。スクリプトを修正し、ジョブを再スケジュールしてください。 PowerShellによって返されるエラー情報は次のとおりです: 'セキュリティエラー。

グーグルでの私の検索では、何も役に立たなかった。ワークステーションでSSMSを介してPowershellコンソールから問題なくスクリプトを実行できます。

実行ポリシーが無制限に設定されています

_PS C:\WINDOWS\system32> Get-ExecutionPolicy
Unrestricted
_

_RemoteSigned -scope process -Force_はコードのどこにもないため、エラー出力で言及されている行はSQL Serverによって自動的に追加される必要があります。

ジョブを実行するためにADアカウントを使用する以外に、SQL Serverエージェントで設定する必要があるものはありますか?

これが_msdb.dbo.syssubsystems_のPowerShell行です

C:\Program Files (x86)\Microsoft SQL Server\120\Tools\Binn\SQLPS.exe

更新

こちらがバージョンです

_PS SQLSERVER:\SQL\CD000023\CEF_2014_1> $PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
2      0      -1     -1
_

2015年1月3日更新

このスクリプトは、中央管理サーバーの登録済みサーバーに基づいて、サーバーリストを作成します。次に、それらの各サーバーに接続し、リッスンしているポートを識別します。

_# connection parameters
 Param ( 
      [string] $CMSServer="someuser\someinstance",  # CMS server that stores serverlist
      [string] $CMSDatabase="msdb",                 # database where the serverlist is stored
      [string] $CMSUser="someuser",         # username to connect to the cms server
      [string] $CMSPassword="somepassword",     # password to connect with the cmsuser
      [string] $CMSTable="dbo.serverlist",          # name of table that stores instances
      [string] $CMSTableNoSchema="serverlist",      # name of table that stores instances
      [string] $UserName="remoteuser",              # username to connect to each instance
      [string] $Password="remotepassword",      # password to connect to each instance
      [string] $SrcDatabase="tempdb",               # database where listening ports are stored
      [string] $SrcTable="#listeningport"           # table where listening ports are stored


 )

 # load in the SQL Server Powershell Module
 [System.Reflection.Assembly]::LoadWithPartialName( `
  "Microsoft.SqlServer.Smo");


 # log file function
 $logfile = "c:\temp\get_server_ports_$(get-date -format `"yyyy_MM_ddtt`").txt"
 # initalize log file
 $logfile | out-file -Filepath $logfile 

  function log($string, $color)
{
   if ($Color -eq $null) {$color = "white"}
   write-Host $string -foregroundcolor $color
   $string | out-file -Filepath $logfile -append
}

# CMS Server connection 
$CMSServerConnectionString = "Data Source=$CMSServer;Initial Catalog=$CMSDatabase;User Id=$CMSUser;PWD=$CMSPassword;"
$CMSServerConnection = new-object system.data.SqlClient.SqlConnection($CMSServerConnectionString);
$CMSServerConnection.Open() 

# create SMO objects so that tables can be created and dropped
$srv = new-Object Microsoft.SqlServer.Management.Smo.Server($CMSServerConnection)
$db = New-Object Microsoft.SqlServer.Management.Smo.Database
$db = $srv.Databases.Item($CMSDatabase)

# drop and recreate the serverlist Table on the CMS server
$tb = $db.Tables[$CMSTableNoSchema]
IF ($tb)
    {$tb.Drop()}

# Create the serverlist Table on the cms server
$tb = new-object Microsoft.SqlServer.Management.Smo.Table($db, $CMSTableNoSchema)
$col1 = new-object Microsoft.SqlServer.Management.Smo.Column($tb, "server_name", [Microsoft.SqlServer.Management.Smo.DataType]::NChar(255))
$col2 = new-object Microsoft.SqlServer.Management.Smo.Column($tb, "server_port", [Microsoft.SqlServer.Management.Smo.DataType]::Int)
$tb.Columns.Add($col1)
$tb.Columns.Add($col2)
$tb.Create()

# collect the list of servers
$cmd4 = new-object System.Data.SQLClient.SQLCommand
$cmd4.CommandText = "
    insert into msdb.dbo.serverlist (server_name, server_port)
    select server_name, 1 from msdb.dbo.sysmanagement_shared_registered_servers_internal
"
$cmd4.Connection = $CMSServerConnection
$rowsInserted = $cmd4.ExecuteNonQuery()


# Create a Dataset to hold the DataTable from server_list
$dataSet = new-object "System.Data.DataSet" "ServerListDataSet"
$query = "SET NOCOUNT ON;"
$query = $query + "SELECT server_name "
$query = $query + "FROM   $CMSDatabase.$CMSTable where server_name not in(
    select server_name from $CMSDatabase.dbo.excludeServerList 
  )"

# Create a DataAdapter which you'll use to populate the DataSet with the results
$dataAdapter = new-object "System.Data.SqlClient.SqlDataAdapter" ($query, $CMSServerConnection)
$dataAdapter.Fill($dataSet) | Out-Null

$dataTable = new-object "System.Data.DataTable" "ServerList"
$dataTable = $dataSet.Tables[0]


# for each server
$dataTable | FOREACH-OBJECT {
 Try 
    {   #write-Host "server_name: " $_.server_name
        log "server_name : $ServerBConnectionString" yellow
        $ServerBConnectionString = "Data Source="+$_.server_name+";Initial Catalog=$SrcDatabase;User Id=$UserName;PWD=$Password" 
        #write-Host "ServerBConnection: " $ServerBConnectionString
        $ServerBConnection = new-object system.data.SqlClient.SqlConnection($ServerBConnectionString);
        $ServerBConnection.Open()

        # create SMO objects so that tables can be created and dropped
        $srv = new-Object Microsoft.SqlServer.Management.Smo.Server($ServerBConnection)
        $db = New-Object Microsoft.SqlServer.Management.Smo.Database
        $db = $srv.Databases.Item($SrcDatabase)

        # collect port number from server
        $cmd3 = new-object System.Data.SQLClient.SQLCommand
        $cmd3.CommandText = "
            SELECT
            @@SERVERNAME as servername,
            cast(CONNECTIONPROPERTY('local_tcp_port') as int) AS port
            INTO $SrcTable
        "
        $cmd3.Connection = $ServerBConnection
        $rowsInserted = $cmd3.ExecuteNonQuery()


        # get port number from table
        $cmd2 = new-object System.Data.SQLClient.SQLCommand
        $cmd2.CommandText = "SELECT port FROM $SrcTable"
        $cmd2.Connection = $ServerBConnection
        $port = [Int32]$cmd2.ExecuteScalar()

        #write-Host "port: " $port
        log "port:  $port" yellow

        # update cms table
          $cmd = new-object System.Data.SQLClient.SQLCommand
          $cmd.CommandText = "UPDATE $CMSDatabase.$CMSTable SET server_port = $port WHERE server_name = '"+$_.server_name+"'"
          #write-Host "success: " $cmd.CommandText
          $cmd.Connection = $CMSServerConnection
          $rowsUpdated = $cmd.ExecuteNonQuery()

        log "success:  $_.server_name" green        
        #write-Host "success: " $_.server_name
        $ServerBConnection.Close()

    } Catch [System.Exception] 
  { 
    $ex = $_.Exception 
    #write-Host "failure: " $ex.Message " on server " $_.server_name
    log "failure: $ex.Message on server $_.server_name" red 
    #Write-Host $ex.Message 
  } 
  Finally 
  { 
    #write-Host "server_name: " $_.server_name
  } 


}

$CMSServerConnection.Close()
_
7
Craig Efrein

受け取ったエラーは実際には connect item で示されていましたが、Microsoftはclosed won't fixと表示しています。この接続項目で見逃されているのは、SQLPSのサブシステムがレジストリキーを介して設定されているという事実です。いつ、実際にこれを設定するのかはわかりません。

このレジストリキーは以下のパスにあり、ローカルボックスではRemoteSignedに設定されています。レジストリキーを変更することはお勧めしませんが、これをRemoteSignedに変更すると、スクリプトがエラーなしで実行される可能性があります。 SQLエージェントサービスの再起動が必要になる場合がありますが、わかりません。

enter image description hereHKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.SqlServer.Management.PowerShell.sqlps120

これで、Unrestrictedを使用して、PowerShellスクリプトの実行時にスクリプトにプロンプ​​トを表示させることができます。 SQLエージェントがプロンプトに応答できないか、プロンプトの処理方法がわからないため、これが実際にエラーを生成している可能性があります。 RemoteSignedは、プロンプトが表示されずにサーバー上で作成および設定したスクリプトを実行できるようにするための十分なポリシーであるため、このポリシー設定を使用する理由は本当にありません。

返された完全なエラーを掘り下げた場合は、以下のメッセージに類似したテキストが含まれている可能性があります。これは、実行ポリシーをUnrestrictedに設定したときに受け取るプロンプトです。

セキュリティ警告

信頼できるスクリプトのみを実行してください。インターネットからのスクリプトは有用ですが、このスクリプトはコンピュータに害を及ぼす可能性があります。走りたいですか

[D]実行しない[R] 1回実行[S]一時停止[?]ヘルプ(デフォルトは "D"):

3
user507

特に組み込みSQLサーバージョブsyspolicy_purge_historyでもこの問題に苦しんでいます。SQLServer 2012以降のSQLPSで問題が発生しているようです。 2008 R2でも同じ問題は発生しません。
SQLPS 2012のSQLPSは、リモート署名されるSQLPSのプロセス実行ポリシーの明示的な設定を追加することでさらに安全になりました。問題は、Powershellの実行ポリシーのスコープが階層であり、set-ExecutionPolicyより低いスコープ(プロセスなど)でより制限的なポリシーを使用すると、次のようなエラーが発生します。 enter image description here

だからあなたのオプションは:

  • レジストリでHKLM:SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.SqlServer.Management.PowerShell.sqlps110\ExecutionPolicyにあるSQLPS実行ポリシーをLocalMachineに一致させるか、またはGPOがMachinePolicyと同じように構成されている場合.
  • LocalMachineポリシーをBypassに設定します。この場合、上記のエラーはなくなります。

私にとって、以前のレジストリ変更は、ドメイン管理者に自分のサーバーだけに別のマシンポリシーを設定するように説得するよりも簡単です。

1
Nick Kavadias