mirror of https://github.com/hashicorp/terraform
parent
b6e5606f4e
commit
874d047f74
@ -0,0 +1,60 @@
|
||||
# Sample Terraform configuration demonstrating AWS provider v3 patterns
|
||||
# that need migration to v4.
|
||||
#
|
||||
# Run: terraform migrate run -migrations-dir=. "v3to4/*"
|
||||
|
||||
# --- S3 Bucket Object Rename ---
|
||||
# v4 renames aws_s3_bucket_object to aws_s3_object.
|
||||
# The migration renames the resource type and rewrites all references.
|
||||
|
||||
resource "aws_s3_bucket" "assets" {
|
||||
bucket = "my-app-assets"
|
||||
|
||||
# v4 extracts versioning into a standalone aws_s3_bucket_versioning resource.
|
||||
# The extract_to_resource migration handles this automatically.
|
||||
versioning {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
# v4 extracts server_side_encryption_configuration into a standalone resource.
|
||||
server_side_encryption_configuration {
|
||||
rule {
|
||||
apply_server_side_encryption_by_default {
|
||||
sse_algorithm = "aws:kms"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# v4 removes the acl argument from aws_s3_bucket.
|
||||
# Must be migrated to a separate aws_s3_bucket_acl resource.
|
||||
acl = "private"
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_object" "config" {
|
||||
bucket = aws_s3_bucket.assets.id
|
||||
key = "config.json"
|
||||
content = jsonencode({ version = "1.0" })
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_object" "readme" {
|
||||
bucket = aws_s3_bucket.assets.id
|
||||
key = "README.md"
|
||||
source = "README.md"
|
||||
}
|
||||
|
||||
data "aws_s3_bucket_objects" "all_objects" {
|
||||
bucket = aws_s3_bucket.assets.id
|
||||
}
|
||||
|
||||
# References to aws_s3_bucket_object get rewritten to aws_s3_object
|
||||
output "config_object_id" {
|
||||
value = aws_s3_bucket_object.config.id
|
||||
}
|
||||
|
||||
output "readme_etag" {
|
||||
value = aws_s3_bucket_object.readme.etag
|
||||
}
|
||||
|
||||
output "object_keys" {
|
||||
value = data.aws_s3_bucket_objects.all_objects.keys
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "v3to4/extract_s3_acl",
|
||||
"description": "Extract acl argument from aws_s3_bucket, move to aws_s3_bucket_acl. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-4-upgrade.html.markdown#s3-bucket-refactor",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_s3_bucket"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"action": "extract_to_resource",
|
||||
"name": "acl",
|
||||
"to": "aws_s3_bucket_acl",
|
||||
"wire_attribute": "bucket",
|
||||
"wire_traversal": "id"
|
||||
},
|
||||
{
|
||||
"action": "remove_attribute",
|
||||
"name": "acl"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "v3to4/extract_s3_server_side_encryption",
|
||||
"description": "Extract aws_s3_bucket.server_side_encryption_configuration into standalone resource. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-4-upgrade.html.markdown#s3-bucket-refactor",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_s3_bucket"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"action": "extract_to_resource",
|
||||
"name": "server_side_encryption_configuration",
|
||||
"to": "aws_s3_bucket_server_side_encryption_configuration",
|
||||
"wire_attribute": "bucket",
|
||||
"wire_traversal": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "v3to4/extract_s3_versioning",
|
||||
"description": "Extract aws_s3_bucket.versioning into standalone aws_s3_bucket_versioning resource. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-4-upgrade.html.markdown#s3-bucket-refactor",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_s3_bucket"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"action": "extract_to_resource",
|
||||
"name": "versioning",
|
||||
"to": "aws_s3_bucket_versioning",
|
||||
"wire_attribute": "bucket",
|
||||
"wire_traversal": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "v3to4/rename_s3_bucket_object",
|
||||
"description": "Rename aws_s3_bucket_object to aws_s3_object. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-4-upgrade.html.markdown#resource-aws_s3_bucket_object",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_s3_bucket_object"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"action": "rename_resource",
|
||||
"to": "aws_s3_object"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "v3to4/rename_s3_bucket_objects_data",
|
||||
"description": "Rename data.aws_s3_bucket_objects to data.aws_s3_objects. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-4-upgrade.html.markdown#data-source-aws_s3_bucket_objects",
|
||||
"match": {
|
||||
"block_type": "data",
|
||||
"label": "aws_s3_bucket_objects"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"action": "rename_resource",
|
||||
"to": "aws_s3_objects"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
# Sample Terraform configuration demonstrating AWS provider v4 patterns
|
||||
# that need migration to v5.
|
||||
#
|
||||
# Run: terraform migrate run -migrations-dir=. "v4to5/*"
|
||||
|
||||
# --- EC2-Classic Attribute Removal ---
|
||||
# v5 removes all EC2-Classic support. These attributes no longer exist.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#ec2-classic-resource-and-data-source-removal
|
||||
|
||||
resource "aws_instance" "web" {
|
||||
ami = "ami-0c55b159cbfafe1f0"
|
||||
instance_type = "t3.micro"
|
||||
security_groups = ["default"]
|
||||
vpc_classic_link_id = "vpc-abc123"
|
||||
vpc_classic_link_security_groups = ["sg-abc123"]
|
||||
vpc_security_group_ids = [aws_security_group.web.id]
|
||||
}
|
||||
|
||||
resource "aws_security_group" "web" {
|
||||
name = "web-sg"
|
||||
}
|
||||
|
||||
# --- Autoscaling Attachment Rename ---
|
||||
# v5 renames alb_target_group_arn to lb_target_group_arn.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_autoscaling_attachment
|
||||
|
||||
resource "aws_autoscaling_attachment" "asg_alb" {
|
||||
autoscaling_group_name = "my-asg"
|
||||
alb_target_group_arn = "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/abc123"
|
||||
}
|
||||
|
||||
# --- Elasticache Replication Group ---
|
||||
# v5 renames several attributes and removes the cluster_mode block.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_elasticache_replication_group
|
||||
|
||||
resource "aws_elasticache_replication_group" "redis" {
|
||||
replication_group_id = "my-redis"
|
||||
replication_group_description = "Production Redis cluster"
|
||||
node_type = "cache.r6g.large"
|
||||
number_cache_clusters = 3
|
||||
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
|
||||
|
||||
cluster_mode {
|
||||
num_node_groups = 3
|
||||
replicas_per_node_group = 2
|
||||
}
|
||||
}
|
||||
|
||||
# --- DB Instance Name Rename ---
|
||||
# v5 renames 'name' to 'db_name' on aws_db_instance.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_db_instance
|
||||
|
||||
resource "aws_db_instance" "postgres" {
|
||||
identifier = "my-postgres"
|
||||
engine = "postgres"
|
||||
engine_version = "14.1"
|
||||
instance_class = "db.t3.micro"
|
||||
name = "myappdb"
|
||||
username = "admin"
|
||||
password = var.db_password
|
||||
}
|
||||
|
||||
# --- DB Security Group Removal (EC2-Classic) ---
|
||||
# v5 removes aws_db_security_group entirely.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#ec2-classic-resource-and-data-source-removal
|
||||
|
||||
resource "aws_db_security_group" "legacy" {
|
||||
name = "legacy-db-sg"
|
||||
}
|
||||
|
||||
resource "aws_db_instance" "legacy_db" {
|
||||
identifier = "legacy"
|
||||
engine = "mysql"
|
||||
instance_class = "db.t3.micro"
|
||||
name = "legacydb"
|
||||
username = "admin"
|
||||
password = var.db_password
|
||||
# This reference will get a FIXME comment when the security group is removed
|
||||
db_security_groups = [aws_db_security_group.legacy.name]
|
||||
}
|
||||
|
||||
# --- OpenSearch Kibana Rename ---
|
||||
# v5 renames kibana_endpoint to dashboard_endpoint.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_opensearch_domain
|
||||
|
||||
resource "aws_opensearch_domain" "search" {
|
||||
domain_name = "my-search"
|
||||
engine_version = "OpenSearch_2.3"
|
||||
}
|
||||
|
||||
output "search_dashboard" {
|
||||
value = aws_opensearch_domain.search.kibana_endpoint
|
||||
}
|
||||
|
||||
# --- RDS Cluster Engine Now Required ---
|
||||
# v5 removes the default for engine on aws_rds_cluster.
|
||||
# The add_attribute action only sets it if not already present.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_rds_cluster
|
||||
|
||||
resource "aws_rds_cluster" "aurora" {
|
||||
cluster_identifier = "my-aurora"
|
||||
master_username = "admin"
|
||||
master_password = var.db_password
|
||||
}
|
||||
|
||||
resource "aws_rds_cluster" "aurora_mysql" {
|
||||
cluster_identifier = "my-aurora-mysql"
|
||||
engine = "aurora-mysql"
|
||||
master_username = "admin"
|
||||
master_password = var.db_password
|
||||
}
|
||||
|
||||
variable "db_password" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v4to5/flatten_elasticache_cluster_mode",
|
||||
"description": "Flatten cluster_mode block into top-level attributes. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_elasticache_replication_group",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_elasticache_replication_group"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "flatten_block", "name": "cluster_mode"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v4to5/remove_db_security_group",
|
||||
"description": "Remove aws_db_security_group (EC2-Classic). See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#ec2-classic-resource-and-data-source-removal",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_db_security_group"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "remove_resource", "text": "FIXME: aws_db_security_group has been removed in v5 (EC2-Classic). Use aws_db_subnet_group with VPC security groups instead."}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "v4to5/remove_ec2_classic",
|
||||
"description": "Remove EC2-Classic attributes from aws_instance. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#ec2-classic-resource-and-data-source-removal",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_instance"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "remove_attribute", "name": "security_groups"},
|
||||
{"action": "remove_attribute", "name": "vpc_classic_link_id"},
|
||||
{"action": "remove_attribute", "name": "vpc_classic_link_security_groups"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v4to5/rename_autoscaling_attachment",
|
||||
"description": "Rename alb_target_group_arn to lb_target_group_arn. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_autoscaling_attachment",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_autoscaling_attachment"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "rename_attribute", "from": "alb_target_group_arn", "to": "lb_target_group_arn"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v4to5/rename_db_instance_name",
|
||||
"description": "Rename 'name' to 'db_name' on aws_db_instance. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_db_instance",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_db_instance"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "rename_attribute", "from": "name", "to": "db_name"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "v4to5/rename_elasticache_attrs",
|
||||
"description": "Rename deprecated attributes on aws_elasticache_replication_group. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_elasticache_replication_group",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_elasticache_replication_group"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "rename_attribute", "from": "replication_group_description", "to": "description"},
|
||||
{"action": "rename_attribute", "from": "number_cache_clusters", "to": "num_cache_clusters"},
|
||||
{"action": "rename_attribute", "from": "availability_zones", "to": "preferred_cache_cluster_azs"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v4to5/rename_opensearch_kibana",
|
||||
"description": "Rename kibana_endpoint to dashboard_endpoint on aws_opensearch_domain. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_opensearch_domain",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_opensearch_domain"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "rename_attribute", "from": "kibana_endpoint", "to": "dashboard_endpoint"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v4to5/require_rds_cluster_engine",
|
||||
"description": "Add engine attribute to aws_rds_cluster (now required, no default). See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#resource-aws_rds_cluster",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_rds_cluster"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "add_attribute", "name": "engine", "value": "aurora"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
# Sample Terraform configuration demonstrating AWS provider v5 patterns
|
||||
# that need migration to v6.
|
||||
#
|
||||
# Run: terraform migrate run -migrations-dir=. "v5to6/*"
|
||||
|
||||
# --- CPU Options Restructure ---
|
||||
# v6 moves cpu_core_count and cpu_threads_per_core into a cpu_options block.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#resource-aws_instance
|
||||
|
||||
resource "aws_instance" "compute" {
|
||||
ami = "ami-0c55b159cbfafe1f0"
|
||||
instance_type = "c5.xlarge"
|
||||
cpu_core_count = 2
|
||||
cpu_threads_per_core = 1
|
||||
}
|
||||
|
||||
resource "aws_instance" "dynamic_compute" {
|
||||
ami = var.ami_id
|
||||
instance_type = var.instance_type
|
||||
cpu_core_count = var.cpu_cores
|
||||
cpu_threads_per_core = var.env == "prod" ? 2 : 1
|
||||
}
|
||||
|
||||
# --- Batch Compute Environment Name Rename ---
|
||||
# v6 renames compute_environment_name to name.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#resource-aws_batch_compute_environment
|
||||
|
||||
resource "aws_batch_compute_environment" "batch" {
|
||||
compute_environment_name = "my-batch-env"
|
||||
type = "MANAGED"
|
||||
|
||||
compute_resources {
|
||||
type = "FARGATE"
|
||||
max_vcpus = 16
|
||||
}
|
||||
}
|
||||
|
||||
# --- S3 Bucket Region Rename ---
|
||||
# v6 renames 'region' to 'bucket_region'.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#resource-aws_s3_bucket
|
||||
|
||||
resource "aws_s3_bucket" "data" {
|
||||
bucket = "my-data-bucket"
|
||||
region = "us-east-1"
|
||||
}
|
||||
|
||||
# --- OpsWorks Removal ---
|
||||
# v6 removes all aws_opsworks_* resources (service discontinued).
|
||||
# The remove_resource migration removes the block and adds FIXME comments
|
||||
# to any files that reference it.
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#removal-of-aws_opsworks_-resources
|
||||
|
||||
resource "aws_opsworks_stack" "legacy" {
|
||||
name = "my-legacy-stack"
|
||||
region = "us-east-1"
|
||||
|
||||
service_role_arn = aws_iam_role.opsworks.arn
|
||||
default_instance_profile_arn = aws_iam_instance_profile.opsworks.arn
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "opsworks" {
|
||||
name = "opsworks-role"
|
||||
assume_role_policy = "{}"
|
||||
}
|
||||
|
||||
resource "aws_iam_instance_profile" "opsworks" {
|
||||
name = "opsworks-profile"
|
||||
role = aws_iam_role.opsworks.name
|
||||
}
|
||||
|
||||
# This output references the opsworks stack and will get a FIXME comment
|
||||
output "stack_id" {
|
||||
value = aws_opsworks_stack.legacy.id
|
||||
}
|
||||
|
||||
# --- Launch Template GPU Removal ---
|
||||
# v6 removes elastic_gpu_specifications (service discontinued).
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#resource-aws_launch_template
|
||||
|
||||
resource "aws_launch_template" "gpu" {
|
||||
name = "gpu-template"
|
||||
instance_type = "p3.2xlarge"
|
||||
image_id = "ami-gpu-123"
|
||||
|
||||
elastic_gpu_specifications {
|
||||
type = "eg1.medium"
|
||||
}
|
||||
}
|
||||
|
||||
variable "ami_id" {
|
||||
type = string
|
||||
default = "ami-0c55b159cbfafe1f0"
|
||||
}
|
||||
|
||||
variable "instance_type" {
|
||||
type = string
|
||||
default = "c5.xlarge"
|
||||
}
|
||||
|
||||
variable "cpu_cores" {
|
||||
type = number
|
||||
default = 2
|
||||
}
|
||||
|
||||
variable "env" {
|
||||
type = string
|
||||
default = "dev"
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "v5to6/move_instance_cpu_core_count",
|
||||
"description": "Move cpu_core_count into cpu_options block. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#resource-aws_instance",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_instance"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "move_attribute_to_block", "name": "cpu_core_count", "block_name": "cpu_options", "to": "core_count"},
|
||||
{"action": "move_attribute_to_block", "name": "cpu_threads_per_core", "block_name": "cpu_options", "to": "threads_per_core"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v5to6/remove_launch_template_gpu",
|
||||
"description": "Remove discontinued elastic_gpu_specifications and elastic_inference_accelerator from aws_launch_template. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#resource-aws_launch_template",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_launch_template"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "add_comment", "text": "FIXME: elastic_gpu_specifications has been removed in v6 (service discontinued). Remove the block manually."}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v5to6/remove_opsworks_stack",
|
||||
"description": "Remove aws_opsworks_stack (service discontinued). See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#removal-of-aws_opsworks_-resources",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_opsworks_stack"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "remove_resource", "text": "FIXME: aws_opsworks_stack has been removed in v6 (OpsWorks discontinued). Migrate to ECS, EKS, or another deployment service."}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v5to6/rename_batch_compute_env",
|
||||
"description": "Rename compute_environment_name to name. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#resource-aws_batch_compute_environment",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_batch_compute_environment"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "rename_attribute", "from": "compute_environment_name", "to": "name"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "v5to6/rename_s3_bucket_region",
|
||||
"description": "Rename region to bucket_region on aws_s3_bucket. See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown#resource-aws_s3_bucket",
|
||||
"match": {
|
||||
"block_type": "resource",
|
||||
"label": "aws_s3_bucket"
|
||||
},
|
||||
"actions": [
|
||||
{"action": "rename_attribute", "from": "region", "to": "bucket_region"}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,197 @@
|
||||
# Patterns that CANNOT be automated with the current migration system.
|
||||
# Each section describes what would be needed to support it.
|
||||
|
||||
# ============================================================================
|
||||
# LIMITATION 1: S3 Lifecycle Rule Extraction with Structural Rewriting
|
||||
# ============================================================================
|
||||
# The v3→v4 S3 lifecycle_rule extraction is the most complex migration.
|
||||
# The nested block structure changes significantly: attribute names change,
|
||||
# values change type (bool→string), and sub-blocks are restructured.
|
||||
#
|
||||
# What would be needed: a "transform" action that can rewrite nested block
|
||||
# structure and map values (e.g., enabled=true → status="Enabled").
|
||||
#
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-4-upgrade.html.markdown#s3-bucket-refactor
|
||||
|
||||
resource "aws_s3_bucket" "with_lifecycle" {
|
||||
bucket = "my-bucket"
|
||||
|
||||
lifecycle_rule {
|
||||
id = "expire-old"
|
||||
enabled = true # v4 changes to: status = "Enabled"
|
||||
prefix = "logs/" # v4 changes to: filter { prefix = "logs/" }
|
||||
|
||||
expiration {
|
||||
days = 90
|
||||
}
|
||||
|
||||
noncurrent_version_expiration {
|
||||
days = 30 # v4 renames to: noncurrent_days = 30
|
||||
}
|
||||
|
||||
transition {
|
||||
days = 30
|
||||
storage_class = "STANDARD_IA"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# LIMITATION 2: Dynamic Blocks with for_each
|
||||
# ============================================================================
|
||||
# When resources use dynamic blocks, the iterator variable creates indirect
|
||||
# references that can't be tracked by simple token-level prefix matching.
|
||||
# The migration system doesn't understand that `each.value.ami` refers to
|
||||
# the same thing as `var.instances["web"].ami`.
|
||||
#
|
||||
# What would be needed: expression-level analysis that understands for_each
|
||||
# iterator bindings and can rename attributes inside dynamic block content.
|
||||
|
||||
resource "aws_instance" "dynamic_fleet" {
|
||||
for_each = var.instances
|
||||
|
||||
ami = each.value.ami # If "ami" is renamed to "image_id",
|
||||
instance_type = each.value.instance_type # this can't be auto-updated because
|
||||
# the rename_attribute action only
|
||||
# touches the block's own attributes,
|
||||
# not the map values feeding for_each.
|
||||
}
|
||||
|
||||
variable "instances" {
|
||||
type = map(object({
|
||||
ami = string # This key name won't be updated
|
||||
instance_type = string
|
||||
}))
|
||||
default = {
|
||||
web = {
|
||||
ami = "ami-web123"
|
||||
instance_type = "t3.micro"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# LIMITATION 3: Conditional Resource Creation with count
|
||||
# ============================================================================
|
||||
# When a removed resource is conditionally created with count, the migration
|
||||
# system removes it but can't update count-dependent references like
|
||||
# aws_db_security_group.legacy[0].name. The reference rewriting works at the
|
||||
# resource type level but doesn't handle indexed references specially.
|
||||
#
|
||||
# What would be needed: reference-aware removal that can find and comment
|
||||
# out indexed references (resource.name[0].attr, resource.name[*].attr).
|
||||
|
||||
resource "aws_db_security_group" "legacy" {
|
||||
count = var.use_classic ? 1 : 0
|
||||
name = "legacy-sg"
|
||||
}
|
||||
|
||||
resource "aws_db_instance" "uses_classic" {
|
||||
identifier = "my-db"
|
||||
engine = "mysql"
|
||||
instance_class = "db.t3.micro"
|
||||
|
||||
# This indexed reference won't be caught by remove_resource's
|
||||
# ReferencesPrefix check because it looks for "aws_db_security_group"
|
||||
# as a traversal root, but this expression uses a complex index operation.
|
||||
db_security_groups = var.use_classic ? [aws_db_security_group.legacy[0].name] : []
|
||||
}
|
||||
|
||||
variable "use_classic" {
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# LIMITATION 4: Provider Configuration Changes
|
||||
# ============================================================================
|
||||
# v5 renames several provider-level attributes. While rename_attribute works
|
||||
# on provider blocks, authentication precedence changes and new required
|
||||
# fields can't be expressed as simple migrations.
|
||||
#
|
||||
# What would be needed: provider blocks matched via {"block_type": "provider",
|
||||
# "label": "aws"} work with rename_attribute. But behavioral changes like
|
||||
# auth credential precedence reordering are not expressible.
|
||||
#
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-5-upgrade.html.markdown#changes-to-authentication
|
||||
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
|
||||
# v5 renames these (automatable):
|
||||
# shared_credentials_file → shared_credentials_files
|
||||
# s3_force_path_style → s3_use_path_style
|
||||
shared_credentials_file = "~/.aws/credentials"
|
||||
s3_force_path_style = true
|
||||
|
||||
# v5 changes auth credential resolution order (NOT automatable):
|
||||
# Previously: env vars > shared credentials > IAM role
|
||||
# Now: env vars > shared credentials > SSO > IAM role
|
||||
# If your config relied on the old precedence, no migration can fix this.
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# LIMITATION 5: Value Type Changes Requiring Expression Rewriting
|
||||
# ============================================================================
|
||||
# Some v6 changes convert between types in ways that require understanding
|
||||
# the full expression, not just pattern-matching a literal value.
|
||||
#
|
||||
# replace_value can handle: true → "Enabled", "" → null, 0 → true
|
||||
# replace_value CANNOT handle: expressions, variables, or interpolations.
|
||||
#
|
||||
# See: https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/guides/version-6-upgrade.html.markdown
|
||||
|
||||
resource "aws_wafv2_web_acl" "example" {
|
||||
name = "my-acl"
|
||||
scope = "REGIONAL"
|
||||
|
||||
# replace_value can fix: enable_machine_learning = true → false
|
||||
# But this conditional can't be pattern-matched:
|
||||
visibility_config {
|
||||
sampled_requests_enabled = true
|
||||
cloudwatch_metrics_enabled = true
|
||||
metric_name = "my-metric"
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# LIMITATION 6: Cross-Resource Wiring After Extraction
|
||||
# ============================================================================
|
||||
# When extract_to_resource pulls a nested block into a new resource, it creates
|
||||
# a basic wiring attribute (e.g., bucket = aws_s3_bucket.X.id). But if other
|
||||
# resources reference attributes of the *extracted* nested block through the
|
||||
# parent, those references break silently.
|
||||
#
|
||||
# Example: if another resource references aws_s3_bucket.main.versioning[0].enabled,
|
||||
# that reference won't exist after extraction. The migration system doesn't
|
||||
# rewrite these cross-resource nested attribute references.
|
||||
#
|
||||
# What would be needed: deep reference analysis that tracks nested block
|
||||
# attribute paths and rewrites them to point at the new standalone resource.
|
||||
|
||||
resource "aws_s3_bucket" "main" {
|
||||
bucket = "my-bucket"
|
||||
versioning {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
# After extraction, this reference path breaks because
|
||||
# aws_s3_bucket.main.versioning no longer exists.
|
||||
output "versioning_status" {
|
||||
value = aws_s3_bucket.main.versioning[0].enabled
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# LIMITATION 7: Import Commands Required After Extraction
|
||||
# ============================================================================
|
||||
# After extract_to_resource creates new standalone resources, Terraform will
|
||||
# see them as new resources to create. The user must run terraform import
|
||||
# for each extracted resource to adopt existing infrastructure.
|
||||
#
|
||||
# This is a fundamental limitation: HCL migration only handles the config
|
||||
# files, not the state. Users need to run:
|
||||
# terraform import aws_s3_bucket_versioning.main <bucket-name>
|
||||
#
|
||||
# What would be needed: integration with terraform state commands, or
|
||||
# generating a shell script of import commands alongside the migration.
|
||||
@ -0,0 +1,236 @@
|
||||
// Copyright IBM Corp. 2014, 2026
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/ast"
|
||||
)
|
||||
|
||||
// TestExamples_AWS_v3to4 verifies the v3→v4 example migrations run against the sample.
|
||||
func TestExamples_AWS_v3to4(t *testing.T) {
|
||||
examplesDir := filepath.Join("examples", "aws-v3-to-v4")
|
||||
migrations, err := DiscoverMigrations(examplesDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
migrations = FilterMigrations(migrations, "v3to4/*")
|
||||
if len(migrations) == 0 {
|
||||
t.Fatal("no v3to4 migrations found")
|
||||
}
|
||||
|
||||
src, err := os.ReadFile(filepath.Join(examplesDir, "sample.tf"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f, err := ast.ParseFile(src, "sample.tf", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mod := ast.NewModule([]*ast.File{f}, "", true, nil)
|
||||
|
||||
for _, m := range migrations {
|
||||
if err := Execute(m, mod); err != nil {
|
||||
t.Fatalf("migration %s failed: %v", m.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
got := string(mod.Bytes()["sample.tf"])
|
||||
|
||||
// resource "aws_s3_bucket_object" should be renamed to "aws_s3_object"
|
||||
if strings.Contains(got, `resource "aws_s3_bucket_object"`) {
|
||||
t.Error("expected resource aws_s3_bucket_object renamed to aws_s3_object")
|
||||
}
|
||||
if !strings.Contains(got, `resource "aws_s3_object" "config"`) {
|
||||
t.Error("expected aws_s3_object resource")
|
||||
}
|
||||
|
||||
// data "aws_s3_bucket_objects" should be renamed to "aws_s3_objects"
|
||||
if strings.Contains(got, `data "aws_s3_bucket_objects"`) {
|
||||
t.Error("expected data aws_s3_bucket_objects renamed to aws_s3_objects")
|
||||
}
|
||||
if !strings.Contains(got, `data "aws_s3_objects"`) {
|
||||
t.Error("expected data aws_s3_objects resource")
|
||||
}
|
||||
|
||||
// Resource references should be rewritten
|
||||
if !strings.Contains(got, "aws_s3_object.config.id") {
|
||||
t.Error("expected references rewritten to aws_s3_object")
|
||||
}
|
||||
// NOTE: data source references (data.aws_s3_bucket_objects...) are NOT
|
||||
// rewritten because the traversal root is "data", not the resource type.
|
||||
// This is a known limitation.
|
||||
|
||||
// Versioning should be extracted to new resource
|
||||
if !strings.Contains(got, `resource "aws_s3_bucket_versioning" "assets"`) {
|
||||
t.Error("expected aws_s3_bucket_versioning resource extracted")
|
||||
}
|
||||
|
||||
// acl should be removed with FIXME comment
|
||||
if strings.Contains(got, `acl = "private"`) {
|
||||
t.Error("expected acl attribute removed")
|
||||
}
|
||||
if !strings.Contains(got, "FIXME") {
|
||||
t.Error("expected FIXME comment for acl removal")
|
||||
}
|
||||
}
|
||||
|
||||
// TestExamples_AWS_v4to5 verifies the v4→v5 example migrations run against the sample.
|
||||
func TestExamples_AWS_v4to5(t *testing.T) {
|
||||
examplesDir := filepath.Join("examples", "aws-v4-to-v5")
|
||||
migrations, err := DiscoverMigrations(examplesDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
migrations = FilterMigrations(migrations, "v4to5/*")
|
||||
if len(migrations) == 0 {
|
||||
// Print all found migrations for debugging
|
||||
allMigs, _ := DiscoverMigrations(examplesDir)
|
||||
t.Fatalf("no v4to5 migrations found after filter. All discovered: %d", len(allMigs))
|
||||
}
|
||||
|
||||
src, err := os.ReadFile(filepath.Join(examplesDir, "sample.tf"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f, err := ast.ParseFile(src, "sample.tf", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mod := ast.NewModule([]*ast.File{f}, "", true, nil)
|
||||
|
||||
for _, m := range migrations {
|
||||
if err := Execute(m, mod); err != nil {
|
||||
t.Fatalf("migration %s failed: %v", m.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
got := string(mod.Bytes()["sample.tf"])
|
||||
|
||||
// EC2-Classic attributes should be removed
|
||||
if strings.Contains(got, "vpc_classic_link_id") {
|
||||
t.Error("expected vpc_classic_link_id removed")
|
||||
}
|
||||
// vpc_security_group_ids should survive
|
||||
if !strings.Contains(got, "vpc_security_group_ids") {
|
||||
t.Error("expected vpc_security_group_ids preserved")
|
||||
}
|
||||
|
||||
// Autoscaling attachment renamed (check attribute assignment, not comments)
|
||||
if strings.Contains(got, "alb_target_group_arn =") {
|
||||
t.Error("expected alb_target_group_arn renamed to lb_target_group_arn")
|
||||
}
|
||||
if !strings.Contains(got, "lb_target_group_arn") {
|
||||
t.Error("expected lb_target_group_arn present")
|
||||
}
|
||||
|
||||
// Elasticache attributes renamed
|
||||
if strings.Contains(got, "replication_group_description") {
|
||||
t.Error("expected replication_group_description renamed to description")
|
||||
}
|
||||
if strings.Contains(got, "number_cache_clusters") {
|
||||
t.Error("expected number_cache_clusters renamed")
|
||||
}
|
||||
|
||||
// cluster_mode block should be flattened
|
||||
if strings.Contains(got, "cluster_mode {") {
|
||||
t.Error("expected cluster_mode block flattened")
|
||||
}
|
||||
if !strings.Contains(got, "num_node_groups") {
|
||||
t.Error("expected num_node_groups at top level")
|
||||
}
|
||||
|
||||
// DB instance name renamed
|
||||
if strings.Contains(got, `name = "myappdb"`) {
|
||||
t.Error("expected 'name' renamed to 'db_name'")
|
||||
}
|
||||
|
||||
// aws_db_security_group should be removed
|
||||
if strings.Contains(got, `resource "aws_db_security_group"`) {
|
||||
t.Error("expected aws_db_security_group removed")
|
||||
}
|
||||
if !strings.Contains(got, "FIXME: aws_db_security_group") {
|
||||
t.Error("expected FIXME comment for removed resource")
|
||||
}
|
||||
|
||||
// RDS cluster without engine should get one added
|
||||
// (the first cluster has no engine, the second already has engine="aurora-mysql")
|
||||
if !strings.Contains(got, "aurora-mysql") {
|
||||
t.Error("expected existing aurora-mysql engine preserved")
|
||||
}
|
||||
}
|
||||
|
||||
// TestExamples_AWS_v5to6 verifies the v5→v6 example migrations run against the sample.
|
||||
func TestExamples_AWS_v5to6(t *testing.T) {
|
||||
examplesDir := filepath.Join("examples", "aws-v5-to-v6")
|
||||
migrations, err := DiscoverMigrations(examplesDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
migrations = FilterMigrations(migrations, "v5to6/*")
|
||||
if len(migrations) == 0 {
|
||||
t.Fatal("no v5to6 migrations found")
|
||||
}
|
||||
|
||||
src, err := os.ReadFile(filepath.Join(examplesDir, "sample.tf"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f, err := ast.ParseFile(src, "sample.tf", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mod := ast.NewModule([]*ast.File{f}, "", true, nil)
|
||||
|
||||
for _, m := range migrations {
|
||||
if err := Execute(m, mod); err != nil {
|
||||
t.Fatalf("migration %s failed: %v", m.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
got := string(mod.Bytes()["sample.tf"])
|
||||
|
||||
// cpu_core_count and cpu_threads_per_core should be moved into cpu_options
|
||||
// (check attribute assignments, not comments)
|
||||
if strings.Contains(got, "cpu_core_count =") {
|
||||
t.Error("expected cpu_core_count moved to cpu_options block")
|
||||
}
|
||||
if strings.Contains(got, "cpu_threads_per_core =") {
|
||||
t.Error("expected cpu_threads_per_core moved to cpu_options block")
|
||||
}
|
||||
if !strings.Contains(got, "cpu_options") {
|
||||
t.Error("expected cpu_options block created")
|
||||
}
|
||||
|
||||
// Batch compute environment rename (check attr assignment, not comments)
|
||||
if strings.Contains(got, "compute_environment_name =") {
|
||||
t.Error("expected compute_environment_name renamed to name")
|
||||
}
|
||||
|
||||
// OpsWorks should be removed (check resource block, not comments)
|
||||
if strings.Contains(got, `resource "aws_opsworks_stack"`) {
|
||||
t.Error("expected aws_opsworks_stack removed")
|
||||
}
|
||||
if !strings.Contains(got, "FIXME: aws_opsworks_stack") {
|
||||
t.Error("expected FIXME comment for opsworks removal")
|
||||
}
|
||||
|
||||
// S3 bucket region renamed (check attr assignment, not comments)
|
||||
if strings.Contains(got, "region = ") && strings.Contains(got, `"my-data-bucket"`) {
|
||||
// The S3 bucket should have bucket_region, not region
|
||||
if !strings.Contains(got, "bucket_region") {
|
||||
t.Error("expected region renamed to bucket_region on aws_s3_bucket")
|
||||
}
|
||||
}
|
||||
|
||||
// elastic_gpu_specifications can't be auto-removed (it's a block, not attribute),
|
||||
// so the migration adds a FIXME comment instead
|
||||
if !strings.Contains(got, "FIXME: elastic_gpu_specifications") {
|
||||
t.Error("expected FIXME comment for elastic_gpu_specifications")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue