Terraform
应用 Terraform 配置
编写 Terraform 配置后,核心 Terraform 工作流程包含三个主要步骤:
- 初始化(Initialize):准备工作空间,以便 Terraform 可以应用配置。
- 计划(Plan):允许您在应用更改之前预览 Terraform 将要进行的更改。
- 应用(Apply):执行计划中定义的更改,以创建、更新或销毁资源。
当你将更改应用于你的基础设施时,Terraform 将在初始化期间安装的提供程序和模块来执行存储在执行计划中的步骤。这些步骤创建、更新和删除基础设施,以匹配你的资源配置。
在本教程中,你将应用示例配置并查看 Terraform 为应用更改所采取的步骤。你还将学习 Terraform 如何从应用期间的错误中恢复,以及如何使用 apply 命令的一些常见方法。
先决条件
您可以使用 Terraform Community Edition 或 HCP Terraform 以相同的流程完成本教程。HCP Terraform 是一个平台,可用于管理和执行您的 Terraform 项目。它包含远程状态和执行、结构化计划输出、工作区资源摘要等功能。
选择 HCP Terraform 标签页以使用 HCP Terraform 完成本教程。
本教程假设您熟悉 Terraform 工作流程。如果您是 Terraform 新手,请先完成 入门教程。
为了完成本教程,您需要以下条件
- 本地安装 Terraform v1.6+。
- 一个配置了本地凭证的AWS 账户,这些凭证已配置为与 Terraform 一起使用。
- 命令行实用工具 jq。
克隆示例仓库
在您的终端中,克隆 learn-terraform-apply 仓库。
$ git clone https://github.com/hashicorp-education/learn-terraform-apply
导航到克隆的仓库。
$ cd learn-terraform-apply
检查配置
此仓库中的示例配置通过资源、本地模块和公共模块创建 EC2 实例。 modules/aws-ec2-instance 子目录包含用于创建实例的本地模块。
$ tree
.
├── LICENSE
├── main.tf
├── outputs.tf
├── README.md
├── terraform.tf
└── variables.tf
Terraform 使用在 terraform.tf 文件中指定的提供程序版本。
terraform.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.2.0"
}
random = {
source = "hashicorp/random"
version = "3.5.1"
}
time = {
source = "hashicorp/time"
version = "0.9.1"
}
}
## ...
required_version = "~> 1.6"
}
打开 main.tf 文件。此文件定义了 EC2 实例和 S3 存储桶的配置。
main.tf
provider "aws" {
region = var.region
}
provider "random" {}
provider "time" {}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "random_pet" "instance" {
length = 2
}
resource "aws_instance" "main" {
count = 3
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
tags = {
Name = "${random_pet.instance.id}-${count.index}"
Owner = "${var.project_name}-tutorial"
}
}
resource "aws_s3_bucket" "example" {
tags = {
Name = "Example Bucket"
Owner = "${var.project_name}-tutorial"
}
}
在此示例配置中,aws_instance.main 资源依赖于 random_pet.instance 资源和 aws_ami.ubuntu 数据源。当您应用此配置时,Terraform 将创建 random_pet 资源并在创建实例之前填充 aws_ami 数据源。
初始化您的配置
为了生成您的执行计划,Terraform 需要安装您的配置引用的提供程序和模块。然后,它将引用它们来创建您的计划。
使用 terraform init 初始化 Terraform 配置。
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of hashicorp/random from the dependency lock file
- Reusing previous version of hashicorp/time from the dependency lock file
- Installing hashicorp/aws v6.2.0...
- Installed hashicorp/aws v6.2.0 (signed by HashiCorp)
- Installing hashicorp/random v3.5.1...
- Installed hashicorp/random v3.5.1 (signed by HashiCorp)
- Installing hashicorp/time v0.9.1...
- Installed hashicorp/time v0.9.1 (signed by HashiCorp)
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
应用配置
应用配置。
$ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 0s [id=ami-055744c75048d8296]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.main[0] will be created
+ resource "aws_instance" "main" {
## ...
Plan: 5 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ bucket_name = (known after apply)
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
由于您没有提供保存的计划,Terraform 创建了一个计划并要求您在对资源进行任何更改之前批准它。
当您批准计划并应用此配置时,Terraform 将
- 锁定工作区的状态,以便 Terraform 的其他实例不会尝试修改您的状态或对您的资源进行更改。如果 Terraform 检测到现有的锁文件 (
.terraform.tfstate.lock.info),它将报告一个错误并退出。 - 创建计划,并等待您批准。或者,您可以提供使用
terraform plan命令创建的保存的计划,在这种情况下,Terraform 将不会提示您批准。 - 使用您在初始化配置时安装的提供程序执行计划中定义的步骤。Terraform 在可能的情况下并行执行步骤,并在一个资源依赖于另一个资源时按顺序执行。
- 使用资源的新状态的快照更新您的工作区状态。
- 解锁您的工作区状态。
- 报告它所做的更改,以及配置中定义的任何输出值。
使用 yes 回应确认提示以应用建议的执行计划。
##...
Enter a value: yes
random_pet.instance: Creating...
random_pet.instance: Creation complete after 0s [id=dashing-tapir]
aws_s3_bucket.example: Creating...
aws_instance.main[2]: Creating...
aws_instance.main[0]: Creating...
aws_instance.main[1]: Creating...
aws_s3_bucket.example: Creation complete after 2s [id=terraform-20230831212958335300000001]
aws_instance.main[2]: Still creating... [10s elapsed]
aws_instance.main[0]: Still creating... [10s elapsed]
aws_instance.main[1]: Still creating... [10s elapsed]
aws_instance.main[2]: Still creating... [20s elapsed]
aws_instance.main[1]: Still creating... [20s elapsed]
aws_instance.main[0]: Still creating... [20s elapsed]
aws_instance.main[0]: Still creating... [30s elapsed]
aws_instance.main[1]: Still creating... [30s elapsed]
aws_instance.main[2]: Still creating... [30s elapsed]
aws_instance.main[2]: Creation complete after 33s [id=i-0c8c41a0cde73a0a3]
aws_instance.main[1]: Still creating... [40s elapsed]
aws_instance.main[0]: Still creating... [40s elapsed]
aws_instance.main[1]: Creation complete after 43s [id=i-0ed29492e0729f3ee]
aws_instance.main[0]: Still creating... [50s elapsed]
aws_instance.main[0]: Creation complete after 53s [id=i-0a612c0bb446b46f4]
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
Outputs:
bucket_name = "terraform-20230831212958335300000001"
当你应用示例配置时,Terraform 首先创建了随机宠物名称和镜像资源,然后并行创建了依赖于它们的四个容器。当 Terraform 创建计划时,它会分析你的资源之间的依赖关系,以便以正确的顺序进行更改,并在可能的情况下并行进行。当它应用你的配置时,Terraform 会报告其进度,因为它创建、更新和删除你的资源。
应用期间的错误
当 Terraform 在应用步骤期间遇到错误时,它将
- 记录错误并将其报告到控制台。
- 使用任何更改更新状态文件,以更新你的资源。
- 解锁状态文件。
- 退出。
Terraform 应用步骤出错后,你的基础设施可能处于无效状态。Terraform 不支持自动回滚部分完成的应用。在解决错误后,你必须再次应用你的配置,以将你的基础设施更新到所需的状态。
要查看 Terraform 如何处理错误,请在应用期间引入一个有意错误。
将以下配置添加到 main.tf 以创建一个新的 S3 对象。
main.tf
resource "aws_s3_object" "example" {
bucket = aws_s3_bucket.example.bucket
key = "README.md"
source = "./README.md"
etag = filemd5("./README.md")
}
为新的配置创建一个保存的计划。
$ terraform plan -out "add-object"
random_pet.instance: Refreshing state... [id=dashing-tapir]
data.aws_ami.ubuntu: Reading...
aws_s3_bucket.example: Refreshing state... [id=terraform-20230831212958335300000001]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-055744c75048d8296]
aws_instance.main[2]: Refreshing state... [id=i-0c8c41a0cde73a0a3]
aws_instance.main[0]: Refreshing state... [id=i-0a612c0bb446b46f4]
aws_instance.main[1]: Refreshing state... [id=i-0ed29492e0729f3ee]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_s3_object.example will be created
+ resource "aws_s3_object" "example" {
+ bucket = "terraform-20230831212958335300000001"
+ bucket_key_enabled = (known after apply)
+ content_type = (known after apply)
+ etag = "87729e4959c183dac2e9382a4d6819e6"
+ force_destroy = false
+ id = (known after apply)
+ key = "README.md"
+ kms_key_id = (known after apply)
+ server_side_encryption = (known after apply)
+ source = "./README.md"
+ storage_class = (known after apply)
+ tags_all = (known after apply)
+ version_id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Saved the plan to: add-object
To perform exactly these actions, run the following command to apply:
terraform apply "add-object"
现在,删除 Terraform 之外的存储桶。这将导致 Terraform 在应用计划时出错,因为该计划假定存储桶存在。使用 AWS 控制台 或 AWS CLI 删除存储桶。
$ aws s3 rb s3://$(terraform output -raw bucket_name)
remove_bucket: terraform-20230821193112318500000003
现在应用该计划。Terraform 在尝试创建 aws_s3_object.example 对象时将出错。
$ terraform apply "add-object"
aws_s3_object.example: Creating...
╷
│ Error: uploading S3 Object (README.md) to Bucket (terraform-20250710195124113200000001): operation error S3: PutObject, https response error StatusCode: 404, RequestID: 6EZ62B8SSRGNM1J0, HostID: 6tGaQQaHYLZyxfvORkOWOJv2wnIk/3td6XSmjgqHvLF1WuyRnVgzvJPcXgakIwFQzU4RE/hfkdEw6vWEaJKZ1JVP/k0TDyF25QyoFDUh8s4=, api error NoSuchBucket: The specified bucket does not exist
│
│ with aws_s3_object.example,
│ on main.tf line 51, in resource "aws_s3_object" "example":
│ 51: resource "aws_s3_object" "example" {
│
╵
由于你在创建计划后删除了 S3 存储桶,因此 AWS 无法创建对象,因此 AWS 提供程序将错误报告给 Terraform。
应用错误的常见原因包括
- 对 Terraform 控制范围之外的资源的更改。
- 网络或其他瞬时错误。
- 来自上游 API 的预期错误,例如重复的资源名称或达到资源限制。
- 来自上游 API 的意外错误,例如内部服务器错误。
- Terraform 提供程序代码或 Terraform 本身中的错误。
根据错误的根本原因,你可能需要通过修改配置或诊断和解决云提供程序 API 中的错误来解决潜在问题。Terraform 仍然在跟踪镜像资源,因为 Terraform 尚未刷新你的资源的当前状态。
使用 terraform show 打印 S3 存储桶的状态。
$ terraform show -json | jq '.values.root_module.resources[] | select( .address == "aws_s3_bucket.example")'
{
"address": "aws_s3_bucket.example",
"mode": "managed",
"type": "aws_s3_bucket",
"name": "example",
"provider_name": "registry.terraform.io/hashicorp/aws",
"schema_version": 0,
"values": {
"acceleration_status": "",
"acl": null,
"arn": "arn:aws:s3:::terraform-20230831212958335300000001",
"bucket": "terraform-20230831212958335300000001",
### ...
"tags": {},
"tags_all": {},
"versioning": [
{}
],
"website": []
}
}
Terraform 将其对你的资源状态的当前理解存储在本地的 terraform.tfstate 文件中,或者存储在远程后端(如 HCP Terraform)中。你可以使用 terraform show 命令来打印你的状态。此命令不会刷新你的状态,因此你的状态中的信息可能已过时。在这种情况下,你的项目的状态报告了你在此教程中更早手动删除的 S3 存储桶的存在。当你应用计划时,Terraform 仅会进行计划中定义的更改。因此,在你计划更改并尝试应用它们之间对你的资源所做的更改可能导致 Terraform 出错,如果该计划无法按写入的方式应用。为了解决此错误,你必须创建并应用一个考虑缺失存储桶的新计划。
下次你计划对此项目进行更改时,Terraform 将使用你安装的提供程序从底层 API 更新你的资源的当前状态。Terraform 会注意到由 aws_s3_bucket.example 资源表示的存储桶不再存在,并生成一个计划,在创建新的 aws_s3_object.example 对象之前先创建它。
应用你的配置。Terraform 将刷新你的工作区的状态,以反映 S3 存储桶不再存在的事实。接下来,它将创建一个计划,通过创建 S3 存储桶和对象来协调你的配置与该状态。资源可以以 Terraform 控制范围之外的任何方式进行更改。在大多数情况下,Terraform 可以通过创建、销毁或更新资源来自动处理这些差异,以使它们与你的配置匹配。Terraform 将创建一个计划来执行此操作,并等待你确认它。
$ terraform apply
## ...
Note: Objects have changed outside of Terraform
Terraform detected the following changes made outside of Terraform since the last "terraform apply" which may
have affected this plan:
Note: Objects have changed outside of Terraform
Terraform detected the following changes made outside of Terraform since the last "terraform apply" which may have affected this plan:
# aws_s3_bucket.example has been deleted
- resource "aws_s3_bucket" "example" {
- bucket = "terraform-20230831212958335300000001" -> null
id = "terraform-20230831212958335300000001"
tags = {
"Name" = "Example Bucket"
"Owner" = "terraform-apply-tutorial"
}
# (10 unchanged attributes hidden)
# (3 unchanged blocks hidden)
}
Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to undo or respond to these changes.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_s3_bucket.example will be created
+ resource "aws_s3_bucket" "example" {
## ...
}
# aws_s3_object.example will be created
+ resource "aws_s3_object" "example" {
+ bucket = (known after apply)
## ...
}
Plan: 2 to add, 0 to change, 0 to destroy.
Changes to Outputs:
~ bucket_name = "terraform-20230831212958335300000001" -> (known after apply)
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
使用 yes 回复确认提示,以配置 S3 存储桶和对象。
Enter a value: yes
aws_s3_bucket.example: Creating...
aws_s3_bucket.example: Creation complete after 1s [id=terraform-20230831213755902000000001]
aws_s3_object.example: Creating...
aws_s3_object.example: Creation complete after 1s [id=README.md]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
bucket_name = "terraform-20230831213755902000000001"
在这种情况下,您可以通过重新应用配置来从错误中恢复。根据错误的根本原因,您可能需要在 Terraform 外部或通过更改 Terraform 配置来解决该错误。例如,如果 Terraform 报告来自您的云提供商 API 的资源限制错误,您可能需要与您的云提供商合作以提高该限制,然后才能应用您的配置。
替换资源
在使用 Terraform 时,您通常会一次性应用整个配置更改。Terraform 及其提供程序将确定要进行的更改以及进行更改的顺序。但是,在某些情况下,您可能需要替换或修改单个资源。Terraform 提供了两个参数给 plan 和 apply 命令,允许您与特定资源进行交互:-replace 和 -target。
当资源变得不健康或以 Terraform 无法控制的方式停止工作时,请使用 -replace 参数。例如,您的 EC2 实例的操作系统配置中的错误可能需要替换该实例。您的 Terraform 配置中没有相应的更改,因此您希望指示 Terraform 使用相同的配置重新配置资源。
-replace 参数需要资源地址。使用 terraform state list 列出您配置中的资源。
$ terraform state list
data.aws_ami.ubuntu
aws_instance.main[0]
aws_instance.main[1]
aws_instance.main[2]
aws_s3_bucket.example
aws_s3_object.example
random_pet.instance
替换第二个 EC2 实例。对确认提示回复 yes。
$ terraform apply -replace "aws_instance.main[1]"
random_pet.instance: Refreshing state... [id=dashing-tapir]
data.aws_ami.ubuntu: Reading...
aws_s3_bucket.example: Refreshing state... [id=terraform-20230831213755902000000001]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-055744c75048d8296]
aws_instance.main[0]: Refreshing state... [id=i-0a612c0bb446b46f4]
aws_instance.main[2]: Refreshing state... [id=i-0c8c41a0cde73a0a3]
aws_instance.main[1]: Refreshing state... [id=i-0ed29492e0729f3ee]
aws_s3_object.example: Refreshing state... [id=README.md]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_instance.main[1] will be replaced, as requested
-/+ resource "aws_instance" "main" {
~ arn = "arn:aws:ec2:us-east-1:841397984957:instance/i-0ed29492e0729f3ee" -> (known after apply)
## ...
Plan: 1 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_instance.main[1]: Destroying... [id=i-0ed29492e0729f3ee]
aws_instance.main[1]: Still destroying... [id=i-0ed29492e0729f3ee, 10s elapsed]
aws_instance.main[1]: Still destroying... [id=i-0ed29492e0729f3ee, 20s elapsed]
aws_instance.main[1]: Still destroying... [id=i-0ed29492e0729f3ee, 30s elapsed]
aws_instance.main[1]: Still destroying... [id=i-0ed29492e0729f3ee, 40s elapsed]
aws_instance.main[1]: Destruction complete after 40s
aws_instance.main[1]: Creating...
aws_instance.main[1]: Still creating... [10s elapsed]
aws_instance.main[1]: Still creating... [20s elapsed]
aws_instance.main[1]: Still creating... [30s elapsed]
aws_instance.main[1]: Still creating... [40s elapsed]
aws_instance.main[1]: Creation complete after 42s [id=i-0cb93ff7e726c46cf]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
Outputs:
bucket_name = "terraform-20230831213755902000000001"
您可能需要部分应用配置的另一种情况是在排查阻止 Terraform 一次性应用整个配置的错误时。当目标 API 或 Terraform 提供程序错误导致您的资源处于 Terraform 无法自动解析的无效状态时,可能会发生这种类型的错误。当您应用以针对单个资源而不是应用整个配置时,使用 -target 命令行参数。请参阅 Target resources 教程以获取更多信息。
清理基础设施
现在您已经了解了 Terraform 如何将更改应用于您的基础设施,请删除您在本教程中配置的资源。用 yes 确认操作。
$ terraform destroy
random_pet.instance: Refreshing state... [id=meet-dassie]
data.aws_ami.ubuntu: Reading...
aws_s3_bucket.example: Refreshing state... [id=terraform-20230901173128874100000001]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-055744c75048d8296]
aws_instance.main[2]: Refreshing state... [id=i-0e0694473a240c3db]
aws_instance.main[1]: Refreshing state... [id=i-0e68bfd135ef460d9]
aws_instance.main[0]: Refreshing state... [id=i-0bba2e53d1d7355cd]
aws_s3_object.example: Refreshing state... [id=README.md]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# aws_instance.main[0] will be destroyed
- resource "aws_instance" "main" {
- ami = "ami-055744c75048d8296" -> null
## ...
Plan: 0 to add, 0 to change, 6 to destroy.
Changes to Outputs:
- bucket_name = "terraform-20230901173128874100000001" -> null
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
aws_s3_object.example: Destroying... [id=README.md]
aws_instance.main[0]: Destroying... [id=i-0bba2e53d1d7355cd]
aws_instance.main[2]: Destroying... [id=i-0e0694473a240c3db]
aws_instance.main[1]: Destroying... [id=i-0e68bfd135ef460d9]
aws_s3_object.example: Destruction complete after 0s
aws_s3_bucket.example: Destroying... [id=terraform-20230901173128874100000001]
aws_s3_bucket.example: Destruction complete after 1s
aws_instance.main[0]: Still destroying... [id=i-0bba2e53d1d7355cd, 10s elapsed]
aws_instance.main[1]: Still destroying... [id=i-0e68bfd135ef460d9, 10s elapsed]
aws_instance.main[2]: Still destroying... [id=i-0e0694473a240c3db, 10s elapsed]
aws_instance.main[0]: Still destroying... [id=i-0bba2e53d1d7355cd, 20s elapsed]
aws_instance.main[1]: Still destroying... [id=i-0e68bfd135ef460d9, 20s elapsed]
aws_instance.main[2]: Still destroying... [id=i-0e0694473a240c3db, 20s elapsed]
aws_instance.main[1]: Still destroying... [id=i-0e68bfd135ef460d9, 30s elapsed]
aws_instance.main[0]: Still destroying... [id=i-0bba2e53d1d7355cd, 30s elapsed]
aws_instance.main[2]: Still destroying... [id=i-0e0694473a240c3db, 30s elapsed]
aws_instance.main[2]: Destruction complete after 30s
aws_instance.main[0]: Destruction complete after 31s
aws_instance.main[1]: Still destroying... [id=i-0e68bfd135ef460d9, 40s elapsed]
aws_instance.main[1]: Destruction complete after 41s
random_pet.instance: Destroying... [id=meet-dassie]
random_pet.instance: Destruction complete after 0s
Destroy complete! Resources: 6 destroyed.
terraform destroy 命令是一个快捷方式,它会创建一个销毁计划来删除您的所有资源,等待您的确认,然后应用该计划。
下一步
在本教程中,你学习了 Terraform 如何应用更改到你的基础设施。你还回顾了 Terraform 如何通过在 apply 步骤中重现错误来处理错误。查看以下资源以了解更多关于管理你的 Terraform 项目的信息
- 了解如何使用变量 自定义 Terraform 配置。
- 了解如何使用 HCP Terraform 入门教程,与你的团队一起使用 Terraform 项目。
- 了解 在自动化中运行 Terraform。