Неожиданный тип переменной, возвращенный заданием приема

Я пытаюсь выполнить Invoke-Sqlcmdкоманда (из модуля SqlServer) для запуска запроса от имени другого пользователя AD. Я знаю, что есть-Credential аргумент, но это, похоже, не работает.

Таким образом, я подумал, используя Start-Job может быть вариантом, как показано во фрагменте ниже.

$username = 'dummy_domain\dummy_user'
$userpassword = 'dummy_pwd' | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential ($username, $password)

$job = Start-Job -ScriptBlock {Import-Module SqlServer; Invoke-Sqlcmd -query "exec sp_who" -ServerInstance 'dummy_mssql_server' -As DataSet} -Credential $credential

$data = Receive-Job -Job $job -Wait -AutoRemoveJob

Однако, глядя на тип переменной, возвращаемой заданием, я не ожидал этого.

> $data.GetType().FullName
System.Management.Automation.PSObject

> $data.Tables[0].GetType().FullName
System.Collections.ArrayList

Если я запустил код в ScriptBlock непосредственно, это типы переменных, которые возвращает PS:

> $data.GetType().FullName
System.Data.DataSet

> $data.Tables[0].GetType().FullName
System.Data.DataTable

Я пробовал использовать $data переменная для [System.Data.DataSet], что привело к появлению следующего сообщения об ошибке:

Cannot convert value "System.Data.DataSet" to type "System.Data.DataSet". 
Error: "Cannot convert the "System.Data.DataSet" value of type 
"Deserialized.System.Data.DataSet" to type "System.Data.DataSet"."

Вопросы:

  1. Есть ли лучший способ запускать SQL-запросы под другой учетной записью AD, используя Invoke-Sqlcmd команда?
  2. Есть ли способ вернуть правильный / ожидаемый тип переменной при вызове Receive-Job?

Обновить

Когда я бегу $data.Tables | Get-Member, одно из возвращаемых свойств:

Tables  Property  Deserialized.System.Data.DataTableCollection {get;set;}    

3 ответа

Решение
  1. Есть ли способ вернуть правильный / ожидаемый тип переменной при вызове Receive-Job?

Из-за использования фонового задания вы теряете верность шрифта: возвращаемые вами объекты являются имитацией исходных типов без использования методов.

Воссоздание исходных типов вручную не стоит усилий и может быть даже невозможно - хотя, возможно, достаточно работы с эмуляциями.

Обновление: согласно вашему собственному ответу, переход от работы сSystem.DataSet к System.DataTableпривели к исправным эмуляциям для вас. [1]

См. Дополнительную информацию в нижнем разделе.

  1. Есть ли лучший способ запускать SQL-запросы под другой учетной записью AD с помощью команды Invoke-Sqlcmd?

Вам нужно в процессе вызова метода для того, чтобы поддерживать тип верности, но я не думаю, что можно с произвольными командами, если вы хотите, чтобы выдавать себя за другого пользователя.

Например, внутрипроцессная (поточная) альтернатива Start-Job - Start-ThreadJob - не имеет -Credential параметр.

Поэтому лучше всего попытаться сделать Invoke-SqlCmd с -Credential параметр работает на вас, или найдите другой внутрипроцессный способ выполнения запросов с учетными данными данного пользователя.


Сериализация и десериализация объектов в фоновых заданиях / удаленном взаимодействии / мини-оболочках:

Каждый раз, когда PowerShell маршалирует объекты через границы процесса, он применяет сериализацию на основе XML в источнике и десериализацию в месте назначения.

Это происходит в контексте удаленного взаимодействия PowerShell (например, Invoke-Command звонки с
-ComputerNameпараметр), а также в фоновых заданиях ( Start-Job) и так называемые мини-оболочки (которые неявно используются, когда вы вызываете интерфейс командной строки PowerShell из самого PowerShell с помощью блока сценария; например,powershell.exe { Get-Item / }).

