惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
I
InfoQ
阮一峰的网络日志
阮一峰的网络日志
云风的 BLOG
云风的 BLOG
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AWS News Blog
AWS News Blog
S
SegmentFault 最新的问题
T
Tailwind CSS Blog
The Hacker News
The Hacker News
GbyAI
GbyAI
P
Palo Alto Networks Blog
博客园 - 三生石上(FineUI控件)
Y
Y Combinator Blog
Stack Overflow Blog
Stack Overflow Blog
博客园 - Franky
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Cyberwarzone
Cyberwarzone
H
Help Net Security
S
Securelist
月光博客
月光博客
博客园 - 【当耐特】
T
Threatpost
T
Tenable Blog
G
GRAHAM CLULEY
博客园 - 司徒正美
I
Intezer
MyScale Blog
MyScale Blog
T
Threat Research - Cisco Blogs
P
Privacy & Cybersecurity Law Blog
The GitHub Blog
The GitHub Blog
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
Google DeepMind News
Google DeepMind News
C
Cybersecurity and Infrastructure Security Agency CISA
罗磊的独立博客
腾讯CDC
P
Privacy International News Feed
博客园_首页
The Cloudflare Blog
Cisco Talos Blog
Cisco Talos Blog
A
About on SuperTechFans
V
Vulnerabilities – Threatpost
A
Arctic Wolf
B
Blog RSS Feed
Recorded Future
Recorded Future
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
S
Security Affairs
Microsoft Security Blog
Microsoft Security Blog
L
LangChain Blog

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 在 Docker 容器内集成 Crontab 定时任务 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 作为技术宅的我,是这样追鬼滅の刃的 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角
Terraform 极简入门:从 AWS-CLI 到基础设施即代码(IaC) - 元视角
飞鸿踏雪 · 2026-05-20 · via 元视角

缘起:为什么需要 Terraform

最近,我参与了一个 AWS Serverless 项目。业务需求本身并不复杂:几个 Lambda 函数、一个 S3 存储、一个 EventBridge 定时触发,再加上精细化的 IAM 权限控制。在部署这套服务的过程中,我先后尝试了三种方案,几乎踩遍了云基础设施管理中的经典巨坑:

  1. AWS Console:一开始为了快,直接上控制台点鼠标。创建 S3 Bucket、手动上传 Lambda zip 包、配置 EventBridge 规则。问题是:项目要部署到 dev、staging、prod 三个环境,在控制台里点一遍要花半小时,三个环境配置完一个半小时。两个月后,当有人问“这个安全组是谁配的,为什么开了 443端口”时,没人说得清。

  2. SAM:手动上传 zip 太繁琐,同事推荐了 SAM,通过一个 template.yaml 定义 Lambda 和权限,结合 sam build && sam deploy 确实省心。但当面对 VPC、安全组、S3 高级配置、更精细的 IAM 策略时,SAM 显得力不从心。于是我变成了“混合打法”:SAM 管理 Lambda,其余资源还是在控制台管理。两个工具,两套流程,让人身心俱疲。

  3. Terraform:运维同事终于看不下去了:“你这些资源应该用 Terraform 管理起来”。我一开始是抗拒的——一个 S3 Bucket 要写好几行代码,还要声明 Provider、定义变量,控制台里点几下搞定的事,为什么要大费周章写代码?

但是,很快一次意外说服了我。某天,有人在控制台里偷偷修改了路由表,却没有同步到代码中。第二天跑 terraform plan,配置漂移直接暴露。那一刻我才真正顿悟:Terraform 不仅仅是帮你创建资源的工具,更是帮你锁定“期望状态”的终极防线

故事到这里,你以为我全面拥抱 Terraform 了?并没有。对于 Lambda 函数的代码部署,我最后还是选择用 GitHub Actions + AWS CLI。因为:Terraform 擅长管理静态的基础设施,而高频的代码迭代更适合放在专业的 CI/CD 流水线中。所以,我最终的工程实践是:Terraform 掌管资源拓扑,CI/CD 流水线负责代码交付。架构没有银弹,关键在于各司其职。

什么是 Terraform

先说结论:用代码描述你期望的基础设施最终状态,然后让工具帮你自动实现,这就是基础设施即代码(IaC, Infrastructure as Code)的核心思想。我们可以对比一下两种思维模式:

  • AWS CLI 是“命令式”——你必须精确告诉它每一步该怎么做:
