Terraform 扩展

Terraform 扩展

Terraform Meta-Arguments 元参数

  • count 创建相似的资源
  • for_each 创建相似的资源
  • depends_on 定义资源或者模块的依赖
  • provider 定义provider选项
  • lifecycle 资源的生命周期行为
参数 使用范围 备注
count resource module 适用于创建多个相似的资源,使用索引count.index作为参数引用。如果资源存在区别,建议使用 for_each
for_each resource module for_each 它只能用于set(string) 或者map(string)
depends_on resource module 处理 Terraform 无法自动推断的资源或模块之间的依赖关系
provider resource module 在资源中定义供应商,通常是 . ,模块中,如果子模块未定义providers则继承父模块
lifecycle resource module 生命周期行为定义

Count 创建资源副本

  • count = num数值类型, 可以通过 length() 计算数量;
  • count.index 索引来遍历列表

示例: 创建三条 DNS 记录

Terraform 配置
## count.tf

locals {
    zone = "evescn.com"
    records = ["devops1","devops2","devops3"]
}

resource "alicloud_dns_record" "record" {
  count = length(local.records)
  name        = local.zone
  host_record = local.records[count.index]
  type        = "A"
  value       = "192.168.1.1"
}

## count.tf

locals {
    zone = "evescn.com"
    records = ["devops1","devops2","devops3"]
}

resource "alicloud_dns_record" "record" {
  count = length(local.records)
  name        = local.zone
  host_record = local.records[count.index]
  type        = "A"
  value       = "192.168.1.1"
}
创建 DNS 解析
evescn@evescndeMacBook-Pro count % terraform init

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/alicloud versions matching "1.164.0"...
- Installing hashicorp/alicloud v1.164.0...
- Installed hashicorp/alicloud v1.164.0 (signed by HashiCorp)

Terraform has been successfully initialized!

evescn@evescndeMacBook-Pro count % terraform fmt 
count.tf

evescn@evescndeMacBook-Pro count % terraform validate
Success! The configuration is valid.

evescn@evescndeMacBook-Pro count % terraform plan    

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:

  # alicloud_dns_record.record[0] will be created
  + resource "alicloud_dns_record" "record" {
      + host_record = "devops1"
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "evescn.com"
      + routing     = "default"
      + status      = (known after apply)
      + ttl         = 600
      + type        = "A"
      + value       = "192.168.1.1"
    }

  # alicloud_dns_record.record[1] will be created
  + resource "alicloud_dns_record" "record" {
      + host_record = "devops2"
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "evescn.com"
      + routing     = "default"
      + status      = (known after apply)
      + ttl         = 600
      + type        = "A"
      + value       = "192.168.1.1"
    }

  # alicloud_dns_record.record[2] will be created
  + resource "alicloud_dns_record" "record" {
      + host_record = "devops3"
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "evescn.com"
      + routing     = "default"
      + status      = (known after apply)
      + ttl         = 600
      + type        = "A"
      + value       = "192.168.1.1"
    }

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


evescn@evescndeMacBook-Pro count % terraform apply --auto-approve

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:

  # alicloud_dns_record.record[0] will be created
  + resource "alicloud_dns_record" "record" {
      + host_record = "devops1"
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "evescn.com"
      + routing     = "default"
      + status      = (known after apply)
      + ttl         = 600
      + type        = "A"
      + value       = "192.168.1.1"
    }

  # alicloud_dns_record.record[1] will be created
  + resource "alicloud_dns_record" "record" {
      + host_record = "devops2"
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "evescn.com"
      + routing     = "default"
      + status      = (known after apply)
      + ttl         = 600
      + type        = "A"
      + value       = "192.168.1.1"
    }

  # alicloud_dns_record.record[2] will be created
  + resource "alicloud_dns_record" "record" {
      + host_record = "devops3"
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "evescn.com"
      + routing     = "default"
      + status      = (known after apply)
      + ttl         = 600
      + type        = "A"
      + value       = "192.168.1.1"
    }

Plan: 3 to add, 0 to change, 0 to destroy.
alicloud_dns_record.record[1]: Creating...
alicloud_dns_record.record[0]: Creating...
alicloud_dns_record.record[2]: Creating...
alicloud_dns_record.record[0]: Creation complete after 1s [id=843289465596745728]
alicloud_dns_record.record[1]: Creation complete after 1s [id=843289465605112832]
alicloud_dns_record.record[2]: Creation complete after 1s [id=843289465619789824]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

删除 devops2 记录,会成功运行?

locals {
  zone    = "evescn.com"
  # records = ["devops1", "devops2", "devops3"]
  records = ["devops1", "devops3"]
}

resource "alicloud_dns_record" "record" {
  count       = length(local.records)
  name        = local.zone
  host_record = local.records[count.index]
  type        = "A"
  value       = "192.168.1.1"
}

