StartStop-VirtualMachine.ps1
# Function to handle power state assertion for resource manager VM function AssertResourceManagerVirtualMachinePowerState { param( [Object]$VirtualMachine, [string]$DesiredState, [bool]$Simulate ) # Get VM with current status $resourceManagerVM = Get-AzVM -ResourceGroupName $VirtualMachine.ResourceGroupName -Name $VirtualMachine.Name -Status $currentStatus = $resourceManagerVM.Statuses | Where-Object Code -Like 'PowerState*' $currentStatus = $currentStatus.Code -replace 'PowerState/', '' # If should be started and isn't, start VM if ($DesiredState -eq 'Started' -and $currentStatus -notmatch 'running') { if ($Simulate) { Write-Host "Az[$($VirtualMachine.Name)]: SIMULATION -- Would have started VM. (No action taken)" } else { Write-Output "[$($VirtualMachine.Name)]: Starting VM" $status = $resourceManagerVM | Start-AzVM -ErrorAction Continue; $status if ($status.Status -match 'Succ') { Write-Host "Starting VM: [$AzureVM] - Succeeded!" } else { Write-Host "##[error]Starting VM: [$AzureVM] - Failed!" } } } # If should be stopped and isn't, stop VM elseif ($DesiredState -eq 'StoppedDeallocated' -and $currentStatus -ne 'deallocated') { if ($Simulate) { Write-Host "Az[$($VirtualMachine.Name)]: SIMULATION -- Would have stopped VM. (No action taken)" } else { Write-Output "[$($VirtualMachine.Name)]: Stopping VM" $status = $resourceManagerVM | Stop-AzVM -Force -ErrorAction Continue; $status if ($status.Status -match 'Succ') { Write-Host "Stopping VM: [$AzureVM] - Succeeded!" } else { Write-Host "##[error]Stopping VM: [$AzureVM] - Failed!" } } } # Otherwise, current power state is correct else { Write-Host "[$($VirtualMachine.Name)]: Current power state [$currentStatus] is correct." } } function StartStop-AzureVMs { Param ( # Action to perform (startup or shutdown) [string][Parameter(Mandatory = $true)]$Action, # Resource group where the vm belongs to [string][Parameter(Mandatory = $false)]$ResourceGroup, # The list of the VM's to perform action to (separate by ',') # [string][Parameter(Mandatory = $false)]$VMList, [string][Parameter(Mandatory = $false)]$vmsExcludedFromAction, [bool]$Simulate ) # Suppress Warnings Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings 'true' # Authenticate with your Automation Account # $Conn = Get-AutomationConnection -Name AzureRunAsConnection # Add-AzAccount -ServicePrincipal -Tenant $Conn.TenantID -ApplicationID $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint $AzureVMsToHandle = @() $AzureVMs = @() $AzureVMs = (Get-AzVM $ResourceGroup).Name $AzureVMsToHandle = $AzureVMs # Get the existing VM's [Object]$VMs = (Get-AzVM $ResourceGroup) #Shut down or Start up VM's ('Exceution initiated...') Write-Host "List of VMs to handle: $($AzureVMsToHandle)" Write-Host "Performing $($Action)" if ($Action -eq 'Start') { foreach ($AzureVM in $AzureVMsToHandle) { if ($VMs | Where-Object { $_.Name -eq $AzureVM }) { if ($vmsExcludedFromAction -match ($AzureVM)) { #for skipping array of VMs 'VM1','VM2' etc Write-Host " ---> Skipping VM: [$AzureVM]" } else { # Readiness parameters Write-Host "Performing [$($Action)] for [$($AzureVM)] resource under [$($ResourceGroup)] RG" $instance = ($VMs | Where-Object { ($_.Name -eq $AzureVM) }) $status = AssertResourceManagerVirtualMachinePowerState -VirtualMachine $instance -DesiredState 'Started' -Simulate $Simulate $status # $status = $VMs | Where-Object { ($_.Name -eq $AzureVM) } | Start-AzVM -ErrorAction Continue; } } else { Write-Host "##[error]AzureVM : [$AzureVM] - Does not exist! - Check your inputs!" } } } elseif ($Action -eq 'Stop') { foreach ($AzureVM in $AzureVMsToHandle) { if ($vmsExcludedFromAction -match ($AzureVM)) { #for skipping array of VMs 'VM1','VM2' etc Write-Host " ---> Skipping VM: [$AzureVM]" } else { # $status = $VMs | Where-Object { ($_.Name -eq $AzureVM) } | Stop-AzVM -Force -ErrorAction Continue; # Readiness parameters Write-Host "Performing [$($Action)] for [$($AzureVM)] resource under [$($ResourceGroup)] RG" $instance = ($VMs | Where-Object { ($_.Name -eq $AzureVM) }) $status = AssertResourceManagerVirtualMachinePowerState -VirtualMachine $instance -DesiredState 'StoppedDeallocated' -Simulate $Simulate $status } } } elseif ($Action -eq 'restart') { foreach ($AzureVM in $AzureVMsToHandle) { if ($vmsExcludedFromAction -match ($AzureVM)) { #for skipping array of VMs 'VM1','VM2' etc Write-Host " ---> Skipping VM: [$AzureVM]" } else { # Readiness parameters Write-Host "Performing [$($Action)] for [$($AzureVM)] resource under [$($ResourceGroup)] RG" $status = $VMs | Where-Object { ($_.Name -eq $AzureVM) } | Restart-AzVM -ErrorAction Continue; $instance = ($VMs | Where-Object { ($_.Name -eq $AzureVM) }) if ($status.Status -match 'Succ') { Write-Host "Retarting VM: [$AzureVM] - Succeeded!" } else { Write-Host "##[error]Retarting VM: [$AzureVM] - Failed!" } } } } else { Write-Host "##[error]Action: [$Action] - Input Error! - Check your inputs! (Only 'start', 'stop' and 'restart' are allowed!)" -TargetObject $_ } } Function StartStop-VirtualMachine { <# .SYNOPSIS .\StartStop-VirtualMachine.ps1. .DESCRIPTION You can START or STOP VM(s) with this command. It takes ResourceGroup or array of VMs are arguments. You can exclude list of VMs from the RG using vmsExcludedFromAction parameter. .PARAMETER Action Specifies the action (Start or Stop) to perform on VM(s). .PARAMETER ResourceGroupName Specifies the name of ResourceGroup. "RG1" or "RG1,RG2,RGn". .PARAMETER VMList Specifies the name of virtual machine(s). "VM1" or "VM1,VM2,VMn". .PARAMETER vmsExcludedFromAction Specifies the name of virtual machine(s) that you want to SKIP from action. "VM1" or "VM1,VM2,VMn". .PARAMETER Simulate Use this switch parameter to perform WhatIf action. .INPUTS None. You cannot pipe objects to command. .OUTPUTS System.String. command returns a string with the status of action performed on workload. .EXAMPLE PS> StartStop-VirtualMachine -Action Stop -ResourceGroupName mytestrg1984 -Simulate # Command simulate the action on the RG mytestrg1984 for all VMs. *** Running in SIMULATE mode. No actions will be performed. *** Performing [stop] for resource group [mytestrg1984] --------------------------------------------------------------------------- Exceution initiated... List of VMs to handle: mytestvm1 mytestvm2 Performing stop Performing [stop] for [mytestvm1] resource under [mytestrg1984] RG Az[mytestvm1]: SIMULATION -- Would have stopped VM. (No action taken) Performing [stop] for [mytestvm2] resource under [mytestrg1984] RG Az[mytestvm2]: SIMULATION -- Would have stopped VM. (No action taken) --------------------------------------------------------------------------- Validating all VM status --------------------------------------------------------------------------- mytestvm1 | PowerState/running --------------------------------------------------------------------------- --------------------------------------------------------------------------- mytestvm2 | PowerState/running --------------------------------------------------------------------------- .EXAMPLE PS> StartStop-VirtualMachine -Action Stop -ResourceGroupName mytestrg1984 # Command perform the action on the RG mytestrg1984 for all VMs. *** Running in LIVE mode. Operation will be enforced. *** Performing [stop] for resource group [mytestrg1984] --------------------------------------------------------------------------- Exceution initiated... List of VMs to handle: mytestvm1 mytestvm2 Performing stop Performing [stop] for [mytestvm1] resource under [mytestrg1984] RG Stopping VM: [mytestvm1] - Succeeded! [mytestvm1]: Stopping VM OperationId : bfc3844a-c88e-47cd-98e9-fd31cbb5ee5c Status : Succeeded StartTime : 1/19/2023 2:16:17 PM EndTime : 1/19/2023 2:17:06 PM Error : Performing [stop] for [mytestvm2] resource under [mytestrg1984] RG Stopping VM: [mytestvm2] - Succeeded! [mytestvm2]: Stopping VM OperationId : c050e8d2-3749-4fc1-a5bd-4550154f1e81 Status : Succeeded StartTime : 1/19/2023 2:17:07 PM EndTime : 1/19/2023 2:17:54 PM Error : --------------------------------------------------------------------------- Validating all VM status --------------------------------------------------------------------------- mytestvm1 | PowerState/deallocated --------------------------------------------------------------------------- --------------------------------------------------------------------------- mytestvm2 | PowerState/deallocated --------------------------------------------------------------------------- .EXAMPLE PS> StartStop-VirtualMachine -Action Stop -ResourceGroupName mytestrg1984 -vmsExcludedFromAction mytestvm1 # Excluding the VM from the RG. In the example we are excluding the operation on mytestvm1 *** Running in LIVE mode. Operation will be enforced. *** Performing [start] for resource group [mytestrg1984] --------------------------------------------------------------------------- Exceution initiated... List of VMs to handle: mytestvm1 mytestvm2 Performing start ---> Skipping VM: [mytestvm1] Performing [start] for [mytestvm2] resource under [mytestrg1984] RG Starting VM: [mytestvm2] - Succeeded! [mytestvm2]: Starting VM OperationId : 57528881-9450-4098-a7a4-ef13ccd17e8b Status : Succeeded StartTime : 1/19/2023 2:29:53 PM EndTime : 1/19/2023 2:30:03 PM Error : --------------------------------------------------------------------------- Validating all VM status --------------------------------------------------------------------------- mytestvm1 | PowerState/deallocated --------------------------------------------------------------------------- --------------------------------------------------------------------------- mytestvm2 | PowerState/running --------------------------------------------------------------------------- .LINK Donation link : https://www.paypal.com/paypalme/aammirmirza/7 #> Param ( # Action to perform (startup or shutdown) [string][Parameter(Mandatory = $true)]$Action, # Resource group where the vm belongs to [string][Parameter(Mandatory = $false)]$ResourceGroupName, # The list of the VM's to perform action to (separate by ',') [string][Parameter(Mandatory = $false)]$VMList, [string][Parameter(Mandatory = $false)]$vmsExcludedFromAction, [switch]$Simulate ) ## Signature $t = @" Extension designed and managed _ _ _ | |__ _ _ /_\ __ _ _ __ (_) _ _ | '_ \| || | / _ \ / _` || ' \ | || '_| |_.__/ \_, | /_/ \_\\__,_||_|_|_||_||_| |__/ Azure DevOps extensions for cost savings. Optimize virtual machine spend by shutting down underutilized instances. Please suggest improvements at aammir.mirza@hotmail.com "@ for ($i = 0; $i -lt $t.length; $i++) { if ($i % 2) { $c = 'blue' } elseif ($i % 5) { $c = 'blue' } elseif ($i % 7) { $c = 'blue' } else { $c = 'blue' } Write-Host $t[$i] -NoNewline -ForegroundColor $c } ################################################################ # Main script content try { if ($Simulate) { Write-Host 'Az*** Running in SIMULATE mode. No actions will be performed. ***' } else { Write-Host 'Az*** Running in LIVE mode. Operation will be enforced. ***' } ########################################################### # if ($VMList) { # if (!$ResourceGroupName) { # Write-Host "##[error]Missing Argument : ResourceGroup name must be provided. Listed Vms should be part of single RG." # } # # StartStop-AzureVMs -Action $Action -resourceGroup $ResourceGroupName -VMList $VMList -vmsExcludedFromAction $vmsExcludedFromAction # Write-Host "AzPerforming [$($Action)] for resource group [$($ResourceGroupName)]" # ('-' * 75) # StartStop-AzureVMs -Action $Action -resourceGroup $ResourceGroupName -VMList $VMList -vmsExcludedFromAction $vmsExcludedFromAction -Simulate $Simulate # ('-' * 75) # } # elseif ($ResourceGroupName) { if ($ResourceGroupName) { $AzureRGs = $ResourceGroupName.Split(',') $AzureRGsToHandle = @($AzureRGs.replace(' ' , '').replace("'" , '').replace('"' , '')) foreach ($rg in $AzureRGsToHandle) { Write-Host "Performing [$($Action)] for resource group [$($rg)]" -ForegroundColor Blue ('-' * 75) StartStop-AzureVMs -Action $Action -ResourceGroup $rg -vmsExcludedFromAction $vmsExcludedFromAction -Simulate $Simulate ('-' * 75) try { Write-Host 'Validating all VM status' -ForegroundColor Blue $VMs = (Get-AzVM $rg) foreach ($currentVM in $VMs) { ('-' * 75) $vmStatus = (Get-AzVM -ResourceGroupName $rg -Name $currentVM.Name -status) $vmStatus.Name + ' | ' + (($vmStatus).Statuses[1].Code) ('-' * 75) } } catch { Write-Warning 'Issues with Get-AzVM command to fetch the current status of all VMs for validation' } } } else { Write-Warning 'You can provide list of RGs (array) or single RG. E.g: "RG1,RG2,RGn"' Write-Error 'Mandatory ResourceGroup parameter missing.' } } catch { $errorMessage = $_.Exception.Message throw "Unexpected exception: $errorMessage" } } |