By using this article, we can start/stop VMs during off-business houses.This greatly benefits the customers especially in cost optimization and manual task overhead of performing this action manually. But we need to make sure that the VMs that we are selecting is present in the same subscription where the automation account and this schedule is created by selecting only the required VMs and excluding the other VMs.
Login to Azure portal
Go to ALL Services and Type Automation Account and Create Automation Account.

Under Process Automation, click Runbooks to open the list of runbooks.

Click on + Create a runbook button to create a new runbook


Provide and Name and Runbook Type as PowerShell for the new runbook and then click Create button.
The Run Book is Empty, must deploy the Code then Save and Publish
Note: You can Find lot of powershell scripts for this task in the github and technet gallery. You can use the below one as well taken from github.
#PowerShell Script to Start and Stop VM's in Azure on Scheduled Intervals using Azure Automation Account# Param( [Parameter(Mandatory=$true,HelpMessage="Enter the value for Action. Values can be either stop or start")][String]$Action, [Parameter(Mandatory=$false,HelpMessage="Enter the value for WhatIf. Values can be either true or false")][bool]$WhatIf = $false, [Parameter(Mandatory=$false,HelpMessage="Enter the VMs separated by comma(,)")][string]$VMList ) function ValidateVMList ($FilterVMList) { [boolean] $ISexists = $false [string[]] $invalidvm=@() $ExAzureVMList=@() foreach($filtervm in $FilterVMList) { $currentVM = Get-AzureRmVM | where Name -Like $filtervm.Trim() -ErrorAction SilentlyContinue if ($currentVM.Count -ge 1) { $ExAzureVMList+= @{Name = $currentVM.Name; Location = $currentVM.Location; ResourceGroupName = $currentVM.ResourceGroupName; Type = "ResourceManager"} $ISexists = $true } elseif($ISexists -eq $false) { $invalidvm = $invalidvm+$filtervm } } if($invalidvm -ne $null) { Write-Output "Runbook Execution Stopped! Invalid VM Name(s) in the VM list: $($invalidvm) " Write-Warning "Runbook Execution Stopped! Invalid VM Name(s) in the VM list: $($invalidvm) " exit } else { return $ExAzureVMList } } function CheckExcludeVM ($FilterVMList) { [boolean] $ISexists = $false [string[]] $invalidvm=@() $ExAzureVMList=@() <pre><code>foreach($filtervm in $FilterVMList) { $currentVM = Get-AzureRmVM | where Name -Like $filtervm.Trim() -ErrorAction SilentlyContinue if ($currentVM.Count -ge 1) { $ExAzureVMList+=$currentVM.Name $ISexists = $true } elseif($ISexists -eq $false) { $invalidvm = $invalidvm+$filtervm } } if($invalidvm -ne $null) { Write-Output "Runbook Execution Stopped! Invalid VM Name(s) in the exclude list: $($invalidvm) " Write-Warning "Runbook Execution Stopped! Invalid VM Name(s) in the exclude list: $($invalidvm) " exit } else { Write-Output "Exclude VM's validation completed..." } </code></pre> } <h1>-----L O G I N - A U T H E N T I C A T I O N-----</h1> $connectionName = "AzureRunAsConnection" try { # Get the connection "AzureRunAsConnection " $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName <pre><code>"Logging in to Azure..." Add-AzureRmAccount ` -ServicePrincipal ` -TenantId $servicePrincipalConnection.TenantId ` -ApplicationId $servicePrincipalConnection.ApplicationId ` -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint </code></pre> } catch { if (!$servicePrincipalConnection) { $ErrorMessage = "Connection $connectionName not found." throw $ErrorMessage } else{ Write-Error -Message $_.Exception throw $_.Exception } } <h1>---------Read all the input variables---------------</h1> $StartResourceGroupNames = Get-AutomationVariable -Name 'RG-Of-VMs-To-Start' $StopResourceGroupNames = Get-AutomationVariable -Name 'RG-Of-VMs-To-Stop' $ExcludeVMNames = Get-AutomationVariable -Name 'Excluded-List-Of-VMs' try { $Action = $Action.Trim().ToLower() <pre><code> if(!($Action -eq "start" -or $Action -eq "stop")) { Write-Output "`$Action parameter value is : $($Action). Value should be either start or stop." Write-Output "Completed the runbook execution..." exit } Write-Output "Runbook Execution Started..." [string[]] $VMfilterList = $ExcludeVMNames -split "," #If user gives the VM list with comma seperated.... $AzurVMlist = Get-AutomationVariable -Name $VMList if(($AzurVMlist)) { Write-Output "list of all VMs = $($AzurVMlist)" [string[]] $AzVMList = $AzurVMlist -split "," } if($Action -eq "stop") { Write-Output "List of all ResourceGroups = $($StopResourceGroupNames)" [string[]] $VMRGList = $StopResourceGroupNames -split "," } if($Action -eq "start") { Write-Output "List of all ResourceGroups = $($StopResourceGroupNames)" [string[]] $VMRGList = $StartResourceGroupNames -split "," } #Validate the Exclude List VM's and stop the execution if the list contains any invalid VM if (([string]::IsNullOrEmpty($ExcludeVMNames) -ne $true) -and ($ExcludeVMNames -ne "none")) { Write-Output "Values exist on the VM's Exclude list. Checking resources against this list..." CheckExcludeVM -FilterVMList $VMfilterList } $AzureVMListTemp = $null $AzureVMList=@() if ($AzVMList -ne $null) { ##Action to be taken based on VM List and not on Resource group. ##Validating the VM List. Write-Output "VM List is given to take action (Exclude list will be ignored)..." $AzureVMList = ValidateVMList -FilterVMList $AzVMList } else { ##Getting VM Details based on RG List or Subscription if (($VMRGList -ne $null) -and ($VMRGList -ne "*")) { foreach($Resource in $VMRGList) { Write-Output "Validating the resource group name ($($Resource))" $checkRGname = Get-AzureRmResourceGroup -Name $Resource.Trim() -ev notPresent -ea 0 if ($checkRGname -eq $null) { Write-Warning "$($Resource) is not a valid Resource Group Name. Please verify your input" } else { # Get resource manager VM resources in group and record target state for each in table $taggedRMVMs = Get-AzureRmVM | ? { $_.ResourceGroupName -eq $Resource} foreach($vmResource in $taggedRMVMs) { if ($vmResource.ResourceGroupName -Like $Resource) { $AzureVMList += @{Name = $vmResource.Name; ResourceGroupName = $vmResource.ResourceGroupName; Type = "ResourceManager"} } } } } } else { Write-Output "Getting all the VM's from the subscription..." $ResourceGroups = Get-AzureRmResourceGroup foreach ($ResourceGroup in $ResourceGroups) { # Get resource manager VM resources in group and record target state for each in table $taggedRMVMs = Get-AzureRmVM | ? { $_.ResourceGroupName -eq $ResourceGroup.ResourceGroupName} foreach($vmResource in $taggedRMVMs) { Write-Output "RG : $($vmResource.ResourceGroupName) , ARM VM $($vmResource.Name)" $AzureVMList += @{Name = $vmResource.Name; ResourceGroupName = $vmResource.ResourceGroupName; Type = "ResourceManager"} } } } } $ActualAzureVMList=@() if($AzVMList -ne $null) { $ActualAzureVMList = $AzureVMList } #Check if exclude vm list has wildcard elseif(($VMfilterList -ne $null) -and ($VMfilterList -ne "none")) { $ExAzureVMList = ValidateVMList -FilterVMList $VMfilterList foreach($VM in $AzureVMList) { ##Checking Vm in excluded list if($ExAzureVMList.Name -notcontains ($($VM.Name))) { $ActualAzureVMList+=$VM } } } else { $ActualAzureVMList = $AzureVMList } Write-Output "The current action is $($Action)" $ActualVMListOutput=@() if($WhatIf -eq $false) { $AzureVMListARM=@() # Store the ARM VM's $AzureVMListARM = $ActualAzureVMList | Where-Object {$_.Type -eq "ResourceManager"} # process the ARM VM's if($AzureVMListARM -ne $null) { foreach($VM in $AzureVMListARM) { $ActualVMListOutput = $ActualVMListOutput + $VM.Name + " " #ScheduleSnoozeAction -VMObject $VM -Action $Action #------------------------ try { Write-Output "VM action is : $($Action)" Write-OutPut $VM.ResourceGroupName $VMState = Get-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -status | Where-Object { $_.Name -eq $VM.Name } if ($Action.Trim().ToLower() -eq "stop") { Write-Output "Stopping the VM : $($VM.Name)" Write-Output "Resource Group is : $($VM.ResourceGroupName)" if($VMState.Statuses[1].DisplayStatus -ne "VM running") { Write-Output "VM is not in running state, No actions performed" } else { $Status = Stop-AzureRmVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName -Force if($Status -eq $null) { Write-Output "Error occured while stopping the Virtual Machine." } else { Write-Output "Successfully stopped the VM $($VM.Name)" } } } elseif($Action.Trim().ToLower() -eq "start") { Write-Output "Starting the VM : $($VM.Name)" Write-Output "Resource Group is : $($VM.ResourceGroupName)" if($VMState.Statuses[1].DisplayStatus -eq "VM running") { Write-Output "VM already Running, No actions performed" } else { $Status = Start-AzureRmVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName if($Status -eq $null) { Write-Output "Error occured while starting the Virtual Machine $($VM.Name)" } else { Write-Output "Successfully started the VM $($VM.Name)" } } } } catch { Write-Output "Error Occurred..." Write-Output $_.Exception } #-------------------------- } Write-Output "Completed the $($Action) action on the following ARM VMs: $($ActualVMListOutput)" } } elseif($WhatIf -eq $true) { Write-Output "WhatIf parameter is set to True..." Write-Output "When 'WhatIf' is set to TRUE, runbook provides a list of Azure Resources (e.g. VMs), that will be impacted if you choose to deploy this runbook." Write-Output "No action will be taken at this time..." Write-Output $($ActualAzureVMList) } Write-Output "Runbook Execution Completed..." } catch { $ex = $_.Exception Write-Output $_.Exception }