执行部署

evescn@evescndeMacBook-Pro count % terraform plan                
alicloud_dns_record.record[0]: Refreshing state... [id=843289465596745728]
alicloud_dns_record.record[2]: Refreshing state... [id=843289465619789824]
alicloud_dns_record.record[1]: Refreshing state... [id=843289465605112832]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place
  - destroy

Terraform will perform the following actions:

  # alicloud_dns_record.record[1] will be updated in-place
  ~ resource "alicloud_dns_record" "record" {
      ~ host_record = "devops2" -> "devops3"
        id          = "843289465605112832"
        name        = "evescn.com"
        # (7 unchanged attributes hidden)
    }

  # alicloud_dns_record.record[2] will be destroyed
  # (because index [2] is out of range for count)
  - resource "alicloud_dns_record" "record" {
      - host_record = "devops3" -> null
      - id          = "843289465619789824" -> null
      - locked      = false -> null
      - name        = "evescn.com" -> null
      - priority    = 0 -> null
      - routing     = "default" -> null
      - status      = "ENABLE" -> null
      - ttl         = 600 -> null
      - type        = "A" -> null
      - value       = "192.168.1.1" -> null
    }

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

evescn@evescndeMacBook-Pro count % terraform apply --auto-approve
alicloud_dns_record.record[1]: Refreshing state... [id=843289465605112832]
alicloud_dns_record.record[0]: Refreshing state... [id=843289465596745728]
alicloud_dns_record.record[2]: Refreshing state... [id=843289465619789824]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place
  - destroy

Terraform will perform the following actions:

  # alicloud_dns_record.record[1] will be updated in-place
  ~ resource "alicloud_dns_record" "record" {
      ~ host_record = "devops2" -> "devops3"
        id          = "843289465605112832"
        name        = "evescn.com"
        # (7 unchanged attributes hidden)
    }

  # alicloud_dns_record.record[2] will be destroyed
  # (because index [2] is out of range for count)
  - resource "alicloud_dns_record" "record" {
      - host_record = "devops3" -> null
      - id          = "843289465619789824" -> null
      - locked      = false -> null
      - name        = "evescn.com" -> null
      - priority    = 0 -> null
      - routing     = "default" -> null
      - status      = "ENABLE" -> null
      - ttl         = 600 -> null
      - type        = "A" -> null
      - value       = "192.168.1.1" -> null
    }

