PowerShell Exit out of the whole script not Just the Invoke-Command - powershell

I have a script that is currently monitoring a couple of windows services on different VMs. If the service is stopped, it will attempt to start the service.
I have added a $LoopCounter to stop the script it it loops to many time as to not fall into an infinite loop if the service cannot be started for what ever reason.
This is all inside a Invoke-Command that runs some code on the remote VMs. I have used Exit to stop the script running but it does not seem to be in the right scope of things and it stops the Invoke-Command but not the whole script.
Script:
Invoke-Command -ComputerName VM1, VM2, VM3 -Credential $Cred -ArgumentList $ServiceList -ScriptBlock {
Foreach ($Service in $Using:ServiceList) {
$C = [System.Net.Dns]::GetHostName()
$S = Get-Service -Name $Service
while ($S.Status -ne 'Running') {
# Code...
$LoopCounter++
If($LoopCounter -gt 2) {
Exit
}
}
}
}

You could have the scriptblock return a value and check for that.
The example below has the returned value 1 if the script should exit as a whole:
$result = Invoke-Command -ComputerName VM1, VM2, VM3 -Credential $Cred -ArgumentList $ServiceList -ScriptBlock {
Foreach ($Service in $Using:ServiceList) {
$C = [System.Net.Dns]::GetHostName()
$S = Get-Service -Name $Service
while ($S.Status -ne 'Running') {
# Code...
$LoopCounter++
If($LoopCounter -gt 2) {
return 1 # return a value other than 0 to the calling script
Exit # exit the scriptblock
}
}
}
}
# check the returned value from Invoke-Command
# if it is not 0 (in this example), exit the whole script
if ($result) { exit }
Hope that helps

Related

Powershell | second Invoke Using:Variable

i have a simple question. I want to use a variable in an invoke-command and pass this to the second invoke-command. In the second invoke-command the variable is empty
here is my code:,
$WaitSeconds = 1234
Invoke-Command -ComputerName $remote -Credential $cred -ScriptBlock {
$computer = dsquery computer "DC=domain,DC=local" -o rdn
$computers = $computer -replace ('"', '')
write-host $computers
foreach ($computer in $computers) {
if ($computer -notmatch "AZUREADSSOACC")
{
Invoke-Command -ComputerName $computer -Credential $using:cred -ScriptBlock {
#### here script
shutdown -s -t $using:waitseconds
}
}
}
}
In the second invoke-command the variable is empty
That's because the variable doesn't exist inside the calling session (the first Invoke-Command call's execution context).
Make sure you first instruct PowerShell to copy the variable to the "outer" Invoke-Command call:
$WaitSeconds = 1234
Invoke-Command {
# PowerShell will now copy the $WaitSeconds variable value from the calling scope to this remote session
$WaitSeconds = $using:WaitSeconds
# ...
Invoke-Command {
# This will now resolve the variable value correctly
shutdown -s -t $using:WaitSeconds
}
}
i got it
my solution is:
$WaitSeconds = 1234
Invoke-Command {
param($WaitSeconds)
$WaitSeconds = $WaitSeconds
# ...
Invoke-Command {
param($WaitSeconds)
shutdown -s -t $WaitSeconds
} -ArgumentList $WaitSeconds
} -ArgumentList $WaitSeconds

LastExitCode of invoke-command does not match condition

I am running script through jenkins and the Invoke command should fail as the file already exits and should reach exit code 1 and error out and show a fail in jenkins but instead it still is exiting 0 as green
I have tried using Try catch and using $? but still cannot get this to show as red when failure
$Time = $args[1]
$Server = $args[2]
$username = $args[3]
$password = $args[4]
$pass="$password"|ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PsCredential("ad\$username",$pass)
$session = New-PSSession -ComputerName $Server -Credential $Cred -ErrorAction Stop
Invoke-Command -Session $session -ScriptBlock {netsh trace start capture=yes tracefile=C:\Traces\netsh.etl}
If ($LastExitCode -eq 1)
{
exit 1
}
write-host "hello world"
start-sleep -Seconds $Time
Invoke-command -Session $session -ScriptBlock {netsh trace stop}
If ($LastExitCode -eq 1)
{
exit 1
}
exit 0
The file already exists which should be expected but not reaching the exit 1 inside either if statements when it should.
The $LastExistCode you are testing is on the local system, but you are running the command on a remote system. Try returning the $LastExistCode at the end of your scriptblock and test the result of the Invoke-Command
For example:
$result = Invoke-command -Session $session -ScriptBlock {netsh trace stop; $LastExitCode}
If ($result -eq 1)
{
exit 1
}
This assumes that netsh itself doesn't return anything (I didn't test it). If it does, you can typically supress the output by piping it to Out-Null:
Invoke-command -Session $session -ScriptBlock {netsh trace stop | Out-Null; $LastExitCode}