# 命令式:按顺序下达指令
aws s3api create-bucket --bucket my-bucket --region us-west-2
aws s3api put-bucket-versioning --bucket my-bucket --versioning-configuration Status=Enabled
  • Terraform 是“声明式”——你只需要描述你想要的最终结果:
# 声明式:只描述期望的最终状态
resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-bucket"
}

resource "aws_s3_bucket_versioning" "my_bucket" {
  bucket = aws_s3_bucket.my_bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

当你执行 terraform apply 命令时,Terraform 会自动查漏补缺,智能处理以下三种场景:

  1. 现实中没有:自动创建。
  2. 现实中有,但配置不一致:自动修改、校准。
  3. 代码里删除了:下次 apply 时自动释放对应资源。

从“命令式指令”“声明式状态”的思维转变,是开发者接触 Terraform 时需要跨越的第一道门槛。

核心概念

第一次看 Terraform 代码,你可能会被一堆配置搞得眼花缭乱。其实,学习 Terraform 不需要死记硬背,牢牢把握住三个核心概念即可:Resource(资源)Provider(服务商)State(状态文件)

Resource:你想要什么

resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-unique-bucket-name"
}

其中:

  • resource:关键字,声明“我要定义一个资源”。
  • aws_s3_bucket:资源类型,由 Provider 定义,此处对应 AWS S3 存储桶。
  • my_bucket:逻辑名称(Logical Name),仅在代码内部引用时使用,可以自由命名。
  • bucket = “…":参数/属性,配置该资源的真实具体属性。

核心避坑点 —— 代码中的逻辑名称 my_bucket 与云端真实的资源名称 my-unique-bucket-name 是两码事:

  • 前者类似于代码中的变量名,方便你在其他 .tf 文件中引用该资源;
  • 后者则是最终在 AWS 控制台上呈现的资源实体名称。

Provider:该找谁要

provider "aws" {
  region = "us-west-2"
}

Provider 扮演着“翻译官”的角色。当你在代码中声明“我需要一个 S3 存储桶”时,Provider 负责将你的意图翻译成对应云厂商的底层的 API 调用:

  • aws_s3_bucket 资源只能由 AWS Provider 来解析和驱动
  • azurerm_storage_container 资源则只能由 Azure Provider 来解析和驱动

State:都有什么

当你运行 terraform apply 命令后,它会在本地或远程生成一个名为 terraform.tfstate 的状态文件。这个文件记录了:

  • 当前代码接管了哪些现实资源。
  • 这些资源在云端的真实 ID 和属性快照。
  • 上一次成功应用配置后的基线状态。

状态文件是 Terraform 的心脏。唯有依靠它,Terraform 才能精准计算出“云端实际状态”与“代码期望状态”之间的偏差。这与 Git 对比两次提交差异的底层逻辑相通:Git 比较的是代码的历史版本,而 Terraform 比较的则是本地代码与云端环境。

举例说明:假设代码中声明了 bucket = "my-bucket"。下次运行 terraform plan 命令时,Terraform 读取状态文件后得知"此前已管理过一个存储桶,云端 ID 为 my-bucket”,随即向 AWS 发起实时校验。如配置完全一致,终端便会输出:No changes一旦状态文件丢失,Terraform 将会“失忆”,误以为这是一份全新的配置,进而尝试在云端重新创建资源,最终导致名称冲突甚至意外覆盖。。


实战:创建第一个资源

让我们从最简单的配置开始,创建 main.tf文件:

provider "aws" {
  region = "us-west-2"
}

resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-tutorial-bucket-2026"
}

接下来,便是标准的 Terraform 三板斧 命令:

# 1. 初始化:下载所需的 provider 插件
terraform init   
# 2. 预览:对比差异,预览将要发生的变更(不会真正动云端资源)
terraform plan 
# 3. 部署:将变更真正应用到云端
terraform apply  

在执行 plan 时,终端会输出清晰的差异对比图:

Terraform will perform the following actions:

  # aws_s3_bucket.my_bucket will be created
  + resource "aws_s3_bucket" "my_bucket" {
      + bucket = "my-tutorial-bucket-2026"
      + id     = (known after apply)
      + arn    = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

此时,必须小心谨慎地对待以下变更差异符号:

  • + 创建:表示创建了新资源
  • - 删除:表示删除了某个资源
  • ~ 修改:表示在原资源上更新特定属性
  • -/+ 破坏性替换:由于修改了不可变参数,,Terraform 必须先删后建。在生产环境中可能到这个符号务必小心,这意味着可能会删除原有数据,属于危险操作

执行完成后,你会看到目录中多出了 terraform.tfstate 文件,里面已经记录了当前存储桶的信息。


Variables:拒绝硬编码

在上面的例子中,我们把 region 和 bucket 名称直接写死了,这在软件工程中属于硬编码

provider "aws" {
  region = "us-west-2"  # hardcode
}

resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-tutorial-bucket-2026"  # hardcode
}

