来自vba的powershell invoke-command exchange mgmt shell,具有stdout和stderr检索功能,没有凭据弹出窗口

时间:2018-11-22 16:13:03

标签: vba powershell exchange-server wsh

我具有以下要求:

以域管理员用户身份登录到管理客户端计算机 我想使用呼叫在交换服务器上执行一些更改 从vba(excel 2013)通过Powershell到交换服务器(2013)。 客户端计算机运行Windows 10(1809)和Powershell v5.1.17763.1

在按钮上按VBA Excel表单,我要执行一个琐碎的操作 任务,例如获取特定邮箱用户的所有信息,阅读 使用WSH.Shell从stdout / stderr返回结果,以后还会更多。

执行以下命令将执行其应有的操作,但有以下两个缺点:

1)仍然需要再次询问凭据,尽管它们已经通过-ArgumentList作为$ Cred传递给了ScriptBlock

2)Powershell窗口在处理后不会自动关闭,需要 被用户主动关闭

最后,检索到的stdout / stderr为我提供了我想要的东西(顺便说一句,是否可以将Powershell对象检索到vba集合中进行直接连接?)

命令行上的WORKS(“单一代码”),但必须通过弹出窗口提供凭据:

powershell -Command { $Username = 'MYDOMAIN\Administrator'; $Password = 'foobar'; $pass = ConvertTo-SecureString -AsPlainText $Password -Force; $Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass; Invoke-Command -ComputerName Exchange -ArgumentList $Cred -ScriptBlock { $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://exchange.somewhere.com/PowerShell/ -Authentication Kerberos -Credential $Cred; Import-PSSession $Session; Get-Mailbox MYUSER; Remove-PSSession $Session } }

vba通过WSH.Shell Exec提供的WORKS,但必须通过弹出窗口提供凭据,并且必须主动关闭Powershell控制台窗口 (并且让我避免在powershell脚本中避免使用双引号(“),还没有弄清楚如何正确地将其转义(”“不起作用)):

powershell -Command "& { $Username = 'MYDOMAIN\Administrator'; $Password = 'foobar'; $pass = ConvertTo-SecureString -AsPlainText $Password -Force; $Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass; Invoke-Command -ComputerName Exchange -ArgumentList $Cred -ScriptBlock { $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://exchange.somewhere.com/PowerShell/ -Authentication Kerberos -Credential $Cred; Import-PSSession $Session; Get-Mailbox MYUSER; Remove-PSSession $Session } }"

所以基本上是写 powershell-命令“&{...}” 在VBA中,通过“ wsh shell exec”而不是调用时 powershell-命令{...} 在命令行上,这似乎是正确检索stdin / stdout所必需的,很高兴为您提供建议,说明为什么是这种情况,或者是否还有其他样式可以编写此代码。

任何建议如何摆脱Powershell弹出窗口要求提供凭据的建议 以及如何摆脱无法自动消失的Powershell窗口?

谢谢, 杰夫

P.S .: 供您参考,可以使用vba方法执行powershell调用(End Function和#if在代码块中损坏了,尽管会发现):

Public Function execPSCommand(ByVal psCmd As String, Optional ByVal label As String = "Debug") As String()
Dim oShell, oExec

Dim retval(2) As String
retval(0) = ""
retval(1) = ""

Set oShell = CreateObject("WScript.Shell")
Set oExec = oShell.Exec(psCmd)

oExec.stdin.Close ' close standard input before reading output

Const WshRunning = 0

Do While oExec.Status = WshRunning
    Sleep 100 ' Sleep a tenth of a second
Loop

Dim stdout As String
Dim stderr As String

stdout = oExec.stdout.ReadAll
stderr = oExec.stderr.ReadAll

retval(0) = stdout
retval(1) = stderr

execPSCommand = retval()

结束功能

' Sleep (fractions of a second, milliseconds to be precise) for VBA

'#If VBA7然后 '声明PtrSafe子睡眠库“ kernel32”(ByVal dw毫秒) '#其他 '声明子睡眠库“ kernel32”(ByVal dw毫秒) '#End If

2 个答案:

答案 0 :(得分:0)

我认为您没有将$ cred参数正确传递给脚本块。如果要使用该局部变量,脚本块应以param($ cred)开头。为什么不在脚本块中定义$ cred?您还可以使用Using修饰符将局部变量推送到远程命令(例如$ Using:cred,查看更多详细信息https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_variables?view=powershell-6

关于最后退出powershell,我想您可以在命令末尾键入“ Exit”或“ Stop-Process $ pid”。

答案 1 :(得分:0)

@Mike:很棒的东西,非常感谢您为我指明了正确的方向。

对我来说,解决方案是根据您的建议添加“ param([PSCredential] $ Cred);”。 当然我也可以在ScriptBlock中创建$ Cred;)

此外,我还记得在某处读过PSSession应该是 然后通过Remove-PSSession命令关闭以释放资源。

但是:这种方法与我忙于等待wsh shell命令完成(在vba中)然后从进程中读取stdout / stderr的方式发生冲突 预计将很快关闭-因此我删除了VBA中针对此特定事件的繁忙等待 情况(远程ps会话)。

事实证明,我根本不需要调用Remove-PSsession,经过交叉检查 最后一个命令是Start-Sleep 60(而不是Remove-PSsession) 并在一段时间内不时执行“ Get-PSsession -ComputerName交换” 在vba中执行时真正的Powershell控制台;最快60秒 通过的会话是否自动清除(没有列出更多会话)。

因此,简短的故事是:当在ps脚本中完成远程PSSession时,省去了繁忙的等待(幕后事情似乎已经删除了该过程,或者我尚不清楚的其他任何事情都以阻塞的方式阻碍了工作) )-为什么在忙碌的等待中仍将oExec.Status留在WshRunning上 情况”超出了我的范围,我希望它是WshFinished或WshFailed, 但是那样一来,就会造成Powershell窗口永远等待的阻塞。

无论如何,硬编码的vba密码也消失了,现在改为使用输入框读入, 快乐的炮击可能会继续;)