目录
Meta-Argument
Meta-Argument是Terraform支持的内置元参数,本节主要介绍 resource块支持的元参数,主要包括:
depends_on:用于指定资源的依赖关系
count:用于创建多个相同配置的资源或是控制资源是否创建(注:count 与 for_each 无法同时使用)
for_each:用于根据映射、字符串集合创建多个资源
provider:用于资源制定特定的provider(即非默认)
lifecycle:用于资源的生命周期控制
depends_on
📌 简单说:本次需创建的资源依赖上个资源的成功创建之后在创建 或是 用于读取依赖资源的数据信息
depends_on 元参数可以用在对声明依赖项的对象执行操作之前完成对依赖项对象的所有操作(包括读取操作)。当依赖对象是个模块时,depends_on 会影响 Terraform 处理与该模块关联的所有资源和数据源的顺序。然而,某些资源的依赖关系对于Terraform是不可见的,这就需要使用 depends_on 来创建显式依赖。我们可以使用 depends_on 来更改资源的创建顺序或执行顺序,使其在所依赖资源之后处理。
我们建议始终给 它加上一条注释,解释为什么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的 resource块只配置一个资源。但是当我们需要创建多个相同配置的资源时,如果配置多个独立的 resource块就显得很啰嗦,且不利于后期维护。我们可以使用 count 或 for_each 参数在同一个 resource块中管理多个相同的资源。
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用于资源是否创建的控制开关
module "us_east_1" {
source = "../eventbridge"
count = contains(local.enabled_regions, "us-east-1") ? 1 : 0
bus_name = var.bus_name
rules = var.rules
targets = var.targets
iam_role_arn = var.iam_role_arn
target_arn = var.target_arn
providers = {
aws = aws.us-east-1
}
}
使用 count 创建的资源需要通过索引值进行访问,格式为:<资源类型>.<名称>[索引值]。 索引值从0开始。
访问第一个ec2 instance
aws_instance.server[0]
访问第一个ec2 instance private_ip
aws_instance.server[0].private_ip
访问所有VPC的ID
aws_instance.server[0].private_ip
for_each
for_each 与 count 在功能上相似,for_each参数接受一个映射或一组字符串,并为该映射或集合中的每个项目创建一个资源。每个资源都有一个与之关联的不同基础设施对象,并且在应用配置时分别创建、更新或销毁每个资源。for_each 使用键值对或字符串集合的形式快速地将值填入到对应的属性中,不仅可以优化脚本结构也有利于理解多实例间的关系。
注意 ⚠️ 在同一个 resource块中是不能同时使用count 和 for_each 参数。
📖 小结
如果单一资源创建 或 用作资源创建控制的开关,建议使用count。
如果多个资源创建,无脑选择 for_each。
举个栗子🌰
在使用映射类型表达时,我们可以使用 "each.key" 和 "each.value" 来访问映射的键和值。
"azurerm_resource_group" "rg" {
for_each = {
a_group = "eastus"
another_group = "westus2"
}
name = each.key
location = each.value
}
在使用字符串集合类型表达时,"each.key" 等同于 "each.value",我们一般使用 each.key表示,另外,可以通过 toset() 函数将定义的 list 类型进行转化
resource "aws_iam_user" "the-accounts" {
for_each = toset( ["Todd", "James", "Alice", "Dottie"] )
name = each.key
}
provider
我们可以使用 provider块创建多个配置,其中一个 provider块为默认配置,其它使用 "alias" 标识为非默认配置。在资源中使用元参数 provider 可以选择非默认的 provider块。在使用非默认provider时,我们需要显示的创建它。如果是使用默认provider时,在 module 或是 resource 资源时 无需特别指定。
# default configuration
provider "aws" {
region = "us-east-1"
}
# alternate configuration, whose alias is "ap-southeast-1"
provider "google" {
region = "ap-southeast-1"
alias = "ap-southeast-1"
}
module "us_east_2" {
source = "../eventbridge"
count = contains(local.enabled_regions, "us-east-2") ? 1 : 0
bus_name = var.bus_name
rules = var.rules
targets = var.targets
iam_role_arn = var.iam_role_arn
target_arn = var.target_arn
providers = {
aws = aws.ap-southeast-1
}
}
lifecycle
资源的生命周期会有创建 、更新和销毁这三个阶段。通过元参数 lifecycle 可以对资源实例的生命周期过程进行改变,lifecycle 支持以下参数:
create_before_destroy
📌简单说:先创建新资源 在删除旧资源 适用于保持业务不中断
默认情况下,Terraform会先销毁已有实例,再使用新配置的参数创建新的对象进行替换。当我们将 create_before_destroy 参数设置为 true 时,Terraform将先创建新的实例,再销毁之前的实例。这个参数可以适用于保持业务连续的场景,由于新旧实例会同时存在,需要提前确认资源实例是否有唯一的名称要求或其他约束。
resource "aws_iam_server_certificate" "this" {
# ...
lifecycle {
create_before_destroy = true
}
}
prevent_destroy
📌简单说:防止资源被意外删除
当我们将 prevent_destroy 参数设置为true时,Terraform将会阻止对此资源的删除操作并返回错误。这个元参数可以作为一种防止因意外操作而重新创建成本较高实例的安全措施,例如数据库实例。如果要删除此资源,需要将这个配置删除后再执行 destroy 操作。
resource "aws_iam_server_certificate" "this" {
# ...
lifecycle {
prevent_destroy = true
}
}
ignore_changes
📌简单说:忽略特定参数变更 执行 `terraform plan/apply` 不显示差异
默认情况下,Terraform plan/apply 操作将检测云上资源的属性和本地资源块中的差异,如果不一致将会调用更新或者重建操作来匹配配置。我们可以用 ignore_changes 来忽略某些参数不进行更新或重新。ignore_changes 的值可以是属性的相对地址列表,对于 Map 和 List 类型,可以使用索引表示法引用,如 tags["Name"],list[0] 等。
resource "aws_instance" "example" {
# ...
lifecycle {
ignore_changes = [
# Ignore changes to tags, e.g. because a management agent
# updates these based on some ruleset managed elsewhere.
tags,
]
}
}
replace_triggered_by
新特性在 Terraform 1.2 中添加。当任何引用项更改时替换资源。提供引用托管资源、实例或实例属性的表达式列表。在使用count或for_each的资源中使用时,可以在表达式中使用count.index或each.key来引用配置相同计数或集合的其他资源的特定实例。
resource "aws_appautoscaling_target" "ecs_target" {
# ...
lifecycle {
replace_triggered_by = [
# Replace `aws_appautoscaling_target` each time this instance of
# the `aws_ecs_service` is replaced.
aws_ecs_service.svc.id
]
}
}
常用函数
💻 Terraform交互式命令提示符 在终端输入 `terraform console` 命令即可进入
concat
concat 可以将多个列表组合成单个列表。
> concat(["I", "am"], ["Jeffrey", "Su"])
[
"I",
"am",
"Jeffrey",
"Su",
]
flatten
flatten 接受一个列表并将任何列表元素替换为列表内容的扁平序列。
> flatten([["I", "am"], ["Jeffrey", "Su"]])
[
"I",
"am",
"Jeffrey",
"Su",
]
📌concat 与 flatten 区别: concat 将两个或多个列表合并为一个列表,而 flatten 将一个列表列表展平为一个平面列表。
lookup
查询map 中指定key的value
> lookup({name="Jeffrey", age=18}, "name", "default_value")
"Jeffrey"
> lookup({name="Jeffrey", age=18}, "age", "default_value")
18
> lookup({name="Jeffrey", age=18}, "rank", "default_value")
"default_value"
element
element 从列表中检索单个元素
> element(["My", "name", "is", "Jeffrey"], 3)
"Jeffrey"
merge
Merge 接受任意数量的映射或对象,并返回包含来自所有参数的合并元素集的单个映射或对象。如果多个给定的映射或对象定义了相同的键或属性,那么参数序列中后面的那个将优先。如果参数类型不匹配,则在应用合并规则之后,结果类型将是与属性类型结构匹配的对象。
> merge({a="b", c="d"}, {e="f", c="z"})
{
"a" = "b"
"c" = "z"
"e" = "f"
}
> merge({a="b"}, {a=[1,2], c="z"}, {d=3})
{
"a" = [
1,
2,
]
"c" = "z"
"d" = 3
}
try
try 是一个特殊的函数。使用频率不高,但是当你有复杂的数据结构时它会很有帮助。 当您不知道数据是否存在或数据将如何形成时,建议使用它。
variable "example" {
type = any
}
locals {
example = try(
[tostring(var.example)],
tolist(var.example),
)
}
coalesce
coalesce 接受任意数量的参数并返回第一个不为 null 或空字符串的参数。
> coalesce(true, false)
true
> coalesce(null, false)
false
dynamic
从技术上讲,它不是函数而是块。您可能拥有一些具有嵌套块的资源,您知道那些具有巨大而复杂的对象的资源。 封装成模块后如何一致更多场景,显示指定参数肯定是不切和实际的。
❎ 错误示例
resource "aws_cloudwatch_event_permission" "this" {
# ...
condition {
# but the "setting" block is always a literal block
}
}
✅正确示例
resource "aws_cloudwatch_event_permission" "this" {
for_each = var.permissions
statement_id = each.key
principal = lookup(each.value, "principal", null)
action = lookup(each.value, "action", null)
event_bus_name = try(each.value["event_bus_name"], aws_cloudwatch_event_bus.this[0].name, var.bus_name, null)
dynamic "condition" {
for_each = lookup(each.value, "condition", null) != null ? [each.value.condition] : []
content {
key = lookup(condition.value, "key", null)
value = lookup(condition.value, "value", null)
type = lookup(condition.value, "type", null)
}
}
}
以上就是terraform meta-argument 以及 常用函数,大家还有什么想知道的或是我遗漏的 欢迎评论区留言 🙆