This repository packages AWS Instance Scheduler into composable Terraform modules for hub-and-spoke platforms. It solves the problem of inconsistent runtime controls across accounts by standardizing schedule deployment, schedule data management, and tag enforcement as code.
In a typical landing zone setup, modules/scheduler runs in a central account, modules/config manages periods/schedules in DynamoDB, and modules/tagging backfills missing schedule tags. For spoke onboarding there are now two explicit options: modules/spoke for standalone deployment into a single target account, and modules/spokes for organizational deployment via CloudFormation StackSets across multiple accounts/OUs. modules/macro can inject default tags into CloudFormation resources through a transform.
- Security by default: S3 template buckets enforce TLS, block insecure uploads, require encryption, and enable versioning.
- Cross-account operations: Supports centralized scheduling patterns with AWS Organizations and StackSets for multi-account rollout.
- Operational excellence: Separates scheduler deployment from schedule data so teams can iterate schedules without replacing the scheduler stack.
- Flexible tagging controls: Provides scheduled Lambda-based tag enforcement for EC2, Auto Scaling, RDS, Aurora, DocumentDB, and Neptune.
- Cloud platform alignment: Designed for AWS landing zone patterns with hub account orchestration and spoke account execution.
- Compliance support: Helps implement cost-control and governance objectives that commonly map to SOC 2, ISO 27001, and PCI-DSS operating controls.
Deploy the scheduler in a central account and define one business-hours schedule.
locals {
tags = {
Environment = "development"
Owner = "platform"
Product = "landing-zone"
}
}
module "scheduler" {
source = "github.com/appvia/terraform-aws-instance-scheduler//modules/scheduler?ref=main"
scheduler_tag_name = "Schedule"
scheduler_regions = ["eu-west-2"]
scheduler_organizations_ids = ["o-abc123xyz0"]
scheduler_frequency = 15
tags = local.tags
}
module "config" {
source = "github.com/appvia/terraform-aws-instance-scheduler//modules/config?ref=main"
dyanmodb_table_name = module.scheduler.scheduler_dynamodb_table
periods = {
office_hours = {
description = "Weekday operating window"
start_time = "08:00"
end_time = "18:00"
weekdays = ["mon-fri"]
}
}
schedules = {
office_hours = {
description = "Run only in office hours"
periods = ["office_hours"]
timezone = "Europe/London"
}
}
}Enable broader resource coverage, explicit security inputs, and centralized tag enforcement across resource classes.
locals {
tags = {
Environment = "production"
Owner = "platform"
Compliance = "pci"
}
}
module "scheduler" {
source = "github.com/appvia/terraform-aws-instance-scheduler//modules/scheduler?ref=main"
cloudformation_bucket_name = "org-prod-instance-scheduler-templates"
enable_cloudformation_macro = true
enable_cloudwatch_dashboard = true
enable_ssm_maintenance_windows = true
enable_rds_snapshot = true
scheduler_tag_name = "Schedule"
scheduler_regions = ["eu-west-2", "eu-central-1"]
scheduler_organizations_ids = ["o-abc123xyz0"]
scheduler_log_group_retention = "30"
scheduler_timezone = "UTC"
kms_key_arns = ["arn:aws:kms:eu-west-2:111122223333:key/abcd-1234"]
tags = local.tags
}
module "spokes" {
source = "github.com/appvia/terraform-aws-instance-scheduler//modules/spokes?ref=main"
scheduler_account_id = "111122223333"
organizational_units = {
engineering = "ou-abcd-11111111"
data = "ou-abcd-22222222"
}
tags = local.tags
}
module "tagging" {
source = "github.com/appvia/terraform-aws-instance-scheduler//modules/tagging?ref=main"
scheduler_tag_name = "Schedule"
scheduler_tag_value = "office_hours"
schedule = "rate(15 minutes)"
enable_autoscaling = true
enable_ec2 = true
enable_rds = true
autoscaling = {
excluded_tags = ["DoNotSchedule=true"]
}
tags = local.tags
}- CloudFormation stack and StackSet updates are asynchronous and can take several minutes before full rollout completes.
- S3 bucket names are globally unique; naming conventions must avoid collisions across AWS accounts.
- Scheduler frequency is constrained to supported values (
1, 2, 5, 10, 15, 30, 60minutes). modules/configwrites directly to the scheduler DynamoDB table; name collisions inperiods/schedulesoverwrite existing items.- Choose
modules/spokefor single-account onboarding andmodules/spokesfor OU-based multi-account onboarding via StackSets.
The terraform-docs utility is used to generate this README. Follow the below steps to update:
- Make changes to the
.terraform-docs.ymlfile - Fetch the
terraform-docsbinary (https://terraform-docs.io/user-guide/installation/) - Run
terraform-docs markdown table --output-file ${PWD}/README.md --output-mode inject .
No providers.
No inputs.
No outputs.