考虑到代码的可维护性,我们引入一个新的文件 variables.tf,将可变参数抽离出来:

# variables.tf
variable "region" {
  type    = string
  default = "us-west-2"
}

variable "bucket_name" {
  type = string
}

然后在 main.tf 中通过 var.xxx 的形式动态引用变量:

# main.tf
provider "aws" {
  region = var.region
}

resource "aws_s3_bucket" "my_bucket" {
  bucket = var.bucket_name
}

在实际执行时,有三种常见的方式注入这些变量的值:

# 1. 命令行
terraform apply -var="bucket_name=my-prod-bucket"

# 2. 变量文件(推荐)
terraform apply -var-file="prod.tfvars"

# 3. 环境变量
export TF_VAR_bucket_name="my-prod-bucket"
terraform apply

其中 prod.tfvars 通常是类似下面这样的键值对形式:

bucket_name = "my-prod-bucket"
region      = "us-west-2"

在需要多环境管理的生产级项目中,更推荐使用 workspaces 目录 + tfvars.json 实现多环境配置管理:

workspaces/
├── dev.tfvars.json
├── staging.tfvars.json
└── prod.tfvars.json

Outputs:交付产物

在 AWS 体系中,资源的 ARN(Amazon Resource Name,唯一资源标识) 是非常核心的概念。例如在编写 IAM 权限策略时,频繁需要引用其他资源的 ARN。当 Terraform 创建完资源后,我们如何将资源的 ID、ARN 等属性安全地暴露给下游服务或团队呢?这就需要用到 outputs.tf

output "bucket_arn" {
  description = "The ARN of the bucket"
  value       = aws_s3_bucket.my_bucket.arn
}

运行 terraform apply 命令后,终端将会打印出类似下面的信息:

Outputs:

bucket_arn = "arn:aws:s3:::my-tutorial-bucket-2026"

Output 的两大核心场景:

  1. 直观可视:让运维人员或 CI/CD 脚本在部署完后,能快速直接获取到关键节点信息
  2. 跨模块联动:下游的 Terraform 模块可以通过 terraform_remote_state 远程读取,实现模块解耦

Locals:计算中间值

variables 负责处理来自外部的输入参数,它可以通过 tfvars 或者 tfvars.json 文件传入,而内部复杂的逻辑计算和复用,则应该交给 locals。例如,我们希望规范资源命名,自动为所有资源带上环境后缀,并统一注入 Tag 标签:

# local.tf
locals {
  bucket_name = "my-app-${var.env}"
  tags = {
    Environment = var.env
    ManagedBy   = "terraform"
  }
}

# main.tf
resource "aws_s3_bucket" "my_bucket" {
  bucket = local.bucket_name
  tags   = local.tags
}

什么时候该用 locals?

  • 多个资源需要共享一段完全相同的计算逻辑或字符串。
  • text 表达式太长、太复杂,不希望在 main.tf 中破坏可读性。
  • 为了提高代码自解释性,给某段计算结果起个有意义的名字。

资源引用:隐式依赖处理

我们来看一个稍微复杂的场景:为刚刚创建的 S3 存储桶配置一条 Bucket Policy。首先,我们在独立的文件 policies/s3-policy.json 中定义好标准的 JSON 策略模板,并预留占位符:

  • 先创建一个 JSON 模板文件 policies/s3-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "${bucket_arn}/*"
    }
  ]
}
  • 接着,在 main.tf 中用 templatefile 函数渲染该 JSON,并建立资源关联:
resource "aws_s3_bucket" "my_bucket" {
  bucket = var.bucket_name
}

resource "aws_s3_bucket_policy" "my_policy" {
  bucket = aws_s3_bucket.my_bucket.id

  policy = templatefile("${path.module}/policies/s3-policy.json", {
    bucket_arn = aws_s3_bucket.my_bucket.arn
  })
}

注意看这里的 aws_s3_bucket.my_bucket.arn:在代码里,我们并没有手动去指定谁先创建、谁后创建。但 Terraform 在解析代码时,会通过这一行引用自动建立一张有向无环图(DAG)。它知道 Policy 依赖 Bucket 的 ARN,因此会极其智能地先创建存储桶,拿到 ARN 后再创建策略。这就是声明式编程带来的巨大红利。