Plan: 0 to add, 1 to change, 1 to destroy.
alicloud_dns_record.record[2]: Destroying... [id=843289465619789824]
alicloud_dns_record.record[1]: Modifying... [id=843289465605112832]
alicloud_dns_record.record[2]: Destruction complete after 1s
╷
│ Error: [ERROR] terraform-provider-alicloud/alicloud/resource_alicloud_dns_record.go:138: Resource 843289465605112832 UpdateDomainRecord Failed!!! [SDK alibaba-cloud-sdk-go ERROR]:
│ SDK.ServerError
│ ErrorCode: DomainRecordDuplicate
│ Recommend: https://api.aliyun.com/troubleshoot?q=DomainRecordDuplicate&product=Alidns
│ RequestId: 17F3D4A1-6E02-5F4E-AEA0-C04C583B9A9E
│ Message: The DNS record already exists.
│ RespHeaders: map[Access-Control-Allow-Origin:[*] Access-Control-Expose-Headers:[*] Connection:[keep-alive] Content-Length:[246] Content-Type:[application/json;charset=utf-8] Date:[Wed, 02 Aug 2023 03:39:11 GMT] Keep-Alive:[timeout=25] X-Acs-Request-Id:[17F3D4A1-6E02-5F4E-AEA0-C04C583B9A9E] X-Acs-Trace-Id:[df248ca1dac580f3e90c12bf599c4e1a]]
│ 
│   with alicloud_dns_record.record[1],
│   on count.tf line 7, in resource "alicloud_dns_record" "record":
│    7: resource "alicloud_dns_record" "record" {
│ 

从 terraform 看,程序执行的操作步骤如下:

  • update: server2 > server3
  • destroy: server3

但是在执行过程中 count = length(local.records) 命令获取不了 [2] 这个下标程序出错,看看 DNS 解析记录

for_each 创建资源副本

  • for_each = set(string) map(string)

  • for_each 只能用于 set(string) 或者 map(string),可以使用 toset 转换 listset 数据

  • for_each = set(string)

    • each.key = each.value
  • for_each = map(string)

    • each.key = key
    • each.value = value

示例: 创建三条 DNS 记录

Terraform 配置
## version.tf

terraform {
  required_version = ">=1.1.9"
  required_providers {
    alicloud = {
      source  = "hashicorp/alicloud"
      version = "1.164.0"
    }
  }
}

# Configure the Alicloud Provider
provider "alicloud" {
  access_key = "xxxxxxxx"
  secret_key = "xxxxxxxx"
}

## for_each.tf

locals {
  zone = "evescn.com"
  # records = ["devops1", "devops2", "devops3"]
  records = ["devops1", "devops3"]
}

resource "alicloud_dns_record" "eachrecord" {
  for_each    = toset(local.records)
  name        = local.zone
  host_record = each.value
  type        = "A"
  value       = "192.168.1.1"
}
创建 DNS 解析
evescn@evescndeMacBook-Pro for_each % terraform init

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/alicloud versions matching "1.164.0"...
- Installing hashicorp/alicloud v1.164.0...
- Installed hashicorp/alicloud v1.164.0 (signed by HashiCorp)

Terraform has been successfully initialized!

evescn@evescndeMacBook-Pro for_each % terraform fmt 
for_each.tf

evescn@evescndeMacBook-Pro for_each % terraform validate
Success! The configuration is valid.

evescn@evescndeMacBook-Pro for_each % terraform apply   

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:

  # alicloud_dns_record.eachrecord["devops1"] will be created
  + resource "alicloud_dns_record" "eachrecord" {
      + host_record = "devops1"
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "evescn.com"
      + routing     = "default"
      + status      = (known after apply)
      + ttl         = 600
      + type        = "A"
      + value       = "192.168.1.1"
    }

  # alicloud_dns_record.eachrecord["devops2"] will be created
  + resource "alicloud_dns_record" "eachrecord" {
      + host_record = "devops2"
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "evescn.com"
      + routing     = "default"
      + status      = (known after apply)
      + ttl         = 600
      + type        = "A"
      + value       = "192.168.1.1"
    }

  # alicloud_dns_record.eachrecord["devops3"] will be created
  + resource "alicloud_dns_record" "eachrecord" {
      + host_record = "devops3"
      + id          = (known after apply)
      + locked      = (known after apply)
      + name        = "evescn.com"
      + routing     = "default"
      + status      = (known after apply)
      + ttl         = 600
      + type        = "A"
      + value       = "192.168.1.1"
    }

Plan: 3 to add, 0 to change, 0 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

alicloud_dns_record.eachrecord["devops1"]: Creating...
alicloud_dns_record.eachrecord["devops2"]: Creating...
alicloud_dns_record.eachrecord["devops3"]: Creating...
alicloud_dns_record.eachrecord["devops2"]: Creation complete after 0s [id=843317481926367232]
alicloud_dns_record.eachrecord["devops3"]: Creation complete after 0s [id=843317481928446976]
alicloud_dns_record.eachrecord["devops1"]: Creation complete after 0s [id=843317481953598464]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

删除 devops2 记录,重新执行

## for_each.tf

locals {
  zone = "evescn.com"
  # records = ["devops1", "devops2", "devops3"]
  records = ["devops1", "devops3"]
}

resource "alicloud_dns_record" "eachrecord" {
  for_each    = toset(local.records)
  name        = local.zone
  host_record = each.value
  type        = "A"
  value       = "192.168.1.1"
}

执行部署

evescn@evescndeMacBook-Pro for_each % terraform apply
alicloud_dns_record.eachrecord["devops2"]: Refreshing state... [id=843317481926367232]
alicloud_dns_record.eachrecord["devops3"]: Refreshing state... [id=843317481928446976]
alicloud_dns_record.eachrecord["devops1"]: Refreshing state... [id=843317481953598464]

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:

  # alicloud_dns_record.eachrecord["devops2"] will be destroyed
  # (because key ["devops2"] is not in for_each map)
  - resource "alicloud_dns_record" "eachrecord" {
      - host_record = "devops2" -> null
      - id          = "843317481926367232" -> null
      - locked      = false -> null
      - name        = "evescn.com" -> null
      - priority    = 0 -> null
      - routing     = "default" -> null
      - status      = "ENABLE" -> null
      - ttl         = 600 -> null
      - type        = "A" -> null
      - value       = "192.168.1.1" -> null
    }

Plan: 0 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

alicloud_dns_record.eachrecord["devops2"]: Destroying... [id=843317481926367232]
alicloud_dns_record.eachrecord["devops2"]: Destruction complete after 0s

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

dynamic 动态的内联块

  • dynamic : 定义动态资源块,后面是 label, 默认为生成的语句块名称;

  • for_each : 要进行迭代循环的对象(map、list、set);

  • iterator : 临时的变量名称,如果未定义则使用 labelports

  • content : 要生成的语句块的内容部分;

    dynamic "label" {
    # 支持 map list set 等数据
    for_each = map | list | set
    iterator = iterator_name
    content {
    key = iterator_name.value
    }
    }

    dynamic "ports" {
    for_each = local.jenkins_ports
    content {
    internal = ports.value.internal
    external = ports.value.external
    ip = "0.0.0.0"
    protocol = "tcp"
    }
    }

Docker 中的 ports{}

resource "docker_container" "jenkins" {

  ports {
    internal = 8080
    external = 8080
    ip       = "0.0.0.0"
    protocol = "tcp"
  }
  
  ports {
    internal = 50000
    external = 50000
    ip       = "0.0.0.0"
    protocol = "tcp"
  }
}

定义一个local变量;使用dynamic动态内联块

for_each遍历资源;ports.value引用遍历的资源

locals {
  jenkins_ports = [
    {
      internal = 8080
      external = 8080
    },
    {
      internal = 50000
      external = 50000
    }
  ]
}

resource "docker_container" "jenkins" {

  // 使用dynamic来实现动态
  dynamic "ports" {
    for_each = local.jenkins_ports
    content {
      internal = ports.value.internal
      external = ports.value.external
      ip       = "0.0.0.0"
      protocol = "tcp"
    }
  }

depends_on 依赖关系

处理 资源或模块之间的依赖关系。

resource "docker_image" "jenkins" {
  name         = "jenkins/jenkins:2.332.2-centos7-jdk8"
  force_remove = false
  keep_locally = true
}


resource "docker_container" "jenkins" {
  image      = docker_image.jenkins.name
  name       = "jenkins-demo"

  # 依赖 docker_image 资源
  depends_on = [
    docker_image.jenkins
  ]
}

provider 自定义供应商

resource 覆盖资源中的 provider

例如:分别在 cn-beijing 和 cn-shanghai 区域分别创建1个实例。

provider "alicloud" {
  alias  = "north"
  region = "cn-beijing"
  access_key = "AKIR"
  secret_key = "MJy5JXmZn"
}

provider "alicloud" {
  region     = "cn-shanghai"
  access_key = "AK5R"
  secret_key = "MJy5JX6HIqmZn"
}

resource "alicloud_instance" "ecs_demo_north" {
  provider      = alicloud.north
  ...
}

resource "alicloud_instance" "ecs_demo" {
  ...
}

module 默认子模块继承根模块的provider;

  • provider = map()

  • key: provider名称

  • value: <provider>.<alias>

    provider "alicloud" {
    alias = "north"
    region = "cn-beijing"
    }

    provider "alicloud" {
    region = "cn-shanghai"
    }

    module "example" {
    source = "./example"
    # module 中的 provider 配置
    providers = {
    alicloud = alicloud.north
    }
    }

lifecycle 生命周期

标记资源不被删除或者销毁前创建新的资源;

lifecycle {
  xxx
}
  • create_before_destroy 先创建新的对象,再销毁旧的对象;

  • prevent_destroy 防止资源被销毁;

  • ignore_changes 忽略资源的差异;

  • replace_triggered_by 当指定的资源修改后替换当前资源;

  • precondition postcondition 条件检查

    lifecycle {
    create_before_destroy = true
    # prevent_destroy = true
    ignore_changes = [
    tags, instance_name
    ]
    replace_triggered_by = [
    alicloud_vpc.vpc.vpc_name
    ]
    }

相关推荐
Linux运维老纪4 小时前
DNS缓存详解(DNS Cache Detailed Explanation)
计算机网络·缓存·云原生·容器·kubernetes·云计算·运维开发
Elastic 中国社区官方博客18 小时前
使用 Ollama 和 Kibana 在本地为 RAG 测试 DeepSeek R1
大数据·数据库·人工智能·elasticsearch·ai·云原生·全文检索
Linux运维老纪1 天前
windows部署deepseek之方法(The Method of Deploying DeepSeek on Windows)
linux·人工智能·分布式·云原生·运维开发·devops
Elastic 中国社区官方博客2 天前
Elastic Cloud Serverless 获得主要合规认证
大数据·数据库·elasticsearch·搜索引擎·云原生·serverless·全文检索
超级阿飞2 天前
在K8s中部署动态nfs存储provisioner
云原生·容器·kubernetes·nfs
赵渝强老师2 天前
【赵渝强老师】K8s中Pod探针的TCPSocketAction
云原生·容器·kubernetes
努力的小T2 天前
Linux二进制部署K8s集群的平滑升级教程
linux·运维·服务器·云原生·容器·kubernetes·云计算
2的n次方_2 天前
Eureka 服务注册和服务发现的使用
spring boot·spring cloud·云原生·eureka·服务发现
赵渝强老师3 天前
【赵渝强老师】K8s中Pod探针的ExecAction
云原生·容器·kubernetes
vibag3 天前
Kubernetes(一)
java·云原生·容器·kubernetes