1. AWS Provider 配置与认证
一句话总结: AWS Provider 通过 region 与认证链完成初始化,认证推荐 IAM Role(instance profile 或 assume role),密钥优先用环境变量或 SSO 而非硬编码。
AWS 是 Terraform 生态中资源最丰富的 Provider 之一。动手之前先保证认证链路可靠、region 明确、Provider 版本可复现,这三个基础决定后续所有资源的可用性。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
1.1 认证方式的取舍
| 方式 | 配置途径 | 适用场景 |
|---|---|---|
| 环境变量 | AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY | 本地开发、CI 注入 |
| 共享凭据文件 | ~/.aws/credentials | 单机日常开发 |
| SSO / 临时令牌 | aws sso login 后由 SDK 自动刷新 | 团队统一身份 |
| Instance Role | 运行在 EC2 上的 instance_profile | 云端运行、Pipeline Runner |
| Assume Role | role_arn + source_profile | 跨账户、最小权限切角色 |
生产环境最推荐 Assume Role + Instance Role 的组合:代码里不出现任何长期密钥,权限由 IAM 策略统一治理。
aws sts get-caller-identity # 确认认证可用
export AWS_PROFILE=platform
terraform plan
1.2 alias 多区域
AWS 资源常需要跨区域编排(如主区域 + 灾备区域),用 provider "aws" 的 alias 声明多个实例,再由资源显式指定用哪一份配置。
provider "aws" {
alias = "us_west"
region = "us-west-2"
}
resource "aws_s3_bucket" "backup" {
provider = aws.us_west
bucket = "backup-${var.account_id}-${var.environment}"
}
一句话:Provider 是「配置模板」,alias 是「多份模板」,资源通过
provider = aws.alias选择用哪一份。
2. VPC / EC2 / 安全组组合
一句话总结: VPC 提供网络边界,子网划分公网/私网,安全组是实例级防火墙,三者组合时先规划网段,再让资源通过引用而非硬编码 ID 建立依赖。
一套最小可用的 Web 架构,网络层的正确组合顺序是:VPC → 子网 → 网关 → 路由表 → 安全组 → EC2。
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = { Name = "${var.environment}-vpc" }
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
tags = { Name = "${var.environment}-public" }
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
}
2.1 安全组只放行必要端口
安全组遵循最小放行原则:SSH 只对办公网段开放,Web 只开放 80/443。
resource "aws_security_group" "web" {
name = "${var.environment}-web-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.office_cidrs
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
2.2 EC2 实例与 user_data
EC2 引用 VPC、子网、安全组时一律用属性引用(aws_subnet.public.id),让 Terraform 自动构建依赖图,避免手工填 ID 造成的「删了重建」风险。
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-22.04-amd64-server-*"]
}
owners = ["099720109477"]
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
key_name = var.key_name
user_data = <<-EOT
#!/bin/bash
apt-get update -y
apt-get install -y nginx
systemctl enable nginx
EOT
tags = { Name = "${var.environment}-web", Role = "web" }
}
一句话:网络层编排的要诀是「引用不硬编码」——每个 ID 都从上游资源取,删除重建时依赖图会自动按序执行。
3. S3 与 CloudFront 静态托管
一句话总结: S3 负责对象存储,CloudFront 负责 CDN 加速与 HTTPS 终结,两者组合是静态站点/前端资源托管的标配,注意在 S3 上关闭对公网的直接写权限。
静态资源托管把「Web 服务器」换成「对象存储 + CDN」,成本更低、扩展性更好,还能避免 EC2 常驻浪费。
resource "aws_s3_bucket" "static" {
bucket = "${var.environment}-web-static"
}
resource "aws_s3_bucket_public_access_block" "static" {
bucket = aws_s3_bucket.static.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
3.1 CloudFront 分发
CloudFront 通过 origin 指向 S3 桶,并把默认证书换成自己的 ACM 证书,实现 HTTPS 加速。
resource "aws_cloudfront_distribution" "cdn" {
enabled = true
default_root_object = "index.html"
aliases = ["static.example.com"]
origin {
domain_name = aws_s3_bucket.static.bucket_regional_domain_name
origin_id = aws_s3_bucket.static.id
}
default_cache_behavior {
target_origin_id = aws_s3_bucket.static.id
viewer_protocol_policy = "redirect-to-https"
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
}
viewer_certificate {
acm_certificate_arn = aws_acm_certificate.main.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
}
4. RDS 与备份
一句话总结: RDS 用
aws_db_instance声明数据库,多可用区与自动备份是默认的安全底线,备份保留期、存储加密与参数组在创建时就要定好。
RDS 部署要点:子网组要覆盖数据库子网、安全组只放行应用端口、开启 backup_retention_period 与 storage_encrypted。
resource "aws_db_subnet_group" "db" {
name = "${var.environment}-db-subnet"
subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
}
resource "aws_security_group" "db" {
name = "${var.environment}-db-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.web.id]
}
}
resource "aws_db_instance" "main" {
identifier = "${var.environment}-mysql"
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.small"
allocated_storage = 50
multi_az = true
db_subnet_group_name = aws_db_subnet_group.db.name
vpc_security_group_ids = [aws_security_group.db.id]
storage_encrypted = true
backup_retention_period = 7
skip_final_snapshot = false
final_snapshot_identifier = "${var.environment}-mysql-final"
username = var.db_username
password = var.db_password
tags = { Name = "${var.environment}-mysql" }
}
4.1 密码与敏感值
RDS 密码绝不要写死在代码里:用变量 + 敏感标记,或直接从 Secrets Manager 读取。
variable "db_password" {
type = string
sensitive = true
description = "数据库密码,由 CI 密钥或 Secrets Manager 注入"
}
output "db_endpoint" {
value = aws_db_instance.main.endpoint
sensitive = true
}
一句话:数据库是「不可随意重建」的资源,因此备份、加密、快照这三件事必须在首次创建时配置到位。
5. IAM 最小权限
一句话总结: IAM 最小权限用「角色 + 托管策略 + 信任关系」组合,先给最小动作集,再按需扩展,用条件与资源限定避免权限泛滥。
IAM 是 AWS 安全的核心。原则只有一条:每个身份只拥有完成任务所需的最少权限。
5.1 角色、策略与信任关系
resource "aws_iam_role" "app" {
name = "${var.environment}-app-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_policy" "s3_read" {
name = "${var.environment}-app-s3-read"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["s3:GetObject", "s3:ListBucket"]
Resource = [
aws_s3_bucket.static.arn,
"${aws_s3_bucket.static.arn}/*",
]
}
]
})
}
resource "aws_iam_role_policy_attachment" "app_s3" {
role = aws_iam_role.app.name
policy_arn = aws_iam_policy.s3_read.arn
}
5.2 权限最小化清单
| 检查项 | 做法 |
|---|---|
| 动作最小化 | 只给 s3:GetObject,不给 s3:* |
| 资源限定 | Resource 指向具体 ARN 而非 * |
| 条件收紧 | 用 Condition 绑定 VPC、标签、时间 |
| 定期回收 | 审计未使用的角色与密钥 |
| 避免长期密钥 | 用 Assume Role / 临时令牌 |
6. 私网出网与分层防护
一句话总结: 安全组、子网、NAT 网关构成 AWS 网络的访问控制矩阵,私网资源出网依赖 NAT,公网入站依赖安全组与网络 ACL 双重把关。
当实例放进私网子网后,它没有公网 IP,出网需要 NAT 网关,入站则只允许来自安全组的流量。
resource "aws_eip" "nat" {
domain = "vpc"
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public.id
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
}
安全组是有状态防火墙(放行的应答流量自动放行),网络 ACL 是无状态防火墙,两者职责不同,不要相互替代;NAT 网关只解决私网出网,入站仍由安全组与 ACL 把关。
7. 常见 AWS 排障与坑
一句话总结: AWS 排障高发区集中在删除顺序、AMI 地域差异、S3 桶名全局唯一、RDS 密码变更触发重建这四类,先看
terraform plan的 diff 再动手。
| 坑 | 现象 | 正确做法 |
|---|---|---|
| 删除顺序错误 | 安全组仍被引用导致删除失败 | 依赖图不完整时用 depends_on 显式声明 |
| AMI 跨区域 | 别的区域 ID 找不到 | AMI 用 data 按名称+拥有者动态查询 |
| 桶名冲突 | BucketAlreadyExists | 桶名加 account/环境后缀保证全局唯一 |
| RDS 密码变更 | 密码变量变化触发实例重建 | 密码存 Secrets Manager,变更走轮换而非重建 |
| 子网扩容 | 子网 CIDR 不可改 | 规划时预留充足网段,避免事后重划 |
7.1 排查工作流
terraform plan -detailed-exitcode # 查看真实差异
terraform state show aws_instance.web # 单资源调试
terraform plan -refresh-only # 只看刷新后的差异
TF_LOG=DEBUG terraform apply # 看 provider 日志
8. 总结
一套真实可用的 AWS 基础设施,核心是把网络、计算、存储、数据库、权限五类资源按「引用组合」的方式组织起来:
| 环节 | 要点 |
|---|---|
| Provider | region + 认证链 + alias 多区域 |
| 网络 | VPC/子网/路由/安全组引用组合 |
| 计算 | EC2 + user_data,AMI 用 data 动态查询 |
| 存储 | S3 + CloudFront,内容上传与 IaC 解耦 |
| 数据库 | RDS 开启多可用区、加密、自动备份 |
| 权限 | IAM 最小权限:角色 + 限定策略 + 条件 |
| 治理 | 网络 ACL / 安全组 / IAM 分层把关 |
一句话收尾:AWS 实战的关键不是「创建资源」,而是「编排资源之间的引用关系与权限边界」。把网段规划、删除顺序、敏感值管理这三件事做对,这套模板就能在 dev/staging/prod 之间稳定复制。下一篇「多环境管理」将讲解如何用 workspace 与目录布局,把这套单环境模板扩成多环境体系。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。