├── provider.tf ├── versions.tf ├── lambda_code ├── screenshots │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── flow_architecture.jpg ├── stop │ └── lambda_function.py └── start │ └── lambda_function.py ├── terraform.tfvars ├── Fetch.tf ├── variables.tf ├── SG.tf ├── EC2.tf ├── IAM.tf ├── Lambda_Stop.tf ├── Lambda_Start.tf └── README.md /provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.aws_region 3 | } 4 | 5 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.13" 3 | 4 | required_providers { 5 | aws = ">= 3.36" 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /lambda_code/screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yousafkhamza/AWS-Lambda-Cost-Optimization-with-Terraform/HEAD/lambda_code/screenshots/1.png -------------------------------------------------------------------------------- /lambda_code/screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yousafkhamza/AWS-Lambda-Cost-Optimization-with-Terraform/HEAD/lambda_code/screenshots/2.png -------------------------------------------------------------------------------- /lambda_code/screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yousafkhamza/AWS-Lambda-Cost-Optimization-with-Terraform/HEAD/lambda_code/screenshots/3.png -------------------------------------------------------------------------------- /lambda_code/screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yousafkhamza/AWS-Lambda-Cost-Optimization-with-Terraform/HEAD/lambda_code/screenshots/4.png -------------------------------------------------------------------------------- /lambda_code/screenshots/flow_architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yousafkhamza/AWS-Lambda-Cost-Optimization-with-Terraform/HEAD/lambda_code/screenshots/flow_architecture.jpg -------------------------------------------------------------------------------- /lambda_code/stop/lambda_function.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | 4 | REGION = os.environ['REGION'] 5 | TAG = os.environ['TAG'] 6 | 7 | def lambda_handler(event, context): 8 | ec2 = boto3.client('ec2',region_name=REGION) 9 | all_ec2 = ec2.describe_instances( 10 | Filters=[ 11 | {'Name':'tag:Name', 'Values':[TAG]} 12 | ]) 13 | 14 | for instance in all_ec2['Reservations'][0]['Instances']: 15 | print("Stopping Ec2 : {} ".format( instance['InstanceId'] )) 16 | ec2.stop_instances(InstanceIds=[ instance['InstanceId'] ]) -------------------------------------------------------------------------------- /lambda_code/start/lambda_function.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | 4 | REGION = os.environ['REGION'] 5 | TAG = os.environ['TAG'] 6 | 7 | def lambda_handler(event, context): 8 | ec2 = boto3.client('ec2',region_name=REGION) 9 | all_ec2 = ec2.describe_instances( 10 | Filters=[ 11 | {'Name':'tag:Name', 'Values':[TAG]} 12 | ]) 13 | 14 | for instance in all_ec2['Reservations'][0]['Instances']: 15 | print("Starting Ec2 : {} ".format( instance['InstanceId'] )) 16 | ec2.start_instances(InstanceIds=[ instance['InstanceId'] ]) -------------------------------------------------------------------------------- /terraform.tfvars: -------------------------------------------------------------------------------- 1 | aws_region = "ap-south-1" # mention which region you need here. 2 | type = "t2.micro" # mention which type of instance would you need and mention here. 3 | vol_size = "10" # EC2 instance volume size mention here. 4 | start_cron = "cron(0 8 * * ? *)" # Which time to start that EC2 instance here. 5 | stop_cron = "cron(0 16 * * ? *)" # Which time to stop that EC2 instance here. 6 | ec2_tag = "python-terraform" # Tag for EC2 instance here and this tag is using to stop that EC2 instance so please be uniq 7 | password_for_ec2 = "T36r@f06m@YKH" # Strong password mentioned here for EC2 because we didn't use key pair if you need please login to the EC2 and do it manually 8 | -------------------------------------------------------------------------------- /Fetch.tf: -------------------------------------------------------------------------------- 1 | # Fetch Account ID for IAM. 2 | data "aws_caller_identity" "current" { 3 | } 4 | 5 | # Fetch Default VPC 6 | data "aws_vpc" "default" { 7 | default = true 8 | } 9 | 10 | # AZ Fetching 11 | data "aws_availability_zones" "available" { 12 | state = "available" 13 | } 14 | 15 | # Subnet Fetching 16 | data "aws_subnets" "my_vpc" { 17 | filter { 18 | name = "vpc-id" 19 | values = [ data.aws_vpc.default.id ] 20 | } 21 | } 22 | 23 | 24 | #------------------------------------------------------ 25 | # Amazon linux AMI choosing through data resource 26 | #------------------------------------------------------ 27 | data "aws_ami" "linux" { 28 | most_recent = true 29 | 30 | filter { 31 | name = "owner-alias" 32 | values = ["amazon"] 33 | } 34 | 35 | filter { 36 | name = "name" 37 | values = ["amzn2-ami-hvm*"] 38 | } 39 | owners = ["137112412989"] # Canonical 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | type = string 3 | description = "Which region do you used" 4 | default = "" 5 | } 6 | 7 | variable "type" { 8 | type = string 9 | description = "Which instance type do you need" 10 | default = "t2.micro" 11 | } 12 | 13 | variable "vol_size" { 14 | type = string 15 | description = "Instance volume size do you need" 16 | default = "8" 17 | } 18 | 19 | variable "start_cron" { 20 | type = string 21 | description = "Cron time start the instance which we created" 22 | default = "cron(0 9 * * ? *)" 23 | } 24 | 25 | variable "stop_cron" { 26 | type = string 27 | description = "Cron time stop the instance which we created" 28 | default = "cron(0 17 * * ? *)" 29 | } 30 | 31 | variable "ec2_tag" { 32 | type = string 33 | description = "TAG for ec2 and this tag to take stop start this instance" 34 | default = "python-terraform" 35 | } 36 | 37 | variable "password_for_ec2" { 38 | type = string 39 | description = "Password for EC2 Server" 40 | default = "T36r@f06m@YKH" 41 | } 42 | 43 | locals { 44 | start_lambda_function = "start-lambda-function" 45 | stop_lambda_function = "stop-lambda-function" 46 | runtime_lambda_function = "python3.9" 47 | } 48 | -------------------------------------------------------------------------------- /SG.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------- 2 | # Security Group 3 | # ------------------------------------- 4 | 5 | resource "aws_security_group" "sg_for_ec2" { 6 | name = "sgec2" 7 | description = "Allow 80,443,22" 8 | 9 | ingress { 10 | description = "HTTPS" 11 | from_port = 80 12 | to_port = 80 13 | protocol = "tcp" 14 | cidr_blocks = ["0.0.0.0/0"] 15 | ipv6_cidr_blocks = ["::/0"] 16 | } 17 | ingress { 18 | description = "HTTPS" 19 | from_port = 443 20 | to_port = 443 21 | protocol = "tcp" 22 | cidr_blocks = ["0.0.0.0/0"] 23 | ipv6_cidr_blocks = ["::/0"] 24 | } 25 | ingress { 26 | description = "SSH" 27 | from_port = 22 28 | to_port = 22 29 | protocol = "tcp" 30 | cidr_blocks = ["0.0.0.0/0"] 31 | ipv6_cidr_blocks = ["::/0"] 32 | } 33 | 34 | egress { 35 | from_port = 0 36 | to_port = 0 37 | protocol = "-1" 38 | cidr_blocks = ["0.0.0.0/0"] 39 | ipv6_cidr_blocks = ["::/0"] 40 | } 41 | 42 | tags = { 43 | Name = "sg-for-stop-start-ec2" 44 | } 45 | lifecycle { 46 | create_before_destroy = true 47 | } 48 | } -------------------------------------------------------------------------------- /EC2.tf: -------------------------------------------------------------------------------- 1 | #----------------------- 2 | # EC2 Instance 3 | #----------------------- 4 | 5 | resource "aws_instance" "ec2_instance" { 6 | ami = data.aws_ami.linux.id 7 | instance_type = var.type 8 | associate_public_ip_address = true 9 | availability_zone = data.aws_availability_zones.available.names[0] 10 | # key_name = var.key #we are using password through userdata once you logged in please change on be half of you! 11 | subnet_id = tolist(data.aws_subnets.my_vpc.ids)[0] 12 | vpc_security_group_ids = [ aws_security_group.sg_for_ec2.id ] 13 | user_data = <> /etc/ssh/sshd_config 17 | echo "LANG=en_US.utf-8" >> /etc/environment 18 | echo "LC_ALL=en_US.utf-8" >> /etc/environment 19 | 20 | echo "${var.password_for_ec2}" | passwd root --stdin 21 | sed -i 's/#PermitRootLogin yes/PermitRootLogin yes/' /etc/ssh/sshd_config 22 | sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config 23 | service sshd restart 24 | EOF 25 | tags = { 26 | Name = var.ec2_tag 27 | } 28 | 29 | root_block_device { 30 | volume_type = "gp2" 31 | volume_size = var.vol_size 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /IAM.tf: -------------------------------------------------------------------------------- 1 | # ---------------------------------- 2 | # IAM Role for Lambda Function 3 | # ---------------------------------- 4 | # Assume Role - For Lambda 5 | data "aws_iam_policy_document" "lambda_assume_role" { 6 | statement { 7 | effect = "Allow" 8 | actions = [ 9 | "sts:AssumeRole"] 10 | principals { 11 | type = "Service" 12 | identifiers = [ 13 | "lambda.amazonaws.com"] 14 | } 15 | } 16 | } 17 | 18 | # Inline policy for ec2 stop start for lambda 19 | data "aws_iam_policy_document" "ec2_stop_start_inline_policy" { 20 | statement { 21 | actions = [ 22 | "ec2:StartInstances", 23 | "ec2:StopInstances", 24 | ] 25 | resources = [ 26 | "arn:aws:ec2:${var.aws_region}:${data.aws_caller_identity.current.account_id}:instance/*", 27 | ] 28 | } 29 | } 30 | 31 | data "aws_iam_policy_document" "ec2_describe_inline_policy" { 32 | statement { 33 | actions = [ 34 | "ec2:DescribeInstances" 35 | ] 36 | resources = [ 37 | "*" 38 | ] 39 | } 40 | } 41 | 42 | # Role for Lambda and both assume and inline integrated 43 | resource "aws_iam_role" "lambda_iam_role_terraform" { 44 | name = "Lambda-IAM-Role-For-EC2-Stop-Start" 45 | path = "/lambda/" 46 | assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json 47 | 48 | description = "IAM role for lambda to stop start that instance which we created" 49 | 50 | inline_policy { 51 | name = "EC2-Stop-Start-Inline-Policy" 52 | policy = data.aws_iam_policy_document.ec2_stop_start_inline_policy.json 53 | } 54 | inline_policy { 55 | name = "EC2-Describe-Inline-Policy" 56 | policy = data.aws_iam_policy_document.ec2_describe_inline_policy.json 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Lambda_Stop.tf: -------------------------------------------------------------------------------- 1 | # ---------------------------------- 2 | # Lambda_Function 3 | # ---------------------------------- 4 | #archiving py file to zip 5 | data "archive_file" "stop_lambda_zip" { 6 | type = "zip" 7 | source_dir = "./lambda_code/stop/" 8 | output_path = "tmp/${local.stop_lambda_function}.zip" 9 | } 10 | 11 | resource "aws_lambda_function" "ec2_stop_lambda_function" { 12 | filename = data.archive_file.stop_lambda_zip.output_path 13 | function_name = "ec2-stop-lambda-function" 14 | role = aws_iam_role.lambda_iam_role_terraform.arn 15 | description = "This lambda using to stop the ec2 instance which we mentioned" 16 | handler = "lambda_function.lambda_handler" 17 | runtime = local.runtime_lambda_function 18 | timeout = 60 19 | memory_size = 128 20 | 21 | environment { 22 | variables = { 23 | REGION = var.aws_region 24 | TAG = var.ec2_tag 25 | } 26 | } 27 | 28 | tags = tomap({"Name" = "ec2 stop lambda function"}) 29 | } 30 | 31 | 32 | #----------------------- 33 | # CloudWatch Trigger to stop ec2 instance 34 | #----------------------- 35 | resource "aws_cloudwatch_event_rule" "trigger_to_stop_ec2_instance" { 36 | name = "Trigger-stop-ec2-instance-lambda" 37 | description = "Trigger that moving data lambda" 38 | schedule_expression = var.stop_cron 39 | tags = tomap({"Name" = "ec2 stop cloudwatch trigger"}) 40 | 41 | depends_on = [aws_lambda_function.ec2_stop_lambda_function] 42 | } 43 | 44 | resource "aws_cloudwatch_event_target" "send_to_stop_lambda_target" { 45 | rule = aws_cloudwatch_event_rule.trigger_to_stop_ec2_instance.name 46 | target_id = "SendToLambda" 47 | arn = aws_lambda_function.ec2_stop_lambda_function.arn 48 | 49 | depends_on = [aws_lambda_function.ec2_stop_lambda_function] 50 | } 51 | 52 | resource "aws_lambda_permission" "allow_cloudwatch_to_call_stop_lambda" { 53 | statement_id = "AllowExecutionFromCloudWatch" 54 | action = "lambda:InvokeFunction" 55 | function_name = aws_lambda_function.ec2_stop_lambda_function.function_name 56 | principal = "events.amazonaws.com" 57 | source_arn = aws_cloudwatch_event_rule.trigger_to_stop_ec2_instance.arn 58 | 59 | depends_on = [aws_lambda_function.ec2_stop_lambda_function,aws_cloudwatch_event_rule.trigger_to_stop_ec2_instance] 60 | } 61 | -------------------------------------------------------------------------------- /Lambda_Start.tf: -------------------------------------------------------------------------------- 1 | # ---------------------------------- 2 | # Lambda_Function 3 | # ---------------------------------- 4 | #archiving py file to zip 5 | data "archive_file" "start_lambda_zip" { 6 | type = "zip" 7 | source_dir = "./lambda_code/start/" 8 | output_path = "tmp/${local.start_lambda_function}.zip" 9 | } 10 | 11 | resource "aws_lambda_function" "ec2_start_lambda_function" { 12 | filename = data.archive_file.start_lambda_zip.output_path 13 | function_name = "ec2-start-lambda-function" 14 | role = aws_iam_role.lambda_iam_role_terraform.arn 15 | description = "This lambda using to start the ec2 instance which we mentioned" 16 | handler = "lambda_function.lambda_handler" 17 | runtime = local.runtime_lambda_function 18 | timeout = 60 19 | memory_size = 128 20 | 21 | environment { 22 | variables = { 23 | REGION = var.aws_region 24 | TAG = var.ec2_tag 25 | } 26 | } 27 | 28 | tags = tomap({"Name" = "ec2 start lambda function"}) 29 | } 30 | 31 | 32 | #----------------------- 33 | # CloudWatch Trigger to start ec2 instance 34 | #----------------------- 35 | resource "aws_cloudwatch_event_rule" "trigger_to_start_ec2_instance" { 36 | name = "Trigger-start-ec2-instance-lambda" 37 | description = "Trigger that moving data lambda" 38 | schedule_expression = var.start_cron 39 | tags = tomap({"Name" = "ec2 start cloudwatch trigger"}) 40 | 41 | depends_on = [aws_lambda_function.ec2_start_lambda_function] 42 | } 43 | 44 | resource "aws_cloudwatch_event_target" "send_to_start_lambda_target" { 45 | rule = aws_cloudwatch_event_rule.trigger_to_start_ec2_instance.name 46 | target_id = "SendToLambda" 47 | arn = aws_lambda_function.ec2_start_lambda_function.arn 48 | 49 | depends_on = [aws_lambda_function.ec2_start_lambda_function] 50 | } 51 | 52 | resource "aws_lambda_permission" "allow_cloudwatch_to_call_start_lambda" { 53 | statement_id = "AllowExecutionFromCloudWatch" 54 | action = "lambda:InvokeFunction" 55 | function_name = aws_lambda_function.ec2_start_lambda_function.function_name 56 | principal = "events.amazonaws.com" 57 | source_arn = aws_cloudwatch_event_rule.trigger_to_start_ec2_instance.arn 58 | 59 | depends_on = [aws_lambda_function.ec2_start_lambda_function,aws_cloudwatch_event_rule.trigger_to_start_ec2_instance] 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS-Lambda-Cost-Optimization-with-Terraform 2 | 3 | [![Build](https://travis-ci.org/joemccann/dillinger.svg?branch=master)](https://travis-ci.org/joemccann/dillinger) 4 | 5 | --- 6 | ## Description 7 | This is a terraform script for cost optimization using lambda. So, this script can set up a cron(schedule) to start and stop ec2 servers. So, if we need to work a server like an office time like 9:00 AM to 7:00 PM then we can save our time and effort to do this thing manually. 8 | 9 | ---- 10 | ## Feature 11 | - We can save billing money 12 | - Automated with CloudWatch 13 | - EC2 instance with root password (if you need key based please enable that after creation) 14 | 15 | ---- 16 | ## Architecture 17 | ![](lambda_code/screenshots/flow_architecture.jpg) 18 | 19 | ---- 20 | ## Services Created 21 | 22 | - EC2 23 | - Security Group 24 | - IAM Role (Custom Inline Policies for lambda) 25 | - Lambda Functions 26 | - Cloudwatch Event Rule Trigger 27 | 28 | ---- 29 | ## Pre-Requests 30 | - Terraform 31 | - Git 32 | 33 | #### Terraform Installation 34 | [Terraform Installation from official](https://www.terraform.io/downloads) 35 | 36 | _Terrafom Installation from my script:_ 37 | ``` 38 | curl -Ls https://raw.githubusercontent.com/yousafkhamza/Terraform_installation/main/terraform.sh | bash 39 | ``` 40 | 41 | #### Pre-Requests (for RedHat-based-Linux) 42 | ``` 43 | yum install -y git 44 | ``` 45 | 46 | #### Pre-Requests (for Debian-based-Linux) 47 | ```` 48 | apt install -y git 49 | ```` 50 | 51 | #### Pre-Requests (for Termux-based-Linux) 52 | ```` 53 | pkg upgrade 54 | pkg install git 55 | ```` 56 | 57 | --- 58 | ## How to Get 59 | ``` 60 | git clone https://github.com/yousafkhamza/AWS-Lambda-Cost-Optimization-with-Terraform.git 61 | cd AWS-Lambda-Cost-Optimization-with-Terraform.git 62 | ``` 63 | 64 | ---- 65 | ## How to execute 66 | ``` 67 | terraform init 68 | terraform plan 69 | terraform apply 70 | ``` 71 | 72 | ---- 73 | ## Output be like 74 | ``` 75 | terraform apply -auto-approve 76 | 77 | Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following 78 | symbols: 79 | + create 80 | 81 | Terraform will perform the following actions: 82 | 83 | # aws_cloudwatch_event_rule.trigger_to_start_ec2_instance will be created 84 | + resource "aws_cloudwatch_event_rule" "trigger_to_start_ec2_instance" { 85 | + arn = (known after apply) 86 | + description = "Trigger that moving data lambda" 87 | + event_bus_name = "default" 88 | + id = (known after apply) 89 | + is_enabled = true 90 | + name = "Trigger-start-ec2-instance-lambda" 91 | + name_prefix = (known after apply) 92 | + schedule_expression = "cron(0 8 * * ? *)" 93 | + tags = { 94 | + "Name" = "ec2 start cloudwatch trigger" 95 | } 96 | + tags_all = { 97 | + "Name" = "ec2 start cloudwatch trigger" 98 | } 99 | } 100 | 101 | # aws_cloudwatch_event_rule.trigger_to_stop_ec2_instance will be created 102 | + resource "aws_cloudwatch_event_rule" "trigger_to_stop_ec2_instance" { 103 | + arn = (known after apply) 104 | + description = "Trigger that moving data lambda" 105 | + event_bus_name = "default" 106 | + id = (known after apply) 107 | + is_enabled = true 108 | + name = "Trigger-stop-ec2-instance-lambda" 109 | + name_prefix = (known after apply) 110 | + schedule_expression = "cron(0 16 * * ? *)" 111 | + tags = { 112 | + "Name" = "ec2 stop cloudwatch trigger" 113 | } 114 | + tags_all = { 115 | + "Name" = "ec2 stop cloudwatch trigger" 116 | } 117 | } 118 | 119 | # aws_cloudwatch_event_target.send_to_start_lambda_target will be created 120 | + resource "aws_cloudwatch_event_target" "send_to_start_lambda_target" { 121 | + arn = (known after apply) 122 | + event_bus_name = "default" 123 | + id = (known after apply) 124 | + rule = "Trigger-start-ec2-instance-lambda" 125 | + target_id = "SendToLambda" 126 | } 127 | 128 | # aws_cloudwatch_event_target.send_to_stop_lambda_target will be created 129 | + resource "aws_cloudwatch_event_target" "send_to_stop_lambda_target" { 130 | + arn = (known after apply) 131 | + event_bus_name = "default" 132 | + id = (known after apply) 133 | + rule = "Trigger-stop-ec2-instance-lambda" 134 | + target_id = "SendToLambda" 135 | } 136 | 137 | # aws_iam_role.lambda_iam_role_terraform will be created 138 | + resource "aws_iam_role" "lambda_iam_role_terraform" { 139 | + arn = (known after apply) 140 | + assume_role_policy = jsonencode( 141 | { 142 | + Statement = [ 143 | + { 144 | + Action = "sts:AssumeRole" 145 | + Effect = "Allow" 146 | + Principal = { 147 | + Service = "lambda.amazonaws.com" 148 | } 149 | + Sid = "" 150 | }, 151 | ] 152 | + Version = "2012-10-17" 153 | } 154 | ) 155 | + create_date = (known after apply) 156 | + description = "IAM role for lambda to stop start that instance which we created" 157 | + force_detach_policies = false 158 | + id = (known after apply) 159 | + managed_policy_arns = (known after apply) 160 | + max_session_duration = 3600 161 | + name = "Lambda-IAM-Role-For-EC2-Stop-Start" 162 | + name_prefix = (known after apply) 163 | + path = "/lambda/" 164 | + tags_all = (known after apply) 165 | + unique_id = (known after apply) 166 | 167 | + inline_policy { 168 | + name = "EC2-Describe-Inline-Policy" 169 | + policy = jsonencode( 170 | { 171 | + Statement = [ 172 | + { 173 | + Action = "ec2:DescribeInstances" 174 | + Effect = "Allow" 175 | + Resource = "*" 176 | + Sid = "" 177 | }, 178 | ] 179 | + Version = "2012-10-17" 180 | } 181 | ) 182 | } 183 | + inline_policy { 184 | + name = "EC2-Stop-Start-Inline-Policy" 185 | + policy = jsonencode( 186 | { 187 | + Statement = [ 188 | + { 189 | + Action = [ 190 | + "ec2:StopInstances", 191 | + "ec2:StartInstances", 192 | ] 193 | + Effect = "Allow" 194 | + Resource = "arn:aws:ec2:ap-south-1:361738388880:instance/*" 195 | + Sid = "" 196 | }, 197 | ] 198 | + Version = "2012-10-17" 199 | } 200 | ) 201 | } 202 | } 203 | 204 | # aws_instance.ec2_instance will be created 205 | + resource "aws_instance" "ec2_instance" { 206 | + ami = "ami-0dafa01c8100180f8" 207 | + arn = (known after apply) 208 | + associate_public_ip_address = true 209 | + availability_zone = "ap-south-1a" 210 | + cpu_core_count = (known after apply) 211 | + cpu_threads_per_core = (known after apply) 212 | + disable_api_termination = (known after apply) 213 | + ebs_optimized = (known after apply) 214 | + get_password_data = false 215 | + host_id = (known after apply) 216 | + id = (known after apply) 217 | + instance_initiated_shutdown_behavior = (known after apply) 218 | + instance_state = (known after apply) 219 | + instance_type = "t2.micro" 220 | + ipv6_address_count = (known after apply) 221 | + ipv6_addresses = (known after apply) 222 | + key_name = (known after apply) 223 | + monitoring = (known after apply) 224 | + outpost_arn = (known after apply) 225 | + password_data = (known after apply) 226 | + placement_group = (known after apply) 227 | + placement_partition_number = (known after apply) 228 | + primary_network_interface_id = (known after apply) 229 | + private_dns = (known after apply) 230 | + private_ip = (known after apply) 231 | + public_dns = (known after apply) 232 | + public_ip = (known after apply) 233 | + secondary_private_ips = (known after apply) 234 | + security_groups = (known after apply) 235 | + source_dest_check = true 236 | + subnet_id = "subnet-0ee7a1d09074c3e0b" 237 | + tags = { 238 | + "Name" = "python-terraform" 239 | } 240 | + tags_all = { 241 | + "Name" = "python-terraform" 242 | } 243 | + tenancy = (known after apply) 244 | + user_data = "3f970a9f1a5d611c3764c372fc96f2c681039d94" 245 | + user_data_base64 = (known after apply) 246 | + vpc_security_group_ids = (known after apply) 247 | 248 | + capacity_reservation_specification { 249 | + capacity_reservation_preference = (known after apply) 250 | 251 | + capacity_reservation_target { 252 | + capacity_reservation_id = (known after apply) 253 | } 254 | } 255 | 256 | + ebs_block_device { 257 | + delete_on_termination = (known after apply) 258 | + device_name = (known after apply) 259 | + encrypted = (known after apply) 260 | + iops = (known after apply) 261 | + kms_key_id = (known after apply) 262 | + snapshot_id = (known after apply) 263 | + tags = (known after apply) 264 | + throughput = (known after apply) 265 | + volume_id = (known after apply) 266 | + volume_size = (known after apply) 267 | + volume_type = (known after apply) 268 | } 269 | 270 | + enclave_options { 271 | + enabled = (known after apply) 272 | } 273 | 274 | + ephemeral_block_device { 275 | + device_name = (known after apply) 276 | + no_device = (known after apply) 277 | + virtual_name = (known after apply) 278 | } 279 | 280 | + metadata_options { 281 | + http_endpoint = (known after apply) 282 | + http_put_response_hop_limit = (known after apply) 283 | + http_tokens = (known after apply) 284 | + instance_metadata_tags = (known after apply) 285 | } 286 | 287 | + network_interface { 288 | + delete_on_termination = (known after apply) 289 | + device_index = (known after apply) 290 | + network_interface_id = (known after apply) 291 | } 292 | 293 | + root_block_device { 294 | + delete_on_termination = true 295 | + device_name = (known after apply) 296 | + encrypted = (known after apply) 297 | + iops = (known after apply) 298 | + kms_key_id = (known after apply) 299 | + throughput = (known after apply) 300 | + volume_id = (known after apply) 301 | + volume_size = 10 302 | + volume_type = "gp2" 303 | } 304 | } 305 | 306 | # aws_lambda_function.ec2_start_lambda_function will be created 307 | + resource "aws_lambda_function" "ec2_start_lambda_function" { 308 | + architectures = (known after apply) 309 | + arn = (known after apply) 310 | + description = "This lambda using to start the ec2 instance which we mentioned" 311 | + filename = "tmp/start-lambda-function.zip" 312 | + function_name = "ec2-start-lambda-function" 313 | + handler = "lambda_function.lambda_handler" 314 | + id = (known after apply) 315 | + invoke_arn = (known after apply) 316 | + last_modified = (known after apply) 317 | + memory_size = 128 318 | + package_type = "Zip" 319 | + publish = false 320 | + qualified_arn = (known after apply) 321 | + reserved_concurrent_executions = -1 322 | + role = (known after apply) 323 | + runtime = "python3.9" 324 | + signing_job_arn = (known after apply) 325 | + signing_profile_version_arn = (known after apply) 326 | + source_code_hash = (known after apply) 327 | + source_code_size = (known after apply) 328 | + tags = { 329 | + "Name" = "ec2 start lambda function" 330 | } 331 | + tags_all = { 332 | + "Name" = "ec2 start lambda function" 333 | } 334 | + timeout = 60 335 | + version = (known after apply) 336 | 337 | + environment { 338 | + variables = { 339 | + "REGION" = "ap-south-1" 340 | + "TAG" = "python-terraform" 341 | } 342 | } 343 | 344 | + tracing_config { 345 | + mode = (known after apply) 346 | } 347 | } 348 | 349 | # aws_lambda_function.ec2_stop_lambda_function will be created 350 | + resource "aws_lambda_function" "ec2_stop_lambda_function" { 351 | + architectures = (known after apply) 352 | + arn = (known after apply) 353 | + description = "This lambda using to stop the ec2 instance which we mentioned" 354 | + filename = "tmp/stop-lambda-function.zip" 355 | + function_name = "ec2-stop-lambda-function" 356 | + handler = "lambda_function.lambda_handler" 357 | + id = (known after apply) 358 | + invoke_arn = (known after apply) 359 | + last_modified = (known after apply) 360 | + memory_size = 128 361 | + package_type = "Zip" 362 | + publish = false 363 | + qualified_arn = (known after apply) 364 | + reserved_concurrent_executions = -1 365 | + role = (known after apply) 366 | + runtime = "python3.9" 367 | + signing_job_arn = (known after apply) 368 | + signing_profile_version_arn = (known after apply) 369 | + source_code_hash = (known after apply) 370 | + source_code_size = (known after apply) 371 | + tags = { 372 | + "Name" = "ec2 stop lambda function" 373 | } 374 | + tags_all = { 375 | + "Name" = "ec2 stop lambda function" 376 | } 377 | + timeout = 60 378 | + version = (known after apply) 379 | 380 | + environment { 381 | + variables = { 382 | + "REGION" = "ap-south-1" 383 | + "TAG" = "python-terraform" 384 | } 385 | } 386 | 387 | + tracing_config { 388 | + mode = (known after apply) 389 | } 390 | } 391 | 392 | # aws_lambda_permission.allow_cloudwatch_to_call_start_lambda will be created 393 | + resource "aws_lambda_permission" "allow_cloudwatch_to_call_start_lambda" { 394 | + action = "lambda:InvokeFunction" 395 | + function_name = "ec2-start-lambda-function" 396 | + id = (known after apply) 397 | + principal = "events.amazonaws.com" 398 | + source_arn = (known after apply) 399 | + statement_id = "AllowExecutionFromCloudWatch" 400 | } 401 | 402 | # aws_lambda_permission.allow_cloudwatch_to_call_stop_lambda will be created 403 | + resource "aws_lambda_permission" "allow_cloudwatch_to_call_stop_lambda" { 404 | + action = "lambda:InvokeFunction" 405 | + function_name = "ec2-stop-lambda-function" 406 | + id = (known after apply) 407 | + principal = "events.amazonaws.com" 408 | + source_arn = (known after apply) 409 | + statement_id = "AllowExecutionFromCloudWatch" 410 | } 411 | 412 | # aws_security_group.sg_for_ec2 will be created 413 | + resource "aws_security_group" "sg_for_ec2" { 414 | + arn = (known after apply) 415 | + description = "Allow 80,443,22" 416 | + egress = [ 417 | + { 418 | + cidr_blocks = [ 419 | + "0.0.0.0/0", 420 | ] 421 | + description = "" 422 | + from_port = 0 423 | + ipv6_cidr_blocks = [ 424 | + "::/0", 425 | ] 426 | + prefix_list_ids = [] 427 | + protocol = "-1" 428 | + security_groups = [] 429 | + self = false 430 | + to_port = 0 431 | }, 432 | ] 433 | + id = (known after apply) 434 | + ingress = [ 435 | + { 436 | + cidr_blocks = [ 437 | + "0.0.0.0/0", 438 | ] 439 | + description = "HTTPS" 440 | + from_port = 443 441 | + ipv6_cidr_blocks = [ 442 | + "::/0", 443 | ] 444 | + prefix_list_ids = [] 445 | + protocol = "tcp" 446 | + security_groups = [] 447 | + self = false 448 | + to_port = 443 449 | }, 450 | + { 451 | + cidr_blocks = [ 452 | + "0.0.0.0/0", 453 | ] 454 | + description = "HTTPS" 455 | + from_port = 80 456 | + ipv6_cidr_blocks = [ 457 | + "::/0", 458 | ] 459 | + prefix_list_ids = [] 460 | + protocol = "tcp" 461 | + security_groups = [] 462 | + self = false 463 | + to_port = 80 464 | }, 465 | + { 466 | + cidr_blocks = [ 467 | + "0.0.0.0/0", 468 | ] 469 | + description = "SSH" 470 | + from_port = 22 471 | + ipv6_cidr_blocks = [ 472 | + "::/0", 473 | ] 474 | + prefix_list_ids = [] 475 | + protocol = "tcp" 476 | + security_groups = [] 477 | + self = false 478 | + to_port = 22 479 | }, 480 | ] 481 | + name = "sgec2" 482 | + name_prefix = (known after apply) 483 | + owner_id = (known after apply) 484 | + revoke_rules_on_delete = false 485 | + tags = { 486 | + "Name" = "sg-for-stop-start-ec2" 487 | } 488 | + tags_all = { 489 | + "Name" = "sg-for-stop-start-ec2" 490 | } 491 | + vpc_id = (known after apply) 492 | } 493 | 494 | Plan: 11 to add, 0 to change, 0 to destroy. 495 | aws_iam_role.lambda_iam_role_terraform: Creating... 496 | aws_security_group.sg_for_ec2: Creating... 497 | aws_security_group.sg_for_ec2: Creation complete after 4s [id=sg-01e05ef2f174955f7] 498 | aws_instance.ec2_instance: Creating... 499 | aws_iam_role.lambda_iam_role_terraform: Creation complete after 5s [id=Lambda-IAM-Role-For-EC2-Stop-Start] 500 | aws_lambda_function.ec2_start_lambda_function: Creating... 501 | aws_lambda_function.ec2_stop_lambda_function: Creating... 502 | aws_instance.ec2_instance: Still creating... [10s elapsed] 503 | aws_lambda_function.ec2_start_lambda_function: Still creating... [10s elapsed] 504 | aws_lambda_function.ec2_stop_lambda_function: Still creating... [10s elapsed] 505 | aws_lambda_function.ec2_stop_lambda_function: Creation complete after 11s [id=ec2-stop-lambda-function] 506 | aws_cloudwatch_event_rule.trigger_to_stop_ec2_instance: Creating... 507 | aws_cloudwatch_event_rule.trigger_to_stop_ec2_instance: Creation complete after 1s [id=Trigger-stop-ec2-instance-lambda] 508 | aws_lambda_permission.allow_cloudwatch_to_call_stop_lambda: Creating... 509 | aws_cloudwatch_event_target.send_to_stop_lambda_target: Creating... 510 | aws_cloudwatch_event_target.send_to_stop_lambda_target: Creation complete after 0s [id=Trigger-stop-ec2-instance-lambda-SendToLambda] 511 | aws_lambda_permission.allow_cloudwatch_to_call_stop_lambda: Creation complete after 0s [id=AllowExecutionFromCloudWatch] 512 | aws_lambda_function.ec2_start_lambda_function: Creation complete after 16s [id=ec2-start-lambda-function] 513 | aws_cloudwatch_event_rule.trigger_to_start_ec2_instance: Creating... 514 | aws_cloudwatch_event_rule.trigger_to_start_ec2_instance: Creation complete after 1s [id=Trigger-start-ec2-instance-lambda] 515 | aws_lambda_permission.allow_cloudwatch_to_call_start_lambda: Creating... 516 | aws_cloudwatch_event_target.send_to_start_lambda_target: Creating... 517 | aws_cloudwatch_event_target.send_to_start_lambda_target: Creation complete after 0s [id=Trigger-start-ec2-instance-lambda-SendToLambda] 518 | aws_lambda_permission.allow_cloudwatch_to_call_start_lambda: Creation complete after 0s [id=AllowExecutionFromCloudWatch] 519 | aws_instance.ec2_instance: Still creating... [20s elapsed] 520 | aws_instance.ec2_instance: Still creating... [30s elapsed] 521 | aws_instance.ec2_instance: Creation complete after 33s [id=i-07d44771b790189bc] 522 | 523 | Apply complete! Resources: 11 added, 0 changed, 0 destroyed. 524 | ``` 525 | ### _Output Screenshots (at AWS)_ 526 | ![](lambda_code/screenshots/1.png) 527 | ![](lambda_code/screenshots/2.png) 528 | ![](lambda_code/screenshots/3.png) 529 | ![](lambda_code/screenshots/4.png) 530 | 531 | ---- 532 | ## Behind the code 533 | ### Terraform code 534 | _EC2.tf_ 535 | ```` 536 | #----------------------- 537 | # EC2 Instance 538 | #----------------------- 539 | 540 | resource "aws_instance" "ec2_instance" { 541 | ami = data.aws_ami.linux.id 542 | instance_type = var.type 543 | associate_public_ip_address = true 544 | availability_zone = data.aws_availability_zones.available.names[0] 545 | # key_name = var.key #we are using password through userdata once you logged in please change on be half of you! 546 | subnet_id = tolist(data.aws_subnets.my_vpc.ids)[0] 547 | vpc_security_group_ids = [ aws_security_group.sg_for_ec2.id ] 548 | user_data = <> /etc/ssh/sshd_config 551 | echo "LANG=en_US.utf-8" >> /etc/environment 552 | echo "LC_ALL=en_US.utf-8" >> /etc/environment 553 | echo "${var.password_for_ec2}" | passwd root --stdin 554 | sed -i 's/#PermitRootLogin yes/PermitRootLogin yes/' /etc/ssh/sshd_config 555 | sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config 556 | service sshd restart 557 | EOF 558 | tags = { 559 | Name = var.ec2_tag 560 | } 561 | 562 | root_block_device { 563 | volume_type = "gp2" 564 | volume_size = var.vol_size 565 | } 566 | } 567 | ```` 568 | > Userdata including this file due to the password fetching through as a variable. and creating an ec2 instance with your requirements and you can set those variables like "Instance type, Volume Size, Tag for lambda, Password for the ec2 instance" so you can choose those strings values as a variable in `terraform.tfvars`. 569 | 570 | _SG.tf_ 571 | ``` 572 | # ------------------------------------- 573 | # Security Group 574 | # ------------------------------------- 575 | 576 | resource "aws_security_group" "sg_for_ec2" { 577 | name = "sgec2" 578 | description = "Allow 80,443,22" 579 | 580 | ingress { 581 | description = "HTTPS" 582 | from_port = 80 583 | to_port = 80 584 | protocol = "tcp" 585 | cidr_blocks = ["0.0.0.0/0"] 586 | ipv6_cidr_blocks = ["::/0"] 587 | } 588 | ingress { 589 | description = "HTTPS" 590 | from_port = 443 591 | to_port = 443 592 | protocol = "tcp" 593 | cidr_blocks = ["0.0.0.0/0"] 594 | ipv6_cidr_blocks = ["::/0"] 595 | } 596 | ingress { 597 | description = "SSH" 598 | from_port = 22 599 | to_port = 22 600 | protocol = "tcp" 601 | cidr_blocks = ["0.0.0.0/0"] 602 | ipv6_cidr_blocks = ["::/0"] 603 | } 604 | 605 | egress { 606 | from_port = 0 607 | to_port = 0 608 | protocol = "-1" 609 | cidr_blocks = ["0.0.0.0/0"] 610 | ipv6_cidr_blocks = ["::/0"] 611 | } 612 | 613 | tags = { 614 | Name = "sg-for-stop-start-ec2" 615 | } 616 | lifecycle { 617 | create_before_destroy = true 618 | } 619 | } 620 | ``` 621 | > Security group for EC2 server which we created through the script and this Security group allowed HTTP, HTTPS, SSH. 622 | 623 | _Fetch.tf_ 624 | ``` 625 | # Fetch Account ID for IAM. 626 | data "aws_caller_identity" "current" { 627 | } 628 | 629 | # Fetch Default VPC 630 | data "aws_vpc" "default" { 631 | default = true 632 | } 633 | 634 | # AZ Fetching 635 | data "aws_availability_zones" "available" { 636 | state = "available" 637 | } 638 | 639 | # Subnet Fetching 640 | data "aws_subnets" "my_vpc" { 641 | filter { 642 | name = "vpc-id" 643 | values = [ data.aws_vpc.default.id ] 644 | } 645 | } 646 | 647 | 648 | #------------------------------------------------------ 649 | # Amazon Linux AMI choosing through data resource 650 | #------------------------------------------------------ 651 | data "aws_ami" "linux" { 652 | most_recent = true 653 | 654 | filter { 655 | name = "owner-alias" 656 | values = ["amazon"] 657 | } 658 | 659 | filter { 660 | name = "name" 661 | values = ["amzn2-ami-hvm*"] 662 | } 663 | owners = ["137112412989"] # Canonical 664 | } 665 | ``` 666 | > Fetching all the details ie. amazon Linux AMI for all-region, current account ID for creating IAM Role security and we are using default VPC to create the EC2 so which we fetched data for instance creation which of those fetching services like those VPC, Availability zone, Subnet 667 | 668 | _IAM.tf_ 669 | ``` 670 | # ---------------------------------- 671 | # IAM Role for Lambda Function 672 | # ---------------------------------- 673 | # Assume Role - For Lambda 674 | data "aws_iam_policy_document" "lambda_assume_role" { 675 | statement { 676 | effect = "Allow" 677 | actions = [ 678 | "sts:AssumeRole"] 679 | principals { 680 | type = "Service" 681 | identifiers = [ 682 | "lambda.amazonaws.com"] 683 | } 684 | } 685 | } 686 | 687 | # Inline policy for ec2 stop start for lambda 688 | data "aws_iam_policy_document" "ec2_stop_start_inline_policy" { 689 | statement { 690 | actions = [ 691 | "ec2:StartInstances", 692 | "ec2:StopInstances", 693 | ] 694 | resources = [ 695 | "arn:aws:ec2:${var.aws_region}:${data.aws_caller_identity.current.account_id}:instance/*", 696 | ] 697 | } 698 | } 699 | 700 | data "aws_iam_policy_document" "ec2_describe_inline_policy" { 701 | statement { 702 | actions = [ 703 | "ec2:DescribeInstances" 704 | ] 705 | resources = [ 706 | "*" 707 | ] 708 | } 709 | } 710 | 711 | # Role for Lambda and both assume and inline integrated 712 | resource "aws_iam_role" "lambda_iam_role_terraform" { 713 | name = "Lambda-IAM-Role-For-EC2-Stop-Start" 714 | path = "/lambda/" 715 | assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json 716 | 717 | description = "IAM role for lambda to stop-start that instance which we created" 718 | 719 | inline_policy { 720 | name = "EC2-Stop-Start-Inline-Policy" 721 | policy = data.aws_iam_policy_document.ec2_stop_start_inline_policy.json 722 | } 723 | inline_policy { 724 | name = "EC2-Describe-Inline-Policy" 725 | policy = data.aws_iam_policy_document.ec2_describe_inline_policy.json 726 | } 727 | } 728 | ``` 729 | > I have created an IAM role for lambda execution and we are using the most secure IAM to run that lambda and lock with the Account ID and Region 730 | 731 | _Lambda_Start.tf_ 732 | ``` 733 | # ---------------------------------- 734 | # Lambda_Function 735 | # ---------------------------------- 736 | #archiving py file to zip 737 | data "archive_file" "start_lambda_zip" { 738 | type = "zip" 739 | source_dir = "./lambda_code/start/" 740 | output_path = "tmp/${local.start_lambda_function}.zip" 741 | } 742 | 743 | resource "aws_lambda_function" "ec2_start_lambda_function" { 744 | filename = data.archive_file.start_lambda_zip.output_path 745 | function_name = "ec2-start-lambda-function" 746 | role = aws_iam_role.lambda_iam_role_terraform.arn 747 | description = "This lambda using to start the ec2 instance which we mentioned" 748 | handler = "lambda_function.lambda_handler" 749 | runtime = local.runtime_lambda_function 750 | timeout = 60 751 | memory_size = 128 752 | 753 | environment { 754 | variables = { 755 | REGION = var.aws_region 756 | TAG = var.ec2_tag 757 | } 758 | } 759 | 760 | tags = tomap({"Name" = "ec2 start lambda function"}) 761 | } 762 | 763 | 764 | #----------------------- 765 | # CloudWatch Trigger to start ec2 instance 766 | #----------------------- 767 | resource "aws_cloudwatch_event_rule" "trigger_to_start_ec2_instance" { 768 | name = "Trigger-start-ec2-instance-lambda" 769 | description = "Trigger that moving data lambda" 770 | schedule_expression = var.start_cron 771 | tags = tomap({"Name" = "ec2 start cloudwatch trigger"}) 772 | 773 | depends_on = [aws_lambda_function.ec2_start_lambda_function] 774 | } 775 | 776 | resource "aws_cloudwatch_event_target" "send_to_start_lambda_target" { 777 | rule = aws_cloudwatch_event_rule.trigger_to_start_ec2_instance.name 778 | target_id = "SendToLambda" 779 | arn = aws_lambda_function.ec2_start_lambda_function.arn 780 | 781 | depends_on = [aws_lambda_function.ec2_start_lambda_function] 782 | } 783 | 784 | resource "aws_lambda_permission" "allow_cloudwatch_to_call_start_lambda" { 785 | statement_id = "AllowExecutionFromCloudWatch" 786 | action = "lambda:InvokeFunction" 787 | function_name = aws_lambda_function.ec2_start_lambda_function.function_name 788 | principal = "events.amazonaws.com" 789 | source_arn = aws_cloudwatch_event_rule.trigger_to_start_ec2_instance.arn 790 | 791 | depends_on = [aws_lambda_function.ec2_start_lambda_function,aws_cloudwatch_event_rule.trigger_to_start_ec2_instance] 792 | } 793 | ``` 794 | > Creating a lambda function with ec2 instance starting python script and the scheduler cloudwatch (so you can choose the cron time as a variable in `terraform.tfvars`) 795 | 796 | _Lambda_Stop.tf_ 797 | ``` 798 | # ---------------------------------- 799 | # Lambda_Function 800 | # ---------------------------------- 801 | #archiving py file to zip 802 | data "archive_file" "stop_lambda_zip" { 803 | type = "zip" 804 | source_dir = "./lambda_code/stop/" 805 | output_path = "tmp/${local.stop_lambda_function}.zip" 806 | } 807 | 808 | resource "aws_lambda_function" "ec2_stop_lambda_function" { 809 | filename = data.archive_file.stop_lambda_zip.output_path 810 | function_name = "ec2-stop-lambda-function" 811 | role = aws_iam_role.lambda_iam_role_terraform.arn 812 | description = "This lambda using to stop the ec2 instance which we mentioned" 813 | handler = "lambda_function.lambda_handler" 814 | runtime = local.runtime_lambda_function 815 | timeout = 60 816 | memory_size = 128 817 | 818 | environment { 819 | variables = { 820 | REGION = var.aws_region 821 | TAG = var.ec2_tag 822 | } 823 | } 824 | 825 | tags = tomap({"Name" = "ec2 stop lambda function"}) 826 | } 827 | 828 | 829 | #----------------------- 830 | # CloudWatch Trigger to stop ec2 instance 831 | #----------------------- 832 | resource "aws_cloudwatch_event_rule" "trigger_to_stop_ec2_instance" { 833 | name = "Trigger-stop-ec2-instance-lambda" 834 | description = "Trigger that moving data lambda" 835 | schedule_expression = var.stop_cron 836 | tags = tomap({"Name" = "ec2 stop cloudwatch trigger"}) 837 | 838 | depends_on = [aws_lambda_function.ec2_stop_lambda_function] 839 | } 840 | 841 | resource "aws_cloudwatch_event_target" "send_to_stop_lambda_target" { 842 | rule = aws_cloudwatch_event_rule.trigger_to_stop_ec2_instance.name 843 | target_id = "SendToLambda" 844 | arn = aws_lambda_function.ec2_stop_lambda_function.arn 845 | 846 | depends_on = [aws_lambda_function.ec2_stop_lambda_function] 847 | } 848 | 849 | resource "aws_lambda_permission" "allow_cloudwatch_to_call_stop_lambda" { 850 | statement_id = "AllowExecutionFromCloudWatch" 851 | action = "lambda:InvokeFunction" 852 | function_name = aws_lambda_function.ec2_stop_lambda_function.function_name 853 | principal = "events.amazonaws.com" 854 | source_arn = aws_cloudwatch_event_rule.trigger_to_stop_ec2_instance.arn 855 | 856 | depends_on = [aws_lambda_function.ec2_stop_lambda_function,aws_cloudwatch_event_rule.trigger_to_stop_ec2_instance] 857 | } 858 | ``` 859 | > Creating a lambda function with ec2 instance starting python script and the scheduler cloudwatch (so you can choose the cron time as a variable in `terraform.tfvars`) 860 | 861 | _variables.tf_ 862 | ``` 863 | variable "aws_region" { 864 | type = string 865 | description = "Which region do you used" 866 | default = "" 867 | } 868 | 869 | variable "type" { 870 | type = string 871 | description = "Which instance type do you need" 872 | default = "t2.micro" 873 | } 874 | 875 | variable "vol_size" { 876 | type = string 877 | description = "Instance volume size do you need" 878 | default = "8" 879 | } 880 | 881 | variable "start_cron" { 882 | type = string 883 | description = "Cron time start the instance which we created" 884 | default = "cron(0 9 * * ? *)" 885 | } 886 | 887 | variable "stop_cron" { 888 | type = string 889 | description = "Cron time stop the instance which we created" 890 | default = "cron(0 17 * * ? *)" 891 | } 892 | 893 | variable "ec2_tag" { 894 | type = string 895 | description = "TAG for ec2 and this tag to take stop start this instance" 896 | default = "python-terraform" 897 | } 898 | 899 | variable "password_for_ec2" { 900 | type = string 901 | description = "Password for EC2 Server" 902 | default = "T36r@f06m@YKH" 903 | } 904 | 905 | locals { 906 | start_lambda_function = "start-lambda-function" 907 | stop_lambda_function = "stop-lambda-function" 908 | runtime_lambda_function = "python3.9" 909 | } 910 | ``` 911 | > Variables name defining here and I have set up some predefined and default values here. you can change all the variable values on `terraform.tfvars` 912 | 913 | _terraform.tfvars_ 914 | ``` 915 | aws_region = "ap-south-1" # mention which region you need here. 916 | type = "t2.micro" # mention which type of instance would you need and mention here. 917 | vol_size = "10" # EC2 instance volume size mention here. 918 | start_cron = "cron(0 8 * * ? *)" # Which time to start that EC2 instance here. 919 | stop_cron = "cron(0 16 * * ? *)" # Which time to stop that EC2 instance here. 920 | ec2_tag = "python-terraform" # Tag for EC2 instance here and this tag is using to stop that EC2 instance so please be uniq 921 | password_for_ec2 = "T36r@f06m@YKH" # Strong password mentioned here for EC2 because we didn't use key pair if you need please login to the EC2 and do it manually 922 | ``` 923 | > **Please read the side notes and change the values as you need like the left side. if you're not changing it will run with those default values so please change it manually before running `terraform apply`** 924 | 925 | ### Python Code 926 | _lambda_code/start/lambda_function.py_ 927 | ``` 928 | import boto3 929 | import os 930 | 931 | REGION = os.environ['REGION'] 932 | TAG = os.environ['TAG'] 933 | 934 | def lambda_handler(event, context): 935 | ec2 = boto3.client('ec2',region_name=REGION) 936 | all_ec2 = ec2.describe_instances( 937 | Filters=[ 938 | {'Name':'tag:Name', 'Values':[TAG]} 939 | ]) 940 | 941 | for instance in all_ec2['Reservations'][0]['Instances']: 942 | print("Starting Ec2 : {} ".format( instance['InstanceId'] )) 943 | ec2.start_instances(InstanceIds=[ instance['InstanceId'] ]) 944 | ``` 945 | > Using environment variables to fetch that tag and region details from variable and so this is used to start that ec2 instance which we created with this script. 946 | 947 | _lambda_code/stop/lambda_function.py_ 948 | ``` 949 | import boto3 950 | import os 951 | 952 | REGION = os.environ['REGION'] 953 | TAG = os.environ['TAG'] 954 | 955 | def lambda_handler(event, context): 956 | ec2 = boto3.client('ec2',region_name=REGION) 957 | all_ec2 = ec2.describe_instances( 958 | Filters=[ 959 | {'Name':'tag:Name', 'Values':[TAG]} 960 | ]) 961 | 962 | for instance in all_ec2['Reservations'][0]['Instances']: 963 | print("Stopping Ec2 : {} ".format( instance['InstanceId'] )) 964 | ec2.stop_instances(InstanceIds=[ instance['InstanceId'] ]) 965 | ``` 966 | > Using environment variables to fetch that tag and region details from variable and so this is used to stop that ec2 instance which we created with this script. 967 | 968 | ---- 969 | ## Conclusion 970 | Q: I would like to start and stop an EC2 instance automatically and it's saving cost and time. Also, please create an instance with my wish. So, is it possible? 971 | 972 | A: Yeah sure, we can do this with lambda and cloudwatch and you can create an ec2 instance with your required values as you wish. Also, those values can only change through in `terraform.tfvars` so, after it will be automated on rest of time. 973 | 974 | ### ⚙️ Connect with Me 975 | 976 |

977 | 978 | 979 | 980 |
981 | --------------------------------------------------------------------------------