Эта десериализация поддерживает точность типов только для ограниченного набора известных типов, как указано в MS-PSRP, спецификации протокола удаленного взаимодействия PowerShell. То есть только экземпляры фиксированного набора типов десериализованы как их исходный тип.

Экземпляры всех других типов эмулировать: список подобных типов стали[System.Collections.ArrayList] экземпляры, типы словарей становятся [hasthable]экземпляры, а другие типы становятся настраиваемыми объектами без методов (только свойствами) ([pscustomobject]экземпляров), чьи.pstypenames свойство содержит имя исходного типа с префиксом Deserialized. (например, Deserialized.System.Data.DataTable), а также имена базовых типов с одинаковым префиксом (иерархия наследования).

Кроме того, глубина рекурсии для графов объектов не-[pscustomobject] экземпляров ограничено 1level: То есть, если свойства входного объекта не являются строками или экземплярами самих примитивных типов.NET (таких как[int]которые сами по себе являются одним значением, а не имеют свойства), они заменяются своими.ToString() представления (например, тип System.IO.DirectoryInfo имеет .Parent собственность это другое System.IO.DirectoryInfo экземпляр, что означает, что .Parent значение свойства сериализуется как .ToString()представление этого экземпляра, которое является строкой его полного пути); вкратце: нестандартные объекты сериализуются со своими непосредственными свойствами, при этом нестроковые и непримитивные значения заменяются их.ToString()представление.
Напротив, явное использование сериализации CLI XML через Export-Clixml по умолчанию на глубину 2.

В зависимости от исходного типа вы можете восстановить экземпляры исходного типа вручную, но это не гарантируется. (Полное имя исходного типа можно получить, позвонив.pstypenames[0] -replace 'Deserialized\.' на заданном настраиваемом объекте.)

Однако в зависимости от ваших потребностей в обработке может быть достаточно эмуляции исходных объектов.


[1] Использование System.DataTable приводит к пригодным к использованию эмулированным объектам, потому что вы получаете System.Collections.ArrayList экземпляр, который имитирует таблицу, и настраиваемые объекты с исходными значениями свойств для его System.DataRowэкземпляры. Причина, по которой это работает, заключается в том, что PowerShell имеет встроенную логику для обработкиSystem.DataTableнеявно как массив строк данных, в то время как то же самое не относится кSystem.DataSet.

Я не могу ответить на вопрос 2, поскольку я никогда не использовал команды задания, но когда дело доходит до запуска Invoke-Sqlcmd Я всегда убеждаюсь, что учетная запись, запускающая сценарий, имеет правильный доступ для запуска SQL.

Плюс к этому в том, что вам не нужно хранить учетные данные внутри скрипта, но обычно это спорный вопрос, так как скрипты хранятся вне досягаемости большинства людей, хотя некоторые боссы могут быть придирчивыми!

Из любопытства, как сравнить результаты, если передать их Get-Member?

Для тех, кто заинтересован, ниже представлен реализованный мной код. В зависимости от того, действительно ли$credential передается, Invoke-Sqlcmd будет работать либо напрямую, либо с использованием фонового задания.

Мне пришлось использовать -As DataTables вместо того -As DataSet, поскольку у последнего, похоже, есть проблемы с сериализацией / десериализацией (см. принятый ответ для получения дополнительной информации).

function Exec-SQL($server, $database, $query, $credential) {

    $sqlData = @()

    $scriptBlock = {
        Param($params)

        Import-Module SqlServer
        return Invoke-Sqlcmd -ServerInstance $params.server -Database $params.database -query $params.query -As DataTables -OutputSqlErrors $true
    }

    if ($PSBoundParameters.ContainsKey("credential")) {

        $job = Start-Job -ScriptBlock $scriptBlock -Credential $credential -ArgumentList $PSBoundParameters
        $sqlData = Receive-Job -Job $job -Wait -AutoRemoveJob

    } else {
        $sqlData = & $scriptBlock -params $PSBoundParameters
    }
    return $sqlData
}
Другие вопросы по тегам