DEV Community

Ali Haydar for AWS Community Builders

Posted on

Automate infrastructure for manually created resources in AWS

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.

Create Lambda Function From AWS Console

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"
  }
Enter fullscreen mode Exit fullscreen mode
  • 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"
  }

Enter fullscreen mode Exit fullscreen mode
  • 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"
}
Enter fullscreen mode Exit fullscreen mode

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"

}

Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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;
};

Enter fullscreen mode Exit fullscreen mode

Package your Lambda by running the following:

cd ../src

zip -r awesome_lambda.zip .

cp lambda_function.zip ../infrastructure/
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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                           = {}
Enter fullscreen mode Exit fullscreen mode

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)