Terraform

  1. 1. Terraform Overview
    1. 1.1. Terraform architecture
    2. 1.2. Terraform Structure
      1. 1.2.1. Terraform Block
      2. 1.2.2. Provider Block
      3. 1.2.3. Resource Block
      4. 1.2.4. Data Block
      5. 1.2.5. Variables Block
      6. 1.2.6. Output Block
      7. 1.2.7. Modules Block
      8. 1.2.8. Locals Block
      9. 1.2.9. Backend Block
      10. 1.2.10. Provisioners Block
  2. 2. Providers in Terraform
  3. 3. 总结核心知识要点
    1. 3.1. 架构组件
    2. 3.2. 核心配置块示例
      1. 3.2.1. 1. Terraform + Provider 配置
      2. 3.2.2. 2. 变量和本地变量
      3. 3.2.3. 3. 资源创建
      4. 3.2.4. 4. 数据源查询
      5. 3.2.5. 5. 输出值
      6. 3.2.6. 6. 模块使用
    3. 3.3. 工作流程
    4. 3.4. 状态管理命令
    5. 3.5. 最佳实践
    6. 3.6. 核心概念
  4. 4. References

Terraform is an infrastructure as code tool that enables you to safely and predictably provision and manage infrastructure in any cloud.

Terraform Overview

Terraform allows you to

  • automate and manage your infrastructure
  • your platform
  • and services that run on that platform

Terraform architecture

Terraform architecture mainly consists of the following components:

  1. Terraform Core
  2. Providers
  3. State file

Terraform Structure

Terraform Block

A Terraform block specifies the required providers that terraform needs in order to execute the script. This block also contains the source block that specifies from where terraform should download the provider and also the required version.

1
2
3
4
5
6
7
8
9
# https://developer.hashicorp.com/terraform/language/providers/requirements
terraform {
required_providers {
aws = {
version = ">= 2.7.0"
source = "hashicorp/aws"
}
}
}

Provider Block

A provider block specifies the cloud provider and the API credentials required to connect to the provider’s services. It includes the provider name, version, access key, and secret key.

1
2
3
4
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs
provider "aws" {
region = "us-east-1"
}

Resource Block

A resource block represents a particular resource in the cloud provider’s services. It includes the resource type, name, and configuration details. This is the main block that specifies the type of resource we are trying to deploy.

1
2
3
4
5
# https://developer.hashicorp.com/terraform/language/resources/syntax
resource "aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}

Data Block

A data block is used to fetch data from the provider’s services, which can be used in resource blocks. It includes the data type and configuration details. This is used in scenarios where the resource is already deployed, and you would like to fetch the details of that resource.

1
2
3
4
5
6
7
8
9
10
# https://developer.hashicorp.com/terraform/language/data-sources
data "aws_ami" "example" {
most_recent = true

owners = ["self"]
tags = {
Name = "app-server"
Tested = "true"
}
}

Variables Block

A variable block is used to define input variables that are used in the Terraform configuration. It includes the variable name, type, and default value.

1
2
3
4
5
6
# https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-variables
variable "instance_name" {
description = "Value of the Name tag for the EC2 instance"
type = string
default = "ExampleAppServerInstance"
}

Output Block

An output block is used to define output values that are generated by the Terraform configuration. It includes the output name and value.

1
2
3
4
# https://developer.hashicorp.com/terraform/language/values/outputs
output "instance_ip_addr" {
value = aws_instance.server.private_ip
}

Modules Block

Modules are containers for multiple resources that are used together. A module consists of .tf and/or .tf.json files stored in a directory. It is the primary way to package and reuse resources in Terraform.

Every Terraform configuration has at least one model (root module) which contains resources defined in the .tf files. Test configuration we created in the third part of these series is a module.

Modules are a great way to compartmentalize reusable collections of resources in multiple configurations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.
├── README.md
├── modules/
│ ├── ec2_module/
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── vpc_module/
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ └── subnet_module/
└── main.tf
├── variables.tf
└── outputs.tf

Locals Block

Often called local variables block, this block is used to keep frequently referenced values or expressions to keep the code clean and tidy.

Locals block can hold many variables inside. Expressions in local values are not limited to literal constants. They can also reference other values in the module to transform or combine them. These variables can be accessed using local.var_name notation, note that it is called local. when used to access values inside.

1
2
3
4
5
6
7
8
9
10
11
12
13
# https://developer.hashicorp.com/terraform/language/values/locals
locals {
# Ids for multiple sets of EC2 instances, merged together
instance_ids = concat(aws_instance.blue.*.id, aws_instance.green.*.id)
}

locals {
# Common tags to be assigned to all resources
common_tags = {
Service = local.service_name
Owner = local.owner
}
}

