web-dev-qa-db-ja.com

コマンドはCmdExecジョブとして失敗しますが、コンソールからは成功します

次のコマンドを実行するSQL Server 2014エージェントジョブ(タイプ: "オペレーティングシステム(CmdExec)"、実行: "SQL Serverエージェントサービスアカウント"、所有者:sa)があります。

powershell -ExecutionPolicy Bypass -Command "$objServiceManager = New-Object -ComObject 'Microsoft.Update.ServiceManager'; $objService = $objServiceManager.AddService2('7971f918-a847-4430-9279-4a52d1efe18d',2,''); $objService.PSTypeNames.Add('PSWindowsUpdate.WUServiceManager');"

SQL Serverエージェントが実行されているサービスアカウントには、インスタンスに対するsysadmin権限がありますが、サーバーへの管理者アクセス権はありません。

エージェントのサービスアカウントとしてサーバーにRDPを実行すると、コマンドプロンプトからコマンドを正常に実行できます。

enter image description here

しかし、SQL Serverエージェントジョブを実行すると、「アクセス拒否」エラーで失敗します。

Exception calling "AddService2" with "3" argument(s): "Access is denied. 
(Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))"
At line:1 char:79
+ $objServiceManager = New-Object -ComObject 
'Microsoft.Update.ServiceManager'; $o ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation

You cannot call a method on a null-valued expression.
At line:1 char:170
+ ... efe18d',2,''); 
$objService.PSTypeNames.Add('PSWindowsUpdate.WUServiceManager');
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

ジョブの[別のユーザーとして実行]を、インスタンスに対するsysadmin特権とサーバーへの管理者アクセス権を持つプロキシアカウントに設定した場合、成功しますが、それは私の質問にはあまり関係ありません。

編集:署名されていないPowershellスクリプトを実行する必要があるため、PowershellではなくCmdExecとしてすべてを実行しています(問題の原因ではないため、コードには含まれていません)。 TypeをPowershellに変更すると、同じエラーが発生します。

私の質問:同じコマンドがジョブとして失敗するが、同じサーバー上の同じユーザーアカウントから両方を実行するとコンソールから成功するのはなぜですか?

3
CaptainSlock

潜在的な回避策

  1. エージェントジョブからSQL Server CmdExecを介してPowerShellを実行しているため、生のPowerShellコマンドを実行するのではなく、保存されたPSロジックを含むPowerShellスクリプトを呼び出して、潜在的な違いがあるかどうかを確認できます。回避策。

  2. CmdExecシェル内のPowerShellで環境変数が正しく機能していない場合に問題が発生する場合に備えて、PowerShell.exeを指すようにサーバーの完全なOSパスを明示的に指定してから、PowerShellコマンドを渡してください。 。

私の質問:同じコマンドがジョブとして失敗するが、同じサーバー上の同じユーザーアカウントから両方を実行するとコンソールから成功するのはなぜですか?

考えられる理由

  • CmdExecシェルを使用してSQL Serverエージェントジョブを介してPowerShell.exeを呼び出すときに問題が発生する可能性があります。また、実行時に、実行されたアカウント(またはサービスアカウント)のセキュリティコンテキストを解釈する方法このようにPowerShell.exeを実行して、EXEへの実行アクセスを認証できないようにします。
  • インストールされているPowerShellのバージョン、インストールされているSQL Serverのバージョン、およびこれらすべてのWindows OSを確認して確認し、この点に関するバージョンの非互換性に関連する「既知の」問題を確認します。これは、既知の問題である場合があります。この環境のすべての詳細。

SQLエージェントジョブからPowerShellを実行する方法

次のことを行う必要があります。

  1. ADに[ユーザーアカウントオブジェクト]サービスアカウントを作成します(<DomainName>\<ServiceAccount>
  2. SQLログイン、つまり#1のサービスアカウントを作成します(<DomainName>\<ServiceAccount>
  3. 同じ(<DomainName>\<ServiceAccount>)アカウント認証情報でSQL認証情報を作成し、
  4. SQL Serverエージェントプロキシの作成 PowerShellを実行するSQLエージェントジョブの「別のユーザーとして実行」に使用するアカウント

以下は、上記の4つのステップを実行した後に実行するこのプロセスのT-SQL部分のスクリプトです。このスクリプトは、私の環境で常に機能し、達成しようとしている説明と似ているように見えます。 。また、ADアカウントに強力で複雑なパスワードが設定されていることを確認し、有効期限が切れないように設定する必要があります。

--- // Create login on SQL Instance for domain\PSSQLJobs service account if it does not exist
IF NOT EXISTS (
        SELECT *
        FROM sys.server_principals
        WHERE NAME = N'domain\PSSQLJobs'
        )
BEGIN
    CREATE LOGIN [domain\PSSQLJobs]
    FROM WINDOWS WITH DEFAULT_DATABASE = [master]
        ,DEFAULT_LANGUAGE = [us_english]
END

USE [master]

IF NOT EXISTS (
        SELECT *
        FROM sys.database_principals
        WHERE NAME = N'domain\PSSQLJobs'
        )
    CREATE USER [domain\PSSQLJobs]
    FOR LOGIN [domain\PSSQLJobs]
    WITH DEFAULT_SCHEMA = [dbo]

EXEC sp_addrolemember N'db_datareader'
    ,N'domain\PSSQLJobs'

GRANT CONNECT
    ON DATABASE::[master]
    TO [domain\PSSQLJobs]

USE [msdb]

IF NOT EXISTS (
        SELECT *
        FROM sys.database_principals
        WHERE NAME = N'domain\PSSQLJobs'
        )
    CREATE USER [domain\PSSQLJobs]
    FOR LOGIN [domain\PSSQLJobs]
    WITH DEFAULT_SCHEMA = [dbo]

EXEC sp_addrolemember N'db_datareader'
    ,N'domain\PSSQLJobs'

EXEC sp_addrolemember N'SQLAgentOperatorRole'
    ,N'domain\PSSQLJobs'

EXEC sp_addrolemember N'SQLAgentReaderRole'
    ,N'domain\PSSQLJobs'

EXEC sp_addrolemember N'SQLAgentUserRole'
    ,N'domain\PSSQLJobs'

GRANT CONNECT
    ON DATABASE::[msdb]
    TO [domain\PSSQLJobs]



--- // Create Credential *** Type in password of account into value of SECRET ***
USE [msdb]

CREATE CREDENTIAL PSSQLJobs
    WITH IDENTITY = 'domain\PSSQLJobs'
        ,SECRET = '*******'


--- // Create PowerShell Proxy account and give PSSQLJobs access to it
USE [msdb]

EXEC msdb.dbo.sp_add_proxy @proxy_name = N'ExecutePowershell'
    ,@credential_name = N'PSSQLJobs'
    ,@enabled = 1

EXEC msdb.dbo.sp_grant_proxy_to_subsystem @proxy_name = N'ExecutePowershell'
    ,@subsystem_id = 12

EXEC msdb.dbo.sp_grant_login_to_proxy @proxy_name = N'ExecutePowershell'
    ,@login_name = N'domain\PSSQLJobs'
2
Pimp Juice IT