跳到主要内容

5.terraform语法

文件和目录

文件拓展名

Terraform 语言中的代码存储在文件扩展名为 .tf 的纯文本文件中。该语言还有一个基于 JSON 的变体,以 .tf.json 文件扩展名命名。这些文件通常称之为配置文件。

文本编码

配置文件必须始终使用 UTF-8 编码,并且按照惯例通常使用 Unix 样式的行尾 (LF) 而不是 Windows 样式的行尾 (CRLF),尽管两者都被接受。

目录和模块

模块是保存在目录中的 .tf 和/或 .tf.json 文件的集合。

Terraform 模块仅包含目录中的top-level配置文件;嵌套目录被视为完全独立的模块,不会自动包含在配置中。

Terraform 评估模块中的所有配置文件,有效地将整个模块视为单个文档。将各种块分成不同的文件纯粹是为了方便读者和维护者,对模块的行为没有影响。

Terraform 模块可以使用模块调用将其他模块显式包含在配置中。这些子模块可以来自本地目录(嵌套在父模块的目录中,或磁盘上的任何其他位置),也可以来自外部源,如 Terraform 注册表。

根模块

Terraform 始终在单个根模块的上下文中运行。完整的 Terraform 配置由根模块和子模块树(包括根模块调用的模块、这些模块调用的任何模块等)组成。

  • 在 Terraform CLI 中,根模块是调用 Terraform 的工作目录。(您可以使用命令行选项在工作目录之外指定根模块,但实际上这种情况很少见。
  • 在 Terraform Cloud 和 Terraform Enterprise 中,工作区的根模块默认为配置目录的顶层(通过版本控制存储库或直接上传提供),但工作区设置可以指定要使用的子目录。

覆盖文件

Terraform 通常加载目录中的所有 .tf 和 .tf.json 文件,并期望每个文件定义一组不同的配置对象。如果两个文件尝试定义同一对象,Terraform 将返回错误。

在极少数情况下,能够在单独的文件中覆盖现有配置对象的特定部分会很方便。例如,可以使用 JSON 语法中的编程生成文件部分覆盖 Terraform 语言本机语法中人工编辑的配置文件。

对于这些罕见的情况,Terraform 对名称以 _override.tf 或 _override.tf.json 结尾的任何配置文件都有特殊处理。此特殊处理也适用于字面上名为 override.tf 或 override.tf.json 的文件。

Terraform 最初在加载配置时跳过这些覆盖文件,然后依次处理每个文件(按字典顺序)。对于覆盖文件中定义的每个顶级块,Terraform 会尝试查找与该块对应的已定义对象,然后将覆盖块内容合并到现有对象中。

仅在特殊情况下使用覆盖文件。过度使用覆盖文件会损害可读性,因为仅查看原始文件的读者如果不查阅存在的所有覆盖文件,就无法轻松看到这些文件的某些部分已被覆盖。使用覆盖文件时,请使用原始文件中的注释来警告将来的读者哪些覆盖文件将更改应用于每个块。

覆盖文件实例

如果您有包含以下内容的 terraform 配置 example.tf :

resource "aws_instance" "web" {
instance_type = "t2.micro"
ami = "ami-408c7f28"
}

...并且您创建了一个包含以下内容的文件 override.tf :

resource "aws_instance" "web" {
ami = "foo"
}

Terraform 会将后者合并到前者中,其行为就像原始配置如下:

resource "aws_instance" "web" {
instance_type = "t2.micro"
ami = "foo"
}

语法

JSON 配置语法记录了如何在 Terraform 语言的纯 JSON 变体中表示 Terraform 语言构造。Terraform 的 JSON 语法对人类不友好,但在使用没有现成 HCL 库的其他系统生成基础设施即代码时非常有用。

Terraform 语言的这种低级语法是根据称为 HCL 的语法定义的,其他应用程序中的配置语言也使用该语法,尤其是其他 HashiCorp 产品。没有必要知道HCL语法的所有细节才能使用Terraform,因此本页总结了最重要的细节。如果您有兴趣,可以在 HCL 本机语法规范中找到 HCL 语法的完整定义。

Arguments 和 blocks

Terraform 语言语法围绕两个关键语法结构构建:arguments和blocks。

arguments(参数)

image_id = "abc123"

等号前面的标识符是参数名称,等号后面的表达式是参数的值。

出现参数的上下文确定哪些值类型是有效的(例如,每个资源类型都有一个定义其参数类型的架构),但许多参数接受任意表达式,这些表达式允许按字面指定值或以编程方式从其他值生成值。

blocks(块)

blocks 是其他内容的容器:

resource "aws_instance" "example" {
ami = "abc123"

network_interface {
# ...
}
}

block 具有类型(在本例中为 resource )。每个块类型定义类型关键字后面必须有多少个标签。 resource 块类型需要两个标签,在上面的示例中分别为 aws_instance 和 example 。特定块类型可能具有任意数量的必需标签,或者可能不需要与嵌套 network_interface 块类型一样的标签。

在块类型关键字和任何标签之后,块正文由 { 和 } 字符分隔。在块主体中,可以嵌套其他参数和块,从而创建块及其关联参数的层次结构。

Terraform 语言使用有限数量的顶级块类型,这些块可以出现在配置文件中任何其他块之外。Terraform的大多数功能(包括资源,输入变量,输出值,数据源等)都是作为顶级块实现的。

标识符

参数名称、块类型名称以及大多数特定于 Terraform 的构造(如资源、输入变量等)的名称。都是标识符。

标识符可以包含字母、数字、下划线 ( _ ) 和连字符 ( - )。标识符的第一个字符不得是数字,以避免文字数字的歧义。

对于完整的标识符规则,Terraform 实现了 Unicode 标识符语法,该语法扩展为包括 ASCII 连字符 - 。

resources

资源是Terraform语言中最重要的元素。每个资源块描述一个或多个基础结构对象,例如虚拟网络、计算实例或更高级别的组件(例如 DNS 记录)。

元参数部分记录了可用于每种资源类型(包括 depends_on 、 count 、 for_each 、 provider 和 lifecycle )的特殊参数。

resource blocks

资源是Terraform语言中最重要的元素。每个资源块描述一个或多个基础结构对象,例如虚拟网络、计算实例或更高级别的组件(例如 DNS 记录)。

resource syntax

资源声明可以包含许多高级功能,但初始使用只需要一小部分。本页稍后将介绍更高级的语法功能,例如生成多个类似远程对象的单个资源声明。

resource "aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}

resource 块声明具有给定本地名称 (“web”) 的给定类型的资源 (“aws_instance”)。该名称用于从同一 Terraform 模块中的其他位置引用此资源,但在该模块的范围之外没有任何意义。

资源类型和名称一起用作给定资源的标识符,因此在模块中必须是唯一的。

块主体 ({ 和 } 之间) 是资源本身的配置参数。本节中的大多数参数都取决于资源类型,实际上在此示例中, ami 和 instance_type 都是专门为 aws_instance 资源类型定义的参数。

resource types

每个资源都与单个资源类型相关联,该类型确定它管理的基础结构对象的类型以及资源支持的参数和其他属性。

providers

每个资源类型都由一个提供程序实现,该提供程序是 Terraform 的插件,提供资源类型的集合。提供商通常提供资源来管理单个云或本地基础结构平台。提供程序与 Terraform 本身分开分发,但 Terraform 可以在初始化工作目录时自动安装大多数提供程序。

为了管理资源,Terraform 模块必须指定它所需的提供程序。此外,大多数提供程序需要一些配置才能访问其远程 API,并且根模块必须提供该配置。

Terraform 通常根据资源类型的名称自动确定要使用的提供程序。(按照约定,资源类型名称以其提供程序的首选本地名称开头。使用提供程序的多个配置(或非首选本地提供程序名称)时,必须使用 provider 元参数手动选择备用提供程序配置。有关更多详细信息,请参阅 provider 元参数。

资源类型文档

每个 Terraform 提供程序都有自己的文档,描述其资源类型及其参数。

大多数公开可用的提供程序都分布在 Terraform 注册表上,该注册表也托管其文档。在 Terraform 注册表上查看提供程序的页面时,您可以单击标题中的“文档”链接以浏览其文档。注册表上的提供程序文档已进行版本控制,您可以使用标题中的下拉版本菜单来切换正在查看的版本文档。

若要浏览公开可用的提供程序及其文档,请参阅 Terraform 注册表的提供程序部分。

自定义条件检查

您可以使用 precondition 和 postcondition 块来指定有关资源运行方式的假设和保证。以下示例创建一个前提条件,用于检查是否正确配置 AMI。

resource "aws_instance" "example" {
instance_type = "t2.micro"
ami = "ami-abc123"

lifecycle {
# The AMI ID must refer to an AMI that contains an operating system
# for the `x86_64` architecture.
precondition {
condition = data.aws_ami.example.architecture == "x86_64"
error_message = "The selected AMI must be for the x86_64 architecture."
}
}
}

自定义条件可以帮助捕获假设,帮助未来的维护者了解配置设计和意图。它们还会更早地在上下文中返回有关错误的有用信息,从而帮助使用者更轻松地诊断其配置中的问题。

超时

某些资源类型提供特殊的 timeouts 嵌套块参数,允许您自定义某些操作在被视为失败之前允许花费的时间。例如, aws_db_instance 允许可配置 create 、 update 和 delete 操作的超时。

超时完全由提供程序中的资源类型实现处理,但提供这些功能的资源类型遵循定义名为 timeouts 的子块的约定,该子块具有以具有可配置超时值的每个操作命名的嵌套参数。这些参数中的每一个都采用持续时间的字符串表示形式,例如 "60m" 表示 60 分钟, "10s" 表示 10 秒,或 "2h" 表示两小时。

resource "aws_db_instance" "example" {
# ...

timeouts {
create = "60m"
delete = "2h"
}
}

可配置操作集由每个资源类型选择。大多数资源类型根本不支持 timeouts 块。请参阅每种资源类型的文档,以查看它为配置提供哪些操作(如果有)。

Resource beheavior 资源行为

resource 块表示您希望特定基础结构对象以给定的设置存在。如果您是第一次编写新配置,它定义的资源将仅存在于配置中,并且还不会代表目标平台中的实际基础结构对象。

applying Terraform 配置是创建、更新和销毁实际基础结构对象以使其设置与配置匹配的过程。

Terraform 如何应用配置

当 Terraform 创建一个由 resource 块表示的新基础设施对象时,该真实对象的标识符将保存在 Terraform 的状态中,从而允许对其进行更新和销毁以响应未来的更改。对于状态中已有关联基础结构对象的资源块,Terraform 会将对象的实际配置与配置中给出的参数进行比较,并在必要时更新对象以匹配配置。

总之,applying Terraform 配置将:

  • 创建配置中存在但与状态中的实际基础结构对象不关联的资源。
  • 销毁状态中存在但配置中不再存在的资源。
  • 更新参数已更改的就地资源。
  • 销毁并重新创建参数已更改但由于远程 API 限制而无法就地更新的资源。

此常规行为适用于所有资源,无论类型如何。对于每种资源类型,创建、更新或销毁资源的含义的详细信息是不同的,但这组标准谓词在所有资源类型中都是通用的。

resource 块内的元参数(记录在以下部分中)允许基于每个资源自定义此标准资源行为的一些详细信息。

访问资源属性

Terraform 模块中的表达式可以访问有关同一模块中的资源的信息,您可以使用该信息来帮助配置其他资源。使用 RESOURCE TYPE.NAME.ATTRIBUTE 语法引用表达式中的资源属性。

除了配置中指定的参数外,资源通常还提供只读属性以及从远程 API 获取的信息;这通常包括在创建资源之前无法知道的内容,例如资源的唯一随机 ID。

许多提供程序还包括数据源,数据源是一种特殊类型的资源,仅用于查找信息。

有关资源或数据源类型提供的属性的列表,请参阅其文档;这些通常包含在可配置参数列表下方的第二个列表中。

资源依赖关系

配置中的大多数资源没有任何特定关系,Terraform 可以并行更改多个不相关的资源。

但是,某些资源必须在其他特定资源之后处理;有时这是因为资源的工作方式,有时资源的配置只需要另一个资源生成的信息。

大多数资源依赖关系都是自动处理的。Terraform 分析 resource 块内的任何表达式以查找对其他对象的引用,并在创建、更新或销毁资源时将这些引用视为隐式排序要求。由于 大多数对其他资源具有行为依赖关系的资源也引用这些资源的数据,因此通常无需手动指定资源之间的依赖关系。

但是,某些依赖项无法在配置中隐式识别。例如,如果 Terraform 必须管理访问控制策略并采取需要存在这些策略的操作,则访问策略与创建依赖于它的资源之间存在隐藏的依赖关系。在这些极少数情况下, depends_on 元参数可以显式指定依赖项。

您还可以使用 replace_triggered_by 元参数在其他独立资源之间添加依赖关系。它强制 Terraform 在引用的资源或资源属性发生更改时替换父资源。

meta-arguments

depends_on

使用 depends_on 元参数来处理 Terraform 无法自动推断的隐藏资源或模块依赖关系。仅当资源或模块依赖于另一个资源的行为但不在其参数中访问该资源的任何数据时,才需要显式指定依赖项。

depends_on 元参数指示 Terraform 在对声明依赖关系的对象执行操作之前完成对依赖对象的所有操作(包括读取操作)。当依赖项对象是整个模块时, depends_on 会影响 Terraform 处理与该模块关联的所有资源和数据源的顺序。有关详细信息,请参阅资源依赖关系和数据资源依赖关系。

您应该使用 depends_on 作为最后的手段,因为它可能会导致 Terraform 创建更保守的计划,从而替换不必要的资源。例如,Terraform 可能会将更多值视为未知“(应用后已知)”,因为不确定上游对象上会发生哪些更改。当您将 depends_on 用于模块时,这种情况尤其可能发生。

我们建议尽可能使用表达式引用来暗示依赖项,而不是 depends_on 。表达式引用使 Terraform 能够了解引用派生自哪个值,并在该特定值未更改时避免计划更改,即使上游对象的其他部分已计划更改也是如此。

无论资源类型如何,您都可以在 module 个块和所有 resource 个块中使用 depends_on 元参数。它需要对同一调用模块中其他资源或子模块的引用列表。此列表不能包含任意表达式,因为必须先知道 depends_on 值,然后 Terraform 才能知道资源关系,因此在可以安全地计算表达式之前。

我们建议始终包含注释,解释为什么需要使用 depends_on 。以下示例使用 depends_on 处理 aws_iam_instance_profile.example 上的“隐藏”依赖项。

resource "aws_iam_role" "example" {
name = "example"

# assume_role_policy is omitted for brevity in this example. Refer to the
# documentation for aws_iam_role for a complete example.
assume_role_policy = "..."
}

resource "aws_iam_instance_profile" "example" {
# Because this expression refers to the role, Terraform can infer
# automatically that the role must be created first.
role = aws_iam_role.example.name
}

resource "aws_iam_role_policy" "example" {
name = "example"
role = aws_iam_role.example.name
policy = jsonencode({
"Statement" = [{
# This policy allows software running on the EC2 instance to
# access the S3 API.
"Action" = "s3:*",
"Effect" = "Allow",
}],
})
}

resource "aws_instance" "example" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"

# Terraform can infer from this that the instance profile must
# be created before the EC2 instance.
iam_instance_profile = aws_iam_instance_profile.example

# However, if software running in this EC2 instance needs access
# to the S3 API in order to boot properly, there is also a "hidden"
# dependency on the aws_iam_role_policy that Terraform cannot
# automatically infer, so it must be declared explicitly:
depends_on = [
aws_iam_role_policy.example
]
}

count

默认情况下,资源块配置一个实际的基础结构对象。(同样,模块块将子模块的内容包含在配置中一次。但是,有时您希望管理多个类似的对象(如固定的计算实例池),而无需为每个对象编写单独的块。Terraform有两种方法可以做到这一点: count 和 for_each 。如果资源或模块块包含值为整数的 count 参数,则 Terraform 将创建该多个实例。

count 是由 Terraform 语言定义的元参数。它可以与模块和每种资源类型一起使用。

count 元参数接受整数,并创建资源或模块的许多实例。每个实例都有一个与之关联的不同基础架构对象,并且在应用配置时单独创建、更新或销毁每个实例。

resource "aws_instance" "server" {
count = 4 # create four similar EC2 instances

ami = "ami-a1b2c3d4"
instance_type = "t2.micro"

tags = {
Name = "Server ${count.index}"
}
}

如果您的实例几乎相同,则 count 是合适的。如果他们的某些参数需要不能直接从整数派生的不同值,则使用 for_each 会更安全。

provider

provider 元参数指定要用于资源的提供程序配置,覆盖 Terraform 根据资源类型名称选择一个提供程序的默认行为。其值应为未引号的 PROVIDER.ALIAS 引用。

如提供程序配置中所述,您可以选择为单个提供程序创建多个配置(通常用于管理多区域服务的不同区域中的资源)。每个提供程序可以有一个默认配置,以及包含额外名称段(或“别名”)的任意数量的备用配置。

名称,并使用该提供程序的默认配置。例如,资源类型 google_compute_instance 自动与名为 google 的提供程序的默认配置相关联。

通过使用 provider 元参数,可以为资源选择备用提供程序配置:

# default configuration
provider "google" {
region = "us-central1"
}

# alternate configuration, whose alias is "europe"
provider "google" {
alias = "europe"
region = "europe-west1"
}

resource "google_compute_instance" "example" {
# This "provider" meta-argument selects the google provider
# configuration whose alias is "europe", rather than the
# default configuration.
provider = google.europe

# ...
}

data sources

数据源允许 Terraform 使用在 Terraform 外部定义、由另一个单独的 Terraform 配置定义或由函数修改的信息。

use data sources

数据源通过一种称为数据资源的特殊资源进行访问,该资源使用 data 块声明:

data "aws_ami" "example" {
most_recent = true

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

data 块请求 Terraform 从给定数据源读取 (“aws_ami”) 并将结果导出到给定的本地名称(“示例”)。该名称用于从同一 Terraform 模块中的其他位置引用此资源,但在模块范围之外没有任何意义。

数据源和名称一起用作给定资源的标识符,因此在模块中必须是唯一的。

与数据资源区分开来时,主要类型的资源(由 resource 块声明)称为托管资源。这两种资源都采用参数并导出属性以用于配置,但托管资源会导致 Terraform 创建、更新和删除基础结构对象,而数据资源会导致 Terraform 仅读取对象。为简洁起见,当上下文中的含义明确时,托管资源通常简称为“资源”。

示例

# Find the latest available AMI that is tagged with Component = web
data "aws_ami" "web" {
filter {
name = "state"
values = ["available"]
}

filter {
name = "tag:Component"
values = ["web"]
}

most_recent = true
}

data 块创建给定类型(第一个块标签)和名称(第二个块标签)的数据实例。类型和名称的组合必须是唯一的。

块 ( { } ) 内是数据实例的配置。配置取决于类型;与资源一样,Terraform 注册表上的每个提供程序都有自己的文档,用于配置和使用其提供的数据类型。

每个数据实例将导出一个或多个属性,这些属性可在其他资源中用作形式为 data.TYPE.NAME.ATTRIBUTE 的引用表达式。例如:

resource "aws_instance" "web" {
ami = data.aws_ami.web.id
instance_type = "t1.micro"
}

providers

Terraform依靠称为提供商的插件与云提供商,SaaS提供商和其他API进行交互。

Terraform 配置必须声明它们需要哪些提供程序,以便 Terraform 可以安装和使用它们。此外,某些提供程序需要配置(如终结点 URL 或云区域)才能使用。

每种资源类型都由提供程序实现;没有提供商,Terraform无法管理任何类型的基础设施。

大多数提供商配置特定的基础架构平台(云或自托管)。提供程序还可以为任务提供本地实用程序,例如为唯一资源名称生成随机数。

提供程序与 Terraform 本身分开分发,每个提供程序都有自己的发布节奏和版本号。

Terraform 注册表是公开可用的 Terraform 提供程序的主目录,也是大多数主要基础设施平台的托管提供程序。

每个提供程序都有自己的文档,描述其资源类型及其参数。

提供程序与 Terraform 本身分开发布,并具有自己的版本号。在生产环境中,我们建议在配置的提供程序要求块中约束可接受的提供程序版本,以确保 terraform init 不会安装与配置不兼容的较新版本的提供程序。

Terraform CLI 在初始化工作目录时查找并安装提供程序。它可以自动从 Terraform 注册表下载提供程序,或从本地镜像或缓存加载它们。如果使用的是持久工作目录,则每当更改配置的提供程序时,都必须重新初始化。

为了节省时间和带宽,Terraform CLI 支持可选的插件缓存。您可以使用 CLI 配置文件中的 plugin_cache_dir 设置启用缓存。

若要确保 Terraform 始终为给定配置安装相同的提供程序版本,可以使用 Terraform CLI 创建依赖项锁定文件,并将其与您的配置一起提交到版本控制。如果存在锁定文件,Terraform Cloud、CLI 和 Enterprise 在安装提供程序时都将遵守该文件。

要查找您使用的基础结构平台的提供程序,请浏览 Terraform 注册表的提供程序部分

variables and outputs and modules

Terraform 语言包括几种用于请求或发布命名值的块。

  • 输入变量用作 Terraform 模块的参数,因此用户可以自定义行为而无需编辑源。

  • 输出值类似于地形模块的返回值。

  • 局部值是用于为表达式分配短名称的便捷功能。

模块是一起使用的多个资源的容器。模块由保存在目录中的 .tf 和/或 .tf.json 文件的集合组成。

详细参考之前的文章

函数

Terraform 语言包括许多内置函数,您可以从表达式中调用这些函数来转换和组合值。函数调用的一般语法是函数名称,后跟括号中的逗号分隔参数:

max(5, 12, 9)

Terraform 语言不支持用户定义的函数,因此只有该语言内置的函数可供使用。该文档包括一个页面,其中包含所有可用的内置函数。

您可以通过运行 terraform console 命令从 Terraform 表达式控制台试验 Terraform 内置函数的行为: