PowerShell workflow Invoke-Command not working (Write-Host) - powershell

Currently trying to use PowerShell workflow to process some remote server stuff that I am doing in parallel.
I am having no luck trying to get the Invoke-Command to log onto the server to actually work. It is not throwing an error but there also is no output to the console like there usually is with a Write-Host statement. Not seeing anything that is helping me looking through older posts.
workflow test {
Param([System.Management.Automation.PSCredential]$cred)
InlineScript {
Write-Host $using:cred
Invoke-Command -Computer "server" -Credential $using:cred -ScriptBlock { Write-Host "hello world" } }
}
}
#Calling the function
test $cred

write-output instead of write-host works. Note that this runs in parallel too:
invoke-command computer1,computer2,computer3 { 'whatever' }
By the way, you have an extra curly bracket at the end.
Another way to do it:
workflow test {
InlineScript {
Write-Host 'hello world'
}
}
test -pscomputername server -pscredential $cred

Related

Remote Invoke-Command - function/cmdlet not found

I've got the following script:
param(
[string[]]$servers
)
function Write-Info($message) {
}
function Introspect($server) {
Write-Info "about to do something on server"
// other powershell stuff that works
}
Foreach ($server in $servers) {
Invoke-Command -ComputerName $server -ScriptBlock ${function:Introspect} -ArgumentList $server -credential 'MyUser'
}
That I'm trying to invoke on a remote server like so (from powershell):
.\myscript.ps1 server1,server2,server3
The script is executing, but the issue is I get an error relating to the Write-Info function like so:
The term 'Write-Info' is not recognised as the name of a cmdlet, function, script file, or operable program
The Introspect function works fine if I embed the function inside but I guess it relates to the function not being on the remote server.
How can I solve this please?
Managed to solve this by embedding the 'missing' function inside the working one:
param(
[string[]]$servers
)
function Introspect($server) {
function Write-Info($message) {
}
Write-Info "about to do something on server"
// other powershell stuff that works
}
Foreach ($server in $servers) {
Invoke-Command -ComputerName $server -ScriptBlock ${function:Introspect} -ArgumentList $server -credential 'MyUser'
}

How to mock a job in Pester?

We're trying to assess if Invoke-Command has been called exactly one time.
Script.ps1
$job = Invoke-Command -ScriptBlock {'test'} -ComputerName localhost -AsJob
$job | Wait-Job
Script.Tests.ps1
BeforeAll {
$testScript = $PSCommandPath.Replace('.Tests.ps1', '.ps1')
Mock Invoke-Command
}
Describe 'Test' {
It 'should be green' {
. $testScript
Should -Invoke Invoke-Command -Times 1 -Exactly -Scope It
}
}
The problem is mocking the job object in Pester. When the job is not mocked, Wait-Job will throw an error that it didn't receive a job object.
How is it possible to mock a PowerShell job object in Pester?
One solution might be to have the Mock of Invoke-Command still create a legitimate job, but executing some script/code that you deem safe for the purpose of testing.
To do this, you need to first put the Invoke-Command cmdlet in a variable so that you can use it via that variable (because a Mock can't directly call its own command).
For example:
$InvokeCommand = Get-Command Invoke-Command
Mock Invoke-Command {
& $InvokeCommand -ScriptBlock {'some safe alternative code'} -ComputerName localhost -AsJob
}

Exit not Exiting in invoke-command in remote ps session

I have a Function invokes a command that starts a new ps session on a remote server. The invoke command has an Exit clause however this is not exiting?
Function CreateID{
Invoke-Command -Session $Script:sesh -ScriptBlock{
Set-Location c:\
Import-Module ActiveDirectory
Try
{
If (Get-ADGroupMember "$Using:IDGroup" | Where-Object Name -match
"$Using:Computer")
{
Write-Host "Already in $using:IDGroup Exiting Script"
Disconnect-PSSession -Session $Script:sesh
Exit-PSSession
Exit
}
}
Catch
{ }
Write-Host "Did not Exit"
}
}
The Get-AD command works fine so where it should not display "did not exit" it does - how can i exit from a scriptblock in a remote ps session?
I am trying the disconnect session and Exit-pssession to see if they would do the same as simply exit but none of those are working.
I have also tried Break and no luck.
Ok so i figured this out - the ps session and invoke-command are red herrings. The basis of this is that you cannot Exit a Try/Catch statement.
i had to do this to get it to work - now it Exits. I just cannot use a Try/ Catch - if anyone knows how to exit a Try/Catch let me know!
#Try
#{
If (Get-ADGroupMember "$Using:IDGroup" | Where-Object Name -match
"$Using:Computer")
{
Write-Host "Already in $using:IDGroup Exiting Script"
Disconnect-PSSession -Session $Script:sesh
Exit-PSSession
Exit
}
#}
#Catch
#{ }
I don't know if this works for PSSession or not, and I don't have an environment to test it, but you can use this to exit powershell within a try catch
[Environment]::Exit(0)
Try/Catch should work in an invoke-command.
I don't usually invoke-commands to sessions, rather I use -ComputerName.
This worked fine for me:
invoke-command -ComputerName "MyDomainController" -ScriptBlock {
try { get-aduser "ValidUser" } catch { "doh!" }
}
I just tried this as well and it also worked:
$sess1 = New-PSSession -ComputerName MyDomainController
invoke-command -Session $sess1 -ScriptBlock { try { get-aduser "ValidUser" } catch { "doh!" } }
If I change either of those "ValidUser" values to invalid users I see the "doh!" as well.
Perhaps it's because you're trying to end the session from within the session.
You should deal with the output of the invoke-command or the function and then handle the session based on that.
Like using my lame example...
$sess1 = New-PSSession -ComputerName MyDomainController
$DoStuff = invoke-command -Session $sess1 -ScriptBlock { try { get-aduser "ValidUser" } catch { "doh!" } }
If ($DoStuff -eq "doh!") { Remove-PSSession $sess1 } else { $DoStuff }

Call a remote script from another with multiple parameters not working

I am trying to create a script that will take input (hardcoded values for now) and call an install PS script and run it on multiple servers. I am using a PSSession and Invoke-Command(see below). The below runs, but does nothing. It doesn't seem to call the other script. Beyond getting it to actually install, I need to know if it was successful or not. I'm pretty novice at Powershell, so any hints/help/suggestions would be great. The below is wrapped in a ForEach to loop the servers with $Computer
Try
{
$session = New-PSSession -ComputerName App02 -Credential $cred
$sourceInstall = $sourceFolder + 'Install\Install.ps1'
Invoke-Command -Session $session -ScriptBlock{param($serviceName, $installFolder, $sourceFolder, $Action, $username, $password) $sourceInstall} -ArgumentList ($ServiceName, $installFolder, $sourceFolder, $Action, $username, $password)
}
Catch
{
$Filename = "Error.txt"
Write-Output "ERROR: Partial Service Deployment. See error log file(s)"
Add-Content $Filename $_.Exception.Message
}
Get-PSSession | Remove-PSSession
You can use it without $Using statement in any version of PowerShell.But pass that too as an argument.
Eg:-
Invoke-Command -ScriptBlock
param($Name)
& $Command $Name
} -ArgumentList 'Get-Process','Notepad'
But you have to pass the arguments positional when using the call operator '&'
Get-Help About_Parameters
https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_parameters
Regards,
Kvprasoon

