Receive-Job 返回的意外变量类型

ƘɌỈ*_*ƬƠƑ 2 sql-server powershell

我正在尝试执行Invoke-Sqlcmd命令(来自SqlServer 模块)以作为不同的 AD 用户运行查询。我知道有-Credential争论,但这似乎不起作用。

因此,我认为 usingStart-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
Run Code Online (Sandbox Code Playgroud)

但是,在查看作业返回的变量类型时,这不是我所期望的。

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

> $data.Tables[0].GetType().FullName
System.Collections.ArrayList
Run Code Online (Sandbox Code Playgroud)

如果我ScriptBlock直接在 中运行代码,这些是 PS 返回的变量类型:

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

> $data.Tables[0].GetType().FullName
System.Data.DataTable
Run Code Online (Sandbox Code Playgroud)

我尝试将$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"."
Run Code Online (Sandbox Code Playgroud)

问题:

  1. 是否有更好的方法在不同的 AD 帐户下运行 SQL 查询,使用 Invoke-Sqlcmd命令?
  2. 有没有办法在调用时获得正确/预期的变量类型Receive-Job

更新

当我运行时$data.Tables | Get-Member,返回的属性之一是:

Tables  Property  Deserialized.System.Data.DataTableCollection {get;set;}    
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 5

  1. 有没有办法在调用时获得正确/预期的变量类型Receive-Job

由于使用后台作业,您会失去类型保真度:您返回的对象是原始类型的无方法模拟

手动重新创建原始类型不值得付出努力,甚至可能无法实现 - 尽管使用仿真可能就足够了。

更新根据您自己的回答,从使用切换System.DataSetSystem.DataTable为您提供有用的仿真。[1]

有关更多信息,请参阅底部部分。

  1. 是否有更好的方法使用 Invoke-Sqlcmd 命令在不同的 AD 帐户下运行 SQL 查询?

您需要一个进程内调用方法来保持类型保真度,但我认为如果您想模拟另一个用户,则使用任意命令是不可能的。

例如,进程内(基于线程)替代Start-Job- Start-ThreadJob- 没有-Credential参数。

因此,您最好的选择是尝试使Invoke-SqlCmd's-Credential参数为您工作,或者找到一种不同的进程内方式来使用给定用户的凭据运行您的查询。


后台作业/远程处理/迷你壳中对象的序列化和反序列化:

每当 PowerShell进程边界封送对象时,它都会在源使用基于 XML 的序列化,并在目标使用称为CLI XML(通用语言基础架构 XML)的格式进行序列化

这发生在PowerShell 远程处理的上下文中(例如,Invoke-Command使用
-ComputerName参数调用)以及后台作业( Start-Job) 和所谓的mini-shells(当您从 PowerShell 内部使用脚本块调用 PowerShell CLI 时隐式使用它们);例如,powershell.exe { Get-Item / })。

此反序列化仅为有限的一组已知类型保持类型保真度,如MS-PSRP(PowerShell 远程处理协议规范)中指定的那样。也就是说,只有一组固定类型的实例被反序列化为其原始类型

模拟所有其他类型的实例:类列表类型成为[System.Collections.ArrayList]实例,字典类型成为[hasthable]实例,其他类型成为无方法(仅属性)自定义对象[pscustomobject]实例),其.pstypenames属性包含以Deserialized.(例如,Deserialized.System.Data.DataTable),以及类型的类型(继承层次结构)的相同前缀名称。

此外,递归深度为对象图-[pscustomobject]实例仅限于1水平-注意,这包括实例PowerShell的自定义,与创建class关键字:也就是说,如果输入对象的属性值是并不知名的类型的实例本身(后者包括单值类型,包括 .NET 基本类型[int],而不是由多个属性组成的类型),它们被它们的.ToString()表示替换(例如,类型System.IO.DirectoryInfo具有作为.Parent另一个System.IO.DirectoryInfo实例的属性,这意味着该.Parent属性值序列化作为.ToString()该实例的表示,即其完整路径字符串);简而言之:非自定义(标量)对象序列化,使得本身不是众所周知类型实例的属性值被它们的.ToString()表示替换;有关具体示例,请参阅此答案
相比之下,显式使用 CLI XML 序列化 viaExport-Clixml默认为深度2(您可以指定自定义深度 via -Depth,如果直接使用基础System.Management.Automation.PSSerializer类型,您也可以类似地控制深度)。

根据原始类型,您也许能够手动重建原始类型的实例,但这并不能保证。(您可以通过调用.pstypenames[0] -replace '^Deserialized\.'给定的自定义对象来获取原始类型的全名。)

但是,根据您的处理需要,原始对象的模拟可能就足够了。


[1]System.DataTable在可用的模拟对象中使用结果,因为您获得了一个System.Collections.ArrayList模拟表的实例,以及具有其System.DataRow实例的原始属性值的自定义对象。这样做的原因是 PowerShell 具有将System.DataTable隐式视为其数据行数组的内置逻辑,而同样不适用于System.DataSet.