I have two servers. Server A needs to contact server B and run a specific function withing a powershell function library (just a .ps1 file) that I've created. The function that is being called also calls other functions during execution, all of which are contained in the same .ps1 file. Finally, when server A calls the function which resides on server B, he also needs to pass 5 parameters to the function.
Using several combinations of New-PSSession, Enter-PSSession, or just Invoke-Command, I am able to successfully call the server B function; however, here's where I'm stuck.
I've only been successful in having the server B function prompt me for the parameters it's expecting if I perform an Import-Module on server A before hand and import a copy of the .ps1 file I'm attempting to call the function from. Without that, the remote function on server B cannot be found or recognized.
If I don't import the module, when I call the function on server B I can get it to perform simple operations such as echo or Write-Output, but functions cannot be recognized.
Finally, building on the situation in which I can import the module and successfully get the function to prompt me for input, following the input the base function on server B begins executing, however it fails when sub functions within it are called, to update logs etc, indicating it can't recognize them. Again, they are all contained in the same .ps1 file.
Here are some code examples outlining what I'm trying to do:
EXAMPLE 1
The following is run on Server A:
Invoke-Command -ScriptBlock { \\SERVERB\c$\orchestration\scripts\IvantiSyncEngine_RemotePatchRequest.ps1 }
Here's the target IvantiSyncEngine_RemotePatchRequest.ps1 file on Server B that we're calling in the above script:
# IvantiSyncEngine_RemotePatchRequest.ps1
Write-Output "Here we go!"
And the output on Server A is as expected:
PS C:\> Invoke-Command -ScriptBlock { \\SERVERB\c$\orchestration\scripts\IvantiSyncEngine_RemotePatchRequest.ps1 }
Here we go!
EXAMPLE 2
Moving forward, I expanded the script on Server B to look as follows:
# IvantiSyncEngine_RemotePatchRequest.ps1
Write-Output "Here we go!"
Request-RemotePatch
The function being called exists on Server B as well, in a separate .ps1 file. That function looks like this:
function Request-RemotePatch {
Param (
# Define the parameters this function accepts
[Parameter(Mandatory=$true, Position=0)]
[string] $PatchHostname,
[Parameter(Mandatory=$true, Position=1)]
[string] $PatchDomain,
[Parameter(Mandatory=$true, Position=2)]
[string] $PatchRebootType,
[Parameter(Mandatory=$true, Position=3)]
[string] $RequesterHostname,
[Parameter(Mandatory=$true, Position=4)]
[string] $RequesterUsername
)
Begin{
}Process{
Write-Output "Testing"
# Empty line for spacing
Update-Log $global:RemotePatchLog "`n"
# Log to RemoteRequests log file
$MixedMessage = "Request received from $RequesterHostname\$RequesterUsername to remotely patch $PatchHostname, on domain $PatchDomain, with reboot type $PatchRebootType."
Update-Log $global:RemotePatchLog $MixedMessage
# Initialize the scan
Update-Log $global:RemotePatchLog "Scanning..."
$scan = Start-PatchScan -Name "RemoteAPI-Scan" -EndpointNames $PatchHostname -TemplateName "Security Patch Scan"
# Wait for the scan to complete and return details
$scan | Watch-PatchScan
Update-Log $global:RemotePatchLog "Scanning complete."
# Deploy against that scan
Update-Log $global:RemotePatchLog "Deploying Patches..."
$DeployTemplateName = "RemoteAPI-" + $PatchRebootType
$deploy = Start-PatchDeploy -ScanUid ($scan.Uid) -TemplateName $DeployTemplateName
# Wait for deploy to complete and return details
$deploy | Watch-PatchDeploy
Update-Log $global:RemotePatchLog "Deployment complete."
}
End{}
}
As you can see, it accepts 5 parameters. It also calls other functions in the same .ps1 file for things like logging mostly.
Finally, when I attempt the same call from Server A, I get this:
PS C:\> Invoke-Command -ScriptBlock { \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_RemotePatchRequest.ps1 }
Here we go!
cmdlet Request-RemotePatch at command pipeline position 1
Supply values for the following parameters:
PatchHostname:
So up until now, things are working as expected. However, I'm not currently passing parameters to the remote function which I want to fix. But for now to illustrate what's happening I'll simply enter the value in the prompts provided. Here's the final setup before I hit enter to execute:
PS C:\> Invoke-Command -ScriptBlock { \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_RemotePatchRequest.ps1 }
Here we go!
cmdlet Request-RemotePatch at command pipeline position 1
Supply values for the following parameters:
PatchHostname: kam1oqapp161
PatchDomain: connex
PatchRebootType: NoReboot
RequesterHostname: kam1oqweb163
RequesterUsername: Joey
And after hitting execute, here is what we get:
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Request received from kam1oqweb163\Joey to remotely patch kam1oqapp161, on domain connex, with reboot type NoReboot.
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Scanning...
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Start-PatchScan : The term 'Start-PatchScan' 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.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:675 char:17
+ $scan = Start-PatchScan -Name "RemoteAPI-Scan" -EndpointNames $PatchHost ...
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Start-PatchScan:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Watch-PatchScan : The term 'Watch-PatchScan' 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.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:678 char:17
+ $scan | Watch-PatchScan
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Watch-PatchScan:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Scanning complete.
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Deploying Patches...
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Start-PatchDeploy : The term 'Start-PatchDeploy' 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.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:684 char:19
+ $deploy = Start-PatchDeploy -ScanUid ($scan.Uid) -TemplateName $DeployTe ...
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Start-PatchDeploy:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Watch-PatchDeploy : The term 'Watch-PatchDeploy' 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.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:687 char:19
+ $deploy | Watch-PatchDeploy
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Watch-PatchDeploy:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Deployment complete.
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
I'll stop here. Code quoting doesn't preserve coloring but there's a lot of red going on here. The errors center around 2 problems. (1) an inability to find folder references and locations, and (2), an inability to recognize other functions and commands being called.
Question 1: Why is the remote function which has references to remote folder locations, having a hard time finding these locations? I'm only calling the starter function from Server A and it's kicking off everything else from Server B. Shouldn't all of those location references be relevant as they're being called from Server B? I'm assuming if I changed all of the remote references from 'C:...' to '\ServerB\C$...' it would fix it, I'm just uncertain as to why that's necessary?
Question 2 For the functions and commands that aren't being recognized, what's the best practice solution here? A module needing to be imported etc?
I'll continue to add more as we flesh out these pieces because this is the stuff I'm getting hung up on.
Update 5/18/2018
Alright, I've partially solved (1) and (2) above. Here's what I did.
I converted the functions .ps1 file I was using to a .psm1 file, created a proper manifest, setup the proper folder structure when referencing PS modules, and performed a proper module installation/import when I call from the remote server. Doing all of this loads all of the necessary functions into the local space on Server (A), removing any errors regarding unrecognized references or functions. When I perform the import I'm using a UNC path. That new code is as follows:
$s = New-PSSession -ComputerName 'SERVERB.DOMAINX.DOMAINY.com'
Invoke-Command -ScriptBlock {Import-Module STProtect -PassThru} -session $s
Invoke-Command -ScriptBlock {Import-Module \\SERVERB\c$\orchestration\scripts\PSModules\IvantiSyncEngine_Functions -DisableNameChecking -PassThru} -session $s
Invoke-Command -ScriptBlock ${function:Request-RemotePatch} -ArgumentList 'TARGETSERVERTOPATCH','TARGETDOMAIN','REBOOTTYPE','MYPCNAME','MYUSER' -session $s
Remove-PSSession -session $s
Exit-PSSession
If you look at the code above, you'll also notice I've had some success with -ArgumentList to pass through variables to the function we're calling remotely. Now, with all of that when I call the remote function on Server B, from Server A, I'm receiving all of the proper prompts but it's failing now with the following error message:
So...
Question 3
It appears something isn't listening, but I haven't managed to figure out what exactly. Any ideas?