Using Local Values

1
2
3
4
5
resource "aws_instance" "example" {
# ...

tags = local.common_tags
}

Backend Block

A backend defines where Terraform stores its state data files.

1
2
3
4
5
6
terraform {
backend "kubernetes" {
secret_suffix = "state"
config_path = "~/.kube/config"
}
}

Example Referencing

1
2
3
4
5
6
7
data "terraform_remote_state" "foo" {
backend = "kubernetes"
config = {
secret_suffix = "state"
load_config_file = true
}
}

Provisioners Block

Provisioners allows us to specify actions to be performed on local or remote machines to prepare resources for service. There are two types of Terraform provisioners:

  1. local-exec invokes local executable after a resource is created. It runs the process on the machine running Terraform, meaning the machine where you run terraform apply. This is most likely your own computer.
  2. remote-exec invokes remote executable, something like an EC2 instance on AWS.

This is an example of a provisioner for an EC2 instance. This example contains both local-exec and a remote-exec:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
resource "aws_instance" "web_server" {
# ...

provisioner "local-exec" {
command = "Get-Date > completed.txt"
interpreter = ["PowerShell", "-Command"]
}
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/script.sh",
"/tmp/script.sh args",
]
}
}

Providers in Terraform

  • Expose resources for specific infrastructure platforms (e.g AWS, Azure, Google Cloud, etc.)
  • Responsible for understanding API of that platform
  • Just code that knows how to talk to specific technology or platform
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# AWS Provider
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

# Configure the AWS Provider
provider "aws" {
region = "us-east-1"
}

总结核心知识要点

架构组件

  • Terraform Core: 核心引擎,负责解析配置文件和执行操作
  • Providers: 连接各种云平台的插件(AWS、Azure、GCP等)
  • State File: 记录基础设施当前状态的关键文件

核心配置块示例

1. Terraform + Provider 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
}
}

provider "aws" {
region = "us-east-1"
}

2. 变量和本地变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
variable "environment" {
type = string
default = "dev"
}

variable "instance_count" {
type = number
default = 2
}

locals {
common_tags = {
Environment = var.environment
ManagedBy = "Terraform"
Project = "MyApp"
}
instance_name = "${var.environment}-web-server"
}

3. 资源创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = local.common_tags
}

resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
tags = merge(local.common_tags, {
Name = "public-subnet"
})
}

resource "aws_instance" "web" {
count = var.instance_count
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id

tags = merge(local.common_tags, {
Name = "${local.instance_name}-${count.index}"
})
}

4. 数据源查询

1
2
3
4
5
6
7
8
9
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]

filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
}

5. 输出值

1
2
3
4
5
6
7
8
output "instance_ips" {
value = aws_instance.web[*].private_ip
description = "Private IPs of web servers"
}

output "vpc_id" {
value = aws_vpc.main.id
}

6. 模块使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module "vpc" {
source = "./modules/vpc_module"

cidr_block = "10.0.0.0/16"
environment = var.environment
tags = local.common_tags
}

module "ec2" {
source = "./modules/ec2_module"

vpc_id = module.vpc.vpc_id
subnet_id = module.vpc.public_subnet_id
instance_count = var.instance_count
}

工作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 初始化项目,下载 providers
terraform init

# 2. 格式化代码
terraform fmt

# 3. 验证配置语法
terraform validate

# 4. 预览变更计划
terraform plan

# 5. 执行变更
terraform apply

# 6. 查看状态
terraform show

# 7. 销毁资源
terraform destroy

状态管理命令

1
2
3
4
5
6
7
8
9
10
11
# 查看 state 列表
terraform state list

# 查看特定资源
terraform state show aws_instance.web[0]

# 移动资源
terraform state mv aws_instance.old aws_instance.new

# 删除资源(仅从 state 中移除)
terraform state rm aws_instance.web

最佳实践

  • 使用 Modules 实现代码复用和模块化
  • Remote Backend (S3、Consul) 共享和锁定 state
  • 变量化配置 避免硬编码,使用 variables.tfterraform.tfvars
  • 分层管理 分离环境配置(dev/staging/prod)
  • 版本控制 锁定 provider 版本避免意外更新
  • 谨慎使用 Provisioners 优先使用云服务原生功能(user_data、cloud-init)

核心概念

  • 声明式语法: 描述期望的最终状态,而非具体步骤
  • 状态管理: state 文件是 Terraform 的"记忆",记录真实资源与配置的映射
  • 依赖解析: 自动分析资源间依赖关系(如 vpc_id = aws_vpc.main.id),按正确顺序创建/删除
  • 幂等性: 多次执行相同配置产生相同结果

References