Now in Automation Account, under Shared Resources click Variables and add these variables [Note: while creating the variables you have to provide some Value, later you can delete the value if required]

Explanation of these variables-
RG-Of-VMs-To-Start: ResourceGroup that contains VMs to be started. Must be in the same subscription that the Azure Automation Run-As account has permission to manage.
RG-Of-VMs-To-Stop: ResourceGroup that contains VMs to be stopped. Must be in the same subscription that the Azure Automation Run-As account has permission to manage.
Excluded-List-Of-VMs: VM names to be excluded from being started/Stopped
Note: These Variables are defined in the PowerShell Code and should not be changed if used by default.
VMstoStartStop: Provide the list of the VMs to be started or stopped.
Now we will create schedule for the start & stop of VMs. Under Shared Resources, click on Schedules and then click + Add a schedule and provide a Name and time and recurrence frequency for the schedule and Click Create

Similarly create schedule for Stop_VM

Now we will attach these schedules with the runbook. Go to Runbook and then open the runbook, click schedules and then + Add a schedule

Now link Start_VM and then provide parameter values. ACTION can have values like start or stop. In VMLIST provide the variable name “VMstoStartStop” which contains the VM names. Click create


Similarly attach the Stop_VM and provide the Action value Stop and VMList value VMstoStartstop.
So now we have two schedules start-vm and stop-vm which would be running on the defined schedules.

Now your runbook will execute the start of VM and stop of VM as per the schedule attached. The results of runbook execution can be seen under Process Automation –> Jobs.
This task greatly benefits the hassle of maintaining and performing this task manually from the admin side.
Leave a Reply