PowerShell Invoke-Command on an advanced function - powershell

I have an advanced function Copy-FilesHC that is available in a module file. This function copies some files from the Source to the Destination folder and generates some output for in a log file.
The function works fine locally:
Copy-FilesHC -Source $Src -Destination $Des *>> $Log
It also works on a remote machine:
# For remote use we need to make it available first
Import-Module (Get-Command Copy-FilesHC).ModuleName
Invoke-Command -Credential $Cred -ComputerName $Host -ScriptBlock ${Function:Copy-FilesHC} -ArgumentList $LocalSrc, $LocalDes
However, I can't seem to figure out how I can have it pass the output to a log file like in the first command. When I try the following, it fails:
Invoke-Command -Credential $Cred -ComputerName $Host -ScriptBlock ${Function:Copy-FilesHC *>> $Log} -ArgumentList $LocalSrc, $LocalDes
Invoke-Command : Cannot validate argument on parameter 'ScriptBlock'. The argument is null. Provide a vali
d value for the argument, and then try running the command again.
As indicated here I thought the $ sign for the ScriptBlock was incorrect. But this way I don't need to put my advanced function in a ScriptBlock to copy it over as it now happens automatically while it's only available within the module. So I just need to find out how to capture the output in the log file.
Thank you for your help.

Found the solution just a few minutes ago:
# For remote use we need to make it available first
Import-Module (Get-Command Copy-FilesHC).ModuleName
Invoke-Command -Credential $Cred -ComputerName $Host -ScriptBlock ${Function:Copy-FilesHC} -ArgumentList $LocalSrc, $LocalDes *>> $Log

Related

Redirect output of Import-Module within ScriptBlock of Invoke-Command

A module I am importing has several Write-Host commands that I wish to suppress (redirect to NULL).
This works great when I run it directly on a machine:
Import-Module 'path\to\module\module.ps1' 2>&1 > $null
When running the exact same on a remote host using Invoke-Command, the redirect is ignored and all the output from the module shows up:
Invoke-Command -ComputerName $server -Credential $cred -ScriptBlock {
Import-Module 'path\to\module\module.ps1' 2>&1 > $null
}
I've tried different ways of re-direct (i.e. Out-Null, etc), all with the same result.
You could use 6>> $null .
so:
Invoke-Command -ComputerName $server -Credential $cred -ScriptBlock {
Import-Module 'path\to\module\module.ps1' 6>> $null
}
This is the "Example 5: Suppress output from Write-Host" provided by Microsoft, modified accordingly to "about_Operators".

Executing CMD or EXE file using PSSession (remote powershell) caused Error 1603 access denied

