Managing Azure VMs Using PowerShell Effectively
Tags: powershell, azure, virtualmachine, cloudcomputing
Published: 2023-10-12 07:07:22

**Imagine this:** You’re responsible for overseeing a range of virtual machines (VMs) in your Azure environment. However, you’re also someone **_who values efficiency_**. Why spend time on manual tasks when automation can make your life easier? In this article, we’ll introduce a script that streamlines the process of stopping, starting, and checking the status of your Azure VMs.


![PowerShell and VMs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dx9h8emzyzbzm8hpds9u.png)


**Why the drama?**

Before we dive into the script, let’s answer a crucial question: why perform these operations on Azure VMs? In the realm of the cloud, managing Azure VMs can be similar to tending to digital pets, especially when _**handling a multitude of them without a fixed schedule**_ for starting, stopping, or status checks.

- Cost Control: Every second your VMs run, coins are drained from your Azure treasure chest. Stopping idle VMs saves you shiny gold.
- Maintenance Marvel: VMs need TLC. Updates, patches, and configurations often require them to be stopped and started.
- Health and Sanity: Sometimes, VMs decide to take a stroll on the wild side. Checking their status ensures awareness of your VM state.

**Let’s script**

To simplify the process, we’ll utilize Azure Cloud Shell. Let’s assume you have a substantial number of VMs, possibly hundreds distributed across various resource groups and environments (e.g., production, development). An approach will be to filter the VMs for operations based on pre-existing tags. In this article, we’ll instead us an **_Excel sheet containing the list of VMs for querying_**. Here is mine called VMlists.csv:


![csv file containing Azure VMs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wl0da4p8w44n8mok79jt.png)

**Start VMs:**

The below script starts the VMs based on the user input. It loops through each VMs in the CSV file. The operation would be run based on the OS type and the action (stop/start/status) specified.

```powershell
Write-Host "`nWelcome, this script allows you to start, stop and check status of multiple VMs in various resource groups in parallel. `n" 

Write-Host "`nEnter action (start/stop/status):"

$Action = Read-Host

Write-Host "`nSpecify environment(prod/dev):"

$OS_TYPE = Read-Host

$VirtualMachinesDetails = Import-Csv .\VMLists.csv #Imports csv file from the specified path

if ($Action -eq "start"){

    Write-Host "Please specifiy the environment (Prod/Dev)"

    $VM_Environment = Read-Host

    Write-Host "`nStarting all $OS_TYPE Virtual machines in the $VM_Environment environment..."

    $VirtualMachinesDetails | Where-Object {($_.VM_OS -eq $OS_TYPE) -and ($_.VM_Environment -eq $VM_Environment)} | ForEach-Object -parallel { #filters and fetches the csv files content based on the OS type.

        $checkIfVMExistInRG = get-azvm -ResourceGroupName $_.VM_RG -name $_.VM_Name -ErrorVariable notPresent -ErrorAction SilentlyContinue # Checks if the VM exists in the RG

        if ($notPresent){
            continue
        }
        else {
            Start-AzVM -ResourceGroupName $_.VM_RG -Name $_.VM_Name
        }
    } -ThrottleLimit 10
}
```
The VMs are started in parallel (10 at once, instead of one by one). The limit was set to 10 to manage your compute resources.

**Stop VMs:**

The below script loops through the VMs based on user inputs and stop the VMs.

```powershell
elseif ($Action -eq "stop"){

    $OS_TYPE = "Windows"

    Write-Host "Please specifiy the environment (Prod/Dev/Test):"

    $VM_Environment = Read-Host

    Write-Host "`nStopping all $OS_TYPE Virtual machines in the $VM_Environment environment..."

    $VirtualMachinesDetails | Where-Object {($_.VM_OS -eq $OS_TYPE) -and ($_.VM_Environment -eq $VM_Environment)} | ForEach-Object -parallel {

        $checkIfVMExistInRG = get-azvm -ResourceGroupName $_.VM_RG -name $_.VM_Name -ErrorVariable notPresent -ErrorAction SilentlyContinue

        if ($notPresent){
            continue
        }
        else {
            Stop-AzVM -ResourceGroupName $_.VM_RG -Name $_.VM_Name -Confirm:$false -ErrorAction SilentlyContinue -Force
        }
    } -ThrottleLimit 10
}
```
**Get VM status:**

The below script loops through the VMs and get their current state. It checks if they are running properly or in a bad state.

```powershell
elseif($Action -eq "status"){

    Write-Host "`nGetting status of all Virtual machines..."

    $Rg_Exist = @()
    $Vm_Exist = @()
    $vmStatuses = @()
    
    $VirtualMachinesDetails | ForEach-Object {
        if ($Rg_Exist.Contains($_.ResourceGroupName) -and $Vm_Exist.Contains($_.VM_Name)) {
            return
        }
        else {
            
            $VMstatus = Get-AzVM -Name $_.VM_Name -ResourceGroupName $_.ResourceGroupName -Status
            $VMdetails = Get-AzVM -Name $_.VM_Name -ResourceGroupName $_.ResourceGroupName
            $Environment = $_.VM_Environment

            $vmInfo = [PSCustomObject]@{
                ResourceGroupName   = $vmStatus.ResourceGroupName
                Name                = $vmStatus.Name
                Environment         = $Environment
                Location            = $VMdetails.Location
                OsType              = $VMdetails.StorageProfile.OsDisk.OsType
                Disk_Status         = $vmStatus.Disks.Statuses.code
                VMAgentStatus       = $vmStatus.VMAgent.Statuses.DisplayStatus
                PowerState          = $vmStatus.Statuses[1].DisplayStatus     
                Provisioning        = $vmStatus.Statuses[0].DisplayStatus
                MaintenanceAllowed  = $vmStatus.MaintenanceRedeployStatus.IsCustomerInitiatedMaintenanceAllowed
            }
            $vmStatuses += $vmInfo

            $Rg_Exist += $_.ResourceGroupName
            $Vm_Exist += $_.VM_Name
        }        
    }
    $vmStatuses | Format-Table -AutoSize
}
else {
    Write-Host "action not recognized"
}
```
You can modify the script based on your use case. For the full script, check my GitHub: https://github.com/Toch-vybe/Powershell-scripts/blob/9a993126ba967746db9f0bad0998b1fd933a7004/vm-manager.ps1

**Now let’s try the script out in the cloud shell.**

Login into your Azure account and click the cloud shell icon at the top.


![Cloud shell location in Azure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y2sfwav31tfhe220n2q3.png)

You will have to create or select an existing storage account where your scripts would be stored.

Upload the csv file containing the VMs and the PowerShell script into the cloud shell:


![Upload csv and script to cloud shell](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52gvkrgnvbsl0tocxm80.png)

Now open the editor to confirm your uploaded files:


![Open editor in cloud shell](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/04szbv7vtl8a0u3mx7ni.png)

Next, run the script in the shell and follow the prompt using:

```powershell
./<the name of your script>.ps1
```

Below is how it should look like when you run the scripts:


![Result of the script once it is run](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f4zzksujsveci5u0zodk.png)

**Conclusion**

In the world of Azure, scripts are valuable tools that can simplify your administrative tasks. They allow you to manage your Azure resources efficiently, saving time and effort. So, I encourage you to embrace the power of automation. Your Azure VMs will benefit, and you’ll free up more time for other pursuits, whether it’s conquering new digital challenges or enjoying some well-deserved relaxation.

Until next time, keep scripting, and continue to excel in your Azure endeavors!