How can I get a remotely executed script to know it's own location? I'm using Invoke-Command to run a script on a remote server. The script needs to create files in the directory in which it lives. Running with relative addressing doesn't work (i.e. .\output.log), the scripts generally end up in my user profile on the remote server. I tried all the methods outlined in this question but none of them seem to work when the script is remote.
Update: Provided script invocation code per request
$server='ad1hfdahp802'
$remotepath='\\ad1hfdahp802\d$\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1'
$SDFEnvironment='INT'
Invoke-Command -ComputerName $server -FilePath $remotepath -ArgumentList($SDFEnvironment,$remotepath)
The remote script takes the $remotepath and turns it into a file system path.
Using -FilePath with Invoke-Command means that you read the script locally and send the content as the scriptblock to the remote computer. $PSScriptRoot only works when the script is executed directly on the target. You could try using:
Invoke-Command - ComputerName "computer1" -Scriptblock { & '\\server\path\to\script.ps1' } -Authentication Credssp
Be aware that you need CredSSP to make this work since the remote computer can't use your credentials to access network-resources without it. As an alternative, you could use psexec (or start a process remotely). Ex.
psexec \\computer1 powershell -noprofile -file \\server\path\to\script.ps1
After trying some of the changes proposed I've come to understand that the Invoke-Command isn't actually running the remote script at its original location, but rather loading it from the original location and then running it under the context of PowerShell as the user running the local script. The "script directory" is actually a directory in the user's workspace regardless of where the script originally lived.
This clarifies things for me somewhat. While there may be ways to divine where the script originally came from or to actually start a session on the remote server then run the script as a "local" script there, the need for the remote script to further access other servers, creating multiple hops in authentication, means I have to add CredSSP to the mix.
It seems my original plan, to pass the path I'm using to locate the script to the script so it can place output files in the original directory, is probably the best approach given that I also have to add CredSSP to the mix.
I'm open to refutation, but I don't think any of the proposed solutions actually improve the functionality of the remote script so I'm going to stick with what I started with for now. Thanks to everyone for their contributions.
Enter a session on the remote server, and call the script from there.
local PS> Enter-PSSession -ComputerName $server ...
remote PS> powershell d:\AHP\...\script.ps1
remote PS> exit
local PS>
Then you can use $PSScriptRoot in the script in the remote server to get the local path of the directory of the script on the remote server.
EDIT:
To locate the script on the remote server, you can use your knowledge of the network path of the script file, and parse the output of net share to map network path to local path on the remote server.
remote PS> net share | where { $_.StartsWith('D$ ') } | foreach { [regex]::Split($_, " +")[1]}
Related
I am unable to access the UNC paths on my servers in a Powershell remote session from my local machine. I am able to use them from Servers Cmd prompt directly.
Actually, I have logged into the server and mapped a UNC path as local drive (say X:). Used Reconnect on Login option.
I have a batch file which resides in this X: drive, I want to run it remotely using invoke command from my local script. But, it fails.
It says "Cannot find drive. A drive with name X doesn't exist".
Also, when I try to map drive using net use command in the scriptblock then also it throws an error - System error 1223 - Native Command Error.
I use administrator credentials to log into this server.
Can anyone please help me on this, all i want to do is to run the script remotely which resides on this UNC path ?
Also, when I map a UNC Path in the server as a local drive, why am I unable to use it in a PS remote session ?
Thanks in Advance.
TS
You've really got 3 different things going on here.
1 & 3. Drives are only mapped when you log on interactively. So when you remoted into the other computer, mapped a drive, and then logged off/disconnected, that mapped drive was disconnected. Except in interactive GUI user sessions, you cannot depend upon a mapped drive letter that you don't create yourself. Within scripts or any remote session, just use UNC paths for everything - it's more reliable.
2 . When you attempt to map the drive in the remote PS session, you're encountering what's known as the "double hop" problem. There is a solution to this, but there's extra setup you have to do. See Double hop access to copy files without CredSSP
alroc's helpful answer explains the problem well and points to resources for solving it through prior configuration.
An explanation of the underlying problem, the infamous Kerberos double-hop problem, as well as an overview of available solutions can be found in this blog post.
As for an ad hoc solution for accessing a network share in a remote session (PSv3+):
Pass the credentials for the session via a variable; e.g., -Credential $cred
Then use these credentials inside the session too - e.g., as $using:cred - to establish a session-scoped auxiliary drive that maps the network location, using New-PSDrive.
Once the drive has been mapped, the target location is accessible - whether via the drive name, or directly by UNC path.
Note: This is a variation of the approach discovered by the OP him/herself and discussed briefly in a comment on alroc's answer, except that New-PSDrive is used rather than net use, which obviates the need for retrieving the password as plain text.
The following sample code demonstrates running a script from a network share from inside a remote session:
# A sample script to run from a network share on a remote computer.
$script = '\\server-001\install\Install-Agent.ps1'
# A sample target computer.
$computer = 'ws-002'
# Obtain the credentials for the remote session and store them in a variable.
$cred = Get-Credential $env:USERNAME
Invoke-Command -ComputerName $computer -Credential $cred {
# Map the target network share as a dummy PS drive using the passed-through
# credentials.
# You may - but needn't - use this drive; the mere fact of having established
# a drive with valid credentials makes the network location accessible in the
# session, even with direct use of UNC paths.
$null = New-PSDrive -Credential $using:cred -Name dummy -Root (Split-Path -Parent $using:script) -PSProvider FileSystem
# Invoke the script via its UNC path.
& $using:script
}
Note:
$null = ... suppresses New-PSDrive's output (it outputs an object describing the newly created drive).
Since the drives created by New-PSDrive are not persistent (except if you pass -Persist), there's no need to explicitly remove the dummy drive again, but you can do so with Remove-PSDrive.
Also note that PS drive definitions are scoped so that only the calling scope and its descendants see the drive; this enables wrapping statements in & { ... } to call them in a child scope, which means the a PS drive created inside that block will automatically go out of scope when the block is exited.
You can try to open the script by calling a WmiMethod :
$cmd = "CMD.EXE /c *\\\servername\somefolder\yourscript*.cmd"
Invoke-WmiMethod -class Win32_process -name Create -ArgumentList $cmd -ComputerName *servername*
Note that this will run the script on the server. You can do much more with that, like installing a package remotely (after copying the package locally on the remote machine).
Hope this help!
I have a command
Invoke-Item \\remote_IP\share\setup.exe
That executes setup.exe on my local computer even though I specified "remote_IP" in the UNC path.
What is missing?
Your question isn't clear, but it looks like you are trying to remotely execute an application. You don't want Invoke-Item for that, you want Invoke-Command which has the -ComputerName switch (along with other things to facilitate remote execution like accepting credentials).
Invoke-Command -ComputerName RemoteIP -ScriptBlock {& 'C:\Path\To\Setup.exe'}
Or however you want to execute the file.
UNC paths have the following form \\remote_name\share_name\path. In your case the share_name is missing.
See https://en.wikipedia.org/wiki/Path_%28computing%29#Uniform_Naming_Convention
Also UNC paths only allow you to access file remotely, not execute them on the remote computer. For excecuting a program remotely you need a special service or tool support: https://serverfault.com/questions/221064/running-remotely-an-app-from-a-shared-folder-with-psexec
From PowerScript, however, you could also use the invoke-command command (see http://technet.microsoft.com/en-us/library/hh849719.aspx)
I am performing below tasks on remote machine from a local machine:
Creating/Deleting/Modifying some directory
Copying some folder from local to remote machine
Installing some .exe silently with noninteractive option
Exectuing some batch files
I want to write a script in PowerShell. Novice to PowerShell. I have done some basic investigation of terms like "PowerShell Remoting" etc.
What are the things I need to look for? Related exmple for this will help, where should I look for those?
Reading from docs on MSDN:
To run a single command on a remote computer, use the ComputerName parameter. To run a series of related commands that share data, use the New-PSSession cmdlet to create a PSSession (a persistent connection) on the remote computer, and then use the Session parameter of Invoke-Command to run the command in the PSSession. To run a command in a disconnected session, use the InDisconnectedSession parameter. To run a command in a background job, use the AsJob parameter.
So basically you should do something like:
$session = New-PSSession
Invoke-Command -Session $session -FilePath <PathToScript>
There is a good section on PowerShell remoting in the Getting Started with PowerShell 3.0 Virtual Academy class. If you don't want to start there, read the about_Remote help topic, then move on to the other remoting help topics listed at the bottom.
I am trying to use the Invoke-Command powershell cmdlet to install a MSI installer. From within powershell on the local machine and from the proper directory, the following works:
./setup /quiet
The following does not seem to work:
$script =
{
param($path)
cd "$path"
& ./setup /quiet
return pwd
}
return Invoke-Command -ComputerName $product.IPs -ScriptBlock $script -Args $sourcePath
For test purposes I am working on the local machine passing in "." for the -ComputerName argument. The paths have been verified correct before passing in to Invoke-Command, and errors generated on different versions of this code indicate the paths are correct. I have also tried with and without the "& " on the remote call to setup. Other Invoke-Command calls are working, so I doubt it is a permissions issue. I have verified that the return from the pwd call is the expected directory.
How do I get the install to work?
What error (if any) are you receiving? Unfortunately, you must run the shell as admin on your local machine to be able to connect to your local machine with invoke-command or any WINRM based command that requires administrative privilege (this is not a requirement when connecting remotely).
When connecting to loopback, I believe it is unable (for some security reason) to enumerate groups and determine if you are in an admin enabled AD or local group, which is how it auto elevates when invoking on a remote machine. The only solution may be to have a conditional which checks for localhost and if so, don't use the -ComputerName parameter.
This GitHub Issue covers it
You might try using Start-Process in your script block:
cd $path
start-process setup.exe -arg "/quiet"
Not sure if you will want or need to wait. Look at help for Start-Process.
I have had weird issues when trying to remotely execute a script on a local machine. In other words, remote powershell to the local machine. It comes back with an error that seems to say that PowerShell remoting is not enabled on the machine, but it was. I can run the script remotely from another machine to the target, but when using remoting to the same box, the issue crops up.
Verify that the WinRM service is running.
Verify powershell remoting has been enabled as in Enable-PSRemoting -force.
Verify your powershell execution policy is loose enough as in Set-ExecutionPolicy Unrestricted, for example. If the policy was set to RemoteSigned, this might be the problem.
You might also want to verify the user you are running the script as (locally, but using remoting) has privileges to "log on as a service" or as a batch job. Just guessing there, if the above list doesn't solve anything.
Just listened to Hansellminutes podcast. He had a talk with two Microsoft PS developers. They mentioned PS V2 remoting features.
I have some scripts based on PS v1. In terms of remoting commands or executions, I installed PS on local and a remote machines. Then I use PsExec.exe to push bat on remote to execute PS scripts. Now I am thinking to take advantage of PS V2.
To simple questions I have, to get a list of files on local, I can use the following codes:
$fs = Get-Item -Path $Path | Where { !$_.PSIsContainer ... } # more constrains in ...
if ( $fs -ne $null )
{
# continue to work on each file in the collection
...
}
What is the equivalent command to get a collection of files from a remote? I prefer to get a similar collection of file objects back so that I can access to their properties.
The second question is how to exec a command on remote with external application? I tried to use WIM Process before, but I could not get WMI class working on a case of Windows 2008 server. Then I used PsExec.exe to push a bat to a remote to execute PS script. It works in the cases. However, the problem I have to install PS on the remote as well. I am going to working another remote. I'll try to avoid to install PS on the remote. Can I take PS V2 advantage to execute a command on a remote Windows? What's the new commands?
By the way, normally, I have to pass user name and pwd to a remote. I guess in PS I have to pass user/pwd as well.
You can either put your code above in a script file and invoke it on a remote computer using V2 remoting like so:
PS> Invoke-Command remotePCName -file c:\myscript.ps1
You will need to be running with admin privs (elevated if UAC enabled) in order to use remoting. The command above will copy the script to the remote machine, execute it and return deserialized objects. These objects are essentially property bags. They are not "live" objects and setting properties on them like IsReadOnly will not affect the remote file. If you want to set properties then do it in your script that executes on the remote PC.
The option if you have a little bit of script is to use a scriptblock like so:
PS> Invoke-Command remotePCName { Get-Item C:\*.txt | Where {$_.IsReadOnly }
You can execute native commands (EXE) on the remote computer in either script or a scriptblock. You only need to make sure the EXE is available on the remote PC.
Regarding credentials, if you're on a domain and you have admin privs on the remote computer you won't need to pass credentials as your default credentials should work. If you need to run as a specific user then use the -Credential parameter on Invoke-Command like so:
PS> $cred = Get-Credential
PS> icm remotePCName { gci c:\windows\system32 -r *.sys } -credential $cred
Regarding your last comment, no PowerShell will use Windows integrated security so you should not have to pass any username or password unless you wanted to run it as a different user.
If you haven't yet enabled PS remoting, every time I've tried I've had to actually turn off UAC while I was enabling remoting (then I could re-enable UAC once remoting was enabled). Running Enable-PSRemoting from an elevated command prompt was not enough and the error message was not at all useful.
EDIT: I've just confirmed in a fresh Windows 7 VM that this is not an issue. It could have been a beta issue that I am no longer experiencing as I've been using beta/rc/ctp of PowerShell and Windows 7 for a long time.