---
url: /courses/cloud-platform-build-management/13-terraform-cloud-storage/index.md
---
# 第十三次课：Terraform 计划与云存储

## 进入本课环境

* `[Windows PowerShell] wsl ~`：打开本机默认的 WSL2 Ubuntu；本课只在 WSL 中执行 `fmt`、`validate` 和 `plan`，不需要 SSH 登录 ECS，也不执行 `apply`。

前两次课我们先看懂了公有云资源，又讨论了生命周期与恢复。本次课开始把资源意图写成代码。代码可以复查、比较和重复使用，但也会带来新的风险：一行配置可能影响许多资源，状态文件和保存的计划还可能包含敏感数据。

::: tip 关键提醒
Terraform 配置描述“想要什么”，plan 展示“准备改变什么”，state 记录“配置对应现实中的什么”。本课只到 plan，不执行 apply。
:::

学完这一课，你可以：

1. 读懂最小 Terraform 配置中的版本、变量、资源和输出。
2. 按 `init → fmt → validate → plan → review` 完成一次只读执行计划。
3. 根据 `add/change/destroy` 评审计划，不把“命令成功”当成“方案安全”。
4. 说明 state、保存的 plan 和变量文件为什么要按敏感数据管理。
5. 设计一个私有 OSS Bucket 的用途、存储类型、保留和恢复边界。

## 13.1 先划清执行边界

本课只走到 `terraform plan`。配置用于练习格式、校验、计划评审和敏感文件治理，不配置真实 AccessKey，也不执行 `apply`。

完成时要能拿出三类证据：配置通过 `fmt` 与 `validate`，计划摘要与预期一致，state、plan、`.env` 和私密变量没有进入提交目录。

## 13.2 IaC 不是把控制台按钮换成脚本

**基础设施即代码（IaC）**（用可版本化的配置描述基础设施目标状态，并通过工具生成、评审和执行变更）。

Terraform 的核心代码块与分工如下：

| 代码块 | 回答的问题 | 本课示例 |
|---|---|---|
| `terraform` 块 | 声明依赖的 Terraform 版本与 Provider | `required_version` |
| `variable` 块 | 哪些参数允许跨环境灵活变动？ | 环境标识、存储类型、保留天数 |
| `resource` 块 | 最终希望在云端创建管理什么资源？ | 内置 `terraform_data` 测试载体 |
| `output` 块 | 执行完毕后需要暴露哪些关键信息？ | 计划生成摘要 |
| state 状态文件 | 配置里的逻辑地址对应现实中的哪项资源？ | 本课仅执行 `plan`，不产生正式 state |

本课的 `main.tf` 采用 Terraform 内置的 `terraform_data` 资源，用来承载“云存储方案模拟评审”。它不连接阿里云 API，也不会创建真实的 OSS Bucket，学生可以练习完整的 Terraform 计划流程，同时避免在本次实训中引入云凭据和资源费用。

```hcl
resource "terraform_data" "course_storage_plan" {
  input = {
    environment    = var.environment
    storage_class  = var.storage_class
    retention_days = var.retention_days
    execution_mode = "plan-only"
  }
}
```

这段 HCL 声明了一个本地计划对象。资源唯一标识是 `terraform_data.course_storage_plan`；`input` 中的 4 个字段会在 `plan` 阶段全部输出，方便审核环境、存储类型与保存期限。

定义变量时，建议显式提供类型约束与校验逻辑：

```hcl
variable "retention_days" {
  description = "拟议的备份保留天数"
  type        = number
  default     = 7

  validation {
    condition     = var.retention_days >= 1 && var.retention_days <= 30
    error_message = "课程演示的保留天数必须在 1—30 天之间。"
  }
}
```

`type = number` 严防非数值混入；`validation` 块则在最外层拦截非法入参。它能预防明显的配置差错，但业务上的最终保留期与成本预算依然需要运维人员人工把关。

## 13.3 从初始化到计划评审

实训包包含 `main.tf`、`.gitignore`、`terraform.tfvars.example` 和 `starter/` 答题模板（从智慧职教课程资源区下载 `lab-13-starter.zip` 解压）。切换到 **WSL**：