Check the AppPool status through power-shell and start that on remote machine

The requirement is to extract the server name one by one and check the AppPool status, if that is found to be stopped, make it running. below code is not helping out.
$ErrorActionPreference = "Continue"
$status = gc -path "D:\Servers\server.txt"|ForEach-Object (invoke-command -ComputerName $_ -ScriptBlock {Import-Module Webadministration Get-WebAppPoolState -name (gc "D:\AppPool.txt")})
if ($status.value -eq "Started")
{
Write-Host ("ApppPool already running")
}
else
{
Invoke-Command -ScriptBlock {Start-WebAppPool}
Write-host ("AppPool has started successfully")
}
There were multiple problems with your code, I've gone through them individually so you can see what was stopping it from working correctly.
The syntax for foreach was wrong, you needed to use {} not () in this case. Normal brackets are only used like this ForEach ($number in $numArray ) {CODE} which you aren't.
You were checking $status outside the foreach loop - so it so was evaluating $status only once (with the final computers AppPool status) rather than for each computer.
Your second Invoke-Command didn't have a ComputerName parameter specified so was only running the command locally not against the remote computer, meaning the AppPool would never be started.
As you were specifying the AppPool name using gc "D:\AppPool.txt" this file would have to be present on every remote computer for it to work. I've changed this to be passed into the command as an argument so the file only needs to be on the computer running the script.
$Credentials = Get-Credential
$AppPools = Get-Content "D:\AppPool.txt"
$Servers = Get-Content -Path "D:\Servers\server.txt"
ForEach ($Server in $Servers) {
ForEach ($AppPool in $AppPools) {
$AppPoolState = Invoke-Command -ComputerName $Server -ScriptBlock {Import-Module WebAdministration; Get-WebAppPoolState -Name $args[0] } -ArgumentList $AppPool -Credential $Credentials
if ($AppPoolState.Value -eq "Started")
{
Write-Host "$AppPool AppPool already running on $Server"
}
else
{
Invoke-Command -ComputerName $Server -ScriptBlock {Start-WebAppPool -Name $args[0] } -ArgumentList $AppPool -Credential $Credentials
Write-Host "$AppPool AppPool started on $Server"
}
}
}
Note: I run a non-privileged account so have to supply Credentials. If the account you're running the script as has appropriate permissions to all the remote computers you can remove the three Credentials references.

Scriptblock works as Invoke-Command, but not as Start-Job

