Mon Jan 29
Gerenciando Ambientes com Terraform Workspaces
Os benefícios de usar Terraform Workspaces para criar recursos em diferentes ambientes de forma isolada e organizada.
Terraform Workspaces provide the flexibility to manage multiple environments, such as development, testing, and production, in an efficient and isolated manner within the same Terraform working directory
After understanding the benefits of using Terraform (IaC) and how to start and execute a project, today I’d like to talk about a feature called Workspaces in the Terraform CLI.
By default, Terraform projects are created with a directory structure, which we used in the previous post to create an EC2 instance on AWS. However, today, we will use this new approach to see the differences and how to explore them best.
Benefits of using Terraform Workspaces — CLI:
- Directory Structure vs. Workspaces: Using a directory structure might require duplicating code. With Workspaces, it’s possible to use the same code in different contexts, reducing redundancy.
- State Management: Standard approaches might require state files for each environment, increasing management complexity and the risk of applying changes to the wrong state file related to the wrong environment, leading to inconsistencies and environment errors. Workspaces, on the other hand, automatically manage these separate states, eliminating the need to maintain state files for each environment manually.
- Environment Variables: Other approaches might depend on the use of environment variables or external configuration files to differentiate environments, and Workspaces allow for the definition of specific variables for each environment within the Terraform configuration itself.
- Environment Isolation: Workspaces manage their resources, allowing different environments (development, testing, and production) to be maintained separately, reducing the risk of conflicts. Additionally, each workspace has its state management file, simplifying state management and reducing complexity.
- Workflow Simplification: When changing the workspace, Terraform automatically switches to the state file corresponding to that workspace.
Terraform Workspaces offers a more organized and efficient code solution to manage different environments. However, it’s important to recognize that they may not be ideal in certain scenarios. For example, in situations where development and production environments differ significantly due to specific needs or ongoing tests, using versioned modules might be more appropriate. This allows changes to various versions of the infrastructure in a more controlled and segregated manner. This topic will be addressed in the post about modules.
Requirements
- AWS Credentials Configuration: The first step is configuring AWS credentials, which is essential for the AWS provider to authenticate and create resources. This requires an AWS account with the appropriate permissions. For more details, refer to the AWS documentation at https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html.
- Terraform Installation: The installation of Terraform is not the focus of this publication, as it can be done by following the instructions in the official Terraform documentation. For more information on how to install Terraform, consult the Terraform Installation Guide.
- Project Preparation: The code for the project implementation is available on GitHub (link provided).
Creating and Managing Environments with Workspaces
The syntax for Workspaces is quite straightforward. You will essentially create and switch between them. There is also a command for when you no longer need a workspace and want to remove it.
➜ terraform-workspaces git:(main) ✗ terraform workspace -help
Usage: terraform [global options] workspace
new, list, show, select and delete Terraform workspaces.
Subcommands:
delete Delete a workspace
list List Workspaces
new Create a new workspace
select Select a workspace
show Show the name of the current workspace
Terraform Workspaces commands only work after the project is initialized with the terraform initcommand. Once the project is initialized, a structure for storing the AWS provider plugin, in this case, is created, along with a file for the project’s state configurations (terraform.tfstate).
Now that the project is initialized, we can execute Workspaces commands. If we list the workspaces at this point, we will see only the defaultworkspace.
➜ .terraform git:(main) ✗ terraform workspace list
* default
We are now creating a new workspace called dev, and it’s noted that after creation, Terraform automatically switches to the new workspace
➜ .terraform git:(main) ✗ terraform workspace new dev
Created and switched to workspace "dev"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
➜ .terraform git:(main) ✗ terraform workspace list
default
* dev
In addition to creating a new workspace, a new state management file for this workspace dev was also created.
The same process occurs when creating workspaces for uat and prd .
➜ .terraform git:(main) ✗ terraform workspace new uat
Created and switched to workspace "uat"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
➜ .terraform git:(main) ✗ terraform workspace new prd
Created and switched to workspace "prd"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
We now have three state management files, one for each workspace and, respectively, for each environment (dev, uat, prod).
From this point forward, once the Workspace is changed, Terraform automatically manages to reference the correct state file for the environment.
Let’s start by switching to the dev workspace, where we will create the first EC2 instance.
➜ .terraform git:(main) ✗ terraform workspace select dev
Switched to workspace "dev".
Deploying resources Using Terraform Workspaces
In this scenario, we are creating EC2 instances, as we did in the last post, but now we are using the workspace approach to create for different environments.
1 — As mentioned earlier, the Terraform code is available on GitHub for download. Below is the directory structure of the repository.
provider.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.31.0"
}
}
}
provider "aws" {
region = var.region
profile = "default"
}
variables.tf
variable "region" {
description = "The region where the resources will be created"
type = string
default = "us-east-1"
}
backend.tf
terraform {
backend "local" {
}
}
locals.tf: This approach is widely used in Terraform. locals is a convention, not a Terraform requirement. It’s very useful for referencing values or expressions repeatedly and simplifying complex expressions by breaking them down into smaller, named parts. A major benefit is consistency: changes in an expression are reflected wherever it’s used.
https://developer.hashicorp.com/terraform/language/values/locals
In our test scenario, the value of the local variable vm_name, for example, is defined by concatenating the current workspacename with the value -vm01. Thus, if the workspace is dev, vm_name will be dev-vm01 . For uat, it will be uat-vm01 and for theproduction it will be prd-vm01. This method facilitates the customization of configurations according to the work environment, maintaining a clean and efficient code structure. We also configured different instance sizes based on the environment type.
locals {
env = {
default = {
instance_name = "${terraform.workspace}-vm01"
instance_type = "t3.small"
environment = "default"
}
dev = {
instance_name = "${terraform.workspace}-vm01"
instance_type = "t3.small"
environment = "dev"
}
uat = {
instance_name = "${terraform.workspace}-vm01"
instance_type = "t3.medium"
environment = "uat"
}
prd = {
instance_name = "${terraform.workspace}-vm01"
instance_type = "t3.large"
environment = "prd"
}
}
}
main.tf: Now that we have declared the default values for each environment in the locals.tf file, we can start implementing the aws_instance resource block to create the EC2 instance.
Unlike the previous post, where we passed the instance name as a variable, this time, we will change it so that the values for the Name, Environment, and Instance_type variables are fetched from the previously configured locals file. Based on the workspace name, the instance name, type, and environment will be changed. I will demonstrate this in practice.
resource "aws_instance" "example" {
ami = "ami-0c7217cdde317cfec"
instance_type = local.env[terraform.workspace].instance_type
tags = {
Name = local.env[terraform.workspace].instance_name
Environment = local.env[terraform.workspace].environment
Terraform = true
}
}
- Initializing the project with
terraform init
➜ terraform-workspaces git:(main) ✗ terraform init
Initializing the backend...
Successfully configured the backend "local"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Finding hashicorp/aws versions matching "5.31.0"...
- Installing hashicorp/aws v5.31.0...
- Installed hashicorp/aws v5.31.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Generating an execution plan with terraform plan
Based on the output below, it is possible to check that the values defined for the dev workspace are functional, with environment = dev and vm_name = dev-vm01 as expected.
➜ terraform-workspaces git:(main) ✗ terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-0c7217cdde317cfec"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_lifecycle = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t3.small"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ spot_instance_request_id = (known after apply)
+ subnet_id = (known after apply)
+ tags = {
+ "Environment" = "dev"
+ "Name" = "dev-vm01"
+ "Terraform" = "true"
}
+ tags_all = {
+ "Environment" = "dev"
+ "Name" = "dev-vm01"
+ "Terraform" = "true"
}
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
Applying the changes defined in the execution plan generated by terraform plan
The instance was successfully created, as shown in the output below.
➜ terraform-workspaces git:(main) ✗ terraform apply --auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-0c7217cdde317cfec"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_lifecycle = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t3.small"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ spot_instance_request_id = (known after apply)
+ subnet_id = (known after apply)
+ tags = {
+ "Environment" = "dev"
+ "Name" = "dev-vm01"
+ "Terraform" = "true"
}
+ tags_all = {
+ "Environment" = "dev"
+ "Name" = "dev-vm01"
+ "Terraform" = "true"
}
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
aws_instance.example: Creation complete after 15s [id=i-05f4da08c3f173fb2]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
We can now see the instance running on AWS.
To create environments for uatand prd, it is necessary to switch from the devworkspace to uatand then deploy the environment with terraform apply.
Switching to the uatworkspace and creating a new instance, it’s noticeable that we have a different environment here, with a different instance name and size, in this case, t3.medium.
➜ terraform-workspaces git:(main) ✗ terraform workspace select uat
Switched to workspace "uat".
➜ terraform-workspaces git:(main) ✗ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-0c7217cdde317cfec"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_lifecycle = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t3.medium"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ spot_instance_request_id = (known after apply)
+ subnet_id = (known after apply)
+ tags = {
+ "Environment" = "uat"
+ "Name" = "uat-vm01"
+ "Terraform" = "true"
}
+ tags_all = {
+ "Environment" = "uat"
+ "Name" = "uat-vm01"
+ "Terraform" = "true"
}
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions in workspace "uat"?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
aws_instance.example: Creation complete after 16s [id=i-0ba49e10e2f49163e]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Now switching to the prdworkspace and deploying the instance.
➜ terraform-workspaces git:(main) ✗ terraform workspace select prd
Switched to workspace "prd".
➜ terraform-workspaces git:(main) ✗ terraform apply --auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-0c7217cdde317cfec"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_lifecycle = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t3.large"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ spot_instance_request_id = (known after apply)
+ subnet_id = (known after apply)
+ tags = {
+ "Environment" = "prd"
+ "Name" = "prd-vm01"
+ "Terraform" = "true"
}
+ tags_all = {
+ "Environment" = "prd"
+ "Name" = "prd-vm01"
+ "Terraform" = "true"
}
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
aws_instance.example: Creation complete after 14s [id=i-01a1e40f3f4ef0fe0]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
After deploying the production environment using the prdworkspace, it’s possible to see all the instances created on AWS with different names, environments, and sizes, adhering to the pre-configured settings based on the environment.
Cleanup
Removing an instance with workspaces is similar to the creation process. To do this, it will be necessary to access each workspace and remove the resources managed by Terraform, in this case, the EC2 instance for each environment.
Removing dev environment: Access the dev workspace and execute the command to remove the Terraform-managed resources, including the EC2 instance associated with the dev environment.
➜ terraform-workspaces git:(main) ✗ terraform workspace select dev
Switched to workspace "dev".
➜ terraform-workspaces git:(main) ✗ terraform destroy --auto-approve
aws_instance.example: Refreshing state... [id=i-05f4da08c3f173fb2]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# aws_instance.example will be destroyed
- resource "aws_instance" "example" {
- ami = "ami-0c7217cdde317cfec" -> null
- arn = "arn:aws:ec2:us-east-1:490387562521:instance/i-05f4da08c3f173fb2" -> null
- associate_public_ip_address = true -> null
- availability_zone = "us-east-1d" -> null
- cpu_core_count = 1 -> null
- cpu_threads_per_core = 2 -> null
- disable_api_stop = false -> null
- disable_api_termination = false -> null
- ebs_optimized = false -> null
- get_password_data = false -> null
- hibernation = false -> null
- id = "i-05f4da08c3f173fb2" -> null
- instance_initiated_shutdown_behavior = "stop" -> null
- instance_state = "running" -> null
- instance_type = "t3.small" -> null
- ipv6_address_count = 0 -> null
- ipv6_addresses = [] -> null
- monitoring = false -> null
- placement_partition_number = 0 -> null
- primary_network_interface_id = "eni-06fd761a61829d5c0" -> null
- private_dns = "ip-172-31-39-241.ec2.internal" -> null
- private_ip = "172.31.39.241" -> null
- public_dns = "ec2-54-165-142-3.compute-1.amazonaws.com" -> null
- public_ip = "54.165.142.3" -> null
- secondary_private_ips = [] -> null
- security_groups = [
- "default",
] -> null
- source_dest_check = true -> null
- subnet_id = "subnet-001897c8e05578090" -> null
- tags = {
- "Environment" = "dev"
- "Name" = "dev-vm01"
- "Terraform" = "true"
} -> null
- tags_all = {
- "Environment" = "dev"
- "Name" = "dev-vm01"
- "Terraform" = "true"
} -> null
- tenancy = "default" -> null
- user_data_replace_on_change = false -> null
- vpc_security_group_ids = [
- "sg-0bba5e4395b647d12",
] -> null
- capacity_reservation_specification {
- capacity_reservation_preference = "open" -> null
}
- cpu_options {
- core_count = 1 -> null
- threads_per_core = 2 -> null
}
- credit_specification {
- cpu_credits = "unlimited" -> null
}
- enclave_options {
- enabled = false -> null
}
- maintenance_options {
- auto_recovery = "default" -> null
}
- metadata_options {
- http_endpoint = "enabled" -> null
- http_protocol_ipv6 = "disabled" -> null
- http_put_response_hop_limit = 1 -> null
- http_tokens = "optional" -> null
- instance_metadata_tags = "disabled" -> null
}
- private_dns_name_options {
- enable_resource_name_dns_a_record = false -> null
- enable_resource_name_dns_aaaa_record = false -> null
- hostname_type = "ip-name" -> null
}
- root_block_device {
- delete_on_termination = true -> null
- device_name = "/dev/sda1" -> null
- encrypted = false -> null
- iops = 100 -> null
- tags = {} -> null
- throughput = 0 -> null
- volume_id = "vol-0192df81300a70083" -> null
- volume_size = 8 -> null
- volume_type = "gp2" -> null
}
}
Plan: 0 to add, 0 to change, 1 to destroy.
aws_instance.example: Destroying... [id=i-05f4da08c3f173fb2]
aws_instance.example: Still destroying... [id=i-05f4da08c3f173fb2, 10s elapsed]
aws_instance.example: Still destroying... [id=i-05f4da08c3f173fb2, 20s elapsed]
aws_instance.example: Still destroying... [id=i-05f4da08c3f173fb2, 30s elapsed]
aws_instance.example: Still destroying... [id=i-05f4da08c3f173fb2, 40s elapsed]
aws_instance.example: Still destroying... [id=i-05f4da08c3f173fb2, 50s elapsed]
aws_instance.example: Destruction complete after 51s
Destroy complete! Resources: 1 destroyed.
- Removing uat environment: Switch to the
uatworkspace and perform the same steps to remove the resources, including the EC2 instance for the uat environment.
➜ terraform-workspaces git:(main) ✗ terraform workspace select uat
Switched to workspace "uat".
➜ terraform-workspaces git:(main) ✗ terraform destroy --auto-approve
aws_instance.example: Refreshing state... [id=i-0ba49e10e2f49163e]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# aws_instance.example will be destroyed
- resource "aws_instance" "example" {
- ami = "ami-0c7217cdde317cfec" -> null
- arn = "arn:aws:ec2:us-east-1:490387562521:instance/i-0ba49e10e2f49163e" -> null
- associate_public_ip_address = true -> null
- availability_zone = "us-east-1d" -> null
- cpu_core_count = 1 -> null
- cpu_threads_per_core = 2 -> null
- disable_api_stop = false -> null
- disable_api_termination = false -> null
- ebs_optimized = false -> null
- get_password_data = false -> null
- hibernation = false -> null
- id = "i-0ba49e10e2f49163e" -> null
- instance_initiated_shutdown_behavior = "stop" -> null
- instance_state = "running" -> null
- instance_type = "t3.medium" -> null
- ipv6_address_count = 0 -> null
- ipv6_addresses = [] -> null
- monitoring = false -> null
- placement_partition_number = 0 -> null
- primary_network_interface_id = "eni-029a42ccf355a4a9a" -> null
- private_dns = "ip-172-31-39-25.ec2.internal" -> null
- private_ip = "172.31.39.25" -> null
- public_dns = "ec2-54-227-152-144.compute-1.amazonaws.com" -> null
- public_ip = "54.227.152.144" -> null
- secondary_private_ips = [] -> null
- security_groups = [
- "default",
] -> null
- source_dest_check = true -> null
- subnet_id = "subnet-001897c8e05578090" -> null
- tags = {
- "Environment" = "uat"
- "Name" = "uat-vm01"
- "Terraform" = "true"
} -> null
- tags_all = {
- "Environment" = "uat"
- "Name" = "uat-vm01"
- "Terraform" = "true"
} -> null
- tenancy = "default" -> null
- user_data_replace_on_change = false -> null
- vpc_security_group_ids = [
- "sg-0bba5e4395b647d12",
] -> null
- capacity_reservation_specification {
- capacity_reservation_preference = "open" -> null
}
- cpu_options {
- core_count = 1 -> null
- threads_per_core = 2 -> null
}
- credit_specification {
- cpu_credits = "unlimited" -> null
}
- enclave_options {
- enabled = false -> null
}
- maintenance_options {
- auto_recovery = "default" -> null
}
- metadata_options {
- http_endpoint = "enabled" -> null
- http_protocol_ipv6 = "disabled" -> null
- http_put_response_hop_limit = 1 -> null
- http_tokens = "optional" -> null
- instance_metadata_tags = "disabled" -> null
}
- private_dns_name_options {
- enable_resource_name_dns_a_record = false -> null
- enable_resource_name_dns_aaaa_record = false -> null
- hostname_type = "ip-name" -> null
}
- root_block_device {
- delete_on_termination = true -> null
- device_name = "/dev/sda1" -> null
- encrypted = false -> null
- iops = 100 -> null
- tags = {} -> null
- throughput = 0 -> null
- volume_id = "vol-024029e503f1672db" -> null
- volume_size = 8 -> null
- volume_type = "gp2" -> null
}
}
Plan: 0 to add, 0 to change, 1 to destroy.
aws_instance.example: Destroying... [id=i-0ba49e10e2f49163e]
aws_instance.example: Still destroying... [id=i-0ba49e10e2f49163e, 10s elapsed]
aws_instance.example: Still destroying... [id=i-0ba49e10e2f49163e, 20s elapsed]
aws_instance.example: Still destroying... [id=i-0ba49e10e2f49163e, 30s elapsed]
aws_instance.example: Still destroying... [id=i-0ba49e10e2f49163e, 40s elapsed]
aws_instance.example: Still destroying... [id=i-0ba49e10e2f49163e, 50s elapsed]
aws_instance.example: Still destroying... [id=i-0ba49e10e2f49163e, 1m0s elapsed]
aws_instance.example: Destruction complete after 1m2s
Destroy complete! Resources: 1 destroyed.
Removing production environment: Finally, switch to the prd workspace and remove the resources for the production environment, including its EC2 instance.
➜ terraform-workspaces git:(main) ✗ terraform workspace select prd
Switched to workspace "prd".
➜ terraform-workspaces git:(main) ✗ terraform destroy --auto-approve
aws_instance.example: Refreshing state... [id=i-01a1e40f3f4ef0fe0]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# aws_instance.example will be destroyed
- resource "aws_instance" "example" {
- ami = "ami-0c7217cdde317cfec" -> null
- arn = "arn:aws:ec2:us-east-1:490387562521:instance/i-01a1e40f3f4ef0fe0" -> null
- associate_public_ip_address = true -> null
- availability_zone = "us-east-1d" -> null
- cpu_core_count = 1 -> null
- cpu_threads_per_core = 2 -> null
- disable_api_stop = false -> null
- disable_api_termination = false -> null
- ebs_optimized = false -> null
- get_password_data = false -> null
- hibernation = false -> null
- id = "i-01a1e40f3f4ef0fe0" -> null
- instance_initiated_shutdown_behavior = "stop" -> null
- instance_state = "running" -> null
- instance_type = "t3.large" -> null
- ipv6_address_count = 0 -> null
- ipv6_addresses = [] -> null
- monitoring = false -> null
- placement_partition_number = 0 -> null
- primary_network_interface_id = "eni-02edf723ab31ed301" -> null
- private_dns = "ip-172-31-42-129.ec2.internal" -> null
- private_ip = "172.31.42.129" -> null
- public_dns = "ec2-52-90-152-236.compute-1.amazonaws.com" -> null
- public_ip = "52.90.152.236" -> null
- secondary_private_ips = [] -> null
- security_groups = [
- "default",
] -> null
- source_dest_check = true -> null
- subnet_id = "subnet-001897c8e05578090" -> null
- tags = {
- "Environment" = "prd"
- "Name" = "prd-vm01"
- "Terraform" = "true"
} -> null
- tags_all = {
- "Environment" = "prd"
- "Name" = "prd-vm01"
- "Terraform" = "true"
} -> null
- tenancy = "default" -> null
- user_data_replace_on_change = false -> null
- vpc_security_group_ids = [
- "sg-0bba5e4395b647d12",
] -> null
- capacity_reservation_specification {
- capacity_reservation_preference = "open" -> null
}
- cpu_options {
- core_count = 1 -> null
- threads_per_core = 2 -> null
}
- credit_specification {
- cpu_credits = "unlimited" -> null
}
- enclave_options {
- enabled = false -> null
}
- maintenance_options {
- auto_recovery = "default" -> null
}
- metadata_options {
- http_endpoint = "enabled" -> null
- http_protocol_ipv6 = "disabled" -> null
- http_put_response_hop_limit = 1 -> null
- http_tokens = "optional" -> null
- instance_metadata_tags = "disabled" -> null
}
- private_dns_name_options {
- enable_resource_name_dns_a_record = false -> null
- enable_resource_name_dns_aaaa_record = false -> null
- hostname_type = "ip-name" -> null
}
- root_block_device {
- delete_on_termination = true -> null
- device_name = "/dev/sda1" -> null
- encrypted = false -> null
- iops = 100 -> null
- tags = {} -> null
- throughput = 0 -> null
- volume_id = "vol-06eb88b4e7ba9b518" -> null
- volume_size = 8 -> null
- volume_type = "gp2" -> null
}
}
Plan: 0 to add, 0 to change, 1 to destroy.
aws_instance.example: Destroying... [id=i-01a1e40f3f4ef0fe0]
aws_instance.example: Still destroying... [id=i-01a1e40f3f4ef0fe0, 10s elapsed]
aws_instance.example: Still destroying... [id=i-01a1e40f3f4ef0fe0, 20s elapsed]
aws_instance.example: Still destroying... [id=i-01a1e40f3f4ef0fe0, 30s elapsed]
aws_instance.example: Still destroying... [id=i-01a1e40f3f4ef0fe0, 40s elapsed]
aws_instance.example: Still destroying... [id=i-01a1e40f3f4ef0fe0, 50s elapsed]
aws_instance.example: Still destroying... [id=i-01a1e40f3f4ef0fe0, 1m0s elapsed]
aws_instance.example: Destruction complete after 1m1s
Destroy complete! Resources: 1 destroyed.
This cleanup process ensures that all resources associated with each workspace are properly removed, maintaining a clean and efficient infrastructure management practice.
Concluding
As we can observe, workspaces can be highly beneficial when aiming to ensure identical infrastructure across all environments. However, when business strategies or testing purposes necessitate distinct environments, and you wish to manage these variations with Terraform, it is strongly recommended to employ versioned modules rather than workspaces. I strongly advise utilizing workspaces whenever feasible to ensure consistency in infrastructure.
Publicado originalmente no Medium.