Last active
September 17, 2025 06:30
-
-
Save nrashok/0338beb5b644ca5b440f2ac4f62b2e4d to your computer and use it in GitHub Desktop.
Revisions
-
nrashok revised this gist
Sep 17, 2025 . 1 changed file with 33 additions and 47 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,19 +1,16 @@ AWSTemplateFormatVersion: '2010-09-09' Description: Automate stopping/starting EC2 and managed EKS NodeGroups with Step Functions + EventBridge + SSM Parameters: InstanceIds: Type: CommaDelimitedList Default: i-07c33ff5a82b668cc,i-047a90a37986a669e ClusterName: Type: String Default: mr-blues-dev NodeGroups: Type: CommaDelimitedList Default: t3-spot-private Region: Type: String Default: ap-south-1 @@ -43,7 +40,6 @@ Resources: - arn:aws:iam::aws:policy/AmazonSSMFullAccess - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy Policies: - PolicyName: EKSCustomNodegroupScaling PolicyDocument: @@ -75,59 +71,57 @@ Resources: def get_ng_name(ng): if ng.startswith("arn:aws:eks:"): # ARN format: arn:aws:eks:region:account:nodegroup/<cluster>/<name>/<uuid> return ng.split("/")[2] # extract <name> return ng def lambda_handler(event, context): action = event.get("Action") cluster_name = event.get("ClusterName", os.environ.get("CLUSTER")) nodegroups = event.get("NodeGroups", []) instances = event.get("InstanceIds", []) start_size = int(event.get("StartSize", os.environ.get("START_SIZE", "2"))) min_size = int(event.get("MinSize", os.environ.get("MIN_SIZE", "1"))) region = event.get("Region", os.environ.get("REGION", "ap-south-1")) ec2 = boto3.client("ec2", region_name=region) eks = boto3.client("eks", region_name=region) # Stop/start EC2 instances if instances: if action == "stop": ec2.stop_instances(InstanceIds=instances) elif action == "start": ec2.start_instances(InstanceIds=instances) # Scale managed nodegroups for ng in nodegroups: ng_name = get_ng_name(ng) if action == "stop": eks.update_nodegroup_config( clusterName=cluster_name, nodegroupName=ng_name, scalingConfig={ "minSize": 0, "desiredSize": 0 } ) elif action == "start": eks.update_nodegroup_config( clusterName=cluster_name, nodegroupName=ng_name, scalingConfig={ "minSize": min_size, "desiredSize": start_size } ) return { "status": "success", "action": action, "instances": instances, "nodegroups": nodegroups } StepFunctionRole: @@ -156,7 +150,7 @@ Resources: DefinitionString: Fn::Sub: | { "Comment": "Stop/Start EC2 and managed EKS NodeGroups", "StartAt": "StopStartAction", "States": { "StopStartAction": { @@ -184,10 +178,9 @@ Resources: Input: | { "Action": "stop", "ClusterName": "mr-blues-dev", "InstanceIds": ["i-07c33ff5a82b668cc","i-047a90a37986a669e"], "NodeGroups": ["t3-spot-private"], "Region": "ap-south-1" } @@ -203,10 +196,9 @@ Resources: Input: | { "Action": "start", "ClusterName": "mr-blues-dev", "InstanceIds": ["i-07c33ff5a82b668cc","i-047a90a37986a669e"], "NodeGroups": ["t3-spot-private"], "Region": "ap-south-1", "StartSize": 2, "MinSize": 1 @@ -218,28 +210,24 @@ Resources: DocumentType: Automation Content: schemaVersion: '0.3' description: "Manually trigger stop/start for EC2 + managed EKS NodeGroups" parameters: Action: type: String description: "Action to perform (stop/start)" allowedValues: ["stop", "start"] InstanceIds: type: StringList description: "List of EC2 instance IDs to start/stop" default: ["i-07c33ff5a82b668cc","i-047a90a37986a669e"] NodeGroups: type: StringList description: "Managed NodeGroup names or ARNs" default: ["t3-spot-private"] ClusterName: type: String description: "EKS Cluster name" default: "mr-blues-dev" StepFunctionArn: type: String description: "Step Function ARN" @@ -260,7 +248,6 @@ Resources: "ClusterName": event["ClusterName"], "InstanceIds": event["InstanceIds"], "NodeGroups": event["NodeGroups"], "MinSize": event.get("MinSize", 1), "StartSize": event.get("StartSize", 2) }) @@ -271,16 +258,15 @@ Resources: ClusterName: "{{ClusterName}}" InstanceIds: "{{InstanceIds}}" NodeGroups: "{{NodeGroups}}" StepFunctionArn: !Ref AutomationStateMachine Outputs: StepFunctionArn: Description: ARN of the Step Function Value: !Ref AutomationStateMachine LambdaName: Description: Lambda function handling EC2/managed EKS actions Value: !Ref StopStartLambda ManualSSMDocumentName: Description: Name of the SSM document for manual trigger Value: !Ref ManualTriggerSSMDocument -
nrashok revised this gist
Sep 17, 2025 . 1 changed file with 47 additions and 33 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,16 +1,19 @@ AWSTemplateFormatVersion: '2010-09-09' Description: Automate stopping/starting EC2, RDS and managed EKS NodeGroups with Step Functions + EventBridge + SSM Parameters: InstanceIds: Type: CommaDelimitedList Default: i-07c33ff5a82b668xx,i-047a90a37986a66xx ClusterName: Type: String Default: ashok-eks-clusterdev NodeGroups: Type: CommaDelimitedList Default: t3-spot-private-ng RDSInstanceIds: Type: CommaDelimitedList Default: mydbinstance1 Region: Type: String Default: ap-south-1 @@ -40,6 +43,7 @@ Resources: - arn:aws:iam::aws:policy/AmazonSSMFullAccess - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy - arn:aws:iam::aws:policy/AmazonRDSFullAccess Policies: - PolicyName: EKSCustomNodegroupScaling PolicyDocument: @@ -71,57 +75,59 @@ Resources: def get_ng_name(ng): if ng.startswith("arn:aws:eks:"): return ng.split("/")[2] return ng def lambda_handler(event, context): action = event.get("Action") cluster_name = event.get("ClusterName", os.environ.get("CLUSTER")) nodegroups = event.get("NodeGroups", []) instances = event.get("InstanceIds", []) rds_instances = event.get("RDSInstanceIds", []) start_size = int(event.get("StartSize", os.environ.get("START_SIZE", "2"))) min_size = int(event.get("MinSize", os.environ.get("MIN_SIZE", "1"))) region = event.get("Region", os.environ.get("REGION", "ap-south-1")) ec2 = boto3.client("ec2", region_name=region) eks = boto3.client("eks", region_name=region) rds = boto3.client("rds", region_name=region) # Stop/start EC2 if instances: if action == "stop": ec2.stop_instances(InstanceIds=instances) elif action == "start": ec2.start_instances(InstanceIds=instances) # Scale Nodegroups for ng in nodegroups: ng_name = get_ng_name(ng) if action == "stop": eks.update_nodegroup_config( clusterName=cluster_name, nodegroupName=ng_name, scalingConfig={"minSize": 0, "desiredSize": 0} ) elif action == "start": eks.update_nodegroup_config( clusterName=cluster_name, nodegroupName=ng_name, scalingConfig={"minSize": min_size, "desiredSize": start_size} ) # Stop/start RDS for rds_id in rds_instances: if action == "stop": rds.stop_db_instance(DBInstanceIdentifier=rds_id) elif action == "start": rds.start_db_instance(DBInstanceIdentifier=rds_id) return { "status": "success", "action": action, "instances": instances, "nodegroups": nodegroups, "rds_instances": rds_instances } StepFunctionRole: @@ -150,7 +156,7 @@ Resources: DefinitionString: Fn::Sub: | { "Comment": "Stop/Start EC2, RDS and managed EKS NodeGroups", "StartAt": "StopStartAction", "States": { "StopStartAction": { @@ -178,9 +184,10 @@ Resources: Input: | { "Action": "stop", "ClusterName": "ashok-eks-clusterdev", "InstanceIds": ["i-07c33ff5a82b668xx","i-047a90a37986a66xx"], "NodeGroups": ["t3-spot-private-ng"], "RDSInstanceIds": ["mydbinstance1"], "Region": "ap-south-1" } @@ -196,9 +203,10 @@ Resources: Input: | { "Action": "start", "ClusterName": "ashok-eks-clusterdev", "InstanceIds": ["i-07c33ff5a82b668xx","i-047a90a37986a66xx"], "NodeGroups": ["t3-spot-private-ng"], "RDSInstanceIds": ["mydbinstance1"], "Region": "ap-south-1", "StartSize": 2, "MinSize": 1 @@ -210,24 +218,28 @@ Resources: DocumentType: Automation Content: schemaVersion: '0.3' description: "Manually trigger stop/start for EC2, RDS and managed EKS NodeGroups" parameters: Action: type: String description: "Action to perform (stop/start)" allowedValues: ["stop", "start"] InstanceIds: type: StringList description: "List of EC2 instance IDs" default: ["i-07c33ff5a82b668xx","i-047a90a37986a66xx"] NodeGroups: type: StringList description: "EKS NodeGroups" default: ["t3-spot-private-ng"] RDSInstanceIds: type: StringList description: "List of RDS DB Instance IDs" default: ["mydbinstance1"] ClusterName: type: String description: "EKS Cluster name" default: "ashok-eks-clusterdev" StepFunctionArn: type: String description: "Step Function ARN" @@ -248,6 +260,7 @@ Resources: "ClusterName": event["ClusterName"], "InstanceIds": event["InstanceIds"], "NodeGroups": event["NodeGroups"], "RDSInstanceIds": event["RDSInstanceIds"], "MinSize": event.get("MinSize", 1), "StartSize": event.get("StartSize", 2) }) @@ -258,15 +271,16 @@ Resources: ClusterName: "{{ClusterName}}" InstanceIds: "{{InstanceIds}}" NodeGroups: "{{NodeGroups}}" RDSInstanceIds: "{{RDSInstanceIds}}" StepFunctionArn: !Ref AutomationStateMachine Outputs: StepFunctionArn: Description: ARN of the Step Function Value: !Ref AutomationStateMachine LambdaName: Description: Lambda function handling EC2/RDS/EKS actions Value: !Ref StopStartLambda ManualSSMDocumentName: Description: Name of the SSM document for manual trigger Value: !Ref ManualTriggerSSMDocument -
nrashok revised this gist
Sep 17, 2025 . 1 changed file with 12 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,13 +4,13 @@ Description: Automate stopping/starting EC2 and managed EKS NodeGroups with Step Parameters: InstanceIds: Type: CommaDelimitedList Default: i-07c33ff5a82b668cc,i-047a90a37986a669e ClusterName: Type: String Default: mr-blues-dev NodeGroups: Type: CommaDelimitedList Default: t3-spot-private Region: Type: String Default: ap-south-1 @@ -178,9 +178,9 @@ Resources: Input: | { "Action": "stop", "ClusterName": "mr-blues-dev", "InstanceIds": ["i-07c33ff5a82b668cc","i-047a90a37986a669e"], "NodeGroups": ["t3-spot-private"], "Region": "ap-south-1" } @@ -196,9 +196,9 @@ Resources: Input: | { "Action": "start", "ClusterName": "mr-blues-dev", "InstanceIds": ["i-07c33ff5a82b668cc","i-047a90a37986a669e"], "NodeGroups": ["t3-spot-private"], "Region": "ap-south-1", "StartSize": 2, "MinSize": 1 @@ -219,15 +219,15 @@ Resources: InstanceIds: type: StringList description: "List of EC2 instance IDs to start/stop" default: ["i-07c33ff5a82b668cc","i-047a90a37986a669e"] NodeGroups: type: StringList description: "Managed NodeGroup names or ARNs" default: ["t3-spot-private"] ClusterName: type: String description: "EKS Cluster name" default: "mr-blues-dev" StepFunctionArn: type: String description: "Step Function ARN" -
nrashok revised this gist
Sep 12, 2025 . 1 changed file with 12 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,13 +4,13 @@ Description: Automate stopping/starting EC2 and managed EKS NodeGroups with Step Parameters: InstanceIds: Type: CommaDelimitedList Default: i-07c33ff5a82b668xx,i-047a90a37986a66xx ClusterName: Type: String Default: ashok-eks-clusterdev NodeGroups: Type: CommaDelimitedList Default: t3-spot-private-ng Region: Type: String Default: ap-south-1 @@ -178,9 +178,9 @@ Resources: Input: | { "Action": "stop", "ClusterName": "ashok-eks-clusterdev", "InstanceIds": ["i-07c33ff5a82b668xx","i-047a90a37986a66xx"], "NodeGroups": ["t3-spot-private-ng"], "Region": "ap-south-1" } @@ -196,9 +196,9 @@ Resources: Input: | { "Action": "start", "ClusterName": "ashok-eks-clusterdev", "InstanceIds": ["i-07c33ff5a82b668xx","i-047a90a37986a66xx"], "NodeGroups": ["t3-spot-private-ng"], "Region": "ap-south-1", "StartSize": 2, "MinSize": 1 @@ -219,15 +219,15 @@ Resources: InstanceIds: type: StringList description: "List of EC2 instance IDs to start/stop" default: ["i-07c33ff5a82b668xx","i-047a90a37986a66xx"] NodeGroups: type: StringList description: "Managed NodeGroup names or ARNs" default: ["t3-spot-private-ng"] ClusterName: type: String description: "EKS Cluster name" default: "ashok-eks-clusterdev" StepFunctionArn: type: String description: "Step Function ARN" -
nrashok revised this gist
Sep 12, 2025 . 1 changed file with 8 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -39,7 +39,7 @@ Resources: - arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess - arn:aws:iam::aws:policy/AmazonSSMFullAccess - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy Policies: - PolicyName: EKSCustomNodegroupScaling PolicyDocument: @@ -69,6 +69,12 @@ Resources: ZipFile: | import boto3, os def get_ng_name(ng): if ng.startswith("arn:aws:eks:"): # ARN format: arn:aws:eks:region:account:nodegroup/<cluster>/<name>/<uuid> return ng.split("/")[2] # extract <name> return ng def lambda_handler(event, context): action = event.get("Action") cluster_name = event.get("ClusterName", os.environ.get("CLUSTER")) @@ -90,7 +96,7 @@ Resources: # Scale managed nodegroups for ng in nodegroups: ng_name = get_ng_name(ng) if action == "stop": eks.update_nodegroup_config( -
nrashok revised this gist
Sep 12, 2025 . 1 changed file with 31 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,6 +17,9 @@ Parameters: StartSize: Type: Number Default: 2 MinSize: Type: Number Default: 1 Resources: LambdaExecutionRole: @@ -33,10 +36,21 @@ Resources: ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/AmazonEC2FullAccess - arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess - arn:aws:iam::aws:policy/AmazonSSMFullAccess - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy - arn:aws:iam::aws:policy/AmazonEKSNodegroupPolicy Policies: - PolicyName: EKSCustomNodegroupScaling PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - eks:UpdateNodegroupConfig - eks:DescribeNodegroup Resource: - !Sub arn:aws:eks:${Region}:${AWS::AccountId}:nodegroup/${ClusterName}/*/* StopStartLambda: Type: AWS::Lambda::Function @@ -50,6 +64,7 @@ Resources: CLUSTER: !Ref ClusterName REGION: !Ref Region START_SIZE: !Ref StartSize MIN_SIZE: !Ref MinSize Code: ZipFile: | import boto3, os @@ -60,6 +75,7 @@ Resources: nodegroups = event.get("NodeGroups", []) instances = event.get("InstanceIds", []) start_size = int(event.get("StartSize", os.environ.get("START_SIZE", "2"))) min_size = int(event.get("MinSize", os.environ.get("MIN_SIZE", "1"))) region = event.get("Region", os.environ.get("REGION", "ap-south-1")) ec2 = boto3.client("ec2", region_name=region) @@ -74,20 +90,25 @@ Resources: # Scale managed nodegroups for ng in nodegroups: ng_name = ng.split("/")[-1] if ng.startswith("arn:aws:eks:") else ng if action == "stop": eks.update_nodegroup_config( clusterName=cluster_name, nodegroupName=ng_name, scalingConfig={ "minSize": 0, "desiredSize": 0 } ) elif action == "start": eks.update_nodegroup_config( clusterName=cluster_name, nodegroupName=ng_name, scalingConfig={ "minSize": min_size, "desiredSize": start_size } ) return { @@ -173,7 +194,8 @@ Resources: "InstanceIds": ["i-07c33ff5a82b668cc","i-047a90a37986a669e"], "NodeGroups": ["t3-spot-private"], "Region": "ap-south-1", "StartSize": 2, "MinSize": 1 } ManualTriggerSSMDocument: @@ -219,7 +241,9 @@ Resources: "Action": event["Action"], "ClusterName": event["ClusterName"], "InstanceIds": event["InstanceIds"], "NodeGroups": event["NodeGroups"], "MinSize": event.get("MinSize", 1), "StartSize": event.get("StartSize", 2) }) ) return response -
nrashok revised this gist
Sep 12, 2025 . No changes.There are no files selected for viewing
-
nrashok revised this gist
Sep 12, 2025 . 1 changed file with 28 additions and 36 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,5 @@ AWSTemplateFormatVersion: '2010-09-09' Description: Automate stopping/starting EC2 and managed EKS NodeGroups with Step Functions + EventBridge + SSM Parameters: InstanceIds: @@ -8,8 +8,8 @@ Parameters: ClusterName: Type: String Default: mr-blues-dev NodeGroups: Type: CommaDelimitedList Default: t3-spot-private Region: Type: String @@ -37,7 +37,6 @@ Resources: - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy - arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess - arn:aws:iam::aws:policy/AmazonSSMFullAccess StopStartLambda: Type: AWS::Lambda::Function @@ -46,19 +45,25 @@ Resources: Handler: index.lambda_handler Role: !GetAtt LambdaExecutionRole.Arn Timeout: 300 Environment: Variables: CLUSTER: !Ref ClusterName REGION: !Ref Region START_SIZE: !Ref StartSize Code: ZipFile: | import boto3, os def lambda_handler(event, context): action = event.get("Action") cluster_name = event.get("ClusterName", os.environ.get("CLUSTER")) nodegroups = event.get("NodeGroups", []) instances = event.get("InstanceIds", []) start_size = int(event.get("StartSize", os.environ.get("START_SIZE", "2"))) region = event.get("Region", os.environ.get("REGION", "ap-south-1")) ec2 = boto3.client("ec2", region_name=region) eks = boto3.client("eks", region_name=region) # Stop/start EC2 instances if instances: @@ -67,35 +72,22 @@ Resources: elif action == "start": ec2.start_instances(InstanceIds=instances) # Scale managed nodegroups for ng in nodegroups: # If ARN, extract name ng_name = ng.split("/")[-1] if ng.startswith("arn:aws:eks:") else ng if action == "stop": eks.update_nodegroup_config( clusterName=cluster_name, nodegroupName=ng_name, scalingConfig={"desiredSize": 0} ) elif action == "start": eks.update_nodegroup_config( clusterName=cluster_name, nodegroupName=ng_name, scalingConfig={"desiredSize": start_size} ) return { @@ -131,7 +123,7 @@ Resources: DefinitionString: Fn::Sub: | { "Comment": "Stop/Start EC2 and managed EKS NodeGroups", "StartAt": "StopStartAction", "States": { "StopStartAction": { @@ -190,7 +182,7 @@ Resources: DocumentType: Automation Content: schemaVersion: '0.3' description: "Manually trigger stop/start for EC2 + managed EKS NodeGroups" parameters: Action: type: String @@ -202,7 +194,7 @@ Resources: default: ["i-07c33ff5a82b668cc","i-047a90a37986a669e"] NodeGroups: type: StringList description: "Managed NodeGroup names or ARNs" default: ["t3-spot-private"] ClusterName: type: String @@ -243,7 +235,7 @@ Outputs: Description: ARN of the Step Function Value: !Ref AutomationStateMachine LambdaName: Description: Lambda function handling EC2/managed EKS actions Value: !Ref StopStartLambda ManualSSMDocumentName: Description: Name of the SSM document for manual trigger -
nrashok revised this gist
Sep 12, 2025 . No changes.There are no files selected for viewing
-
nrashok revised this gist
Sep 12, 2025 . 1 changed file with 94 additions and 62 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,5 @@ AWSTemplateFormatVersion: '2010-09-09' Description: Automate stopping EC2 and self-managed EKS NodeGroups with Step Functions + EventBridge + SSM Parameters: InstanceIds: @@ -14,6 +14,9 @@ Parameters: Region: Type: String Default: ap-south-1 StartSize: Type: Number Default: 2 Resources: LambdaExecutionRole: @@ -32,10 +35,9 @@ Resources: - arn:aws:iam::aws:policy/AmazonEC2FullAccess - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy - arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess - arn:aws:iam::aws:policy/AmazonSSMFullAccess - arn:aws:iam::aws:policy/AutoScalingFullAccess StopStartLambda: Type: AWS::Lambda::Function @@ -46,35 +48,62 @@ Resources: Timeout: 300 Code: ZipFile: | import boto3, os def lambda_handler(event, context): action = event.get("Action") instances = event.get("InstanceIds", []) nodegroups = event.get("NodeGroups", []) start_size = int(event.get("StartSize", os.environ.get("START_SIZE", "2"))) region = event.get("Region", os.environ.get("REGION", "ap-south-1")) ec2 = boto3.client('ec2', region_name=region) asg_client = boto3.client('autoscaling', region_name=region) # Stop/start EC2 instances if instances: if action == "stop": ec2.stop_instances(InstanceIds=instances) elif action == "start": ec2.start_instances(InstanceIds=instances) # Scale self-managed nodegroups via ASG for ng_name in nodegroups: # Auto-discover the ASG associated with nodegroup response = asg_client.describe_auto_scaling_groups() asg_name = None for asg in response['AutoScalingGroups']: for tag in asg.get('Tags', []): if tag['Key'] == 'eks:nodegroup-name' and tag['Value'] == ng_name: asg_name = asg['AutoScalingGroupName'] break if asg_name: break if not asg_name: raise Exception(f"ASG for nodegroup {ng_name} not found in region {region}") if action == "stop": asg_client.update_auto_scaling_group( AutoScalingGroupName=asg_name, MinSize=0, MaxSize=0, DesiredCapacity=0 ) elif action == "start": asg_client.update_auto_scaling_group( AutoScalingGroupName=asg_name, MinSize=start_size, MaxSize=start_size, DesiredCapacity=start_size ) return { "status": "success", "action": action, "instances": instances, "nodegroups": nodegroups } StepFunctionRole: Type: AWS::IAM::Role @@ -102,7 +131,7 @@ Resources: DefinitionString: Fn::Sub: | { "Comment": "Stop/Start EC2 and self-managed EKS NodeGroups", "StartAt": "StopStartAction", "States": { "StopStartAction": { @@ -118,13 +147,50 @@ Resources: } } StopScheduleRule: Type: AWS::Events::Rule Properties: ScheduleExpression: cron(0 16 * * ? *) # 9:30 PM IST State: ENABLED Targets: - Arn: !Ref AutomationStateMachine Id: StopTarget RoleArn: !GetAtt StepFunctionRole.Arn Input: | { "Action": "stop", "ClusterName": "mr-blues-dev", "InstanceIds": ["i-07c33ff5a82b668cc","i-047a90a37986a669e"], "NodeGroups": ["t3-spot-private"], "Region": "ap-south-1" } StartScheduleRule: Type: AWS::Events::Rule Properties: ScheduleExpression: cron(0 2 * * ? *) # 7:30 AM IST State: ENABLED Targets: - Arn: !Ref AutomationStateMachine Id: StartTarget RoleArn: !GetAtt StepFunctionRole.Arn Input: | { "Action": "start", "ClusterName": "mr-blues-dev", "InstanceIds": ["i-07c33ff5a82b668cc","i-047a90a37986a669e"], "NodeGroups": ["t3-spot-private"], "Region": "ap-south-1", "StartSize": 2 } ManualTriggerSSMDocument: Type: AWS::SSM::Document Properties: DocumentType: Automation Content: schemaVersion: '0.3' description: "Manually trigger stop/start for EC2 + self-managed EKS NodeGroups" parameters: Action: type: String @@ -136,7 +202,7 @@ Resources: default: ["i-07c33ff5a82b668cc","i-047a90a37986a669e"] NodeGroups: type: StringList description: "Self-managed NodeGroup names (auto-discovered ASG)" default: ["t3-spot-private"] ClusterName: type: String @@ -172,46 +238,12 @@ Resources: NodeGroups: "{{NodeGroups}}" StepFunctionArn: !Ref AutomationStateMachine Outputs: StepFunctionArn: Description: ARN of the Step Function Value: !Ref AutomationStateMachine LambdaName: Description: Lambda function handling EC2/self-managed EKS actions Value: !Ref StopStartLambda ManualSSMDocumentName: Description: Name of the SSM document for manual trigger -
nrashok revised this gist
Sep 12, 2025 . 1 changed file with 22 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -76,10 +76,29 @@ Resources: ) return {"status": "success", "action": action} StepFunctionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: states.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: InvokeLambdaPolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: lambda:InvokeFunction Resource: !GetAtt StopStartLambda.Arn AutomationStateMachine: Type: AWS::StepFunctions::StateMachine Properties: RoleArn: !GetAtt StepFunctionRole.Arn DefinitionString: Fn::Sub: | { @@ -161,7 +180,7 @@ Resources: Targets: - Arn: !Ref AutomationStateMachine Id: StopTarget RoleArn: !GetAtt StepFunctionRole.Arn Input: | { "Action": "stop", @@ -178,7 +197,7 @@ Resources: Targets: - Arn: !Ref AutomationStateMachine Id: StartTarget RoleArn: !GetAtt StepFunctionRole.Arn Input: | { "Action": "start", -
nrashok created this gist
Sep 12, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,199 @@ AWSTemplateFormatVersion: '2010-09-09' Description: Automate stopping EC2 instances and scaling EKS NodeGroup using Lambda, Step Functions, SSM, and EventBridge Parameters: InstanceIds: Type: CommaDelimitedList Default: i-07c33ff5a82b668cc,i-047a90a37986a669e ClusterName: Type: String Default: mr-blues-dev NodeGroupName: Type: String Default: t3-spot-private Region: Type: String Default: ap-south-1 Resources: LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/AmazonEC2FullAccess - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy - arn:aws:iam::aws:policy/AmazonEKSServicePolicy - arn:aws:iam::aws:policy/AmazonSSMFullAccess - arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess StopStartLambda: Type: AWS::Lambda::Function Properties: Runtime: python3.9 Handler: index.lambda_handler Role: !GetAtt LambdaExecutionRole.Arn Timeout: 300 Code: ZipFile: | import boto3 def lambda_handler(event, context): action = event.get("Action") instances = event.get("InstanceIds", []) cluster = event.get("ClusterName") nodegroups = event.get("NodeGroups", []) ec2 = boto3.client('ec2') eks = boto3.client('eks') if action == "stop": if instances: ec2.stop_instances(InstanceIds=instances) for ng in nodegroups: eks.update_nodegroup_config( clusterName=cluster, nodegroupName=ng, scalingConfig={"minSize": 0, "maxSize": 0, "desiredSize": 0} ) elif action == "start": if instances: ec2.start_instances(InstanceIds=instances) for ng in nodegroups: eks.update_nodegroup_config( clusterName=cluster, nodegroupName=ng, scalingConfig={"minSize": 1, "maxSize": 3, "desiredSize": 1} ) return {"status": "success", "action": action} AutomationStateMachine: Type: AWS::StepFunctions::StateMachine Properties: RoleArn: !GetAtt LambdaExecutionRole.Arn DefinitionString: Fn::Sub: | { "Comment": "Stop/Start EC2 and EKS NodeGroup", "StartAt": "StopStartAction", "States": { "StopStartAction": { "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "OutputPath": "$.Payload", "Parameters": { "FunctionName": "${StopStartLambda}", "Payload.$": "$" }, "End": true } } } ManualTriggerSSMDocument: Type: AWS::SSM::Document Properties: DocumentType: Automation Content: schemaVersion: '0.3' description: "Manually trigger stop/start for EC2 + EKS NodeGroups" parameters: Action: type: String description: "Action to perform (stop/start)" allowedValues: ["stop", "start"] InstanceIds: type: StringList description: "List of EC2 instance IDs to start/stop" default: ["i-07c33ff5a82b668cc","i-047a90a37986a669e"] NodeGroups: type: StringList description: "EKS NodeGroup names to scale" default: ["t3-spot-private"] ClusterName: type: String description: "EKS Cluster name" default: "mr-blues-dev" StepFunctionArn: type: String description: "Step Function ARN" mainSteps: - name: InvokeStepFunction action: aws:executeScript inputs: Runtime: python3.8 Handler: handler Script: | import boto3, json def handler(event, context): sfn = boto3.client("stepfunctions") response = sfn.start_execution( stateMachineArn=event["StepFunctionArn"], input=json.dumps({ "Action": event["Action"], "ClusterName": event["ClusterName"], "InstanceIds": event["InstanceIds"], "NodeGroups": event["NodeGroups"] }) ) return response InputPayload: Action: "{{Action}}" ClusterName: "{{ClusterName}}" InstanceIds: "{{InstanceIds}}" NodeGroups: "{{NodeGroups}}" StepFunctionArn: !Ref AutomationStateMachine StopScheduleRule: Type: AWS::Events::Rule Properties: ScheduleExpression: cron(0 16 * * ? *) # 9:30 PM IST State: ENABLED Targets: - Arn: !Ref AutomationStateMachine Id: StopTarget RoleArn: !GetAtt LambdaExecutionRole.Arn Input: | { "Action": "stop", "ClusterName": "mr-blues-dev", "InstanceIds": ["i-07c33ff5a82b668cc","i-047a90a37986a669e"], "NodeGroups": ["t3-spot-private"] } StartScheduleRule: Type: AWS::Events::Rule Properties: ScheduleExpression: cron(0 2 * * ? *) # 7:30 AM IST State: ENABLED Targets: - Arn: !Ref AutomationStateMachine Id: StartTarget RoleArn: !GetAtt LambdaExecutionRole.Arn Input: | { "Action": "start", "ClusterName": "mr-blues-dev", "InstanceIds": ["i-07c33ff5a82b668cc","i-047a90a37986a669e"], "NodeGroups": ["t3-spot-private"] } Outputs: StepFunctionArn: Description: ARN of the Step Function Value: !Ref AutomationStateMachine LambdaName: Description: Lambda function handling EC2/EKS actions Value: !Ref StopStartLambda ManualSSMDocumentName: Description: Name of the SSM document for manual trigger Value: !Ref ManualTriggerSSMDocument