Terraform -- AWS
This example demonstrates best practices in using Terraform to manage AWS infrastructure, including remote state management, module organization, version control, and planning/review processes.
Directory Structure
1
2
3
4
5
6
7
8
9
10
.
├── main.tf
├── variables.tf
├── outputs.tf
├── modules
│   └── ec2-instance
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── terraform.tfvars
1. Remote State Management
First, configure Terraform to store the state file in an S3 bucket to ensure consistency and enable collaboration.
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
provider "aws" {
  region = var.region
}
terraform {
  backend "s3" {
    bucket         = "your-terraform-state-bucket"
    key            = "path/to/terraform.tfstate"
    region         = var.region
    encrypt        = true
    dynamodb_table = "your-lock-table"
  }
}
module "ec2_instance" {
  source = "./modules/ec2-instance"
  instance_type = var.instance_type
  ami           = var.ami
}
variables.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
variable "region" {
  description = "AWS region"
  default     = "us-west-2"
}
variable "instance_type" {
  description = "Type of the instance"
  default     = "t2.micro"
}
variable "ami" {
  description = "AMI ID"
  default     = "ami-0c55b159cbfafe1f0"
}
outputs.tf
1
2
3
4
5
6
7
output "instance_id" {
  value = module.ec2_instance.instance_id
}
output "instance_public_ip" {
  value = module.ec2_instance.instance_public_ip
}
2. Module Organization
Organize your Terraform configurations into modules for better manageability.
modules/ec2-instance/main.tf
1
2
3
4
5
6
7
8
resource "aws_instance" "this" {
  ami           = var.ami
  instance_type = var.instance_type
  tags = {
    Name = "ExampleInstance"
  }
}
modules/ec2-instance/variables.tf
1
2
3
4
5
6
7
variable "instance_type" {
  description = "Type of the instance"
}
variable "ami" {
  description = "AMI ID"
}
modules/ec2-instance/outputs.tf
1
2
3
4
5
6
7
output "instance_id" {
  value = aws_instance.this.id
}
output "instance_public_ip" {
  value = aws_instance.this.public_ip
}
3. Version Control
Keep your Terraform configurations in a version control system like Git.
.gitignore
Create a .gitignore file to ignore sensitive files and directories.
1
2
3
4
*.tfstate
*.tfstate.backup
.terraform/
.terraform.lock.hcl
Initialize a Git repository and commit your Terraform files.
1
2
3
git init
git add .
git commit -m "Initial commit of Terraform configurations"
4. Plan and Review
Always run terraform plan before applying changes to review what will be modified.
Commands
- 
    
Initialize Terraform:
1
terraform init
 - 
    
Plan Changes:
1
terraform plan
 - 
    
Apply Changes:
1
terraform apply
 
terraform.tfvars
Use a terraform.tfvars file to set variable values.
1
2
3
region        = "us-west-2"
instance_type = "t2.micro"
ami           = "ami-0c55b159cbfafe1f0"
Explanation of “this”
In Terraform, the second string in the resource block declaration (in this case, "this") is the name or identifier of the resource within the Terraform configuration. This identifier is used to reference this specific resource elsewhere in the configuration or outputs.
The general syntax for defining a resource in Terraform is:
1
2
3
resource "<PROVIDER>_<RESOURCE_TYPE>" "<NAME>" {
  # Configuration options
}
<PROVIDER>: The name of the provider, such asaws,google,azurerm, etc.<RESOURCE_TYPE>: The type of resource being defined, such asinstance,s3_bucket,vpc, etc.<NAME>: A unique identifier for this resource within the configuration.
Example: aws_instance.this
1
2
3
4
5
6
7
8
resource "aws_instance" "this" {
  ami           = var.ami
  instance_type = var.instance_type
  tags = {
    Name = "ExampleInstance"
  }
}
aws_instance: Specifies that the resource type is an AWS EC2 instance.this: Is a unique name for this specific EC2 instance within the Terraform configuration. You can use any valid identifier here.
Usage
The name "this" (or any chosen identifier) is used to reference this resource in other parts of the configuration. For example, if you want to output the instance ID and public IP, you would reference it like this:
outputs.tf
1
2
3
4
5
6
7
output "instance_id" {
  value = aws_instance.this.id
}
output "instance_public_ip" {
  value = aws_instance.this.public_ip
}
In this example:
aws_instance.this.idrefers to theidattribute of theaws_instanceresource namedthis.aws_instance.this.public_iprefers to thepublic_ipattribute of the same resource.
Naming Convention
Using "this" as the identifier is a common convention when there is only one resource of that type, but you can choose more descriptive names, especially in more complex configurations with multiple similar resources. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
resource "aws_instance" "web_server" {
  ami           = var.ami
  instance_type = var.instance_type
  tags = {
    Name = "WebServerInstance"
  }
}
resource "aws_instance" "db_server" {
  ami           = var.ami
  instance_type = var.instance_type
  tags = {
    Name = "DBServerInstance"
  }
}
In this case, "web_server" and "db_server" are the identifiers, making it clear what each resource represents.
Using meaningful names helps improve the readability and maintainability of your Terraform configurations, especially in larger projects.