I have the following script powershell command but it returns access denied. I assume the Error 1603 is caused by remote accessing the server. However, the $username has admin rights in the computer01 server.
To recheck if my hunch was right, I tried to test with the following and I got access denied:
Start-Process cmd -Credential $Cred
Update
The error was due to the $Cred . Removing the -Credential argument works fine.
End of Update
The commands have no problems executing directly in the computer01 machine using the cmd.exe.
I want to use cmd /c in this case as I need to get the real exit code from the SETUP.EXE installer.
See full script below:
$script = {
#Param(
# [String]$username,
# [String]$password
#)
# $Cred = New-Object System.Management.Automation.PSCredential ($username, $password)
$respfile = "$env:TEMP\test.resp"
echo 'key=value' > $respfile
$username = "$env:USERDOMAIN\$env:USERNAME"
Write-Host Hello $username
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
Start-Process cmd -Credential $Creds
#This command cannot be run due to the error: Access is denied.
# + CategoryInfo : InvalidOperation: (:) [Start-Process], #InvalidOperationException
# + FullyQualifiedErrorId : #InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
# + PSComputerName : computer01
# cmd /c "$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
runas /user:$Username "SETUP.EXE" /INSTALL -s /RESPFILE:"$respfile"
echo $LASTEXITCODE
# Error 1603
}
#$username = 'domain/user'
#$password = 'password'
$server = 'computer01'
$Creds = New-Object System.Management.Automation.PSCredential
$session = New-PSSession -ComputerName $server
#Invoke-Command -Session $session -Scriptblock $script -Argumentlist $username, $password
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds #updated based on #postanote advise
Remove-PSSession -ComputerName $server
I have found the following similar link install-remotely but do not want to use the ENTER-PSSession command. I do not want to exit the current PSSession and remotely join again in server just to install then exit.
Any suggestions how to use only PSSession and successfully executing installers in the remote server?
As one mentioned in the comments, you don't need cmd.exe. You can use the call/invocation operator - & - to specify that the next token on the line is a command:
& "$path\SETUP.EXE" /INSTALL -s /RESPFILE:$respfile
Of course, for this to work, the parameters to SETUP.EXE need to be correct (I don't know whether that's the case or not).
Never pass plain text passwords in scripts. It exposes you to uneeded risks.
Use proper secured credentials models.
• Working with Passwords, Secure Strings and Credentials in Windows PowerShell
• quickly-and-securely-storing-your-credentials-powershell
PowerShell remoting requires the use of an implicit (New-PSSession) or explicit (Enter-PSSession) session.
• About Remote Requirements
There are only a handful of cmdlets you can use as non-Admin ir run without PSRemoting enabled.
• Tip: Work Remotely with Windows PowerShell without using Remoting or WinRM
As noted in the Powershell Help file | MS Docs link above, with PSRemoting, you must be using an account that is an admin on the remote host.
In Windows OS proper, to install software, you must be an admin and running that in an admin session.
PowerShell runs in the context of the user who started it.
If you are trying to run in another user context, that is a Windows Security boundary, and you cannot do that without PowerShell natively, you'd need other tools like MS Sysinternals PSExec. See also:
Find-Module -Name '*Invoke*' | Format-Table -AutoSize
# Results
<#
Version Name Repository Description
------- ---- ---------- -----------
...
3.1.6 Invoke-CommandAs PSGallery Invoke Command as System/User on Local/Remote computer using ScheduleTask.
...
#>
Try this refactored option...
$script = {
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
$respfile = 'whatever this is'
& "SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
Write-Output $LASTEXITCODE
}
$server = 'computer01'
$session = New-PSSession -ComputerName $server
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds
Remove-PSSession -ComputerName $server
Details
# Get specifics for a module, cmdlet, or function
(Get-Command -Name Invoke-Command).Parameters
(Get-Command -Name Invoke-Command).Parameters.Keys
Get-help -Name Invoke-Command -Examples
# Results
<#
Invoke-Command -ComputerName server01 -Credential domain01\user01 -ScriptBlock {Get-Culture}
$s = New-PSSession -ComputerName Server02 -Credential Domain01\User01
$LiveCred = Get-Credential
Invoke-Command -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.exchangelabs.com/PowerShell -Credential $LiveCred -Authentication Basic
Invoke-Command -Session $s -ScriptBlock { Get-HotFix } -SessionOption $so -Credential server01\user01
Enable-WSManCredSSP -Delegate Server02
Set-Item WSMan:\Server02*\Service\Auth\CredSSP -Value $True
Invoke-Command -Session $s -ScriptBlock {Get-Item \\Net03\Scripts\LogFiles.ps1} -Authentication CredSSP -Credential Domain01\Admin01
#>
Get-help -Name Invoke-Command -Full
Get-help -Name Invoke-Command -Online
So, I was able to solve my problem.
1603 is the error thrown by the setup.exe.
Just to be sure, I manually executed first the following directly in the server using CMD and it was working!
$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'
I did a lot of testings. Researched and as mentioned from comments above, I did different ways to execute programs using powershell. I even used ACL to change ownership of installer directory/ files, switching to different user accounts (with different priviledges) but still getting access denied (including the Admin account).
It took days before I realized the difference in output file size of manual run in machine and the remote. The cause was the $respfile. It really is worth checking every possible reason/ scenario why there's access denied. Plus I cannot extract the setup.exe and its contents to troubleshoot.
The $respfile was created via powershell. I noticed the size created by powershell is doubled compared to a CMD size that was needed. With that, I assumed that the setup.exe reads file in UTF-8 format. I only know that it's working when triggered via CMD and not via powershell.
I suddenly bumped on this links differrent Powershell and CMD sizes and convert file content to CMD readable file - utf8. After converting the $respfile to UTF-8 format, I was able to run the exe successfully.
Hopefully, this can help others too!

Passing variables to Invoke-Command, inside inlineScript, inside workflow

