Powershell invoke-command multihopping - powershell

I have a question regarding multihopping in a windows environment.
Let's say I have a schedule running on Server A (Central Scheduler) which executes a command on Server B. This script contains a call to save files on a remote filer (UNC path, Server C). Hop 1 (from A to B) works well, hop 2 (from B to C) fails.
I already tested to save the files locally on server B, that works flawlessly.
I think there's a problem with the second hop. I remember reading something like this on a forum a while ago, but can't remember a solution.
In detail, the command looks like this:
$session = New-PSSession -computer ComputerName
$templatepath = "\\filerpath\"
Invoke-Command -Session $session -Scriptblock { powershell ovpmutil cfg pol dnl $Using:templatepath /p \BSH }
To clarify: Powershell gives me an "Access denied" when performing the second hop. I already enabled Credential delegation as described here:
Enabling Multihop Remoting
Any help is appreciated. Thanks in advance

The solution is a real pain in the backside if you ask me but here it is...
On the originating server (A):
Set-Item WSMAN:\localhost\client\auth\credssp -value $true
On the intermediate server (B):
Set-Item WSMAN:\localhost\client\auth\credssp -value $true
Open Group Policy editor on server A, navigate to:
Computer Configuration > Administrative Templates > System > Credentials Delegation
Enable these options:
Allow delegating fresh credentials
Allow delegating fresh credentials with NTLM-only server authentication
Both policies need to have server B added to the allowed list, wildcards are allowed. Note that if you use RDP from server A you'll also need to add TERMSRV/*
When running Invoke-Command from server A, include the -Authentication CredSSP param.
Note that if saving SecureStrings somewhere for the credential to connect to server C, you'll want to either use a fixed encryption (specify byte array) or plain text and convert it.

Related

Double-Hop Errors when running Skype for Business Cmdlets

I am attempting to automate the Skype for Business Server installation process in Powershell, I have a script that remotes into specified machines and begins preparing them as Front-End servers. The problem lies when certain SfB cmdlets (SfB commands are all of the form "verb-Cs...", ex. Get-CsUser or Get-CsPool) are run in remote sessions, they throw the double-hop error:
Exception: Active Directory error "-2147016672" occurred while searching for domain controllers in domain...
This is after running Enable-CsComputer, which enables the computer's role-based off its definition in the topology (topology was published successfully). The user object is in all required groups (RTCUniversalServerAdmins, Schema Admins, CsAdministrators & Local Admin rights on all SfB Servers). Oddly enough, the command 'Import-CsConfiguration -localstore" does not throw errors, and it's in the same remote session. There may be other local or domain groups that I need to be in, but I cannot pinpoint exactly which and have not seen them documented in the Skype build guides. Skype commands that have parameters to specify targets or just pull data, such as Get-CsPool or Get-CsAdForest, do not have errors because they are run in the local scope. The Enable-CsComputer has no parameter for the computer name, it has to be executed from that machine itself.
Enabling CredSSP delegation on each server is not an option, and I'm not understanding why there is a "second hop" in this command! If the second hop was a resource on a file server or database, that would make sense, and be easy to solve, but in this case, I can't track it. Can anyone tell me what I may be missing?
Here's a code sample to try and illustrate. From the jumbox I get the pool data to create an array, and a session is opened to each machine:
$ServerArray =get-cspool -identity $poolName
$i=0
$SessionArray = #{}
foreach($server in $ServerArray.Computers){$SessionArray[$i] = new-PsSession -ComputerName $server}
foreach($session in $SessionArray.values){
invoke-Command -session $session -scriptBlock {
#remote commands:
import-csConfiguration -<config file path> -localstore; #no errors
enable-CsReplica; #no errors
enable-cscomputer; #double hop error here
}}
If I log into that machine and run the same command, it executes fine but the intention of the project is to automate it on an arbitrary number of machines.
It looks like it's just trying to authenticate to a domain controller, which is reasonable. You'll have to approach this like any other double-hop issue.
Microsoft has an article dedicated to the double hop issue, and has a few solutions other than CredSSP that you can look at: Making the second hop in PowerShell Remoting

vNext Work Around for Second Hop Issue on NonDomain Machines

