Posted on 2 Comments

Run powershell command on virtual machines scale set

Azure Virtual Machine Scale Sets let you create and manage a group of load balanced VMs. The number of VM instances can automatically increase or decrease in response to demand or a defined schedule. Scale sets are commonly used for critical infrastructure like Kubernetes and service fabric. In this guide we will examine how we can perform an action massively on all nodes of the vmss.

The below vmss is composed of windows virtual machines that belong to an azure resource.

When you need to update all the nodes of vmss with a specific action, for instance to install a powershell module, you will need to use the run-command.

az vmss run-command | Microsoft Learn

First you will need to get all instances ids as they are a parameter for the next commands.

az vmss list-instances -n $vmss_name -g $rg_name --query "[].id" --output tsv

The output should be similar with the below.

/subscriptions/ID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/vmssname/virtualMachines/0
/subscriptions/ID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/vmssname/virtualMachines/1
/subscriptions/ID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/vmssname/virtualMachines/2

Then you can execute a powershell script on the vmss nodes by specifying the node id of the previous command and the resource group name along with the vmss name.

az vmss run-command invoke  --command-id RunPowerShellScript -n $vmss_name -g $rg_name --scripts 'hostname' --instance-id 0

Youtube video:

Posted on Leave a comment

Multi threading in powershell

Multi threading can save you a lot of time when you process multiple multiple jobs at the same time. Powershell is a very advanced scripting tool and there are a lot of things that can be implemented with it. As a result in many occasions you will need to do things in parallel so that you have a quicker execution.

Lets examine a simple scenario on how we can use multi threading on powershell. We will use the Test-Connection method which sends ICMP echo request packets, or pings, to one or more computers. We will use the below three domains and we will try to execute a test connection to all of them.

$computers = @("google.gr","microsoft.com","facebook.com")

In order to calculate the time that this operation needs we will use the StopWatch class.

$watch = New-Object System.Diagnostics.Stopwatch; 
$watch.Start(); 
foreach ($item in $computers) { test-connection $item; }; 
echo "Elapsed time:" $watch.Elapsed.TotalSeconds;

The total Elapsed Time for this operation to complete would be around 10 seconds by performing the actions serially.

Lets examine how could we could use multi threading in order to complete this operation faster. In powershell v7 you can use the ForEach-Object with Parallel parameter. An example can be found below. This operation will be executed in parallel by multiple threads.

$watch = New-Object System.Diagnostics.Stopwatch;  
$watch.Start(); 
$computers | ForEach-Object -Parallel {test-connection $_} -AsJob | Receive-Job -Wait ; 
echo "Elapsed time:" $watch.Elapsed.TotalSeconds;

The result for this execution would be 3.8 seconds which means that we have 6 seconds less execution time or about 2.5 times faster.

If we use powershell v5 we can implement multi threading with the Start-Job. By using that we will need to write our logic inside the ScriptBlock.

$watch = New-Object System.Diagnostics.Stopwatch; 
$watch.Start(); 
$job1 = Start-Job -ScriptBlock { test-connection -ComputerName "google.gr" }
$job2 = Start-Job -ScriptBlock { test-connection -ComputerName "microsoft.com" }
$job3 = Start-Job -ScriptBlock { test-connection -ComputerName "facebook.com" }

Get-Job | Wait-Job

Receive-Job $job1
Receive-Job $job2
Receive-Job $job3

echo "Elapsed time:" $watch.Elapsed.TotalSeconds;

As we can see the execution time is about 3,9 seconds which is close enough with the time of powershell 7 script.

Youtube video:

Posted on Leave a comment

Get standard error output from command on powershell

There could be times that you will need to get the standard output from a script or command in powershell and handle this information. An example that I use is the output of docker info which I use to monitor the service state along with other things. The output of the docker info will inform you about an error during connection if the engine is not working. In the latest version of docker they changed the way message is displayed and this information will be printed on standard error instead of standard output.

docker info

As a result when you try to find the connection state through a powershell command you will fail identifying the correct state.

 (docker info).Contains('ERROR: error during connect')

This behavior is noticed because the error is printed on standard error.

In order to bypass and get the standard error on the output, you will need to use 2>&1 along with the command.

docker info 2>&1 

After that you can handle the output message appropriately.

Posted on Leave a comment

Get powershell command result as string

Sometimes you may end up with wrong results on powershell because of the return object. A detailed demonstration can be located below where the return object is not a string and the evaluation of equals is not correct.

For example lets assume that we need to check docker status from powershell and catch this result through the string that is returned. When docker is not running you can expect a similar message like the below.

By getting the result of the docker info command into a variable we can see that the return object is of type Object in powershell.

When you try to use the contains functions with this object in order to evaluate the docker status you will end up with a false result as is not evaluated correctly.

In order to resolve this issue you should specify that the result should be a string with Out-String function.

Then when you evaluate the expression with Contains function this is performed as expected and the correct result is returned.