Importing Existing Infrastructure to Terraform
Tags: terraform, infrastructureascode
Published: 2020-11-22 05:38:10


>Cover page image credits to: terraform.io

####Purpose
The purpose of this blog post is to show you how to import already deployed infrastructure in azure by other means under Terraform control.

####Assumptions
I assume that you have basic knowledge on Azure & Terraform.

####Need for import

Terraform is a IaC tool that lets you to manage your infrastructure regardless of where its hosted (on-premises or cloud) and how its deployed (manually deployed or by other IaC tools like ARM Templates). In case of Azure, you might run into a scenario where the existing infrastructure is already deployed and your team or organization has decided to use Terraform for managing the infrastructure going forward. In such scenario, you need to bring existing infrastructure under Terraform’s control.

In this blog post, we’ll see how to achieve this on an existing infrastructure that’s already deployed on Azure. Here are the steps that are involved in importing the deployed infrastructure.

1. Identify the resources that you want to import to Terraform.
2. Write the configuration for deployed resource.
3. Run ‘Terraform Import’ to import the changes to a state file.
4. Run ‘Terraform Plan’ to review the changes between the Terraform configuration and actual state.
5. Make the changes in configuration to include the missing changes, run ‘Terraform Plan’ again to make sure that configuration includes all the attributes from deployed infrastructure.

>Please note that when I refer to the word configuration, I mean the terraform template file (.tf) that we use to declare our desired state configuration

Let’s get started.

I have following resources deployed in my resource group from a different deployment.
1. App Service Plan
2. WebApp with Staging Slot
3. Application Insights
4. Storage Account

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/orko26f9suj3cwgacvhb.png)

In order to import these resources, we’ll have to create a terraform configuration file that includes all these components. Let’s start with App Service Plan. We’ll refer to the documentation of terraform resource for [azurerm_app_service_plan] (https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_custom_hostname_binding)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6bxaxwo02irlv8gpcs95.png)

I created a new created a new main.tf file and copied the above block to it and added azurerm provider on top of it and also updated the values from existing infrastructure.

```
provider "azurerm" {
  version = "2.0.0"
  features {}
}

resource "azurerm_app_service_plan" "example" {
  name                = "plan-sp-dev"
  location            = East US
  resource_group_name = “az-terf-dev-rg”

  sku {
    tier = "Standard"
    size = "S1"
  }
}
```
Details of the service plan can be obtained from overview in the resource blade of app service plan.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ovu3tlqesr0upzurj5iy.png)

let’s initialize terraform using `terraform init` command to install the azurerm provider.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/g1ee8xxwntr0bun9xz0r.png)

As a first step to import the resource, we wrote the configuration. To identify the command associated with importing a particular resource, we would have the import command in terraform documentation for that resource.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/oexhju48g6lywwcf7xzg.png)

Here is the command syntax:

` terraform import azurerm_app_service_plan.instance1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Web/serverfarms/instance1`

If you observe carefully, terraform import command is followed by the resource_type which is azurerm_app_service_plan and the resource identifier name which is instance1.  There are the values from the configuration file in current terraform’s working directory.

So, it’s important that we write the configuration first and the import process is dependent on it and terraform import doesn’t create a configuration file by itself.

Transforming the import command we obtained above to current implementation, the command we should use is as follows:

`terraform import azurerm_app_service_plan.appserviceplan /subscriptions/yoursubscriptionidgoeshere/resourceGroups/az-terf-dev-rg/providers/Microsoft.Web/serverfarms/plan-sp-dev`


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xsns4macytnkgm6cfs8d.png)

After running the above command, it shows that the App Service Plan resource import is successful and going forward, this resource will be managed by Terraform.

We need to make sure that next apply doesn’t do any breaking modifications. Let’s run ‘Terraform Plan’ to view the changes between the configuration we wrote and the current state.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lyrrmnwbsqs3c6uje25i.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qixzj759oautfg3ab8kj.png)

From the above screenshot we can see that it now says that our configuration matches the current state of service plan. This means we can safely run ‘terraform apply’ now if we want to. 

Let’s do the same with other resources as well. We’ll import app service by adding the configuration in existing main.tf file.

I took the configuration block from terraform documentation and added it by removing “site_config” section as I don’t have any custom site config for my site.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2826iqkjstkriqvoaeib.png)

