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.
- Identify the resources that you want to import to Terraform.
- Write the configuration for deployed resource.
- Run ‘Terraform Import’ to import the changes to a state file.
- Run ‘Terraform Plan’ to review the changes between the Terraform configuration and actual state.
- 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.
- App Service Plan
- WebApp with Staging Slot
- Application Insights
- Storage Account
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
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.
let’s initialize terraform using terraform init
command to install the azurerm provider.
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.
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
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.
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.
I need app_service_plan_id and that can be obtained from app service plan properties.
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.
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.
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.
Now if we run terraform plan it should show no changes to the existing infrastructure.
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.
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’
I’ve added the slot configuration & intentionally gave the name wrong to identify the different after we run import & plan commands.
Here is the output of terraform import command.
Import command will succeed regardless of the name in terraform configuration as we gave the correct name in the terraform import command.
Now, terraform plan shows that there is a change 1 to add and 1 to destroy.
In the output, it clearly shows that there is a change which is forcing the replacement.
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.
In the same way, we can import other resources (storage account, app insights)
While, app insights import and plan commands ran as expected, storage account resulted in showing that there is a new change.
I haven’t changed the account replication type to “LRS” which is the current setting for the storage account.
I’ve changed the configuration back to “LRS” and it showed no new changes.
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 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!!!
Top comments (4)
how to use imported terraform.tfstate file in Azure DevOps setup ?
I mean how we can connect to the imported terraform.tfstate file in a Azure DevOps setup after migrating/uploading terraform.tfstate to Azure Storage ?
Main purpose is to import the existing resources to terraform.tfstate and then use the imported terraform.tfstate file in Azure DevOps instead of creating a new terraform.tfstate through Azure DevOps
The suggested way is to keep the tfstate file in a storage account rather than in Azure DevOps and have it configured as remote backend..please see my other post on using terraform with Azure devops.
Hope this helps.
So If I import state file and store it in Azure Storage and give path to this state file in the Azure DevOps , will it create a new state file or pick this one ?
You should be giving the configuring location of the tfstate file in your terraform script rather than Azure DevOps as below and Configure Azure DevOps Pipelines to run:
terraform {
backend "azurerm" {
resource_group_name = "StorageAccount-ResourceGroup"
storage_account_name = "abcd1234"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}