I am trying to get the resource azurerm_automation_schedule
to deploy at a specific time (ex: 18:00)
occurring monthly.
I'm using the following code:
locals {
update_time = "18:00"
update_date = formatdate("YYYY-MM-DD", timeadd(timestamp(), "24h"))
update_timezone = "UTC"
}
resource "azurerm_automation_schedule" "main" {
name = "test"
resource_group_name = "myresourcegroupname"
automation_account_name = "myautomationaccountname"
frequency = "Month"
timezone = local.update_timezone
start_time = "${local.update_date}T${local.update_time}:00+02:00"
description = "This is an example schedule"
monthly_occurrence {
day = "Tuesday"
occurrence = "1"
}
}
The "${local.update_date}T${local.update_time}:00+02:00"
adds 2 hours to the current time and sets the day forward 1. This is required to ensure the schedule starts in the future.
This works fine, except the next time I come back to run a deploy, it detects a new change due to the date changing, even if no real changes have occurred. The start_time will always tick forward.
I can't seem to find any terraform logic that can assist. Is there a way to set a static start time in a variable, and only have it updated if it changes? (not the date).
The psuedocode would be:
if [update_time] has not changed, do not update [azurerm_automation_schedule]
else update [azurerm_automation_schedule] with the new time, incrementing the day forward
Update
My final working code (BONUS: With windows update scheduler which is a pain to get working!)
//== Provider used to store timestamp for updates ==//
provider "time" {
version = "~> 0.4"
}
//== Store 1 day in the future, only update if [local.update_time] is altered ==//
resource "time_offset" "next_day" {
offset_days = 1
triggers = {
update_time = local.update_time
}
}
locals {
update_time = "19:40"
update_date = substr(time_offset.next_day.rfc3339, 0, 10)
update_timezone = "UTC"
update_max_hours = "4"
update_classifications = "Critical, Security, UpdateRollup, ServicePack, Definition, Updates"
update_reboot_settings = "IfRequired"
update_day = "Tuesday"
update_occurrence = "2"
}
#This type should eventually replace the manual deploy via azurerm: azurerm_automation_softwareUpdateConfigurations
#https://github.com/terraform-providers/terraform-provider-azurerm/issues/2812
resource "azurerm_template_deployment" "windows" {
name = "windows-update"
resource_group_name = module.stack.azurerm_resource_group.name
template_body = <<DEPLOY
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"apiVersion": "2017-05-15-preview",
"type": "Microsoft.Automation/automationAccounts/softwareUpdateConfigurations",
"name": "${module.stack.azurerm_automation_account.name}/windows-updates",
"properties": {
"updateConfiguration": {
"operatingSystem": "Windows",
"duration": "PT${local.update_max_hours}H",
"windows": {
"excludedKbNumbers": [
],
"includedUpdateClassifications": "${local.update_classifications}",
"rebootSetting": "${local.update_reboot_settings}"
},
"azureVirtualMachines": [
"${module.server_1.azurerm_virtual_machine.id}",
"${module.server_2.azurerm_virtual_machine.id}"
],
"nonAzureComputerNames": [
]
},
"scheduleInfo": {
"frequency": "Month",
"startTime": "${local.update_date}T${local.update_time}:00",
"timeZone": "${local.update_timezone}",
"interval": 1,
"advancedSchedule": {
"monthlyOccurrences": [
{
"occurrence": "${local.update_occurrence}",
"day": "${local.update_day}"
}
]
}
}
}
}
]
}
DEPLOY
deployment_mode = "Incremental"
}
The reason it keeps planning changes is because your code, as written, refers to the current time, rather than getting "tomorrow" and tracking it somehow.
To do that, you need a way to get "tomorrow" once, and stick it in the state. Things that live in the state are resources, so you need a resource that represents a time with an offset. That's where the time provider comes in.
Here's the essential piece:
resource "time_offset" "tomorrow" {
offset_days = 1
}
That will get "tomorrow" for you and after an apply it will be saved in the Terraform state.
time_offset.tomorrow.rfc3339
Will evaluate to something like:
2020-05-13T04:28:07Z
But, we only want the YYYY-MM-DD from that, so we use substr to get the first 10 characters:
substr(time_offset.tomorrow.rfc3339, 0, 10)
Putting it all together, we get this (4 lines added including whitespace, 1 line changed):
locals {
update_time = "18:00"
update_date = substr(time_offset.tomorrow.rfc3339, 0, 10)
update_timezone = "UTC"
}
resource "time_offset" "tomorrow" {
offset_days = 1
}
resource "azurerm_automation_schedule" "main" {
name = "test"
resource_group_name = "myresourcegroupname"
automation_account_name = "myautomationaccountname"
frequency = "Month"
timezone = local.update_timezone
start_time = "${local.update_date}T${local.update_time}:00+02:00"
description = "This is an example schedule"
monthly_occurrence {
day = "Tuesday"
occurrence = "1"
}
}
You may need to bring in the time provider to use it (put this alongside your AzureRM provider if it doesn't work without it):
provider "time" {}
You can use terraform taint 'time_offset.tomorrow'
to force the time to be recalculated if you need it to be.