Amazon Machine Image is known as AMI that makes it easier and faster to recover an instance in case of disaster or failure of an instance. So, here we are going to create custom AMIs from any instances that have some particular tags in an automation way using Lambda.
Lambda is an event–driven, server less computing platform provided by AWS. It simplifies the process of building smaller, on-demand applications that are responsive to events and new information. It runs code in response to events and automatically manages instances or any other services based on the code.
Now we are going to create some of the resources that we need to automate this process using Terraform. So, Let’s dive into this.
Create an IAM Role and Policy
First we have to create an IAM Policy that has some permissions to access our instances.
So create a file called provider.tf and add the following code in it.
provider "aws" { region = "us-east-1" }
Then create another file named as iam.tf file. This is the one that has the policy and role.
Add the below code in iam.tf file.
resource "aws_iam_policy" "policy" { name = "AMIAutomationLamdaPolicy" path = "/" description = "My test policy" policy = jsonencode( { "Version" : "2012-10-17", "Statement" : [ { "Effect" : "Allow", "Action" : [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource" : "arn:aws:logs:*:*:*" }, { "Effect" : "Allow", "Action" : [ "ec2:CreateImage", "ec2:CreateTags", "ec2:DeleteSnapshot", "ec2:DeregisterImage", "ec2:DescribeImages", "ec2:DescribeInstances" ], "Resource" : "*" } ] } ) } resource "aws_iam_role" "role" { name = "AMIAutomationLamdaRole" assume_role_policy = jsonencode( { "Version" : "2012-10-17", "Statement" : [ { "Effect" : "Allow", "Principal" : { "Service" : "lambda.amazonaws.com" }, "Action" : "sts:AssumeRole" } ] }) managed_policy_arns = [aws_iam_policy.policy.arn] }
- This code will create an IAM Policy named as AMIAutomationLamdaPolicy and an IAM Role named AMIAutomationLamdaRole.
- So now run terraform init command to initialize the dependencies for our code.
- Then run terraform plan and apply commands to deploy your code to aws.
Once creation is completed successfully, you can go to your aws account and navigate to IAM and you can see the role and policy will be created.
And click on the IAM Role it shows the list of policies that are assigned with this role.
Here we just create one custom policy and that will be assigned with our created role.
Create EC2 Instance
Now we need to create an EC2 instance which is the source for our custom AMI.
Create a new file instance.tf and copy the below code and paste it.
resource "aws_instance" "test" { ami = "ami-0ff8a91507f77f867" instance_type = "t2.micro" tags = { Name = "lambda-automation" AutoDigiBackupRetentionDays = 7 AutoDigiBackup = "yes" AutoDigiBackupSchedule = "*" AutoDigiNoReboot = true } }
Our code will create an instance with some tags. Because these tags are helped to identify by Lambda function to which instances are needed to create AMIs.
So now run terraform apply command to create an instance.
Once it completes go to aws account and Ec2 page, you can see the Instance will be created.
Create Lambda Functions and Triggers with EventRules
- We have to create 2 Lambda functions, one for creating AMIs and another one for deleting AMIs.
- Because the AMIs will expire at a particular time.
- Click this createami.zip to download the code for AMICreationLambda function and Click this deleteami.zip to download the code for AMIDeletionLambda function.
- Add these two zip files to the folder where you have your Terraform code.
- Now create a lambda.tf file and copy and paste the below code into that file.
resource "aws_lambda_function" "lambda" { count = 2 filename = var.lambda_file[count.index] function_name = var.function_name[count.index] role = aws_iam_role.role.arn handler = "${local.handler[count.index]}.handler" source_code_hash = filebase64sha256("${var.lambda_file[count.index]}") timeout = 180 runtime = "nodejs16.x" } locals { handler = ["createami", "deleteami"] }
This code will create 2 Lambda functions and it uses the Role which we created at the beginning of this article.
Create another file named variable.tf and paste the below code to that file.
variable "lambda_file" { description = "List of Name for the two lambda code files " type = list(string) default = ["createami.zip", "deleteami.zip"] } variable "function_name" { description = "List of Name for the event rules" type = list(string) default = ["AMICreationLambda", "AMIDeletionLambda"] }
- These variables give values to the Lambda code. It gives 2 function names and 2 file names.
- In our Lambda function we use the latest runtime node 16x.
- Copy the below code and paste into the lambda.tf file.
resource "aws_cloudwatch_event_rule" "lambda" { count = 2 name = var.event_name[count.index] schedule_expression = var.crontab[count.index] } resource "aws_cloudwatch_event_target" "lambda" { count = 2 rule = aws_cloudwatch_event_rule.lambda[count.index].id arn = aws_lambda_function.lambda[count.index].arn } resource "aws_lambda_permission" "allow_event" { count = 2 action = "lambda:InvokeFunction" function_name = aws_lambda_function.lambda[count.index].function_name principal = "events.amazonaws.com" source_arn = aws_cloudwatch_event_rule.lambda[count.index].arn }
What this code will do is it will create 2 EventRules for Invoke the Lambda functions in a particular time regularly.
Copy this code and paste into variable.tf file.
variable "event_name" { description = "List of Name for the event rules" type = list(string) default = ["AMICreationRule", "AMIDeletionRule"] } variable "crontab" { description = "List of crontab schedule for the event rules" type = list(any) default = ["cron(0 07 * * ? *)", "cron(0 02 * * ? *)"] #cron(0 02 * * ? *) # IST 07:30am }
- To trigger the Lambda function on a regular basis, we have to set a time as a crontab in EventRule.
- As I mentioned in the above code, crontab is uses type UTC. So we have converted our IST time to UTC. You can set for everyday morning at 7.30 am to crontab is crontab(0 02 * * ? *).
- For the first one I use my current time when I write this article, to demonstrate you. You can set the time to an extra 10 minutes from now. Then only you can check whether the Lambda function will create AMI or not.
- Now run terraform to create Lambda Functions and Event Rules.
Once completed go to your aws account and Lambda page to see your Lambda Functions.
Now go to the EventBridge section and click EventRule on the left navigation, you are able to see the event rules that your terraform script creates.
Click the AMICreationRule Event Rule to see the details of the rule.
Here you can see the next 10 trigger dates in your local time zone. So wait for it to trigger the Lambda function.
Lambda Functions Creates AMI
Once the time comes that you have scheduled the EventRule to trigger your Lambda function, go to your AWS account and navigate to the CloudWatch group section.
You are able to see a log group which is created by your lambda Function. Click the log group.
In the Log streams section there will be a log stream. Click it to see the logs that come from Lambda.
You can see the log details. It says, find the instance which has some tags and create an AMI named lambda-automation_DATETODEL from that instance and add a tag NoReboot: true to that AMI.
So now go to the EC2 section and choose AMIs, there will be an AMI which is created by your Lambda Function.
Click the AMI named lambda-automation_DATETODEL and choose Tags you can see the tag NoReboot: true.
This is the tag helps to identify the AMI by the AMIDeletionLambda Function to delete the AMI once it expires.
Go to the Lambda section and select the AMIDeletionLambda function.
Click the Test section and then click the Test button to execute the Lambda.
Once it completes its execution, navigate to the CloudWatch Log group section.
When this AMIDeletionLambda function triggers, it creates a new log group as shown in the above screenshot. Select the Log group and you can see the log streams like the below picture.
Click the log stream and get into it. You can see the logs that show Not yet time to delete. It means Our newly created AMI has not expired. So, This lambda function could not delete the AMI.
That’s all I want to show you about the Lambda automation process. In this process our lambda uses node js code for creating and deleting the AMIs. We successfully created this automation process using a simple terraform script.