I need app_service_plan_id and that can be obtained from app service plan properties.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/07kh58gzonjwzr8l4f5w.png)

```
resource "azurerm_app_service" "appservice" {
  name                = "app-dev-webapp"
  location            = "EastUS"
  resource_group_name = "az-terf-dev-rg"
  app_service_plan_id = "/subscriptions/yoursubscriptionidgoeshere/resourceGroups/az-terf-dev-rg/providers/Microsoft.Web/serverFarms/plan-sp-dev"
}
```

Here is how my configuration file looks like so far.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/urlbjw6pa2ijqsysul5l.png)

Let’s try to import this resource and check. The procedure to find the command for importing is same as service plan. Syntax is as follows:

`terraform import azurerm_app_service.instance1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Web/sites/instance1`

Let’s run a terraform import.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/gg58178gikthp6pw7549.png)

Let’s take a minute to examine the. tfstate file that got updated after terraform import command. Every time the import command is run, it would update the state file with the attributes that terraform imported for. So we haven’t mentioned the app insights key information in our configuration, but terraform state file got updated with all the attributes that were set during the manual deployment of this resource. So we need to add the app insights instrumentation key in our configuration file.

If we omit this, terraform would still show that there are no changes to be applied, however if someone changes this key, it won’t be under terraform’s control to revert the change back.

So we need to identify all the configs that we need to control via terraform and add them in our configuration file.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hkzucv5ukyqlcgeutl9c.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/69p1judhpvefuz5bjkwp.png)

Now if we run terraform plan it should show no changes to the existing infrastructure.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/wri26443br09h7jal2ja.png)

Incase if you see any changes in the output of terraform plan, make sure that you identify and correct the changes. Once the changes are correct, run ‘terraform plan’ to make sure that there are no new changes to be applied.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/n9n58vho0qdvoaiy5kec.png)

Similarly, we can do the same for other resources as well. Let’s add staging slot with different name to see how it will show the changes and we can take a corrective action.

Name of the staging slot is ‘webappname-staging’

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/dkhrpeekqmygioworker.png)

I’ve added the slot configuration & intentionally gave the name wrong to identify the different after we run import & plan commands.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/snj4fobddpho0dsx3uqx.png)

Here is the output of terraform import command.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/asn18kddly96wzg6wdwb.png)

Import command will succeed regardless of the name in terraform configuration as we gave the correct name in the terraform import command.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pvy1ag8ay17o1f49z3w5.png)

Now, terraform plan shows that there is a change 1 to add and 1 to destroy.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ghjbvgav0eqr7enrwrai.png)

In the output, it clearly shows that there is a change which is forcing the replacement. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ptrr2oa60b5k2f7nkoe2.png)

So, it’s important that we check the configuration thoroughly before running the plan. 
Let’s correct the configuration by changing the name from “staging-test” to “staging” and run ‘terraform plan’ again.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ag6v459b59z68jzu5u99.png)

In the same way, we can import other resources (storage account, app insights)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hdbzuw2bi4a3b0046f21.png)

While, app insights import and plan commands ran as expected, storage account resulted in showing that there is a new change.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bmlj2k3eazprvco4yzc6.png)

I haven’t changed the account replication type to “LRS” which is the current setting for the storage account.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7l1gabvu3n5dmn3oxn5m.png)

I’ve changed the configuration back to “LRS” and it showed no new changes.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jksa8wd0shdnv4jj84g2.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ja66fyurj7yx9umr138o.png)

We have seen how to import the configuration by hardcoding the values in a single configuration file. However, the best practice is to maintain separate variables file and individual module based architecture of all components you wish to deploy. 

Check out my other [blog post]( https://dev.to/vivekanandrapaka/terraform-and-azure-devops-120p) on how to integrate terraform with DevOps and deploy to various environments with ease with module based architecture.

Here are key take-aways for importing existing infrastructure to terraform:

1.Write the configuration first for the resources you would like to import into terraform state.
2.Terraform import process is dependent on the configuration we write.
3.Import command can be found in the resource documentation and the bottom of the page.
4.Always review the statefile to identify the attributes & the values that were imported.
5.Make necessary changes to your configuration file based on the state file review.
6.Run terraform plan to make sure that there are no breaking changes.

It’s always a best practice to source control terraform configuration files and store the state file in remote back end.

This brings us to the end of this blog post. Hope you enjoyed reading it.

Happy Learning!!!