Unless you've been working on greenfield projects all of the time in the past few years, you have likely encountered scenarios where AWS resources are provisioned manually. So, you might have a few EC2 instances, Lambda functions, and databases created manually from the Web console.
You just encountered a case where you want to modify or extend that infrastructure, and you might be thinking about whether to update it manually from the console. Still, you get that feeling of unease and frustration to update these resources, especially if they are in a production account.
You might opt to do this manually due to time (or other) constraints, and you ping your colleague and pair on doing the update because it is always good to have another pair of eyes when touching production resources. That's fine. However, if you prefer to turn your infrastructure to code with Terraform, here's how you do it.
In this article, we will build a Lambda manually from the AWS console and import it to Terraform.
Build your Lambda Manually
We will create a Lambda function from the AWS Console in this step.
Write your Terraform Config
We want to change the Lambda function but don't want to do it from the console. So, we will create our Terraform configuration.
I am using Terraform Cloud, so I will create a workspace and call it terraform-import
. However, you can run the same config from your local machine.
- Create a folder to contain your infrastructure code
- Create a new file called
main.tf
to specify the AWS provider
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
required_version = ">= 1.1.0"
cloud {
organization = "MyAwesomeOrganisation"
workspaces {
name = "terraform-import"
}
}
}
provider "aws" {
region = "us-east-1"
}
- Run
terraform login
if you're using Terraform cloud. Otherwise, skip this step - Run
terraform init
to initialise the workspace - Run
terraform plan
- you will get the following output:No changes. Your infrastructure matches the configuration.
Now we need to create the configuration using Terraform import.
- Create a
lambda.tf
file
resource "aws_lambda_function" "my_awesome_lambda" {
function_name = "MyAwesomeFunction"
}
- Run
terraform import aws_lambda_function.my_awesome_lambda MyAwesomeFunction
This command will generate a state file that would map the resource in AWS in the terraform state. If you're using Terraform Cloud, Navigate to the states
file in the left panel under your workspace. If you're working on your local, notice a new file generated, terraform.tfstate
.
Have a look at the state and see what's different.
Based on the state file, we need to update the lambda config so that we get No Changes
when we run terraform apply
the next time. We should not have a resource deletion or replacement. That's the goal.
When I ran terraform plan
first, I got an error saying that the role
argument was required. So I updated my lambda.tf
file to become as follows:
resource "aws_lambda_function" "my_awesome_lambda" {
function_name = "MyAwesomeFunction"
role = "arn:aws:iam::24886324234235555:role/service-role/MyAwesomeFunction-role-r09h0n7l"
}
I got the role from the state file, the Lambda execution role.
Run terraform plan
again. The following error I got is: Error: handler and runtime must be set when PackageType is Zip
. So I updated my Lambda as follows:
resource "aws_lambda_function" "my_awesome_lambda" {
function_name = "MyAwesomeFunction"
handler = "index.handler"
runtime = "nodejs16.x"
role = "arn:aws:iam::248869629908:role/service-role/MyAwesomeFunction-role-r09h0n7l"
}
Run terraform plan
again. That's the result I got:
# aws_lambda_function.my_awesome_lambda will be updated in-place
~ resource "aws_lambda_function" "my_awesome_lambda" {
id = "MyAwesomeFunction"
+ publish = false
~ runtime = "nodejs18.x" -> "nodejs16.x"
tags = {}
# (18 unchanged attributes hidden)
# (3 unchanged blocks hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
That means it will change my Lambda from NodeJs 18 to NodeJs 16. I don't want that, so I will update my lambda.tf
to use Node 18 runtime.
Run terraform plan
again. You should get the following message: No changes. Your infrastructure matches the configuration.
Now you have your infrastructure documented, so you can update it, get it reviewed, and add it to the version control with peace of mind.
We can improve this. So, for example, instead of typing the arn of the Lambda execution role, we can also automate this with Terraform. Try this out.
Update the lambda function
Create a new folder, src
and an index.mjs
file. Add the following code:
export const handler = async (event) => {
console.log("This lambda was updated using Terraform");
const response = {
statusCode: 200,
body: JSON.stringify("Hello from Lambda!"),
};
return response;
};
Package your Lambda by running the following:
cd ../src
zip -r awesome_lambda.zip .
cp lambda_function.zip ../infrastructure/
This will package the Lambda into a zip file and places it under the infrastructure
directory.
Add the following line to the lambda resource:
filename = "${path.module}/awesome_lambda.zip"
Run terraform plan
. You will get the following changes:
Terraform will perform the following output:
# aws_lambda_function.my_awesome_lambda will be updated in-place
~ resource "aws_lambda_function" "my_awesome_lambda" {
+ filename = "./awesome_lambda.zip"
id = "MyAwesomeFunction"
~ last_modified = "2022-12-09T18:30:44.000+0000" -> (known after apply)
+ publish = false
tags = {}
Run terraform apply
. Open the Lambda in your AWS console, and notice how the code has changed.
Run terraform destroy
to destroy your infrastructure.
Do you have a better way to automate your legacy infrastructure? I'd like to hear your thoughts.
Thanks for reading this far. Did you like this article, and do you think others might find it useful? Feel free to share it on Twitter or LinkedIn.
Top comments (0)