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.id
refers to theid
attribute of theaws_instance
resource namedthis
.aws_instance.this.public_ip
refers to thepublic_ip
attribute 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.