1

我正在将我的 0.11 代码转换为 0.12。大多数事情似乎都很顺利,但我真的迷失在 SSM 文档上。

在我的 0.11 代码中,我有这个代码:

resource "aws_ssm_document" "ssm_document" {
    name = "ssm_document_${terraform.workspace}${var.addomainsuffix}"
    document_type = "Command"
    content = <<DOC
    {
        "schemaVersion": "1.0",
        "description": "Automatic Domain Join Configuration",
        "runtimeConfig": {
            "aws:domainJoin": {
                "properties": {
                    "directoryId": "${aws_directory_service_directory.microsoftad-lab.id}",
                    "directoryName": "${aws_directory_service_directory.microsoftad-lab.name}",
                    "dnsIpAddresses": [
                        "${aws_directory_service_directory.microsoftad-lab.dns_ip_addresses[0]}",
                        "${aws_directory_service_directory.microsoftad-lab.dns_ip_addresses[1]}"
                    ]
                }
            }
        }
    }
    DOC
    depends_on = ["aws_directory_service_directory.microsoftad-lab"]
}

这工作得相当好。然而,Terraform 0.12 不接受这个代码,说

该值没有任何索引。

我一直在尝试在网上查找不同的解决方案,但我遇到了无数关于数据类型的问题。例如,我见过的解决方案之一提出了这一点:

"dnsIpAddresses": [
                    "${sort(aws_directory_service_directory.oit-microsoftad-lab.dns_ip_addresses)[0]}",
                    "${sort(aws_directory_service_directory.oit-microsoftad-lab.dns_ip_addresses)[1]}",
                ]
            }

我得到

InvalidDocumentContent:JSON 格式不正确

这对我来说有点奇怪,因为如果我正在查看跟踪日志,我似乎得到了相对正确的值:

{"Content":"{\n    \"schemaVersion\": \"1.0\",\n    \"description\": \"Automatic Domain Join Configuration\",\n    \"runtimeConfig\": {\n        \"aws:domainJoin\": {\n            \"properties\": {\n                \"directoryId\": \"d-9967245377\",\n                \"directoryName\": \"012mig.lab\",\n                \"dnsIpAddresses\": [\n                    \"10.0.0.227\",\n
      \"10.0.7.103\",\n                ]\n            }\n        }\n    }\n}\n    \n","DocumentFormat":"JSON","DocumentType":"Command","Name":"ssm_document_012mig.lab"}

我尝试过 concat 和 list 将值放在一起,但后来我得到了数据类型错误。现在,看起来我在这里循环。

有没有人在这里给我任何指示?

4

1 回答 1

1

Terraform 0.12 具有比 0.11 更严格的类型,并且在幕后进行的自动类型强制更少,所以在这里你会遇到这样一个事实,即aws_directory_service_directory资源dns_ip_addresses属性的输出 不是列表而是集合

            "dns_ip_addresses": {
                Type:     schema.TypeSet,
                Elem:     &schema.Schema{Type: schema.TypeString},
                Set:      schema.HashString,
                Computed: true,
            },

Set 不能直接索引,而是必须首先在 0.12 中显式转换为列表。

举个例子:

variable "example_list" {
  type = list(string)
  default = [
    "foo",
    "bar",
  ]
}


output "list_first_element" {
  value = var.example_list[0]
}

在此运行terraform apply将输出以下内容:

Outputs:

list_first_element = foo

但是,如果我们改用 set 变量:

variable "example_set" {
  type = set(string)
  default = [
    "foo",
    "bar",
  ]
}

output "set_first_element" {
  value = var.example_set[0]
}

然后尝试运行terraform apply会抛出以下错误:

Error: Invalid index

  on main.tf line 22, in output "set_foo":
  22:   value = var.example_set[0]

This value does not have any indices.

如果我们使用 first 将 set 变量转换为列表,tolist那么它可以工作:

variable "example_set" {
  type = set(string)
  default = [
    "foo",
    "bar",
  ]
}

output "set_first_element" {
  value = tolist(var.example_set)[0]
}
Outputs:

set_first_element = bar

请注意,集合的顺序可能与您所期望的不同(在这种情况下,它是按字母顺序排列的,而不是按声明的顺序排列的)。在您的情况下,这不是问题,但是在索引期望元素按照您声明它们的顺序时值得考虑。

这里的另一个可能选项是,您可以直接使用函数dns_ip_addresses将属性编码为 JSON,而不是从输出集或列表构建 JSON 输出:jsonencode

variable "example_set" {
  type = set(string)
  default = [
    "foo",
    "bar",
  ]
}

output "set_first_element" {
  value = jsonencode(var.example_set)
}

运行后输出以下内容terraform apply

Outputs:

set_first_element = ["bar","foo"]

因此,对于您的具体示例,我们希望执行以下操作:

resource "aws_ssm_document" "ssm_document" {
    name = "ssm_document_${terraform.workspace}${var.addomainsuffix}"
    document_type = "Command"
    content = <<DOC
    {
        "schemaVersion": "1.0",
        "description": "Automatic Domain Join Configuration",
        "runtimeConfig": {
            "aws:domainJoin": {
                "properties": {
                    "directoryId": "${aws_directory_service_directory.microsoftad-lab.id}",
                    "directoryName": "${aws_directory_service_directory.microsoftad-lab.name}",
                    "dnsIpAddresses": ${jsonencode(aws_directory_service_directory.microsoftad-lab.dns_ip_addresses)}
                }
            }
        }
    }
    DOC
}

请注意,我还删除了不必要的depends_on. 如果一个资源从另一个资源中插入,那么 Terraform 将自动理解需要在引用它之前创建插入的资源。

资源依赖项文档对此进行了更详细的介绍:

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

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

于 2020-07-01T08:46:25.720 回答