```bash
[WSL] mkdir -p ~/cloud-course/lab-13
[WSL] cd ~/cloud-course/lab-13
[WSL] cp -r /path/to/starter/* .
```

确认当前的工作目录。执行 `pwd` 预期结果末尾应包含 `/cloud-course/lab-13`。

```bash
[WSL] terraform version
```

输出当前安装的 Terraform CLI 版本与系统平台。如果提示命令未找到，请暂停实训，按官方文档和课程安装说明处理，严禁使用来源不明的版本。

```bash
[WSL] terraform init -backend=false -input=false
```

初始化工作目录。`-backend=false` 不配置远程状态，`-input=false` 禁止交互提问。本例只使用内置 Provider，因此不会索取云凭据。

```bash
[WSL] terraform fmt
```

按 Terraform 规范格式化 `.tf` 文件；有改动时会显示文件名。格式化只调整代码样式，不判断云资源是否合理。

```bash
[WSL] terraform fmt -check -diff
```

检查格式而不写入，`-diff` 在不合格时显示差异。命令没有输出且退出码为 0，表示格式检查通过。

```bash
[WSL] terraform validate
```

检查配置语法和内部一致性。HashiCorp 官方文档明确说明，`validate` 不会验证远程服务或 Provider API；配置语法通过只是过了第一关，账号、配额、地域和资源名称是否可用还得另外确认。

2026-07-28 在云主机 B 的隔离目录中真实运行，使用的是通过官方 SHA-256 校验的 Terraform 1.15.8：

## 13.4 plan 是变更提案，不是执行结果

```bash
[WSL] terraform plan -input=false -lock=false -out=course.tfplan -var-file=terraform.tfvars.example
```

生成并保存执行计划。`-out` 把计划写入二进制文件，`-var-file` 读取本课非敏感示例值；`-lock=false` 仅用于本课没有共享远程 state 的隔离演示，真实团队环境不应随意关闭锁。

计划输出中的符号要逐项确认：

| 符号或摘要 | 含义 | 评审动作 |
|---|---|---|
| `+ create` | 准备创建新对象 | 核对名称、地域、费用、默认访问权限 |
| `~ update in-place` | 准备原地修改 | 核对服务影响和回退 |
| `-/+ replace` | 先删后建或替换 | 按高风险动作评审数据与停机 |
| `- destroy` | 准备删除 | 没有明确授权和恢复点就停止 |
| `(known after apply)` | 只有执行后才能得到 | 不能把 unknown 编造成确定值 |

本课真实 plan 只有内置教学对象：

```bash
[WSL] terraform show -no-color course.tfplan
```

以可读形式再次查看保存的计划。`-no-color` 便于放入日志或报告；不要把完整计划文件或包含敏感值的输出上传到作业平台。

{{guided-demo:lesson-13-plan-review}}

看到 `Plan: 1 to add` 还不能直接批准。继续检查：

1. 创建的是哪一个资源地址？
2. 输入来自哪个环境，变量值是否符合本课范围？
3. 有没有 `destroy` 或 `replace`？
4. 访问权限、加密、版本、保留和费用是否明确？
5. state 在哪里，谁能读写，是否支持锁？
6. 计划是否使用了真实凭据或敏感变量？

## 13.5 state 和 plan 都可能泄露秘密

Terraform 使用 state 把配置中的资源地址映射到远端对象，并保存依赖和属性。默认本地文件名为 `terraform.tfstate`。它不是普通构建产物，也不能直接手工编辑。

::: warning 安全边界
变量标记 sensitive 主要隐藏终端显示，不保证值不会进入 state 或保存的 plan。state、tfplan、含密钥的 tfvars 和后端配置都要按敏感文件管理。
:::

本课 `.gitignore` 至少包含：

```gitignore
.terraform/
*.tfstate
*.tfstate.*
*.tfplan
*.auto.tfvars
.env
```

