Automate infrastructure for manually created resources in AWS
Published: 2022-12-18 20:09:33



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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v54t8r0pjs63brwaewij.gif)
## 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](https://app.terraform.io/), 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](twitter.com/intent/tweet?url=https%3A%2F%2Fdev.to%2Faws-builders%2Fcreate-iac-for-manually-created-resources-in-aws-48pe%0A%0A%23aws%20%23awscommunity%20%23awscommunitybuilder%20%23softwaredeveloment&text=Check%20this%20article%20by%20%40Alee_Haydar%20on%20how%20to%20automate%20your%20manually%20created%20infrastructure%3A%20%F0%9F%91%89) or [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://dev.to/aws-builders/create-iac-for-manually-created-resources-in-aws-48pe).