Migration strategy
- Incremental migration: Migrate resources incrementally rather than all at once. This reduces risk and allows for easier troubleshooting and rollback if issues arise.
- Parallel stacks: Run CloudFormation and Terraform in parallel during the transition period to validate that Terraform correctly manages the resources before fully decommissioning CloudFormation.
CloudFormation to Terraform migration examples
Example 1: Simple EC2 instance migration
It is important to understand that CloudFormation YAML and Terraform are two distinct languages for infrastructure provisioning. CloudFormation is designed specifically from an AWS perspective, whereas Terraform is declarative and supports multiple providers. In this example, you can look at the basics of an EC2 instance configuration using CloudFormation YAML and compare it with how it would look in Terraform HCL.
CloudFormation YAML:
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-0862be96e41dcbf74
KeyName: ubuntu
Terraform HCL:
provider "aws" {
region = "us-east-2" # Specify your AWS region here
}
resource "aws_instance" "my_ec2_instance" {
instance_type = "t2.micro"
ami = "ami-0862be96e41dcbf74"
key_name = "ubuntu"
tags = {
Name = "MyEC2Instance"
}
}
Example 2: Complex VPC migration
This is another example that demonstrates a VPC configuration in CloudFormation YAML and how you can define the same configuration using Terraform HCL.
CloudFormation YAML:
Resources:
MyVPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: "10.0.0.0/16"
Tags:
- Key: "Name"
Value: "demo-vpc"
MySubnet:
Type: "AWS::EC2::Subnet"
Properties:
VpcId: !Ref MyVPC
CidrBlock: "10.0.1.0/24"
Tags:
- Key: "Name"
Value: "demo-subnet1"
Terraform HCL:
provider "aws" {
region = "us-east-2"
}
resource "aws_vpc" "my_vpc" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "demo-vpc"
}
}
resource "aws_subnet" "my_subnet" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "10.0.1.0/24"
tags = {
Name = "demo-subnet1"
}
}
Example 3: IAM role migration
Now let us look at another example for an IAM role migration from CloudFormation YAML to Terraform HCL. In Terraform, the actual IAM policy is JSON-encoded.
CloudFormation YAML:
Resources:
MyIAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Terraform HCL:
resource "aws_iam_role" "example" {
name = "example-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
Example 4: DynamoDB migration
This example shows the migration of an AWS DynamoDB table from a CloudFormation YAML template to a Terraform HCL configuration. Both configurations define a DynamoDB table with similar attributes, key schema, provisioned throughput, global secondary indexes, and local secondary indexes. The Terraform configuration maps each element from the CloudFormation template, ensuring a smooth migration.
CloudFormation YAML:
AWSTemplateFormatVersion: "2010-09-09"
Resources:
myDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
-
AttributeName: "Album"
AttributeType: "S"
-
AttributeName: "Artist"
AttributeType: "S"
-
AttributeName: "Sales"
AttributeType: "N"
-
AttributeName: "NumberOfSongs"
AttributeType: "N"
KeySchema:
-
AttributeName: "Album"
KeyType: "HASH"
-
AttributeName: "Artist"
KeyType: "RANGE"
ProvisionedThroughput:
ReadCapacityUnits: "5"
WriteCapacityUnits: "5"
TableName: "myTableName"
GlobalSecondaryIndexes:
-
IndexName: "myGSI"
KeySchema:
-
AttributeName: "Sales"
KeyType: "HASH"
-
AttributeName: "Artist"
KeyType: "RANGE"
Projection:
NonKeyAttributes:
- "Album"
- "NumberOfSongs"
ProjectionType: "INCLUDE"
ProvisionedThroughput:
ReadCapacityUnits: "5"
WriteCapacityUnits: "5"
-
IndexName: "myGSI2"
KeySchema:
-
AttributeName: "NumberOfSongs"
KeyType: "HASH"
-
AttributeName: "Sales"
KeyType: "RANGE"
Projection:
NonKeyAttributes:
- "Album"
- "Artist"
ProjectionType: "INCLUDE"
ProvisionedThroughput:
ReadCapacityUnits: "5"
WriteCapacityUnits: "5"
LocalSecondaryIndexes:
-
IndexName: "myLSI"
KeySchema:
-
AttributeName: "Album"
KeyType: "HASH"
-
AttributeName: "Sales"
KeyType: "RANGE"
Projection:
NonKeyAttributes:
- "Artist"
- "NumberOfSongs"
ProjectionType: "INCLUDE"
Terraform HCL:
provider "aws" {
region = "us-east-2" # Specify your AWS region here
}
resource "aws_dynamodb_table" "my_dynamodb_table" {
name = "myTableName"
billing_mode = "PROVISIONED"
read_capacity = 5
write_capacity = 5
hash_key = "Album"
range_key = "Artist"
attribute {
name = "Album"
type = "S"
}
attribute {
name = "Artist"
type = "S"
}
attribute {
name = "Sales"
type = "N"
}
attribute {
name = "NumberOfSongs"
type = "N"
}
global_secondary_index {
name = "myGSI"
hash_key = "Sales"
range_key = "Artist"
read_capacity = 5
write_capacity = 5
projection_type = "INCLUDE"
non_key_attributes = [
"Album",
"NumberOfSongs"
]
}
global_secondary_index {
name = "myGSI2"
hash_key = "NumberOfSongs"
range_key = "Sales"
read_capacity = 5
write_capacity = 5
projection_type = "INCLUDE"
non_key_attributes = [
"Album",
"Artist"
]
}
local_secondary_index {
name = "myLSI"
range_key = "Sales"
projection_type = "INCLUDE"
non_key_attributes = [
"Artist",
"NumberOfSongs"
]
}
tags = {
Name = "myDynamoDBTable"
}
}
Using Terraform to execute existing CloudFormation templates
Terraform can be used to execute existing CloudFormation runs. This is useful when you have completed runbooks in CloudFormation and are seeking an easier way to migrate existing CloudFormation scripts to HCP Terraform. This approach allows for a smoother transition while maintaining the integrity of your current infrastructure setup.
Example 1: Executing CloudFormation templates stored in S3
Suppose you have a CloudFormation template stored in an S3 bucket. You can use the aws_cloudformation_stack resource in Terraform to deploy the template.
CloudFormation Template (S3) Example:
provider "aws" {
region = "us-west-2"
}
resource "aws_cloudformation_stack" "example" {
name = "example-stack"
template_url = "https://my-bucket.s3.amazonaws.com/templates/my-template.yaml"
parameters = {
ParameterKey = "ParameterValue"
}
tags = {
Name = "example-stack"
}
}
Example 2: Executing CloudFormation templates stored in GitHub
provider "aws" {
region = "us-west-2"
}
resource "aws_cloudformation_stack" "example" {
name = "example-stack"
template_url = "https://raw.githubusercontent.com/my-repo/cloudformation-templates/main/templates/my-template.yaml"
parameters = {
ParameterKey = "ParameterValue"
}
tags = {
Name = "example-stack"
}
}
Using Terraform to manage and run your current CloudFormation templates makes it easy to switch to Terraform while keeping your existing infrastructure setup. This approach helps you stay consistent and efficient during the move to HCP Terraform.
Migrating CloudFormation resources using Terraform import workflow
Migrating CloudFormation resources into Terraform using the Terraform config-driven import workflow is similar to importing any AWS resource into Terraform. Terraform stores all information in the state file and checks the configuration against this state file to determine any necessary changes to the infrastructure.
Our recommended approach is to leverage Terraform's terraform import
functionality to facilitate a seamless transition. By using terraform import
, existing AWS resources defined in CloudFormation can be efficiently brought under Terraform management without disrupting current operations.
The migration process begins with the identification of the AWS resources managed by CloudFormation. Each resource is then imported into the corresponding Terraform state using the terraform import
command. This method preserves the existing infrastructure while allowing it to be managed through Terraform's robust and flexible tooling.
For a more streamlined approach, the config-driven workflow is recommended, as it simplifies the migration by using a Terraform configuration block that clearly defines resource imports in a declarative manner. This workflow reduces manual steps and ensures a clear mapping between existing resources and their new Terraform-managed resources.
Key benefits of using terraform import
include:
- Minimal disruption: The existing infrastructure remains intact during the migration process, avoiding downtime.
- State preservation: Terraform accurately reflects the current state of resources, providing a reliable foundation for further changes.
- Flexibility: Once migrated, the infrastructure can be easily extended and managed using Terraform's extensive features.
For more details, please refer to the related resources section.
Related resources
When migrating from CloudFormation to Terraform, there are several tools available that can assist with the process. While none may fit every specific requirement, they can be very helpful:
- Terraformer: A CLI tool that generates Terraform configuration files and state from existing infrastructure (e.g., AWS, GCP, etc.). It can be found at Terraformer on GitHub.
- cf-to-tf: A tool designed to convert CloudFormation templates to Terraform configuration. It can be accessed at cf-to-tf on GitHub.
- Terraform import config block
- Terraform Config-driven imports and checks
- HVD: Terraform Import
- Import CloudFormation Resources into Terraform
- Migrate configuration to HCP Terraform.
- Manage resources in Terraform state