Эта заметка описывает модификацию решения Запуск и остановка Azure серверов по расписанию от апреля 2019 года.
В процессе работы с инфраструктурой Azure обнаружился не очень приятный факт, что команды на включение виртуальных машин могут не исполняться, в результате чего сервер остается в выключенном состоянии. Например, на картинке ниже видно, как несколько раз подавались команды на запуск сервера и от скрипта, и непосредственно от пользователя через портал Azure, но облако выдавало ошибку "Allocation failed. If you are trying to add a new VM to an Availability Set or update/resize an existing VM in an Availability Set, please note that such Availability Set allocation is scoped to a single cluster, and it is possible that the cluster is out of capacity..." А бот из центра поддержки центра отвечал: "The hardware cluster where the VM is currently deployed did not have enough capacity of this size to support your allocation request." Инженер из центра поддержки Microsoft подтвердил проблему в Дата Центре Azure и посоветовал модифицировать сценарий запуска сервера - проверять через некоторое время, запустился ли сервер и присылать отчет администратору.
Это сподвигло меня проверить скрипты, используемые в решении, дополнить их несколькими улучшениями, а так же написать еще один скрипт, который бы проверял состояние серверов и отправлял письма об ошибках запуска виртуальных машин администраторам системы, что им необходимо подключиться к порталу Azure и проконтролировать процесс.
Основные отличия в обновленном сценарии - это
- добавление переменной $Location, чтобы назначать регион в котором отрабатывать скрипт,
- добавление штампа даты и времени к событиям в логе скрипта - команда Get-Date -Format "HH:mm:ss" в каждой строке вывода, что помогает в расследовании инцидентов с запуском виртуальных машин,
- обработка машин только из назначенного региона, то есть серверов с одними и теми же настройками часового пояса (это сделано, чтоб избежать ошибок вычисления времени запуска серверов в разных регионах) - опция -Location $Location в команде выборки виртуальных машин,
В остальном же сценарий остался без изменений.
Ниже представлены сами модифицированные скрипты, а в заметке Проверка запуска Azure серверов и уведомление администраторов рассказывается о контролирующем запуск скрипте.
$connectionName = "AzureRunAsConnection" $TagsTimeZone = "Central Europe Standard Time" $TagsTimeZoneAbbreviation = "CET" $Location = "westeurope" Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "----- The script started -----") Try { # Get the connection "AzureRunAsConnection " $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Logging in to Azure...") Login-AzureRmAccount ` -ServicePrincipal ` -TenantId $servicePrincipalConnection.TenantId ` -ApplicationId $servicePrincipalConnection.ApplicationId ` -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint | Out-Null } catch { if (!$servicePrincipalConnection) { $ErrorMessage = "Connection $connectionName not found" throw $ErrorMessage } else { Write-Error -Message $_.Exception throw $_.Exception } } # Making time filter Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "System time zone = " + ([TimeZoneInfo]::Local).Id) Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Current system time = " + (Get-Date -Format "dd.MM.yyyy HH:mm, ddd") ) $TagsTime = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), $TagsTimeZone)).ToString("dd.MM.yyyy HH:mm, ddd") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Tags time zone = $($TagsTimeZone)") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Tags time = $($TagsTime)") $START_TIME = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), $TagsTimeZone)).ToString('H:00') $START_DAY = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), $TagsTimeZone)).ToString('ddd') Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Adapting time to search for ""$($START_TIME)"" in tags") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Looking for instances where ""Schedule Start"" tag = ""$($START_TIME)"" ...") [array]$VMs = Get-AzureRMVm -Location $Location | Where-Object {$PSItem.Tags["Schedule Start"] -eq $START_TIME} Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "$($VMs.Count) instances found") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Processing the instances...") ForEach ($VM in $VMs) { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "$($VM.Name) instance in $($VM.ResourceGroupName) resource group:") $VMTags = $VM.Tags Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " Checking the ""Schedule Days"" tag ...") If ( -not($VMTags.Keys -contains "Schedule Days") -or $VMTags["Schedule Days"].Split(',').Trim() -contains $START_DAY ) { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " The instance is allowed to be processed today" ) Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " Checking the instance status ...") $VMStatus = (Get-AzureRMVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status).Statuses[1].DisplayStatus If ($VMStatus -eq "VM deallocated") { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " The instance is in ""$($VMStatus)"" state") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " Starting the instance") Start-AzureRMVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName -AsJob | Out-Null Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " Updating COMMENT tag for the instance") $VMTagCommentText = ("Started by Start-Stop Scheduler at " + ` ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), $TagsTimeZone)).ToString("dd.MM.yyyy HH:mm") + ` " $($TagsTimeZoneAbbreviation)") If (-not($VMTags.ContainsKey("Comment"))) { $VMTags.Add("Comment", $VMTagCommentText) } Else { $VMTags["Comment"] = $VMTagCommentText } Set-AzureRMResource -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -ResourceType "Microsoft.Compute/VirtualMachines" ` -Tag $VMTags -Force -AsJob | Out-Null } Else { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " The instance is in ""$($VMStatus)"" state") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " No action needed") } } Else { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " The instance is not allowed to be processed today" ) } } Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "----- The script stopped -----") Write-Output (Get-Job)
$connectionName = "AzureRunAsConnection" $TagsTimeZone = "Central Europe Standard Time" $TagsTimeZoneAbbreviation = "CET" $Location = "westeurope" Write-Output ("----- The script started -----") Try { # Get the connection "AzureRunAsConnection " $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Logging in to Azure...") Login-AzureRmAccount ` -ServicePrincipal ` -TenantId $servicePrincipalConnection.TenantId ` -ApplicationId $servicePrincipalConnection.ApplicationId ` -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint | Out-Null } catch { if (!$servicePrincipalConnection) { $ErrorMessage = "Connection $connectionName not found" throw $ErrorMessage } else { Write-Error -Message $_.Exception throw $_.Exception } } # Making time filter Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "System time zone = " + ([TimeZoneInfo]::Local).Id) Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Current system time = " + (Get-Date -Format "dd.MM.yyyy HH:mm, ddd") ) $TagsTime = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), $TagsTimeZone)).ToString("dd.MM.yyyy HH:mm, ddd") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Tags time zone = $($TagsTimeZone)") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Tags time = $($TagsTime)") $STOP_TIME = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), $TagsTimeZone)).ToString('H:00') $STOP_DAY = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), $TagsTimeZone)).ToString('ddd') Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Adapting time to search for ""$($STOP_TIME)"" in tags") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Looking for instances where ""Schedule Stop"" tag = ""$($STOP_TIME)"" ...") [array]$VMs = Get-AzureRMVm -Location $Location | Where-Object {$PSItem.Tags["Schedule Stop"] -eq $STOP_TIME} Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "$($VMs.Count) instances found") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "Processing the instances...") ForEach ($VM in $VMs) { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + "$($VM.Name) instance in $($VM.ResourceGroupName) resource group:") $VMTags = $VM.Tags Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " Checking the ""Schedule Days"" tag ...") IF ( -not($VMTags.Keys -contains "Schedule Days") -or $VMTags["Schedule Days"].Split(',').Trim() -contains $STOP_DAY ) { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " The instance is allowed to be processed today" ) Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " Checking the instance status ...") $VMStatus = (Get-AzureRMVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status).Statuses[1].DisplayStatus If ($VMStatus -eq "VM running") { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " The instance is in ""$($VMStatus)"" state") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " Stopping the instance") Stop-AzureRMVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName -Force -AsJob | Out-Null Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " Updating COMMENT tag for the instance") $VMTagCommentText = ("Stopped by Start-Stop Scheduler at " + ` ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), $TagsTimeZone)).ToString("dd.MM.yyyy HH:mm") + ` " $($TagsTimeZoneAbbreviation)") If (-not($VMTags.ContainsKey("Comment"))) { $VMTags.Add("Comment", $VMTagCommentText) } Else { $VMTags["Comment"] = $VMTagCommentText } Set-AzureRMResource -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -ResourceType "Microsoft.Compute/VirtualMachines" ` -Tag $VMTags -Force -AsJob | Out-Null } Else { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " The instance is in ""$($VMStatus)"" state") Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " No action needed") } } Else { Write-Output ((Get-Date -Format "HH:mm:ss") + ": " + " The instance is not allowed to be processed today") } } Write-Output ("----- The script stopped -----")