I'm trying to create a script that connects to various servers, should attach a PSDrive and copy over files. The problem resides in that I an unable to pass the variable into the Invoke-Command script-block.
workflow kopijobb {
param ([string[]]$serverList, $creds, $basePath)
foreach -parallel ($server in $serverList){
# Use the sequence keyword, to ensure everything inside of it runs in order on each computer.
sequence {
#Use the inlinescript keyword to allow PowerShell workflow to run regular PowerShell cmdlets
inlineScript{
$path = $using:basePath
Write-Host "Starting $using:server using $path"
#Create session for New-PSSession
$session = New-PSSession -ComputerName $using:server -Credential $using:creds
# Copy Java and recreate symlink
Invoke-Command -Session $session -ScriptBlock {
# Make a PSDrive, since directly copying from UNC-path doesn't work due to credential-issues
New-PSDrive -Name N -PSProvider FileSystem -root $using:path -Credential $using:creds | out-null
I pass the network path to $basePath, and I'm able to read it inside the inlineScript block (where I have tried storing it in a new variable to test), but once I try accessing it in the New-PSDrive command, the variable is suddenly empty/unreachable, and the mounting of the drive fails with the error Cannot bind argument to parameter 'Root' because it is null.
I'm at a loss to why this fails, so I'm turning to the collective wisdom here instead.
If feels embarrassing to answer my own question, especially on the same day, but I bumped into a PowerShell guru at work and he took one glance at the script and saw the problem:
I had to add -Args to the Invoke-Command
Invoke-Command -Session $session -ScriptBlock {
param($srv,$login,$path,$...)
#Make a PSDrive, since directly copying from UNC-path doesn't work due to credential-issues
New-PSDrive -Name N -PSProvider FileSystem -root $path -Credential $login | out-null
} -Args $using:server,$using:creds,$using:basePath,$using:...
This does of course mean that I had to import all the needed arguments from the top level into the workflow, and then into the Invoke-Command.

PowerShell function with parameters? [duplicate]

This question already has an answer here:
Parameter interpretation when running jobs
(1 answer)
Closed 6 years ago.
I have a PoSH script that I can't figure out is not running..
function Connect-AD
{
Param($mod,$cmd)
Write-Host "$mod $cmd"
Write-Host "`tConnecting to AD: $DC`n"
$ADSession = New-PSsession -ComputerName $DC -Credential $MyCredential
Invoke-Command -Command {Import-Module ('$mod') -Cmdlet ('$cmd')} -Session $ADSession
Import-PSSession -Session $ADSession -Module ('$mod') -Prefix r | Out-Null
}
I then try to call this with..
Connect-AD -mod 'ActiveDirectory' -cmd 'Get-ADUser,New-ADUser'
But no mater what I do I keep getting..
The specified module '$mod' was not loaded because no valid module file was found in any module directory.
The Write-Host inside the function outputs the parameters correctly, so it is getting that far. However it is not being passed into the Invoke-Command or Import-PSSession?
I've tried different ways to escape the parameters, etc.. but no luck.
What am I not doing correctly? Anyone able to help me out? Thanks.
Single quoted strings don't interpolate variables, '$mod' is a literal string "dollar m o d".
And you probably need to read all the similar questions on passing parameters to Invoke-Command, because the command {} is running on another computer - how will it know what the variable $mod is on your computer?
Passing string $variable to invoke-command scriptblock parameter -name
Powershell: How to pass parameter with invoke-command and -filepath remotely?
Something like
Invoke-Command -Command {param($mod, $cmd) Import-Module $mod -Cmdlet $cmd} -Session $ADSession -ArgumentList $mod,$cmd
Help Links (if available):
Invoke-Command
Import-Module

Powershell - Pass multiple parameters to Invoke-Command

I'm trying to write a one-liner to leverage some of the capabilities of netbackup remotely. I know how to pass parameters to Invoke Command using -args[0] and [1] at the end, with repeating parameters. An example of what I'm trying to accomplish:
CC = Country Code (Will repeat due to the naming conventions
SS = Site (Also repeats due to naming convention)
Invoke-Command -ComputerName RemoteServer -ScriptBlock {& "C:\Program Files\Veritas\NetBackup\bin\admincmd\bpplinfo.exe" CC0SITE_VMW_BRON -set -L -M CC0SITEb0100d0a.s0SITE.CC.DOMAIN.COM}
After getting user-input and declaring the parameters, it doesn't seem to pass to the invoke-command
Invoke-Command -ComputerName RemoteServer -ScriptBlock {& "C:\Program Files\Veritas\NetBackup\bin\admincmd\bpplinfo.exe" $args[0]0$args[1]_VMW_BRON -L -M $args[0]0$args[1]b0100d0a.s0$args[1].$args[0].DOMAIN.com} -Args $CCode, $Site
Use param($val1,...) inside the scriptblock to pass the arguments.
Invoke-Command -ComputerName 'SERVERNAME' -ScriptBlock {
param($argument1, $argument2) #<--- this is required!
write-host $CCode
write-host $Site
} -ArgumentList ($argument1, $argument2)
More information and syntax can be found at ArgumentList (alias Args) section for Invoke-Command cmdlet.
You may have an issue with the way you're expanding your variables and hence it may appear the args are not being passed correctly, when I'm debugging I simply use write to test the output. For example:
Invoke-Command -ComputerName localhost -ScriptBlock {write "C:\Program Files\Veritas\NetBackup\bin\admincmd\bpplinfo.exe CC0SITE_VMW_BRON -set -L -M CC0SITEb0100d0a.s0SITE.CC.DOMAIN.com"}
Invoke-Command -ComputerName localhost -ScriptBlock {write "C:\Program Files\Veritas\NetBackup\bin\admincmd\bpplinfo.exe $($args[0])0$($args[1])_VMW_BRON -set -L -M $($args[0])0$($args[1])b0100d0a.s0$($args[1]).$($args[0]).DOMAIN.com"} -Args "CC", "SITE"