Search code examples
powershellparameterspowershell-3.0powershell-remoting

Issues with Invoke-Command while installing softwares in remote server


I need to install an application in several remote servers in quiet mode. I have created a script (Installer.ps1) like below using Powershell v3.0:

param(
[String] $ServerNameFilePath = $(throw "Provide the path of text file which contains the server names"),
[String] $InstallerFolderPath = $(throw "Provide the Installer Folder Path. This should be a network location"),
[String] $UserName = $(throw "Provide the User Name"),
[String] $Password= $(throw "Provide the Password")
)
Function InstallApp
{
    $secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
    $mycreds = New-Object System.Management.Automation.PSCredential ($UserName, $secpasswd)

    $ScrBlock = {param($InstallerFolderPath) $ExePath = Join-Path $InstallerFolderPath "ServerReleaseManager.exe";  & $ExePath /q;}
    Invoke-Command -ComputerName (Get-Content Servers.txt) -Credential $mycreds $ScrBlock -ArgumentList $InstallerFolderPath

}

InstallApp -ServerNameFilePath $ServerNameFilePath -InstallerFolderPath $InstallerFolderPath -UserName $UserName -Password $Password

Then I call the script like below (Installer folder path can have white spaces and the executable ServerReleaseManager.exe accepts argument):

.\Installer.ps1 -ServerNameFilePath Servers.txt -InstallerFolderPath "\\TestServer01\Public\Stable Applications\Server Release Manager Update 2\2.7" -UserName "Domain\User" -Password "Test123"

I am getting below CommandNotFoundException always:

The term '\\TestServer01\Public\Stable Applications\Server Release Manager Update 2\2.7\ServerReleaseManager.exe' is not recognized as the name of a cmdlet, function, script file, or 
operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

I have tried other options like using -FilePath with Invoke-Command but same error. I am really blocked here. Can you please let me know why this error has shown? How to resolve the error? Or are there any better ways to deal with this. Thanks for your help.


Solution

  • This sounds like a double-hop authentication issue. Once you're remoted into the server, you can't access a file share on a third server because you can't pass your kerberos-based authentication to it.

    You could try copying from the share to the remote server, first (this has to be done on the computer executing the script), and then in the scriptblock refer to the (now local) path.

    You could set up CredSSP which isn't a great idea for this purpose.

    Basically, you need to avoid connecting to one machine, then connecting to another through that connection.

    Code that implements the workaround I'm describing:

    param(
    [String] $ServerNameFilePath = $(throw "Provide the path of text file which contains the server names"),
    [String] $InstallerFolderPath = $(throw "Provide the Installer Folder Path. This should be a network location"),
    [String] $UserName = $(throw "Provide the User Name"),
    [String] $Password= $(throw "Provide the Password")
    )
    Function InstallApp
    {
        $secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
        $mycreds = New-Object System.Management.Automation.PSCredential ($UserName, $secpasswd)
    
    
        $ScrBlock = {param($InstallerFolderPath) $ExePath = Join-Path $InstallerFolderPath "ServerReleaseManager.exe";  & $ExePath /q;}
    
        Get-Content Servers.txt | ForEach-Item {
            $remoteDest = "\\$_\c`$\some\temp\folder"
            $localDest = "C:\some\temp\folder" | Join-Path -ChildPath ($InstallerFolderPath | Split-Path -Leaf)
    
            try {
                Copy-Item -Path $InstallerFolderPath -Destination $dest -Force
                Invoke-Command -ComputerName $_ -Credential $mycreds $ScrBlock -ArgumentList $localDest
            finally {
                Remove-Item $remoteDest -Force -ErrorAction Ignore
            }
        }
    }
    
    InstallApp -ServerNameFilePath $ServerNameFilePath -InstallerFolderPath $InstallerFolderPath -UserName $UserName -Password $Password
    

    Notes

    1. This is untested.
    2. As mentioned by Swonkie, you should set your parameters as mandatory if that's what you're looking to achieve (not addressed in my code).
    3. You shouldn't pass separate plain text user name and password parameters and then convert them to a credential object. Instead pass a single [PSCredential] parameter. You can use a default value that prompts, like [PSCredential] $Cred = (Get-Credential). (this is not addressed in my code either).