New to Terraform? Start here
The Latitude.sh Terraform provider supports importing existing infrastructure into Terraform. This feature is useful when you want to add existing Latitude.sh resources to Terraform to start managing them with code.
Before importing your resources, it helps to understand how Terraform keeps track of your infrastructure.
Terraform maps the attributes and metadata of your resources to its internal state and save it to a local file named terraform.tfstate
. Every change to a .tf
file will be compared with the Terraform state to determine what changes need to be made.
Here is an example of a .tfstate
file:
{
"version": 4,
"terraform_version": "1.8.2",
"serial": 20,
"lineage": "",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "latitudesh_project",
"name": "guide_project",
"provider": "provider[\"registry.terraform.io/latitudesh/latitudesh\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"created": "2024-05-08T22:16:58+00:00",
"description": "Terraform Guides example project",
"environment": "Development",
"id": "proj_7pWRawmOpNrD6",
"name": "Terraform Guide",
"tags": [],
"updated": "2024-05-09T15:20:57+00:00"
},
"sensitive_attributes": [],
"private": ""
}
]
}
],
"check_results": null
}
Editing the state file directly is discouraged. Terraform modifies it to reflect your current infrastructure by running terraform refresh
before terraform plan
.
If you need more advanced state management, use the terraform state
command
to edit it in a more controlled environment.
Migrating your existing Infrastructure
If you already have resources deployed on Latitude.sh, importing them on Terraform is straightforward. Let’s walk through an example:
Creating the .tf
file
To start the migration of our infrastructure, let’s initialize a Terraform project in a new directory:
# create a new directory
mkdir latitudesh-infra && cd latitudesh-infra
# create the main file
touch main.tf
In our main file, we need to set the provider and its version:
# main.tf
terraform {
required_providers {
latitudesh = {
source = "latitudesh/latitudesh"
version = "1.1.0" # check the latest version at https://registry.terraform.io/providers/latitudesh/latitudesh/latest
}
}
}
Now we can initialize terraform to download the Latitude.sh provider and start managing our infrastructure:
Before we start importing our infrastructure, we need to export our Auth Token so Terraform can communicate with the Latitude.sh API and access our resources:
export LATITUDESH_AUTH_TOKEN=<YOUR-AUTH-TOKEN> # replace with your actual token
Now that we are all set up, let’s import our resources
Importing resources
Let’s exemplify the import process with a Project resource.
To begin we’ll need an import
block with two attributes
- to: The resource defined inside Terraform that will be used to manage your infrastructure.
- id: The id of the existing resource.
You can find the id of the project in the Home section of the dashboard.
Now let’s create the file for importing this project:
# projects.tf
import {
to = latitudesh_project.guide_project
id = "proj_7pWRawmOpNrD6" # replace with the project id you want to import
}
resource "latitudesh_project" "guide_project" {
name = "Terraform Import Guide"
environment = "Development"
}
You’ll notice that we have set both the name and the environment of the Project resource. This is needed because both are required attributes.
Running terraform plan
should render something like this:
latitudesh_project.guide_project: Preparing import... [id=proj_7pWRawmOpNrD6]
latitudesh_project.guide_project: Refreshing state... [id=proj_7pWRawmOpNrD6]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# latitudesh_project.guide_project will be updated in-place
# (imported from "proj_7pWRawmOpNrD6")
~ resource "latitudesh_project" "guide_project" {
created = "2024-05-08T22:16:58+00:00"
- description = "Terraform Guides example project" -> null
environment = "Development"
id = "proj_7pWRawmOpNrD6"
name = "Terraform Guide"
tags = []
updated = "2024-06-04T22:20:09+00:00"
}
Plan: 1 to import, 0 to add, 1 to change, 0 to destroy.
Here you’ll notice that there’s a ”-” signal before the description. That occurs because we haven’t defined a description attribute in our project resource block. to avoid updating the resource we will add the missing attribute to our resource.
# projects.tf
import {
to = latitudesh_project.guide_project
id = "proj_7pWRawmOpNrD6"
}
resource "latitudesh_project" "guide_project" {
name = "Terraform Import Guide"
description = "Terraform Guides example project"
environment = "Development"
}
Run terraform plan
again. If everything looks good, run terraform apply
to import the resource:
latitudesh_project.guide_project: Preparing import... [id=proj_7pWRawmOpNrD6]
latitudesh_project.guide_project: Refreshing state... [id=proj_7pWRawmOpNrD6]
Terraform will perform the following actions:
# latitudesh_project.guide_project will be imported
resource "latitudesh_project" "guide_project" {
created = "2024-05-08T22:16:58+00:00"
description = "Terraform Guides example project"
environment = "Development"
id = "proj_7pWRawmOpNrD6"
name = "Terraform Import Guide"
tags = []
updated = "2024-06-04T22:20:09+00:00"
}
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
And it’s done! You can now manage this resource with Terraform.
Now let’s import some other resources. All Latitude.sh resources and how to define them can be found on the Latitude.sh Terraform Provider docs.
Finding resources IDs
As we’ve seen, it is necessary to pass the ID of the resource to the import block. The fastest way to obtain the IDs is by using our CLI, but you can also find them from the dashboard and API.
To learn how to set up the CLI, see the Latitude.sh CLI documentation page.
After installation, you can use the lsh
command in your terminal to access your infrastructure.
For example, to find the IDs of your tags, run lsh tags list
.
Importing using for_each
Here we have two servers that we want to import using Terraform’s for_each syntax:
Create the following file:
# servers.tf
locals {
servers = {
"tf-server" = {
id = "sv_6B9VaLJmZN7vr", # replace with the server id you want to import
project = latitudesh_project.guide_project.id
operating_system = "ubuntu_22_04_x64_lts"
site = "SAO"
plan = "c2-small-x86"
tags = [ "tag_mQ1EM1yJ6XIYlO1DKxXpIdevjPP" ] # replace with the tag id you want to import
},
"another-server" = {
id = "sv_7pWRawQkENrD6" # replace with the server id you want to import
project = latitudesh_project.guide_project.id
operating_system = "ubuntu_22_04_x64_lts"
site = "SAO"
plan = "c2-small-x86"
tags = []
},
}
}
import {
for_each = local.servers
id = each.value.id
to = latitudesh_server.server[each.key]
}
resource "latitudesh_server" "server" {
for_each = local.servers
hostname = each.key
project = each.value.project
operating_system = each.value.operating_system
site = each.value.site
plan = each.value.plan
}
Here we defined the server attributes as a local value object. Then we used the for_each
keyword to loop over the objects and get their key-value pair to build our resource. Running terraform apply
should import the servers successfully.
Always run terraform plan
before applying your changes to verify the changes
that will be made.
Generating Configuration
Terraform allows us to generate the resource based on our import block. Let’s import the rest of our infrastructure.
# import.tf
import {
to = latitudesh_virtual_network.vlan
id = "proj_7pWRawmOpNrD6:vlan_dexA0qj36NlQV"
}
import {
to = latitudesh_vlan_assignment.vlan_assignment
id = "proj_7pWRawmOpNrD6:vnasg_Yx2za1g9KNVrL"
}
import {
to = latitudesh_ssh_key.sshkey
id = "proj_7pWRawmOpNrD6:ssh_MDEOaPmyq0wgB"
}
import {
to = latitudesh_user_data.user_data
id = "proj_7pWRawmOpNrD6:ud_owPOapbdNB4xr"
}
import {
to = latitudesh_tag.guide_tag
id = "proj_7pWRawmOpNrD6:tag_mQ1EM1yJ6XIYlO1DKxXpIdevjPP"
}
The IDs here are in the form of projectID:resourceID
. This is needed for nested resources. On the provider documentation, you can verify if a resource is a nested resource or not.
To automatically generate the resource blocks, run:
terraform plan -generate-config-out=generated_resources.tf
This will create a generated_resources.tf
file with the resource blocks of the remaining infrastructure. Generating configuration is still an experimental feature, and it’s highly encouraged for you to verify the generated resources.
You can edit and organize resources in different files. When you’ve finished verifying the generated resources, run terraform apply
to finish your configuration.
Now you know everything needed to import your existing Latitude.sh infrastructure to Terraform.
Happy coding!