I've asked the Microsoft Developer Community this question but I haven't had much success.
I am trying to create some automation tests with a vNext Build Definition in which the build agent RemotePSSession into a non-domain virtual machine (the test machine) and runs a batch file that can take several arguments. This batch file may read (installer files) or write (reports) to a network share that is on the domain. The issue that I am coming across is the second hop issue. Here is an article about it: https://blogs.technet.microsoft.com/ashleymcglone/2016/08/30/powershell-remoting-kerberos-double-hop-solved-securely/
In my instance, the PowerShell Remote Session is not able to pass the credentials we have authenticated previously in the test machine to access the network share’s resources. We have tried using CredSSP authentication on both the agent and the test machine to enable access but that has failed. The Net Use and other commands which call domain resources have also failed. We’ve even tried modifying the custom task PowerShell on Target Machines task and did not have much luck with it.
From what we have discovered is that there is no way to access the domain network shares with RemotePSSession with the following topology: Server A (which is in the domain or workgroup) ⇒ RemotePSSession + CredSSP into Server B (which is non-domain), using a local admin Server B account ⇒ Calls the network shares, with Net Use using some domain account.
It seems that the second hop only works for domain-joined machines (we have been testing it CredSSP using as well).
Let us know if there is a solution or workaround that we can implement.
Their response was >> If you can make sure the method is correct and the issue is caused by DevOps, we will be happy to help you with your issues about DevOps. Here are some documents might be helpful: https://learn.microsoft.com/en-us/powershell/scripting/learn/remoting/ps-remoting-second-hop?view=powershell-6.
I've looked at this documentation before and I haven't had much success, does anyone else have any suggestions?
Here is a code snippet:
#serverA - local machine
#serverB - $remoteServer
#serverC - DFS namespace server
$session = New-PSSession -ComputerName $remoteServer -Credential $credential -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck) -Authentication Credssp
$scriptBlock_runFile = {
#Scenario 0 which works:
#ipconfig
#Scenario 1 which doesn't work:
#& dir \\contoso.com\departments\folder"
#Scenario 2 which doesn't work:
#& net use x: \\contoso.com\departments\folder /user:CONTOSO\user "password"
}
Invoke-Command -Session $session -ScriptBlock $scriptBlock_runFile

Powershell "screen" - keep the processes running even the connection is dropped?