PowerShell: error checking in a while true loop?

I have a while($true) loop with a start-sleep -s 60 at the end of it. The purpose is to start an external PowerShell script as a different user which will run through a list of servers, check the event log for changes within the last minute and react accordingly.
Since my while loop (below) is using the -credential flag to run the script as someone else, I'm concerned about errors (e.g. account locked out, expired password, missing file, etc).
I tried an if ($error) statement, and changed the filename of the external script, but I was never alerted. I'm thinking it's because it never stops to re-check itself?
while($true) {
# Start the scan
Start-Process powershell -Credential $credentials -ArgumentList '-noprofile -command & c:\batch\02-Scan.ps1'
# Sleep 60 seconds
start-sleep -s 60
}
I suppose I could change my scheduled task to run every minute, but so far this loop seems to have been working great. Just want to introduce error checking while the loop is active.
Have you tried try/catch blocks? Wrong credentials is a terminating error, so the rest of the code in the try block won't run after a credentials-error. When you catch it, you can do whatever you want.. Ex.
try {
Start-Process powershell -Credential $credentials -ArgumentList '-noprofile -command & c:\batch\02-Scan.ps1'
} catch {
#Catches terminating exceptions and display it's message
Write-Error $_.message
}
If you want to catch all errors, add -ErrorAction Stopto the Start-Processline. As said, credentials should be a terminating error, which make the erroraction parameter unnecessary
EDIT Why would you use Start-Process to run a script in the first place? I switched it to Invoke-Command which runs a powershell script remotely. When the scriptfile is missing, you will recieve a NON-terminating error. Because it's a non-terminating error, we need to use the -ErrorAction Stop parameter. To catch the missing-file error and every other error(like credentials), use something like this:
try { Invoke-Command -ScriptBlock { & c:\batch\02-Scan.ps1 } -ErrorAction Stop
} catch {
if ($_.Exception.GetType().Name -eq "CommandNotFoundException") {
Write-Error "File is missing"
} else {
Write-Error "Something went wrong. Errormessage: $_"
#or throw it directly:
#throw $_
}
}
Maybe?
while($true) {
# Start the scan
try{
Start-Process powershell -Credential $credentials -ArgumentList '-noprofile -command & c:\batch\02-Scan.ps1' -ErrorAction Stop
}
catch {
send-alert
break
}
# Sleep 60 seconds
start-sleep -s 60
}