I have written a ScriptBlock that POSTs file uploads to a custom http server and saves the response to disk. It is intended to be used for testing said server and in addition to checking the correct response I'd like to run it in parallel to load the server.
The code in the ScriptBlock was built as separate ps1 script and works. I have then transplanted it into a framework-script using Start-Job for managing the jobs and eventually presenting overall results.
Unfortunately it does not work after that transplantation.
The symptom is that using Start-Job the job never finishes. For testing I have switched to Invoke-Command. A simple Invoke-Command works, InvokeCommand -AsJob does not.
Works:
Invoke-Command -Scriptblock $ScriptBlock -ArgumentList $Name,"C:\Projects\DCA\CCIT\ServiceBroker4\Java\eclipse-workspace\XFAWorker\testdata","P7-T"
Do not work:
Invoke-Command -AsJob -Computer localhost -Scriptblock $ScriptBlock -ArgumentList $Name,"C:\Projects\DCA\CCIT\ServiceBroker4\Java\eclipse-workspace\XFAWorker\testdata","P7-T"
Start-Job -Name $Name -ScriptBlock $Scriptblock -ArgumentList $Name,"C:\Projects\DCA\CCIT\ServiceBroker4\Java\eclipse-workspace\XFAWorker\testdata","P7-T"
The ScriptBlock being used is rather longish. It starts off by declaring Parameters:
$Scriptblock = {
Param (
[string]$JobName,
[string]$path,
[string]$BASEJOBNAME
)
And further down uses HttpWebRequest and a few other .NET classes:
$url = "http://localhost:8081/fillandflatten"
[System.Net.HttpWebRequest] $req = [System.Net.WebRequest]::create($url)
...
$xfabuffer = [System.IO.File]::ReadAllBytes("$path\$BASEJOBNAME.xml")
...
$header = "--$boundary`r`nContent-Disposition: form-data; name=`"xfa`"; filename=`"xfa`"`r`nContent-Type: text/xml`r`n`r`n"
$buffer = [Text.Encoding]::ascii.getbytes($header)
...
[System.Net.httpWebResponse] $res = $req.getResponse()
As described when using Start-Job or Invoke-Command -AsJob the child job started simply remains in Running state forever.
What can cause this behaviour? What can be used to debug it? If there is some error can I force the child-job to terminate and tell me what it does not like?
I am using PowerShell 2.0 on Windows XP.
In the framework I came up with I do the Start-Job (currently just one of them, but I plan to ramp up that number for stress-testing). Then I have a loop waiting for them all to terminate:
do {
$Jobs = #(Get-Job | Where { $_.State -eq "Running" -and $_.Name.StartsWith($BASEJOBNAME) });
$n = $Jobs.Count
if ($n -gt 0)
{
Log -message "Waiting for $n jobs to finish..."
Get-Job | Where { $_.Name.StartsWith($BASEJOBNAME) } | Format-Table -AutoSize -Property Id,Name,State,HasMoreData,Location
start-Sleep -Seconds 3
}
} until ($n -eq 0)
This produces output of the form:
2014-08-01 18:58:52 - Waiting for 1 jobs to finish...
Id Name State HasMoreData Location
-- ---- ----- ----------- --------
2 XFA001 Running True localhost
Forever.
A full minimal test-case is:
# Code for the Jobs:
$Scriptblock = {
[System.Net.HttpWebRequest] $req = [System.Net.WebRequest]::create("http://locahost/index.html")
return "done"
}
# Start a job. Three variants, The Job-based ones hang.
# Invoke-Command -Scriptblock $ScriptBlock
#Invoke-Command -AsJob -Computer localhost -Scriptblock $ScriptBlock
$Job = Start-Job -Name $Name -ScriptBlock $Scriptblock
## The rest of the code is only applicable for the Start-Job-Variant
# Wait for all Jobs to finish
do {
$Jobs = #(Get-Job | Where { $_.State -eq "Running" });
$n = $Jobs.Count
if ($n -gt 0)
{
Write-Host "Waiting for $n jobs to finish..."
Get-Job | Format-Table -AutoSize -Property Id,Name,State,HasMoreData
Start-Sleep -Seconds 3
}
} until ($n -eq 0)
# Get output from all jobs
$Data = ForEach ($Job in (#(Get-Job | Where { $_.Name.StartsWith($BASEJOBNAME) } ))) {
Receive-Job $Job
}
# Clean out all jobs
ForEach ($Job in (#(Get-Job | Where { $_.Name.StartsWith($BASEJOBNAME) } ))) {
Remove-Job $Job
}
# Dump output
Write-Host "Output data:"
$Data | Format-Table
Write-Host ""
This hangs for me. If I comment out the line creating the WebRequest object it works.
Thank you.
When you run Get-Job , does the job's "HasMoreData" properties is "True" ?
If yes, check the output of the job :
Receive-Job <JobName or JobID> -Keep

Stop remote service using PowerShell

I've a script that stops remote services through WMI:
(get-service -ComputerName $server_ip -Name $service).Stop()
I want to force a service after five tries. I have build a counter, but what is the command to force a stop?
If you have winRM enabled you can use the following:
Invoke-Command -ComputerName server1 -ScriptBlock {
Stop-Service $args[0] -Force } -ArgumentList $service
If by "force a stop" you mean you want to forcibly terminate (i.e. kill) the service process you could try killing the process via its PID:
$server = '1.2.3.4'
$service = 'name'
Invoke-Command -Computer $server -ScriptBlock {
param($svc)
$Error.Clear()
1..5 | % {
Stop-Service -Name $svc
if ($?) { break } # exit from loop if previous command succeeded
}
if ($Error.Count -eq 5) {
$pid = (Get-WmiObject Win32_Service -Filter "Name='$svc'").ProcessId
(Get-Process -Id $pid).Kill()
}
} -ArgumentList $service
Running the code via Invoke-Command is to avoid multiple remote connections.
This should work even for services that have the NOT_STOPPABLE flag set.