I'm using enter-pssession to run scripts on remote servers. So I can login remotely to the servers. Run commands interactively, close the powershell console and later I can reattach the session and check the commands outputs.
Is there a Linux screen like functionality in powershell? I cannot use Windows remote desktop to connect the servers.
You can use Invoke-Command with -InDisconnectedSession, it will start session in asynchronous mode. After you can connect to this session, take data from it, etc. You can read more about this here.
You can create session, disconnect from session, connect back to it.
May be useful for you: New-PSSessionOption with -IdleTimeout.
-IdleTimeout:
Determines how long the session stays open if the remote computer does not receive any communication from the local computer. This includes the heartbeat signal. When the interval expires, the session closes. MSDN Link
I have recently run into double-hop issues with using PSSessions. What I did to work around that is to create a Session Configuration on the remote server that uses the -RunAs parameter to set the credentials that I need the commands on the remote server to be executed as. Then you connect to that session configuration on the remote server, and things should work as expected.
$MyCreds = Get-Credential ''
Invoke-Command -ScriptBlock {
Set-PSSessionConfiguration -Name "My Remote Config" -RunAsCredential $using:MyCreds -Force
} -ComputerName Server01
Then once the session configuration exists I can start a session using that config, and the whole double hop issue is null and void.
Now, mind you I do add some additional security, so that other people cannot use my session config, since that config has my credentials cached on the server (encrypted), and if they used that config they could do whatever they wanted as me. So to accomplish that I get my domain account SID, generate a SDDL line, and restrict access to the Session Config to only my account.
$Searcher = [adsisearcher]"(&(sAMAccountName=$($Creds.UserName.Split('\')[1]))(objectClass=user))"
$Results=$Searcher.FindOne().GetDirectoryEntry()
$MySID = new-object System.Security.Principal.SecurityIdentifier($Results.objectSid.value,0)|% value
$SDDL = "O:NSG:BAD:P(A;;GR;;;BA)(A;;GR;;;IU)(A;;GA;;;$MySID)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)"
$FQDN = $Server.ServerName,$Server.Forest -join '.'
$MySessionName = "DoubleHop-{0}" -f $MyCreds.UserName.Split('\')[1]
Invoke-Command -ScriptBlock {
Register-PSSessionConfiguration -Name $using:MySessionName -RunAsCredential $using:MyCreds -Force -SecurityDescriptorSddl $using:SDDL
} -ComputerName $FQDN -ea 4

Powershell remoting - cannot execute an exe as another user

I've a commandline program (c#) that encrypts config files based on machine key.
A powershell script copies the build to a Target Server, modifies configs accordingly and installs windows services.
All the windows services run as local system account (standard user, non-admin) - let's call this account "locuser".
The Target Server is a Win 2012 R2 Server. All of the above is achieved by PS remoting from the Build Server to this Target server.
Now, I need to run the encrypt commandline program as "locuser", so that the program can use the account specific key to do the encryption.
I know that this can be easily achieved by calling Start-Process cmdlet with -Credentials parameter. Well, here's the catch, the above works fine, if I remote in (RDP) to the Target Server and then run the Start-Process .... -Credential $cred from a Powershell Console.
However, I need this to be working while I remote-in (using my scripts) to the TargetServer whilst deploying. When I remote-in to the TargetServer I use credentials that has Admin privileges.
I've tried the following
I've granted "locuser" both "Full Control" and "Invoke (Execute)" permissions by using the Set-PSSessionConfiguration -Name Microsoft.PowerShell -ShowSecurityDescriptorUI command. I've run this command for both Microsoft.Powershell and Microsoft.Powershell32 - Still get Access Denied
I've edited the "Local Security Policy"->"Local Policies"->"User Rights Assignment"->Impersonate a client after authentication - and added both the Admin account (that I login with) and the "locuser" account - Still get Access Denied
I've also granted locuser admin rights - Still get Access Denied
I'm pretty sure, there is some configuration on the PS Remoting Side of things that I'm missing out but can't figure out what - because all Powershell throws me is a Access Denied error (see screenshot) with little to no useful information to troubleshoot further.
Also, checked Event logs for any traces but to no avail.
You've fallen prey to the dreaded Double Hop. Basically you're authenticating from computer A to computer B, then trying to authenticate again from computer B to computer C (which also happens to be B in this case).
If at all possible, you would be better off ending the session and starting a new one with the locuser credentials, then just calling Start-Process. Another, more messy approach is to use schtasks.
I can tell you how to do it in the same session but it's a bit messy and very complicated, and should only be a last resort:
On the originating server (Build Server):
Run the command Enable-WSManCredSSP -Role Client -Delegate [name] where [name] is an IP or DNS address / range including any target servers (eg "192.168.1.*")
Open GPEdit.msc, navigate to Computer Configuration\Administrative Templates\System\Credentials Delegation and check that the rules Allow delegating fresh credentials and Allow delegating fresh credentials with NTLM... are enabled and include [name]
On the Target Server:
Run the command Enable-WSManCredSSP -Role Server
Running the command:
Invoke-Command [targetserver] [-Credential $cred] -Scriptblock {
## do stuff
Invoke-Command . -Credential $locusercred -Authentication Credssp -ScriptBlock {
Start-Process -FilePath $sc #etc
}
}
Some things to be aware of:
Firstly I used this setup to create a local session, then remote from there (so A-A-B instead of A-B-B) so the Group Policy stuff might be in the wrong place but pretty sure it's right.
Secondly I found that credentials are a pain to get working in sessions (in this case $locusercred). I did get it going natively but weirdly it suddenly couldn't decrypt the securestring. I ended up saving a securestring with a defined key to the registry so it can always be decrypted from any account, you may need to come up with your own solution there.
All this stuff is explained in the free eBook "The Secrets of PowerShell Remoting", if you go for the double-hop approach I recommend giving it a read.

Invalid computer name error with remote execution of PowerShell script

I am trying to execute some PowerShell code in a remote computer using the following:
$session = New-PSSession -Credential "myDomain\myUserName" -ComputerName "remoteCompName"
$result = Invoke-Command -Session $session -ScriptBlock {
New-Item -type file C:\test10.txt
}
I am prompted to enter my password in a GUI. I do that. It then errors out with:
New-PSSession : One or more computer names are not valid. If you are
trying to pass a URI, use the -ConnectionUri parameter, or pass URI
objects instead of strings.
I replaced the computer name with the FQDN. Still no luck. What is going on here?
There are other questions on stackoverflow on executing PowerShell scripts on remote machines of course but none address this error.
BTW, the machine is part of the domain and is running.
Additional info added later [EDIT]
Things to know:
The machine is part of the domain and is running.
I checked if PS remoting is enabled. It was.
I checked if WinRM is running. It is.
The remote machine is a VM and it is a 2012 R2.
Here is what I've tried:
I replaced the computer name with the FQDN. Still no luck.
I removed the credential parameter
I tried another remote machine (also a VM)
I tried another source machine, i.e. the machine I am running the command from)
Thanks!
-Rohan.
When I use a remote machine with a name that is just alphanumeric (no underscores, dashes, etc.), it works! The name of all machines I tried before had leading '_' in them. That was the cause of the error. (The answer was suggested by Rhys W Edwards on the Windows PowerShell TechNet forum, which is within Windows Server forums).
Just put your ComputerName to braces like this {Remote_Computer_Name}