`.terraform/` 可能含 Provider、模块和后端配置；`*.tfstate*` 排除状态与备份；`*.tfplan` 排除保存的计划；`*.auto.tfvars` 和 `.env` 防止常见私密输入误提交。`.terraform.lock.hcl` 是 Provider 依赖锁文件，真实项目通常应提交，它与 state 锁不是同一个概念。

实训包中已包含 `.gitignore` 文件，确认它在当前目录：

```bash
[WSL] cp starter/.gitignore .gitignore 2>/dev/null || test -f .gitignore
[WSL] git init
```

只在当前实验目录创建本地 `.git/` 元数据，不会提交文件，也不会连接远程仓库。完成这一步后，`git check-ignore` 才有仓库上下文可以读取本课 `.gitignore`。

```bash
[WSL] git check-ignore -v --no-index terraform.tfstate course.tfplan secrets.auto.tfvars .env .terraform/
```

显示每个路径匹配了 `.gitignore` 的哪一行。`--no-index` 也检查未跟踪或不存在的示例路径。

```bash
[WSL] find . -maxdepth 1 -name '*.tfstate*' -print
```

只在当前目录查找 state 文件。本课没有执行 apply，预期没有输出；没有本地 state 不代表可以忽略远程后端治理。

团队使用远程 state 时，还要确认以下问题：

* 是否加密传输和静态存储？
* 是否最小化读写权限？
* 是否支持 state locking，避免多人同时修改？
* 是否有版本和恢复能力？
* 后端凭据是否通过环境或凭据链提供，而不是写进配置？

## 13.6 把 OSS 作为资源来设计

**对象存储**（以 Bucket、Object Key、对象数据和元数据管理非结构化数据的存储模型）。

阿里云官方文档说明 OSS 提供 RESTful API，并支持标准、低频、归档等存储类型。它适合备份包、图片、日志和静态资源，不是直接挂载给数据库的普通块设备。

设计课程备份 Bucket 时，至少要明确以下内容：

| 设计项 | 要回答的问题 | 课程默认 |
|---|---|---|
| 用途 | 保存站点备份、数据库备份还是静态资源？ | 站点备份 |
| 地域 | 与 ECS、合规和传输成本怎样匹配？ | 按本人资源核对 |
| ACL | 谁能读取对象？ | 私有 |
| 存储类型 | 访问频率和最短存储周期怎样？ | 先评审 Standard |
| 版本与保留 | 误删后能否恢复，保留多久？ | 写入设计，不执行 |
| 生命周期规则 | 何时转低频、归档或删除？ | 不自动删除未知对象 |
| 加密 | 平台侧和客户端侧怎样保护？ | 核对官方能力 |
| 费用 | 容量、请求、取回和流量怎样计费？ | 以控制台与账单为准 |

在真实的阿里云 Provider 中，OSS Bucket 和 ACL 已经是独立资源。以下代码仅供阅读，不要放进本课的可执行目录：

```hcl
resource "alicloud_oss_bucket" "course_backup" {
  bucket        = "必须全局唯一且经过批准的名称"
  storage_class = "Standard"
  force_destroy = false
}

resource "alicloud_oss_bucket_acl" "course_backup" {
  bucket = alicloud_oss_bucket.course_backup.bucket
  acl    = "private"
}
```

第一段声明 Bucket 并保持 `force_destroy = false`，避免删除 Bucket 时顺带删除全部对象。第二段单独设置私有 ACL。代码仍缺少 Provider、地域、凭据、版本、生命周期、加密、费用和授权，不能直接 apply。

阿里云还提供 OSS Backend 方案，把 Terraform state 存入 OSS，并可结合锁机制支持团队协作。远程保存不是“把 tfstate 随便上传到一个 Bucket”：Bucket 权限、加密、版本、锁、恢复和后端凭据都必须一起设计。

{{chat-lab:lesson-13-terraform-evidence}}

## 13.7 本课实训

在 `~/cloud-course/lab-13` 完成：

```bash
[WSL] mkdir -p answers
```

创建答题目录。

```bash
[WSL] cp starter/plan-review.tsv answers/plan-review.tsv
```

复制计划评审表，逐项填写资源地址、动作、风险、费用、验证和批准结论。