进阶:目录拆分

随着项目规模的扩大,把所有基础设施全部写在一个 main.tf 里会变成一场灾难。优秀的实践是将资源按职责和生命周期拆分成独立的子目录。每个目录拥有自己独立的状态文件,这样可以极大程度地控制爆炸半径(Blast Radius)——哪怕你在改 Lambda 代码时配置写错了,绝对不会波及核心的网络和 IAM 权限:

infra/
├── iam/              # IAM 角色和策略
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   └── workspaces/
│       └── prod.tfvars.json
└── lambda/           # Lambda 函数
    ├── main.tf
    ├── variables.tf
    └── workspaces/
        └── prod.tfvars.json

在部署时,各自独立执行:

cd iam && terraform apply
cd lambda && terraform apply

此时面临一个新挑战:lambda 目录在配置函数时,需要引用 iam 目录刚刚创建出来的角色 ARN。它们不仅文件独立,连状态文件都不在一个地方,该怎么打通?答案是利用 terraform_remote_state 数据源,跨边界直接读取对方的部署产物:

data "terraform_remote_state" "iam" {
  backend = "s3"
  config = {
    bucket = "my-terraform-state"
    key    = "iam/terraform.tfstate"
    region = "us-west-2"
  }
}

resource "aws_lambda_function" "my_function" {
  role = data.terraform_remote_state.iam.outputs.lambda_role_arn
  # ...
}

架构痛点与思考:使用这种方式,你需要在代码里硬编码对方的远程 S3 路径。当子目录繁多时,这些路径依赖会交织成一张复杂的蜘蛛网,维护成本极高。这解释了为什么在团队规模扩大后,往往需要引入 Terraform Cloud、Atlantis 或 Terragrunt 等高级编排工具的原因——它们能够帮你统一托管 Backend 和拓扑依赖,告别手动配置。


总结

万变不离其宗,掌握 Terraform 的核心其实就三件事:

  1. 声明你想要什么(Resource 描述期望状态)
  2. 记录现在有什么(State 文件记录现实快照)
  3. 计算差距并严格执行(Plan 预览变更,Apply 抹平差距)

至于变量、输出、局部变量、数据源、模块等概念,统统是围绕这三件事衍生出来的效率工具。例如,当一个资源由你来控制时,你应该使用 resource 关键字描述资源;当一个资源已经存在时,你应该使用 data 关键字查询资源。

什么时候用什么工具?

业务场景推荐选型选用理由
临时测试、一次性调研、做完就忘AWS 控制台纯可视化,直观方便,无额外心智负担
临时运维小脚本、快速查询或单步销毁AWS CLI轻量快捷,不需要初始化环境和声明状态
纯粹的 Serverless / Lambda 应用开发AWS SAM / Serverless 框架针对 FaaS 深度优化,代码打包与热编译体验极佳
多云环境、多层资源交织、团队协作、长期演进Terraform完善的状态管理、声明式依赖拓扑、变更计划预览

在实际的工业界项目中,混用(Hybrid)才是常态:我们用 Terraform 锁死 VPC、数据库、IAM 等底层重型基础设施;用 SAM 或专有 CI/CD 流水线去高频发布 Lambda 业务代码;用 AWS CLI 临时处理线上紧急巡检。

最后,请允许我向 Terraform 新手们提供一份生存指南:

  • 状态文件必须上云锁死:本地存 State 只适合个人玩具项目。多人协作务必配置远程 Backend(如 S3),并开启 DynamoDB 状态锁,防止两人同时 apply 导致状态文件损坏。
  • 严禁控制台“偷渡”修改:一旦用了 Terraform,就请管住去控制台修改参数的手。任何微小的线上人肉微调,都会在下一次自动化 apply 时被无情覆盖,甚至引发灾难。
  • 重视 prevent_destroy 护身符:对于生产环境的数据库、核心存储桶等不可再生资源,务必在代码中的 lifecycle 块里加上 prevent_destroy = true,最大程度防止误删风险。
  • 警惕 -/+ 符号:再次强调,plan 结果里一旦出现 -/+,说明有资源要被干掉重建,核对好该资源是否有未备份的持久化数据!

如果你发现自己正在重复敲着一堆类似的 AWS CLI 命令,或者在控制台频繁为新客户克隆同一套配置,那么,请不要怀疑,你该用 Terraform 啦!