Invoke-Command behavior clarification - powershell

I'm running into some trouble with the Invoke-Command cmdlet. I am logged into my local machine with my domain identity, which has admin rights on $server. If I manually enter my credentials, then use Invoke-Command, I get the error:
Cannot open Service Control Manager on computer ''. This operation might require other privileges.
# Works
Get-Service -ComputerName $server-ErrorAction Ignore
# Doesn't work
$cred = Get-Credential
Invoke-Command -ComputerName localhost -ScriptBlock {param($serverIPAddress) Get-Service -ComputerName $server -ErrorAction Ignore} -Credential $cred -ArgumentList $server
Is there something special about the built-in credentials that makes this work?

This is the classic kerberos double-hop.
The special thing that's happening is that the local computer has your credentials. It can talk to a remote computer and prove it has the credentials, without ever sending them.
However if the remote computer needs to access something on a third computer (second hop), it cannot prove it has the credentials (because it doesn't), so it cannot authenticate.
This is Kerberos working as designed.
Using Invoke-Command to localhost is still doing a remoting connection, so it still counts as a hop. The Get-Service call is a second hop.
Consider:
Invoke-Command -ComputerName $server -ScriptBlock { Get-Service -ErrorAction Ignore } -Credential $cred
That will work (as long as powershell remoting is enabled on the remote machine).
Otherwise, you need to enable kerberos delegation, or CredSSP, or (best if possible) rework whatever you're doing to not require a double hop.
Be wary of CredSSP (and delegation in general).

Related

Is it possible to host an "always running" powershell remoting process?

I'm researching whether it's possible to host an always-running Powershell remoting process.
By default, PSRemoting will start a process as the authenticated user.
However, using custom PS Session Configurations, it's possible to set a "run as account" that will ensure that the remote process (on the server, so to speak) always runs as a "known" user, regardless of the identify of the connecting client.
The reason for me researching this, is that I notice that Ansible runs quite slowly against windows servers, and I suspect this is due to the fact that the remoting process on the server gets spun up with each command Ansible sends. I'd like to see if it's possible to have an always-running process that is "ready" in order to speed up executions.
This is as far as I've gotten:
$ansiblecred = get-credential
New-PSSessionConfigurationFile `
-path "C:\sessionconfig.pssc" -SessionType Default `
-RequiredGroups #{ And = 'Administrators' }
Unregister-PSSessionConfiguration -Name ansible -force
Register-PSSessionConfiguration `
-Path "C:\sessionconfig.pssc" -Name ansible `
-RunAsCredential $ansiblecred -AccessMode Remote `
-UseSharedProcess -ThreadOptions ReuseThread
restart-service winrm
$remotecred = Get-Credential
$i = 0
while ($i -lt 10)
{
#This is slow because the remoting session is setup/teard down every time
Invoke-Command -ComputerName "localhost" -Credential $remotecred -Authentication Basic -ScriptBlock {$env:computername} -ConfigurationName ansible
$i ++
}
Even tho I'm connecting to the session with a different credential, the actual process runs as the "service" credential, so that part's good.
However, it seems to be still spinning up and down the process on each execution.
Just for clarification: The client here is not regular Powershell, it's client which will interact directly with the wsman service over http. So while I appreciate all responses, suggestions based around client-side Powershell code (such as new-pssession, invoke-command etc) are not gonna help :-|
Any pointers would be appreciated here, I'm trying to get to a place where the remoting process simply lives regardless of sessions being executed or not. Is this possible?
Create a session outside and use it in the loop.
$Session = New-PSSession -ComputerName $Server -ConfigurationName MyConfiguration
While(1){
Invoke-Command -Session $Session -Credential $remotecred -Authentication Basic -ScriptBlock {'my code'}
sleep 10
}

Run .ps1 on remote machine

I need to execute powershell script on remote computer with admin privilegies.
I have 2 scripts: client and server.
When i start client.ps1 i invoke command on server machine but get access error. I get no error if I use simple code in server.ps1 like write-host "hello".
server.ps1:
Get-service -ComputerName 'client'
client.ps1:
$ScriptBlockContent = {
d:\server.ps1
}
$remote=New-PSSession -ComputerName 'server'
Invoke-Command $remote -ScriptBlock $ScriptBlockContent
Your problem is authentication. You have to enable the server to use your credentials for that. You can do this by using CredSSP.
Enable this on your client:
Enable-WSManCredSSP -Role Client -DelegateComputer ServerNameHere
Enable it on your server:
Enable-WSManCredSSP -Role Server
Now add this to your Invoke-Command:
-Credential Domain\YourUsername -Authentication CredSSP
A remark on that: With CredSSP, its easy to steal your credentials, if you connect to a compromised system (same as RDP). Be sure that you do this on secure computers only.
Your client is trying to open D:\server.ps1 and getting access denied. Your script block doesn't even contain the neccesary code to cause powershell to process the contents of the server.ps1 anyway. You have it way to complicated.
You need to properly define a script block:
$scriptblock = { Get-service -ComputerName 'client' }
$remote=New-PSSession -ComputerName 'server'
Invoke-Command $remote -ScriptBlock $scriptblock
Running this command will connect to the machine called 'Server' and tell it to run Get-Service on 'Client' You don't need a client.ps1 and server.ps1 It can all be done from 1 script.
You also need to ensure winrm is running and configured on the server.

Access is Denied when Reset-ComputerMachinePassword is run through Invoke-command

I'm using the following command to reset a remote machine'
s password.
$user="Domain\domainadmin";
$pass="dapassword" | ConvertTo-SecureString -AsPlainText -Force;
$creds=New-Object System.Management.Automation.PSCredential -ArgumentList $UserName, $pass;
Invoke-Command -Credential $creds -ComputerName "DomainControllerMachine" -ScriptBlock{
$ComputerName = #"
SomeRemoteHost
"#
Import-Module ActiveDirectory;
Reset-ComputerMachinePassword -Server ${ComputerName};
}
I keep getting 'Access is denied' error.
This command cannot be executed on target computer('DomainControllerMachine') due to following error: Access is
denied.
+ CategoryInfo : InvalidOperation: (DomainControllerMachine:String) [Reset-ComputerMachinePasswor
d], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.ResetCompute
rMachinePasswordCommand
The account I use has all levels of access to the ActiveDirectory. So there won't be a issue with the credentials used for authentication.
If I run the same command on the 'DomainControllerMachine' (logged in as same user) it works fine.
Import-Module ActiveDirectory;
Reset-ComputerMachinePassword -Server "SomeRemoteHost";
Even the whole invoke-command block above just works without complaining on the DomainControllerMachine.
But when I do it remotely through Invoke-Command, or Enter-PSSession I get that dreaded access denied error..
I've also tried using CredSSP after setting up the WSManCredSSP (Client, delegation and Server) on the machines with no luck.
I may have missed something, or is there a better way to handle such a case?
It looks to me like you are running the Reset-computermachinepassword command on the domaincontroller. As far as I know it should be run on the computer that needs to be reset with the DC name in the -server field.
To do this you would need to run the command on the computer that needs it's credentials reset:
Reset-Computermachinepassword -server "DomainControllerMachine" -credential $PScredential
You can try to do it remotely with a PSsession if the computer has powershell remoting enabled. You will need to specify a different authentication method to reach a computer that has lost it's trust with the domain.
You can use Credssp but this will only work if your GPO allows delegating your credentials to the target computer.
Or you can use Basic authentication. But for that to work the Target must accept unencrypted traffic.
The command to do it remotely would probably look something like this:
$session = new-PSSession "targetcomputer" -Authentication Basic -Credential "Domain\domainadmin"
Invoke-Command -Session $session -scriptblock {Reset-Computermachinepassword -server "Domain\domainadmin"}

Powershell Server Network drive

I have a client and a server. The client will call a script like:
#Predefine necessary information
$Username = "Niels"
$Password = "password"
$ComputerName = "192.168.1.51"
$Script = {powershell c:/build/jclbuild2.bat}
#Create credential object
$SecurePassWord = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $Username, $SecurePassWord
#Create session object with this
$Session = New-PSSession -ComputerName $ComputerName -credential $Cred
#Invoke-Command
$Job = Invoke-Command -Session $Session -Scriptblock $Script
echo $Job
#Close Session
Remove-PSSession -Session $Session
On the server the jclbuild2.bat will run and access a network drive like \\otherserver\something, it says access denied if I do this command:
cmd.exe /C copy "\\server\file1.pdf" "\\server2\file1.pdf"
How do I access a network drive from a powershell file on a remote server? The user I use with the $username and $password should have access to the network drive.
I think it's a double hop issue, which I don't know how to solve.
You can't do this using the default authentication mechanism. You need to use an authentication mechanism that allows you to flow credentials, not just identity. Kerberos is one of these. CredSSP is another that is built into Windows starting from Vista/Server 2008 onwards.
I have experience setting up CredSSP. Note that there is some security risk because the target machine will have access to the credentials as plain text.
To set it up you will need to run two commands (both from an elevated shell). One on the machine you are running the above script on (the client) and another on the target that you will be connecting to via remoting (the server).
Enable-WSManCredSSP -Role Client -DelegateComputer $ComputerName -Force
This enables delegation to $ComputerName from the client (note you may have to use the FQDN). For security reasons you should avoid using the wild card '*' although you might consider using '*.mydomain.int' to enable delegation to all machines on the domain.
On the target server
Enable-WSManCredSSP -Role Server
Then when you create the session use the -Authentication flag
$Session = New-PSSession -ComputerName $ComputerName -credential $Cred -Authentication Credssp
There are questions on ServerFault on setting up CredSSP. There is also a blog post here with additional explanation. This post has troubleshooting tips for some commonly encountered error messages.
Another option is to use a delegated session on your server.
Basically, you create a custom remote session that uses the -RunAs parameter to designate the credentials that the session will run under. You can also constrain what scripts and cmdlets can be run in the session and specify who can connect to the session.
In this case, the session would run as the Niels account, and everything done in the session would be under that account authority, regardless of who was connected to the session. From that session, you can now make one hop to another server without needing CredSSP.
This also eliminates the security risk involved in storing that account password in the script file on the client computer.
http://blogs.technet.com/b/heyscriptingguy/archive/2014/04/03/use-delegated-administration-and-proxy-functions.aspx

New-PsDrive Remote copy from DFS share errors: A specified logon session does not exist

So to recap the situation: I am at one computer trying to run powershell using enter-pssession computername, then from the remote session, run the logic below:
$DFSPath = "\\DFSpath.com"
$RDL1 = [char](1+[char](gdr ?)[-1].name)
New-PSDrive -Name $RDL1 -PSProvider FileSystem -Root $DFSPath -Persist -credential domain\UN
The get-variable shows the variables properly. But when I try to create with New-PSDrive, it gives:
New-PSDrive : A specified logon session does not exist. It may already have
been terminated
I did look at this: PowerShell 2.0: Accessing Windows Shares during a Remote Session but wasn't able to get it to work. Also I wouldn't know how to devise it in my script above (which will be run on multiple computers). Is there anything newer? I am using v3 powershell. Thanks so much!
From the looks of things it appears that you are experiencing the dreaded "Double-Hop". If you only what to remote to a few computers it's pretty easy to setup the "fix" for the "Double-Hop". On the computers that you want to remote to you need to run the following commands:
Enable-PSRemoting
Enable-WSManCredSSP Server
Then on the computer you want to remote from you need to run the command:
Enable-WSManCredSSP Client –DelegateComputer [<FQDN of the server>][*]
In place of the fully qualified domain name you can put a * instead. That will allow you to send your credentials to any computer (that could be dangerous).
Now how would you work this into a script? There is a command called Invoke-Command. If you look at the parameters of Get-Help Invoke-Command -Parameter *, you'll see that it take a Credential and a Authentication. Here's how you would run a command on multiple computers.
$MyCred = Get-Credential
Invoke-Command -ComputerName Computer1,Computer2 -Credential $MyCred -Authentication Credssp -ScriptBlock {Get-ChildItem $args[0]} -ArgumentList '\\Server\Share' -ErrorAction SilentlyContinue
Now if you'll be remoting onto many machines and you know how to use Group Policy. I'd recommend setting up PSRemoting and enabling WSManCred with the Group Policy.