```bash
[WSL] cp starter/storage-design.tsv answers/storage-design.tsv
```

复制云存储设计表，填写 Bucket 用途、访问、存储类型、版本、保留、加密、费用和恢复。

```bash
[WSL] bash verify.sh
```

脚本实际运行 fmt、init、validate 和 plan，检查 `1 add / 0 change / 0 destroy`、敏感文件排除与两张答题表。脚本不会执行 apply，也不会连接阿里云 API。

完整步骤见[实训 13：Terraform 计划评审与 OSS 设计](./lab-13.md)。

## 13.8 清理和提交

```bash
[WSL] find . -maxdepth 2 -type f -print
```

清理前列出两层内的文件，确认没有真实 `.env`、私密 tfvars、state 或未知 plan。

```bash
[WSL] find .terraform -maxdepth 2 -type f -print 2>/dev/null
```

只读查看初始化目录。确认报告证据已保存后，执行清理：

```bash
[WSL] rm -rf .terraform course.tfplan
```

不要在路径不清楚时执行递归删除——先用 `pwd` 确认当前位于 `lab-13` 目录。

提交：

```txt
班级_学号_姓名_第13次课_Terraform计划与云存储报告.docx
```

只提交一个 Word 到智慧职教“第13次课”。不提交 `.terraform/`、`terraform.tfstate*`、`*.tfplan`、私密 `*.tfvars`、`.env`、AccessKey 或完整控制台页面。

{{reflection-checkpoint:lesson-13-iac-ready}}

### 13.8.1 第九至十三次课回归测评

这组题把阶段排障、OpenStack 与阿里云资源映射、ECS 生命周期边界和 Terraform 计划放在同一张变更评审表里。作答时注意区分“文档或计划能证明什么”和“真实资源已经发生了什么”。

{{assessment:stage-13-regression}}

## 13.9 第十三次课小测

{{assessment:lesson-13-check}}

## 13.10 小结

* IaC 把资源意图写成可复查的配置，但不会自动保证方案安全。
* `fmt` 管样式，`validate` 管语法与内部一致性，`plan` 展示拟议变更。
* `plan` 不会执行变更，`apply` 才会改变真实对象。
* state 建立配置与现实对象的映射，保存的 plan 也可能含敏感数据。
* OSS 是对象存储，设计时要考虑 ACL、存储类型、版本、保留、恢复和费用。
* 本课停在 plan，不配置真实 AccessKey，不创建付费资源。

下一次课将回到 Compose，把目录、配置、健康检查和从空目录重建整理成可交付标准。

为下一步整理一个交接包：保留脱敏后的配置与 plan 摘要，确认 state、计划文件、`.env` 和私密变量没有进入提交目录，并写清“未执行 apply”。接下来的 Compose 应用交付也沿用“配置可审、运行文件受控、执行结果可验”的边界。

## 13.11 资料来源

以下资料在 2026-07-28 核对：

* [HashiCorp：Terraform CLI 安装](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)
* [HashiCorp：terraform fmt](https://developer.hashicorp.com/terraform/cli/commands/fmt)
* [HashiCorp：terraform validate](https://developer.hashicorp.com/terraform/cli/commands/validate)
* [HashiCorp：terraform plan](https://developer.hashicorp.com/terraform/cli/commands/plan)
* [HashiCorp：Terraform State](https://developer.hashicorp.com/terraform/language/state)
* [HashiCorp：管理敏感数据](https://developer.hashicorp.com/terraform/language/manage-sensitive-data)
* [阿里云：什么是对象存储 OSS](https://help.aliyun.com/zh/oss/user-guide/what-is-oss)
* [阿里云：使用 Terraform 管理 OSS](https://help.aliyun.com/en/oss/developer-reference/terraform-overview/)
* [Terraform Registry：alicloud\_oss\_bucket](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/oss_bucket.html)
* [阿里云：Terraform OSS Backend](https://help.aliyun.com/zh/terraform/five-minute-introduction-to-alibaba-cloud-terraform-oss-backend)

{{assistant-invite:lesson-13-finish}}
