Import existing AWS infrastructure into Terraform
by Afanasy Barbarov
The recipe: migrate the existing blog to AWS
AWS has tons of services. Multiplying that to the number of regions gives us a lot more. It's hard to remember what and where something is applied in the AWS console. Let's fix that by migrating this blog to Terraform managed solution.
Since we don't create the infrastructure from scratch, we need to import it to Terraform. I use these services in my blog so far, so need to import them:
- S3
- Route53
- CloudFront
Let's start importing them one by one. But first, create a folder, called aws/tf to keep everything inside and create an empty main.tf file inside. Naming is not strict, but I chose that one for logical separation.
We need a provider to work with AWS. Adding
provider "aws" {
profile = "barbarov"
region = "eu-west-1"
# Make it faster by skipping some checks
skip_get_ec2_platforms = true
skip_metadata_api_check = true
skip_region_validation = true
skip_credentials_validation = true
skip_requesting_account_id = true
}will tell Terraform that we are using AWS. The next two things are a backend (to store Terrafom's state), and a DynamoDB table (to handle concurrent locks of the state).
resource "aws_s3_bucket" "s3_terraform_state" {
bucket = "terraform-state.barbarov.com"
versioning {
enabled = true
}
lifecycle {
prevent_destroy = true
}
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
tags = {
Name = "S3 Remote Terraform State"
Environment = "Production"
}
}
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-barbarov-com-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = "DynamoDB Table Terraform Locks"
Environment = "Production"
}
}We first initialize DynamoDB and S3 bucket, and only then use them to keep the state. To physically create resources, navigate to the aws/tf folder and run terraform init and then terraform apply.
Since I have multiple AWS profiles installed, I need to run first export AWS_PROFILE="barbarov" to use the correct one, otherwise I'd face permission errors.
Now let's glue both resources to keep the state. Remove .terraform folder and .terraform lock files. Then add this part and running terraform init once again and then terraform apply will achieve that.
terraform {
backend "s3" {
encrypt = true
bucket = "terraform-state.barbarov.com"
region = "eu-west-1"
key = "global/terraform.tfstate"
dynamodb_table = "terraform-barbarov-com-locks"
}
}So we have initialized the infrastructure, so let's import services. Create an empty resource
resource "aws_s3_bucket" "static_site" {
}And import configuration into it.
terraform import aws_s3_bucket.static_site barbarov.comNow, the resource is imported, but we need to control it. So we must add some resource description to terraform. To achieve that, there is a hack - comment the resource and try terraform plan to see the changes and copy the configuration (but DON'T APPLY the changes).
My config become similar to this after the step from above:
resource "aws_s3_bucket" "static_site" {
bucket = "barbarov.com"
acl = "private"
tags = {
"Project" = "portfolio"
"Type" = "S3"
}
website {
index_document = "index.html"
}
force_destroy = false
}Let's do the same for the CloudFront - create an empty resource definition
resource "aws_cloudfront_distribution" "static_site_distribution" {
}and import it:
terraform import \
aws_cloudfront_distribution.static_site_distribution DISTRIBUTIONIDAnd last, but not least, import Route53 - create a resource
resource "aws_cloudfront_distribution" "static_site_distribution" {
}and import it:
terraform import aws_route53_record.main_domain ZONEID_barbarov.com_ANow we are free to change everything via Terraform scripts
That's all, folks!