Octopus step process to start Azure VM if not already running

I am looking to update my deployment process to add an additional step before deploying that basically does a health check and if that fails (as the Azure VM might be off) I would like to start the VM, wait for it to be healthy and then deploy.

What is the best solution on Octopus 3.7.10 to do that, the only thing I can see that might match is Run a Script or Run an Azure Powershell Script but unsure how to approach either option.

1 Like

Hi,

Thanks for getting in touch.

Yes, you’re on the right track. You’d need a “Run an Azure PowerShell Script” step. To use this, you’ll firstly need to setup an Azure Service Principal account.

Once you have that setup and passing a test, you can then run any of the Azure PowerShell cmdlets for Virtual Machines.

For example, I have a VM called “marktopus” (in a resource group of the same name), and can query my specific VM by running this script:

'--start'
$result = Get-AzureRmVM -ResourceGroupName "marktopus" -Name "marktopus" -Status
$result.Statuses | Format-Table
'--end'

The Statuses property appears to be a collection. In my case, it was the second entry that told me about whether the VM was deallocated or running.

E.g. If I queried the Code property on the second object in the Statuses collection like this:

$result.Statuses[1].Code

It resulted in either of these values:

  • PowerState/deallocated
  • PowerState/running

So you could play around and see what the above script returns for your scenario.

Then you can call either of these PowerShell cmdlets to start/stop your VM respectively:

Start-AzureRmVM
Stop-AzureRmVM

Hope this helps.
Mark

Thanks Mark, in terms of the Azure PowerShell all that makes sense.
I guess I would need to run this script on the Octo server correct?
I would like to know how to pass on the VM name from Octopus itself (the machine I am running this against) so I don’t have to create multiple scripts for multiple machines but only one with parameters (though not sure that’s possible if the resource group needs to always be passed in)
Any idea?

Yes, this Azure PowerShell Script step runs on/from your Octopus Server.

Re. passing the VM name(s) from Octopus, you could experiment with variable to store all the names of the VMs that you want the scrpt to have access to.

E.g. Create a project variable with a CSV value:

MyVmNames: name1,name2,name3

Then in your Octopus script, you could access these values like so:

$vmNames = $OctopusParameters["MyVmNames"].Split(",");
#$vmNames | Format-List
foreach ($vmName in $vmNames) {
    write-host $("VM: $vmName");
    #TODO: whatever you wanna do with this VM name...
}

That might work if all your VMs share a single resource group.

However if each VM has a unique resource group, then you could approach it several ways, but one approach might be to include the resource group name piped next to the VM name in that CSV value like so:

MyVmNames: name1|resourceGroup1,name2|resourceGroup2,name3|resourceGroup3

Then as you’re iterating over that names, you’d have to split by | to then access the name and resource group for each entry.

The good thing about scripting is you can play around in PowerShell first (with some local variables), then when you have something working, you just transfer the variables and script to Octopus :slight_smile:

Hope that helps.

Cheers
Mark

Thanks again Mark. I did some testing and that works as expected.
I am wondering whether to use the Check Health step or just use Azure Powershell (machine status) to determine if the VM is switched off.
For the Check Health step I am not sure how to conditionally run the power on powershell if the connection-only test fails

Glad you got that working.

If you have Tentacles on each of these VMs, it might get difficult to try and run health check steps to detect that machines were offline, then successfully continue the deployment. But you could probably do that by first running a health check step set to perform a connectivity-only check + skip targets that are unavailable, then have a script step that queries the Octopus REST API for machines that are offline matching your expected tags, then take those machines and run whatever magic you need to turn those machines on in Azure, then re-run another health check step after that. But as you can imagine, it starts getting pretty complex to setup.

It’s probably easier to reason about if you kick off a deployment, and the first step that essentially says: “Azure, I expect X, Y and Z machines to be online, turn them on”, then run a health check step and your Tentacles should then pass a health check. Then you continue with the rest of your deployment knowing those VMs are online.

But if you do get some other combination working, we’d be interested in how you approached it, so please let us know :slight_smile:

Cheers
Mark

Thanks again Mark, the script is coming along nicely.
One more question, instead of adding the VM names as a variable for the Project, how can I call directly on the name variable of the Octopus hosts that the deployment process targets? Is there a keyword for that?

Hi, Sorry could you confirm what you mean by “the name variable of the Octopus hosts”?

One thing that may help is OctopusPrintVariables. You can set a project variable named OctopusPrintVariables with a value of True, create a new release and run a deployment, and it will then show you all available variables at deployment-time in the verbose logs. This is useful for seeing what variable name/values are available at the time of your deployment (for each of your targets).

Hope this helps.

Cheers
Mark

Found it.

$targetName = $OctopusParameters[“Octopus.Machine.Name”];

This was the variable I was looking for.

1 Like