From b74aeac165a21b0cde593ced82c7315b226b58fc565edf28f73dd21c8136ec45 Mon Sep 17 00:00:00 2001 From: xpk Date: Fri, 20 Mar 2026 20:56:22 +0800 Subject: [PATCH] feat: new module ec2-instance-scheduler-sfn --- .../ec2-instance-scheduler-sfn/README.md | 52 +++++ .../ec2-instance-scheduler-sfn/main.tf | 194 ++++++++++++++++++ .../ec2-instance-scheduler-sfn/variables.tf | 36 ++++ 3 files changed, 282 insertions(+) create mode 100644 modules/compute/ec2-instance-scheduler-sfn/README.md create mode 100644 modules/compute/ec2-instance-scheduler-sfn/main.tf create mode 100644 modules/compute/ec2-instance-scheduler-sfn/variables.tf diff --git a/modules/compute/ec2-instance-scheduler-sfn/README.md b/modules/compute/ec2-instance-scheduler-sfn/README.md new file mode 100644 index 0000000..f443925 --- /dev/null +++ b/modules/compute/ec2-instance-scheduler-sfn/README.md @@ -0,0 +1,52 @@ + +# ec2-instance-scheduler-sfn + +This module start or stop ec2 instances using step function. +Schedules are made with Event Scheduler + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | +| random | n/a | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| EventSchedulerRole | ../../../whk1-bea-sys-ss-dev-codecommit-sharedmodules/SecurityIdentityCompliance/iam-role-v2 | n/a | +| StepFunctionRole | ../../../whk1-bea-sys-ss-dev-codecommit-sharedmodules/SecurityIdentityCompliance/iam-role-v2 | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_cloudwatch_log_group.Ec2Scheduler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_scheduler_schedule.StartInstances](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule) | resource | +| [aws_scheduler_schedule.StopInstances](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule) | resource | +| [aws_sfn_state_machine.Ec2Scheduler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sfn_state_machine) | resource | +| [random_id.rid](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| cloudwatchlog-retention | Cloudwatch log retention days | `number` | `30` | no | +| instance-ids | Instances to be automatically started/stopped on schedule | `list(string)` | n/a | yes | +| instance-start-cron-expression | Cron expression for instance start schedule | `string` | n/a | yes | +| instance-stop-cron-expression | Cron expression for instance stop schedule | `string` | n/a | yes | +| kms-key-id | CMK id for cloudwatchlog encryption | `string` | n/a | yes | +| scheduler-timezone | Timezone of event scheduler | `string` | `"Asia/Hong_Kong"` | no | + +## Outputs + +No outputs. + +--- +## Authorship +This module was developed by xpk. diff --git a/modules/compute/ec2-instance-scheduler-sfn/main.tf b/modules/compute/ec2-instance-scheduler-sfn/main.tf new file mode 100644 index 0000000..26dc130 --- /dev/null +++ b/modules/compute/ec2-instance-scheduler-sfn/main.tf @@ -0,0 +1,194 @@ +/** +* # ec2-instance-scheduler-sfn +* +* This module start or stop ec2 instances using step function. +* Schedules are made with Event Scheduler +*/ + +resource "random_id" "rid" { + byte_length = 3 +} + +module "StepFunctionRole" { + source = "../../../whk1-bea-sys-ss-dev-codecommit-sharedmodules/SecurityIdentityCompliance/iam-role-v2" + role-name = "InstanceStartStopScheduler-StepFunction-${random_id.rid.dec}" + description = "Step function role for starting or stopping ec2 instances" + trusted-entity = "states.amazonaws.com" + policies = { + "InstanceStartStop0-${random_id.rid.dec}" = { + description = "Allow starting or stopping of ec2 instances and logging" + policy = jsonencode( + { + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "ec2:StartInstances", + "ec2:StopInstances", + "ec2:DescribeInstances" + ] + Resource = "arn:aws:ec2:*:*:instance/*" + }, + { + Effect = "Allow", + Action = [ + "logs:CreateLogDelivery", + "logs:CreateLogStream", + "logs:GetLogDelivery", + "logs:UpdateLogDelivery", + "logs:ListLogDeliveries", + "logs:PutLogEvents", + "logs:PutResourcePolicy", + "logs:DescribeResourcePolicies", + "logs:DescribeLogGroups" + ] + "Resource" : "*" + } + ] + } + ) + } + } +} + +module "EventSchedulerRole" { + source = "../../../whk1-bea-sys-ss-dev-codecommit-sharedmodules/SecurityIdentityCompliance/iam-role-v2" + role-name = "InstanceStartStopScheduler-EventScheduler-${random_id.rid.dec}" + description = "Event scheduler role for invoking step function" + trusted-entity = "scheduler.amazonaws.com" + policies = { + ExecuteStepFunction = { + description = "Allow execution of step function" + policy = jsonencode( + { + Version = "2012-10-17" + Statement = [{ + Action = "states:StartExecution" + Effect = "Allow" + Resource = aws_sfn_state_machine.Ec2Scheduler.arn + }] + } + ) + } + } +} + +resource "aws_cloudwatch_log_group" "Ec2Scheduler" { + name = "/aws/sfn/Ec2InstanceScheduler-${random_id.rid.dec}" + retention_in_days = var.cloudwatchlog-retention + kms_key_id = var.kms-key-id +} + +resource "aws_sfn_state_machine" "Ec2Scheduler" { + name = "Ec2InstanceScheduler-${random_id.rid.dec}" + role_arn = module.StepFunctionRole.role-arn + type = "STANDARD" + + + logging_configuration { + log_destination = "${aws_cloudwatch_log_group.Ec2Scheduler.arn}:*" + include_execution_data = true + level = "ALL" + } + + definition = jsonencode({ + "StartAt" : "DetermineAction", + "States" : { + "DetermineAction" : { + "Type" : "Choice", + "Choices" : [ + { + "Variable" : "$.action", + "StringEquals" : "START", + "Next" : "StartEC2Instances" + }, + { + "Variable" : "$.action", + "StringEquals" : "STOP", + "Next" : "StopEC2Instances" + } + ], + "Default" : "InvalidAction" + }, + "StartEC2Instances" : { + "Type" : "Task", + "Resource" : "arn:aws:states:::aws-sdk:ec2:startInstances", + "Parameters" : { + "InstanceIds.$" : "$.instanceIds" + }, + "End" : true + }, + "StopEC2Instances" : { + "Type" : "Task", + "Resource" : "arn:aws:states:::aws-sdk:ec2:stopInstances", + "Parameters" : { + "InstanceIds.$" : "$.instanceIds" + }, + "End" : true + }, + "InvalidAction" : { + "Type" : "Fail", + "Error" : "ActionError", + "Cause" : "The action provided must be either START or STOP." + } + } + }) +} + + +resource "aws_scheduler_schedule" "StartInstances" { + name = "start-ec2-${random_id.rid.dec}" + group_name = "default" + description = "Start ec2 instances" + + flexible_time_window { + mode = "OFF" + } + + schedule_expression = var.instance-start-cron-expression + schedule_expression_timezone = var.scheduler-timezone + + target { + arn = aws_sfn_state_machine.Ec2Scheduler.arn + role_arn = module.EventSchedulerRole.role-arn + + input = jsonencode({ + action = "START" + instanceIds = var.instance-ids + }) + + retry_policy { + maximum_retry_attempts = 0 + } + } + +} + +resource "aws_scheduler_schedule" "StopInstances" { + name = "stop-ec2-${random_id.rid.dec}" + group_name = "default" + description = "Stop ec2 instances" + + flexible_time_window { + mode = "OFF" + } + + schedule_expression = var.instance-stop-cron-expression + schedule_expression_timezone = var.scheduler-timezone + + target { + arn = aws_sfn_state_machine.Ec2Scheduler.arn + role_arn = module.EventSchedulerRole.role-arn + + input = jsonencode({ + action = "STOP" + instanceIds = var.instance-ids + }) + + retry_policy { + maximum_retry_attempts = 0 + } + } + +} \ No newline at end of file diff --git a/modules/compute/ec2-instance-scheduler-sfn/variables.tf b/modules/compute/ec2-instance-scheduler-sfn/variables.tf new file mode 100644 index 0000000..d6393ba --- /dev/null +++ b/modules/compute/ec2-instance-scheduler-sfn/variables.tf @@ -0,0 +1,36 @@ +variable "instance-start-cron-expression" { + type = string + description = "Cron expression for instance start schedule" +} + +variable "instance-stop-cron-expression" { + type = string + description = "Cron expression for instance stop schedule" +} + +variable "instance-ids" { + type = list(string) + description = "Instances to be automatically started/stopped on schedule" +} + +# variable "description" { +# type = string +# description = "A description of instances to be started/stopped on schedule" +# } + +variable "cloudwatchlog-retention" { + type = number + description = "Cloudwatch log retention days" + default = 30 +} + +variable "kms-key-id" { + type = string + description = "CMK id for cloudwatchlog encryption" +} + +variable "scheduler-timezone" { + type = string + default = "Asia/Hong_Kong" + description = "Timezone of event scheduler" +} \ No newline at end of file