I'm trying to register an OCX file on a remote machine using Powershell 2.0.
This doesn't doesn't work:
$LocalOCXPath = "C:\Windows\SysWOW64\dxapi.ocx"
Invoke-Command -ComputerName $ComputerName -ScriptBlock { "C:\windows\system32\Regsvr32.exe $args" } -argumentlist $LocalOCXPath
But this does:
Invoke-Command -ComputerName $ComputerName -ScriptBlock { & 'regsvr32.exe' 'C:\Windows\SysWOW64\dxapi.ocx' }
I really need to be able to pass the path in via a variable, but I don't think I can do that using option 2. And I have no idea why option 1 doesn't work. By doesn't work, I mean that powershell looks like it invokes the command properly, but when I look on the remote machine, the ocx file isn't registered.
I think you are overenginnering, try:
$LocalOCXPath = "C:\Windows\SysWOW64\dxapi.ocx"
Invoke-Command -ComputerName $ComputerName -ScriptBlock { & 'regsvr32.exe' $LocalOCXPath }
Invoke-Command runs the scriptblock in the context of the remote system so your $LocalOCXPath variable doesn't exist there. In PowerShell 3.0 you can force the variable to be seen in the remote context by prefixing it with $Using:, as in $Using:$LocalOCXPath.
Related
Is it possible to launch devenv.exe and open a .sln file from cmder? After I clone a git repo from the command line, I'd like to quickly open the solution in Visual Studio without having to leave cmder.
Danny is correct, you simply type xxx.sln.
I'm a bit lazy and developed the script below to be able to type vs and it will find and open the first sln file it finds in the current directory.
Here are some example commands:
vs - Will open the first sln file it finds, if it doesn't find one it will just open Visual Studio without a solution. I default to opening with Visual Studio 2017. Just change the default case in the Get-VisualStudioCommand function if you'd rather default to a different version.
vs 15 - I have a bunch of versions of Visual Studio on my machine, so this will open similarly to vs, but will open with Visual Studio 2015.
vs 13 'helloWorld.sln' - this will open a specific sln file, i.e., helloWorld.sln. One of the projects I worked on recently had multiple solutions in the same directory.
vs -WhatIf -Verbose - this will show you what the script would do if it ran and will print out the Write-Verbose messages. Handy for debugging. I show and example of this in the image below.
For this solution, I'm assuming that you're using a PowerShell console. If so, you can define a .ps1 file that will run before the console opens via the -File option. It's like defining a different PowerShell profile with different commands per console window (very powerful).
See the image below where I show going to settings and modify the PowerShell:PowerShell task to execute PowerShell -NoExit -NoLogo -File C:\src\ps\Cmder\general_setup.ps1 -new_console:d:"C:\src"
Here is the image (note, although I use an alias for ls that outputs in PowerShell like a Linux ls command...this is a PowerShell console):
If you add the following script to the C:\src\ps\Cmder\general_setup.ps1 file (or wherever you want to put it)...you can then type in vs and it will execute. Notice in the Get-VisualStudioCommand function, I've put all the file paths...not the most elegant looking code, but if your file paths to devenv.exe are different than mine, just change them there.
Note, you could also get fancy and add a pass-through to the -ArgumentList or flag for the /SafeMode command to be able to open Visual Studio in safe mode when needed, I've just created these as a quick and dirty shortcut.
function Get-VisualStudioCommand
{
[CmdletBinding()]
param ( [AllowNull()][String] $vsVersion )
$vs10 = """${env:ProgramFiles(x86)}\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"""
$vs13 = """${env:ProgramFiles(x86)}\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe"""
$vs15 = """${env:ProgramFiles(x86)}\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe"""
$vs17 = """${env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe"""
switch ($vsVersion)
{
'10' {$vs10}
'13' {$vs13}
'15' {$vs15}
'17' {$vs17}
default {$vs17}
}
}
function Get-SolutionName
{
[CmdletBinding()]
param ( [AllowNull()][String] $Name )
if (!$Name)
{
$Name = Get-ChildItem -Filter *.sln
}
if ($Name)
{
$Name = ('"{0}"' -f $Name)
}
return $Name
}
function Start-VisualStudioProcess
{
[CmdletBinding(SupportsShouldProcess=$true)]
param([String]$Version, [String]$Sln)
$VsCommand = Get-VisualStudioCommand -vsVersion $Version
$Sln = Get-SolutionName -Name $Sln
Write-Verbose -Message ('Starting: command={0} solutionName={1}' -f $VsCommand, $Sln)
if ($PSCmdlet.ShouldProcess($VsCommand, 'Start-Process'))
{
if ($Sln)
{
Start-Process -FilePath $vsCommand -ArgumentList $sln
}
else
{
Start-Process -FilePath $vsCommand
}
}
}
Set-Alias -Name vs -Value Start-VisualStudioProcess
Let me know if you have any questions. I hope it helps.
Run this command in your prompt(cmd, cmder, etc.)
reg add "HKCU\Software\Microsoft\Command Processor" /v Autorun /d "doskey vs=for /f \"delims=\" %a IN ('dir /b *.sln') do start %a" /f
Write "vs" for open the first *.sln in folder of Solution and be happy!
I have written the following script for SQL patching:
cls
$computers = Get-Content D:\Abhi\Server.txt
foreach ($line in $computers)
{
psexec \\$line -s -u Adminuser -p AdminPassword msiexec /i D:\SQL_PATCH\rsSharePoint.msi SKIPCA=1 /qb
}
My doubt here is to parallelize this script execution on all the servers mentioned in the text file. Meaning, as soon I start the execution of the script, this should initiate the patching activity on the servers simultaneously and also to track the progess on all the servers, as this script is doing now only for one server.
Kindly help me on this.
Thanks Ansgar Wiechers.
This piece of code did it. It helps in executing the .exe simultaneously on all the servers as well as track their status:
cls
$servers = Get-Content 'D:\Abhi\Server.txt'
$servers | ForEach-Object {$comp = $_
Start-Job -ScriptBlock {psexec \\$input -s -u Adminuser -p AdminPassword C:\SQL_PATCH\SQLServer2008R2SP3-KB2979597-x64-ENU.exe /quiet /action=patch /allinstances /IAcceptSQLServerLicenseTerms} -InputObject $comp}
While (Get-Job -State Running)
{
Get-Job | Receive-Job
#Start-Sleep 2
#Write-Host "Waiting for update removal to finish ..."
}
Remove-Job *
I am looking to execute a command in remote machine using invoke but the .cmd file will call for additional .vbs script. So i guess i may have to mention CScript if so how do i mention both cmd/c and cscript in the below command
Invoke-Command -computername blrscrv01 -ScriptBlock { param($path, $command ) cmd /c $path $command } -args '"C:\windows\system32\cscript.exe"','"/?"'
Your example worked for me when I removed the extra level of quoting.
Invoke-Command -computername blrscrv01 -ScriptBlock { param($path, $command ) cmd /c $path $command } -args 'C:\windows\system32\cscript.exe','/?'
Troubleshooting
Enter a remote session and poke around.
Enter-PSSession -computername blrscrv01
Verify that the target script exists and is accessible.
dir \\lcsap027\deploy\c2.cmd
dir \\lcsap027\deploy
type \\lcsap027\deploy\c2.cmd
Attempt to run the script interactively.
\\lcsap027\deploy\c2.cmd
or
cmd /c \\lcsap027\deploy\c2.cmd
Alternative
Another thing you might try is not invoking a cmd script remotely, but issuing the commands remotely. New-PSSession will return a handle you can use to deal interactively with the remote machine. You can repeatedly issue commands with Invoke-Command and get the results (as primitive data types and generic objects, though, not the actual objects themselves).
Altered Script
Here's an altered version of the script you put in your comment. I've removed the nested Invoke-Command (I don't know why it was necessary, you're already running commands on the remote machine). Since the line breaks got lost in the comment, I don't know if there were any statement separator problems (I'll just assume there weren't, though in its "formatted" form as a one-liner, it would have died horribly because PoSH wouldn't have known where one statement ended and the next began).
param(
[string]$ComputerName,
[string]$User,
[string]$pass
)
Get-PSSEssion | Remove-PSSession
$session = New-PSSession -ComputerName $ComputerName
Invoke-Command -Session $session -ScriptBlock {
param(
[string]$ComputerName,
[string]$Username,
[string]$Password
)
$net = new-object -ComObject WScript.Network
$net.MapNetworkDrive("x:", "\\machinename\sharename", $false, $Username, $Password)
cmd.exe /c "x:\c2.cmd"
$net.RemoveNetworkDrive("x:")
} -args $ComputerName, $User, $pass
This at least got the remote script to run and produced the expected output. (I emitted the computer name, user name, and file location.)
Bear in mind that this method doesn't employ any transport-/application-layer encryption, so the password is sent cleartext over the network.
I am attempting to execute a Powershell script on a remote computer from a Powershell window on my local computer using Powershell 2.0. My goal is to store an exit code from the script on the remote computer to a variable on my local computer, whether it be in a local script or just a local Powershell window. Currently all I get back is an error message stating the following . . .
Cannot invoke pipeline because run space is not in the Opened state. Current state of run space is Closed. + CategoryInfo: OperationStopped: Microsoft.Power...tHelperRunspace:ExecutionCmdletHelperRunspace) [], InvalidRunspaceStateException + FullyQualifiedErrorID : RemotePipeLineExecutionFailed
My situation is this . . . I am trying to return an exit code to a variable on my local machine from a script on a remote machine. There are actually two scripts involved here and both are on the remote machine and in the same folder. The first script is called "VerifyBatchExistence.ps1". This file takes as input a batch file called "Batch.ps1", an integer that represents an exit code to be output if "Batch.ps1" does not exist, another integer representing the number of parameters needed by "Batch.ps1", and the variable string(s) needed as input for "Batch.ps1". Within "Batch.ps1" I have the following piece of code which I expect to be triggered when one of my passed-in parameters is invalid. . .
exit 931
Within "VerifyBatchExistence.ps1" I have the following piece of code . . .
Powershell - File .\Batch.ps1 BATCHPARAM1 BATCHPARAM2
$Code1 = $LASTEXITCODE
$Host.SetShouldExit($Code1)
exit
On my local machine, I did the following . . .
$Remote=New-PSSession -ComputerName 7.7.7.7 -Credential $cred
Invoke-Command -Session $Remote -ScriptBlock {$Code1=.\VerifyBatchExistence.ps1 7777 2 .\Batch.ps1 NAME1 NAME2}
$Code2=Invoke-Command -Session $Remote -ScriptBlock {$Code1}
$Code2
After this, I receive the error stated above. Any idea what I am doing wrong or how I can return an exit code to a variable on my local machine from a remote machine which has a script consuming and running a second script? Any answer to this would be appreciated. Thanks.
I do it like this:
invoke-command -session $Remote -scriptblock { mycommand }
$remotelastexitcode = invoke-command -session $Remote -ScriptBlock { $lastexitcode }
if ( $remotelastexitcode -ne 0 )
{
"$remotelastexitcode"
exit 1
}
I'm using Powershell on Win 2008r2 to make a remote call to msiexec as follows:
Invoke-Command -session $Session -ScriptBlock{param($arguments) start-process -FilePath "msiexec.exe" -Wait $arguments } -Argument $arguments
Currently I'm checking for success using if(!$?) but this is no good because I've just seen the msiexec process throw a 1638 error (because the app is already installed on the remote server) but the value of $? was True.
Can anyone please tell me how I can capture the 1638 code, or whatever else, is returned by msiexec on the remote server?
Thanks, Rob.
This was a very hackish way to do this, but I got around this by using a global-like variable in terms of $script:functionexitcode which I would assign with the value of the .ErrorCode from the msiexec.exe using Start-Process.
Then in the main part of the PowerShell script, I would test that value if ($functionexitcode -eq 0).
Here is the full snippet from a very similar install scenario with Start-Process:
# Start MSP upgrade to UR
$upgrade = (Start-Process -Filepath $msiexecpath -ArgumentList $argumentlist_BEGIN$argumentlist_MSP$argumentlist_END -PassThru -Wait -ErrorAction Stop)
if ($upgrade.ExitCode -eq 0) {
Write-Host "Upgrade successful. Error code:" $upgrade.ExitCode `
"`nUpgrade logfile location: " $workingdirectory\$msi_logfile_upgrade
$script:FunctionExitCode = $upgrade.ExitCode