Terraform Basics
Terraform is an Infrastructure as Code (IaC) tool developed by HashiCorp. It uses the declarative configuration language HCL (HashiCorp Configuration Language) to define and manage cloud resources. With Terraform, you can describe your complete infrastructure architecture in code, enabling version control and repeatable deployments.
Installing Terraform
Section titled “Installing Terraform”Installing via the Official Repository
Section titled “Installing via the Official Repository”# Install the DNF plugin that provides config-managersudo dnf install -y dnf-plugins-core
# Add the HashiCorp official repositorysudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
# Install Terraformsudo dnf install -y terraform
# Verify installationterraform versionManual Installation
Section titled “Manual Installation”# Download the latest version (see https://developer.hashicorp.com/terraform/install for the current version)TERRAFORM_VERSION="1.9.0" # example version — replace with the latest from the official sitewget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip
# Extract and installsudo dnf install -y unzipunzip terraform_${TERRAFORM_VERSION}_linux_amd64.zipsudo mv terraform /usr/local/bin/sudo chmod +x /usr/local/bin/terraform
# Verifyterraform versionEnabling Command Auto-Completion
Section titled “Enabling Command Auto-Completion”# Install auto-completionterraform -install-autocomplete
# Reload the shellsource ~/.bashrcHCL Syntax Basics
Section titled “HCL Syntax Basics”Terraform configuration files use the .tf extension and HCL syntax.
Basic Structure
Section titled “Basic Structure”# Comments start with #
# Block structureresource "type" "name" { param1 = "value1" param2 = 123
# Nested block nested_block { key = "value" }}
# String interpolationvariable "name" { default = "myserver"}
resource "example" "demo" { name = "prefix-${var.name}"}Data Types
Section titled “Data Types”# Stringname = "web-server"
# Numbercount = 3
# Booleanenabled = true
# Listavailability_zones = ["cn-hangzhou-a", "cn-hangzhou-b"]
# Maptags = { Name = "web-server" Environment = "production" Team = "ops"}Provider Configuration
Section titled “Provider Configuration”Providers are plugins that allow Terraform to interact with cloud platforms.
AWS Example
Section titled “AWS Example”terraform { required_version = ">= 1.5.0"
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }}
provider "aws" { region = "ap-northeast-1" # Tokyo region access_key = var.aws_access_key secret_key = var.aws_secret_key
# Or use environment variables (recommended): # export AWS_ACCESS_KEY_ID="your-access-key" # export AWS_SECRET_ACCESS_KEY="your-secret-key"}Alibaba Cloud Example
Section titled “Alibaba Cloud Example”terraform { required_version = ">= 1.5.0"
required_providers { alicloud = { source = "aliyun/alicloud" version = "~> 1.220" } }}
provider "alicloud" { region = "cn-hangzhou" access_key = var.alicloud_access_key secret_key = var.alicloud_secret_key}Creating Resources
Section titled “Creating Resources”AWS EC2 Instance Example
Section titled “AWS EC2 Instance Example”# variables.tf - Variable definitionsvariable "aws_access_key" { description = "AWS Access Key" type = string sensitive = true}
variable "aws_secret_key" { description = "AWS Secret Key" type = string sensitive = true}
variable "instance_type" { description = "EC2 instance type" type = string default = "t3.micro"}
variable "instance_count" { description = "Number of instances" type = number default = 1}# main.tf - Main configuration file# Query the latest AlmaLinux 9 AMIdata "aws_ami" "almalinux9" { most_recent = true owners = ["764336703387"] # AlmaLinux official
filter { name = "name" values = ["AlmaLinux OS 9*x86_64*"] }
filter { name = "virtualization-type" values = ["hvm"] }}
# Create a security groupresource "aws_security_group" "web" { name = "web-sg" description = "Allow HTTP and SSH"
ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] description = "SSH" }
ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] description = "HTTP" }
ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] description = "HTTPS" }
egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] }
tags = { Name = "web-security-group" }}
# Create EC2 instancesresource "aws_instance" "web" { count = var.instance_count ami = data.aws_ami.almalinux9.id instance_type = var.instance_type
vpc_security_group_ids = [aws_security_group.web.id]
user_data = <<-EOF #!/bin/bash dnf install -y nginx systemctl enable --now nginx echo "<h1>Server ${count.index + 1}</h1>" > /usr/share/nginx/html/index.html EOF
tags = { Name = "web-server-${count.index + 1}" Env = "production" }}# outputs.tf - Output definitionsoutput "instance_public_ips" { description = "List of instance public IPs" value = aws_instance.web[*].public_ip}
output "instance_ids" { description = "List of instance IDs" value = aws_instance.web[*].id}
output "ami_id" { description = "AMI ID used" value = data.aws_ami.almalinux9.id}Alibaba Cloud ECS Instance Example
Section titled “Alibaba Cloud ECS Instance Example”# main.tf - Alibaba Cloud ECS examplevariable "alicloud_access_key" { type = string sensitive = true}
variable "alicloud_secret_key" { type = string sensitive = true}
# Query available zonesdata "alicloud_zones" "default" { available_resource_creation = "VSwitch"}
# Query imagesdata "alicloud_images" "almalinux" { name_regex = "^almalinux_9" most_recent = true owners = "system"}
# Create a VPCresource "alicloud_vpc" "main" { vpc_name = "tf-demo-vpc" cidr_block = "172.16.0.0/16"}
# Create a VSwitchresource "alicloud_vswitch" "main" { vswitch_name = "tf-demo-vsw" vpc_id = alicloud_vpc.main.id cidr_block = "172.16.1.0/24" zone_id = data.alicloud_zones.default.zones[0].id}
# Create a security groupresource "alicloud_security_group" "web" { name = "tf-demo-sg" vpc_id = alicloud_vpc.main.id}
resource "alicloud_security_group_rule" "allow_ssh" { type = "ingress" ip_protocol = "tcp" port_range = "22/22" security_group_id = alicloud_security_group.web.id cidr_ip = "0.0.0.0/0"}
resource "alicloud_security_group_rule" "allow_http" { type = "ingress" ip_protocol = "tcp" port_range = "80/80" security_group_id = alicloud_security_group.web.id cidr_ip = "0.0.0.0/0"}
# Create an ECS instanceresource "alicloud_instance" "web" { instance_name = "tf-demo-ecs" image_id = data.alicloud_images.almalinux.images[0].id instance_type = "ecs.t6-c1m1.large" security_groups = [alicloud_security_group.web.id] vswitch_id = alicloud_vswitch.main.id internet_max_bandwidth_out = 10 system_disk_size = 40
user_data = base64encode(<<-EOF #!/bin/bash dnf install -y nginx systemctl enable --now nginx EOF )
tags = { Name = "web-server" Env = "demo" }}
output "public_ip" { value = alicloud_instance.web.public_ip}Plan / Apply / Destroy Workflow
Section titled “Plan / Apply / Destroy Workflow”Initializing the Project
Section titled “Initializing the Project”# Enter the project directorycd /opt/terraform/myproject
# Initialize (downloads Provider plugins)terraform init
# Upgrade Provider versionsterraform init -upgradeViewing the Execution Plan
Section titled “Viewing the Execution Plan”# View the plan (preview changes that will be made)terraform plan
# Save the plan to a fileterraform plan -out=tfplan
# Use a variable fileterraform plan -var-file="production.tfvars"
# Pass a single variableterraform plan -var="instance_count=3"Applying Changes
Section titled “Applying Changes”# Apply changes (requires confirmation)terraform apply
# Use a saved plan file (no confirmation needed)terraform apply tfplan
# Auto-approve (for CI/CD)terraform apply -auto-approve
# Use a variable fileterraform apply -var-file="production.tfvars"Destroying Resources
Section titled “Destroying Resources”# Destroy all resourcesterraform destroy
# Auto-approve destructionterraform destroy -auto-approve
# Destroy only a specific resourceterraform destroy -target=aws_instance.webOther Common Commands
Section titled “Other Common Commands”# Format configuration filesterraform fmt
# Recursively formatterraform fmt -recursive
# Validate configuration syntaxterraform validate
# View current stateterraform show
# List all resourcesterraform state list
# View details for a specific resourceterraform state show aws_instance.web[0]
# Refresh state (sync with actual remote state)terraform refresh
# Generate a dependency graphterraform graph | dot -Tpng > graph.pngVariable Files
Section titled “Variable Files”Use .tfvars files to manage variables for different environments:
instance_type = "t3.large"instance_count = 3instance_type = "t3.micro"instance_count = 1# Use different environment variablesterraform apply -var-file="production.tfvars"terraform apply -var-file="staging.tfvars"Pass sensitive information via environment variables:
# Terraform automatically reads environment variables with the TF_VAR_ prefixexport TF_VAR_aws_access_key="AKIAIOSFODNN7EXAMPLE"export TF_VAR_aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
terraform applyState Management
Section titled “State Management”The Terraform state file (terraform.tfstate) records the current state of the infrastructure. For team collaboration, a remote backend should be used to store the state file.
Local State (Default)
Section titled “Local State (Default)”# State file is located in the project directoryls -la terraform.tfstatels -la terraform.tfstate.backupRemote State - S3 Backend
Section titled “Remote State - S3 Backend”terraform { backend "s3" { bucket = "my-terraform-state" key = "production/terraform.tfstate" region = "ap-northeast-1" encrypt = true dynamodb_table = "terraform-lock" # State locking }}Remote State - Alibaba Cloud OSS Backend
Section titled “Remote State - Alibaba Cloud OSS Backend”terraform { backend "oss" { bucket = "my-terraform-state" prefix = "production" region = "cn-hangzhou" encrypt = true
tablestore_endpoint = "https://tf-state-lock.cn-hangzhou.ots.aliyuncs.com" tablestore_table = "terraform-lock" }}State Operations
Section titled “State Operations”# List resources in the stateterraform state list
# View resource detailsterraform state show aws_instance.web[0]
# Move a resource (rename)terraform state mv aws_instance.web aws_instance.webserver
# Remove a resource from state (without deleting the actual resource)terraform state rm aws_instance.web[0]
# Import an existing resource into Terraform managementterraform import aws_instance.web i-0abc123def456
# Pull remote stateterraform state pull > state.json
# Push state to remoteterraform state push state.jsonBasic Modules
Section titled “Basic Modules”Modules are used to encapsulate and reuse Terraform configurations.
Creating a Module
Section titled “Creating a Module”# Module directory structuremkdir -p modules/ecs-instancevariable "instance_name" { description = "Instance name" type = string}
variable "instance_type" { description = "Instance specification" type = string default = "ecs.t6-c1m1.large"}
variable "image_id" { description = "Image ID" type = string}
variable "vswitch_id" { description = "VSwitch ID" type = string}
variable "security_group_id" { description = "Security group ID" type = string}
variable "tags" { description = "Resource tags" type = map(string) default = {}}resource "alicloud_instance" "this" { instance_name = var.instance_name instance_type = var.instance_type image_id = var.image_id vswitch_id = var.vswitch_id security_groups = [var.security_group_id] internet_max_bandwidth_out = 10 system_disk_size = 40 tags = var.tags}output "instance_id" { value = alicloud_instance.this.id}
output "public_ip" { value = alicloud_instance.this.public_ip}
output "private_ip" { value = alicloud_instance.this.private_ip}Calling a Module
Section titled “Calling a Module”# main.tf - Using a custom modulemodule "web_server" { source = "./modules/ecs-instance"
instance_name = "web-server-01" instance_type = "ecs.t6-c1m1.large" image_id = data.alicloud_images.almalinux.images[0].id vswitch_id = alicloud_vswitch.main.id security_group_id = alicloud_security_group.web.id
tags = { Role = "web" Env = "production" }}
module "api_server" { source = "./modules/ecs-instance"
instance_name = "api-server-01" instance_type = "ecs.c6.large" image_id = data.alicloud_images.almalinux.images[0].id vswitch_id = alicloud_vswitch.main.id security_group_id = alicloud_security_group.web.id
tags = { Role = "api" Env = "production" }}
output "web_server_ip" { value = module.web_server.public_ip}
output "api_server_ip" { value = module.api_server.public_ip}Using Remote Modules
Section titled “Using Remote Modules”# Using a module from the Terraform Registrymodule "vpc" { source = "alibaba/vpc/alicloud" version = "1.10.0"
vpc_name = "production-vpc" vpc_cidr = "10.0.0.0/8"}Recommended Project Structure
Section titled “Recommended Project Structure”terraform-project/├── environments/│ ├── production/│ │ ├── main.tf│ │ ├── variables.tf│ │ ├── outputs.tf│ │ ├── terraform.tfvars│ │ └── backend.tf│ └── staging/│ ├── main.tf│ ├── variables.tf│ ├── outputs.tf│ ├── terraform.tfvars│ └── backend.tf├── modules/│ ├── ecs-instance/│ ├── vpc/│ └── security-group/└── .gitignoreCreate a .gitignore for your Terraform project:
cat > .gitignore << 'EOF'# Terraform.terraform/*.tfstate*.tfstate.backup*.tfplan*.tfvars!example.tfvars
# Sensitive files*.pem*.keyEOFTerraform is the core tool for Infrastructure as Code, bringing cloud resource creation and management under version control. Combined with Cloud-Init for passing user-data and Ansible for subsequent configuration, these three tools together form a complete automated infrastructure delivery pipeline.