Неожиданный тип переменной, возвращенный заданием приема
Я пытаюсь выполнить 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"."
Вопросы:
- Есть ли лучший способ запускать SQL-запросы под другой учетной записью AD, используя
Invoke-Sqlcmd
команда? - Есть ли способ вернуть правильный / ожидаемый тип переменной при вызове
Receive-Job
?
Обновить
Когда я бегу $data.Tables | Get-Member
, одно из возвращаемых свойств:
Tables Property Deserialized.System.Data.DataTableCollection {get;set;}
3 ответа
- Есть ли способ вернуть правильный / ожидаемый тип переменной при вызове
Receive-Job
?
Из-за использования фонового задания вы теряете верность шрифта: возвращаемые вами объекты являются имитацией исходных типов без использования методов.
Воссоздание исходных типов вручную не стоит усилий и может быть даже невозможно - хотя, возможно, достаточно работы с эмуляциями.
Обновление: согласно вашему собственному ответу, переход от работы сSystem.DataSet
к System.DataTable
привели к исправным эмуляциям для вас. [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]
экземпляров ограничено 1
level: То есть, если свойства входного объекта не являются строками или экземплярами самих примитивных типов.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
}