AWS doesn’t charge users for a stopped instance. When a stopped instance is started, you are automatically charged for a minute’s worth of usage. After one minute, AWS charges you by the second. So if you run an instance for 30 or 40 seconds, you are still charged for one minute but if you run it for 2 minutes and 15 seconds, you are charged for that exact time.
You can get a lot of benefits from deciding to automatically stop and start your EC2 instances.
- Choose what period you want instances to run
- Reduce costs
- Dictate which instances will be worked on by employees and when
You can make changes to the volume of the instance when it is in a stopped state. You have the potential to treat the root volume of your instance the same as any other volume. You can detach it and add it to another machine, modify it, etc.
The difference between a stopped instance and a terminated instance is that once you stop it, you can still be charged for storage but if you terminate it, you are freed of all charges.
You can modify the following attributes of an instance only when it is stopped:
- Instance type
- RAM
- Kernel
- User data
Modifying these attributes while the instance is running gives the IncorrectInstanceState error.
What happens when you stop an Instance?
- The instance will perform a shutdown and stop running.
- Amazon EBS volumes attached to the instance remain and their data persists.
- If there is data stored in the RAM of the host computer or the instance storage volumes, they will be gone.
- The private IPv4 and IPv6 addresses are retained when an instance is restarted.
- Elastic IP addresses are also retained. AWS charges you for the Elastic IP addresses of an instance. This doesn’t apply for EC2-Classic, Elastic IP addresses are removed when they are stopped.
How to Stop and Start your EC2 Instance
The AWS Instance Scheduler Method
How to set up the AWS Instance Scheduler
The Instance Scheduler will give you the option to shut down, start or restart the specifically tagged instances according to the time you deem appropriate.
To set up the Instance Scheduler, we will first install the stack by provisioning all the resources onto AWS CloudFormation.
- Open AWS Cloudformation. You can choose whichever region as Cloudformation allows cross-region configuration.
- Specify the template
- Provide a name for the stack
- Enter the tag Key that will be used on Instances that you want to schedule. The Value will be the name of the Schedule set in the DynamoDB table.
- Enter the regions of the machines
- Set the timezone
- Set an adequate time for the SchedulerFrequency. I recommend 30 mins.
Leave all the other options as it is and proceed.
Wait for the resources to be provisioned. You will see a success message when it is done.
With this process, you will have three resources set up.
- AWS Lambda functions
- DynamoDB tables
- Cloudwatch Events
Setting up the shutdown tag
Now we’ll define the shutdown period. You can make the necessary configurations on DynamoDB.
- Open DynamoDB
- Open the table named ConfigTable
- Delete the rows named period and schedule and leave the config row as is.
-- CODE language-js line-numbers --
{
"description": {
"S": "Shut-down period. Daily (end of the day)."
},
"endtime": {
"S": "20:00"
},
"starttime": {
"S": "9:00"
},
"name": {
"S": "office-hours"
},
"type": {
"S": "period"
},
"weekdays": {
"SS": [
"Mon-sun"
]
}
}
This defines the period to match the time we need the instances to shutdown.
-- CODE language-js line-numbers --
{
"description": {
"S": "Office hours in Canada (AST)"
},
"enforced": {
"S": "false"
},
"name": {
"S": "ca-autostop"
},
"periods": {
"SS": [
"office-hours"
]
},
"retain_running": {
"S": "false"
},
"stop_new_instances": {
"S": "true"
},
"timezone": {
"S": "Canada/Atlantic"
},
"type": {
"S": "schedule"
}
}
This defines the schedule which will be used to match the instances according to tags. The tag used here is “ca-autostop”. The Schedule also contains information about the timezones so instances from other regions operate under the same scheduling hours.
Now, any instance with the tag you have defined will be manipulated by the Instance Scheduler.
Downsides of AWS Instance Scheduler
- You have to provision multiple resources to set up an instance scheduler
- The management overhead for all the resources will make troubleshooting complicated
- You have to pay for each of the service you make use of
- You can only make configurations by updating the CloudFormation stack
AWS Tools for Windows Powershell
You can schedule your Instances through a PowerShell class as well. You will need to use the AWS Tools for Windows Powershell.
You can install the AWS Power Shell module from the PowerShell Gallery.
-- CODE language-js line-numbers --
Install-Module -Name AWSPowerShell
Create a class named AWSInstance. This class will allow you to start and stop your machines.
Declare the class
-- CODE language-js line-numbers --
Class AwsInstance {}
Next, we define all the properties we need to manipulate the instance.
-- CODE language-js line-numbers --
[String] $TagName
[String] $InstanceId
[String] $Status
Hidden [Object] $EC2Instance
The hidden property is used to identify the right instance. The value that is passed to this class finds the Instance Tag or the Instance ID.
-- CODE language-js line-numbers --
AwsInstance([String]$name) {
if ($name -match '^i-[a-f0-9]+$') {
$this.InstanceId = $name
$this.EC2Instance = $this.getInstanceFromInstanceId($name)
$this.TagName = ($this.EC2Instance.RunningInstance.Tag).Where({ $_.key -eq 'Name' }).Value
}
else {
$this.EC2Instance = $this.getInstanceFromTagName($name)
$this.TagName = $name
$this.InstanceId = $this.EC2Instance.RunningInstance.InstanceId
}
$this.GetStatus()
The AWSinstance constructor takes either the instanceid or the tagname and compares it with the machines using regexp in an if condition.
-- CODE language-js line-numbers --
[Amazon.EC2.Model.Reservation] getInstanceFromTagName([String]$name) {
$Filter1 = [Amazon.EC2.Model.Filter]::new("key", ('Name', 'name'))
$Filter2 = [Amazon.EC2.Model.Filter]::new('value', $name)
$ResourceId = (Get-EC2Tag -Filter @($filter1, $filter2)).Where({$_.ResourceType -eq 'instance'})
return Get-EC2Instance -InstanceId $ResourceId.ResourceId
}
[Amazon.EC2.Model.Reservation] getInstanceFromInstanceId([String]$name) {
return Get-EC2Instance -InstanceId $name
}
[Void] Start() {
try {
$this.EC2Instance | Start-EC2Instance
$this.GetStatus()
}
catch {
$this.Status = $_.Exception.Message
}
}
[Void] Restart() {
try {
$this.EC2Instance | Restart-EC2Instance
$this.GetStatus()
}
catch {
$this.Status = $_.Exception.Message
}
}
[Void] Stop() {
try {
$this.EC2Instance | Stop-EC2Instance
$this.GetStatus()
}
catch {
$this.Status = $_.Exception.Message
}
}
[Void] GetStatus() {
try {
$this.Status = ($this.EC2Instance | Get-EC2InstanceStatus).InstanceState.Name.Value
}
catch {
$this.Status = $_.Exception.Message
}
}
Hidden [Void] getInstance() {
$this.EC2Instance = Get-EC2Instance -InstanceId $this.InstanceId
}
static [String] Start([String]$name) {
$instance = [AwsInstance]::new($name)
$instance.Start()
return "Status: " + $instance.Status
}
static [String] Stop([String]$name) {
$instance = [AwsInstance]::new($name)
$instance.Stop()
return "Status: " + $(if (-not $instance.Status) { "Stopped" })
}
static [String] Restart([String]$name) {
$instance = [AwsInstance]::new($name)
$instance.Restart()
return "Status: " + $instance.Status
}
static [AwsInstance] GetStatus([String]$name) {
$instance = [AwsInstance]::new($name)
$instance.GetStatus()
return $instance
}
}
Each static method creates an instance of the class:
$instance = [AwsInstance]::new($name)
Each static method creates an instance of the class:
-- CODE language-js line-numbers --
$instance = [AwsInstance]::new($name)
Once the static method has created an instance of the machine, it can call methods to change the running state.
You can also get the status of a machine using this command:
-- CODE language-js line-numbers --
[AwsInstance]::GetStatus('Server2016Test')
By changing the status method as shown below, you can start, stop or restart the instances.
-- CODE language-js line-numbers --
AwsInstance]::Start(‘Server2016Test’)
[AwsInstance]::Stop(‘Server2016Test’)
[AwsInstance]::Restart(‘Server2016Test’)
Terraform
This is an open-source tool run on Terraform. You can find it here.
To run this you need to install kitchen-terraform and awspec.
gem install bundler
bundle install
This will install the dependencies.
Launch the Kitchen tests.
-- CODE language-js line-numbers --
# List all tests with kitchen
kitchen list
# Build, and tests terraform module
kitchen test lambda-scheduler-aws
# for development, create environment
kitchen converge lambda-scheduler-aws
# Apply awspec tests
kitchen verify lambda-scheduler-aws
Start and Stop EC2 Instance
-- CODE language-js line-numbers --
module "stop_ec2_instance" {
source = "diodonfrost/lambda-scheduler-stop-start/aws"
name = "ec2_stop"
cloudwatch_schedule_expression = "cron(0 00 ? * FRI *)" schedule_action = "stop"
ec2_schedule = "true"
rds_schedule = "false"
autoscaling_schedule = "false"
resources_tag = {
key = "tostop"
value = "true"
}
}
Change the values according to your choice to initiate the schedule.
Totalcloud
The Totalcloud tool allows you to perform these operations in an intuitive and user-friendly platform. For this, you will need to log in to the platform here.
- Once you are on the platform, choose Schedules.
- Click Add Schedules
- Choose EC2 scheduling template
- Choose your scheduling period
- Filter out the instances based on tagname or instanceid
- You can also choose to schedule based on activity or other metrics, this can be determined in the last field named, ‘Smart Schedule’.
- Save and Deploy the schedule.
The Next step
Utilise the right start and stop method with whatever tools you deem most suitable. Once you are confident in your scheduling method, the next step is to make sure you are following the best scheduling practices to spend the least amount of money on your resources. Hope you can cut down your expenses and manage your instances with the techniques provided in this guide.