目前还没有 Terraform 底层语法 HCL 的 Python 实现可以支持对现有文件进行外科手术修改,因为这需要保留典型 HCL 解析器不会保留的信息,例如注释和块内参数的顺序。
另一种选择是使用 Terraform 的一个较少使用的功能,称为覆盖文件 https://www.terraform.io/docs/configuration/override.html,它允许您编写一个覆盖现有文件部分的新文件。这种机制专门针对这种情况:您想要使用人类编写的文件中的内容,但用机器生成的内容覆盖其中的一部分。
如果您将给定的示例代码作为example.tf
然后你可以将另一个文件放在磁盘上它旁边,称为example_override.tf.json
包含以下内容:
{
"resource": {
"aws_iam_role": {
"MY_AWS_ACCOUNT": {
"assume_role_policy": "{\n\t\"Version\": \"2012-10-17\",\n\t\"Statement\": [\n\t {\n\t\t\"Action\": \"sts:AssumeRole\",\n\t\t\"Condition\": {\n\t\t \"Bool\": {\n\t\t\t\"aws:MultiFactorAuthPresent\": \"true\"\n\t\t }\n\t\t},\n\t\t\"Principal\": {\n\t\t \"AWS\": [\n\t\t\t\"arn:aws:iam::<AWS_ACCOUNT_ID>:user/user.name2\"\n\t\t ]\n\t\t},\n\t\t\"Effect\": \"Allow\",\n\t\t\"Sid\": \"\"\n\t }\n\t]\n }"
}
}
}
}
因为这个附加文件使用Terraform 的替代 JSON 语法 https://www.terraform.io/docs/configuration/syntax-json.html,很容易从任何具有可用 JSON 序列化器的语言(包括 Python)生成。 Terraform 本身将读取这两个文件并获取assume_role_policy
从覆盖文件中覆盖基础文件中的相应文件,如中所述合并行为 https://www.terraform.io/docs/configuration/override.html#merging-behavior。然后,您可以不修改原始 HCL 文件,从而避免干扰该文件中以人为本的格式决策的问题。
HCL 的 Go 实现是规范的实现,也是 Terraform 本身使用的实现,有一个包hclwrite http://pkg.go.dev/github.com/hashicorp/hcl/v2/hclwrite这是一个专门的 API,用于直接修改现有的 HCL 源代码,同时保留所有未修改的令牌及其顺序。因此编写的程序Go使用该包可能会达到您想要的结果:
package main
import (
"fmt"
"log"
hcl "github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
)
func main() {
// In a real program, use ioutil.ReadFile to read a file from disk.
// But for this example, I will use a const string.
src := []byte(oldSource)
f, diags := hclwrite.ParseConfig(src, "example.tf", hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
log.Fatal(diags)
}
for _, block := range f.Body().Blocks() {
if block.Type() != "resource" {
continue
}
if labels := block.Labels(); len(labels) < 2 || labels[0] != "aws_iam_role" || labels[1] != "MY_AWS_ACCOUNT" {
continue
}
block.Body().SetAttributeValue("assume_role_policy", cty.StringVal(newPolicy))
}
// In a real program, maybe write these bytes to a file on disk.
fmt.Println(string(f.Bytes()))
}
const oldSource = `
variable "variable_1" {}
variable "variable_2" {}
locals {
temp_locals = 0
}
resource "aws_iam_role" "MY_AWS_ACCOUNT" {
name = "MY_AWS_ACCOUNT"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
},
"Principal": {
"AWS": [
"arn:aws:iam::<AWS_ACCOUNT_ID>:user/user.name1"
]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "aws_iam_role_policy_attachment" {
}
`
const newPolicy = `
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
},
"Principal": {
"AWS": [
"arn:aws:iam::<AWS_ACCOUNT_ID>:user/user.name2"
]
},
"Effect": "Allow",
"Sid": ""
}
]
}
`
然而,该程序的输出确实证明了自动生成主要供人类编写和阅读的语言的局限性之一:
variable "variable_1" {}
variable "variable_2" {}
locals {
temp_locals = 0
}
resource "aws_iam_role" "MY_AWS_ACCOUNT" {
name = "MY_AWS_ACCOUNT"
assume_role_policy = "\n{\n\t\"Version\": \"2012-10-17\",\n\t\"Statement\": [\n\t {\n\t\t\"Action\": \"sts:AssumeRole\",\n\t\t\"Condition\": {\n\t\t \"Bool\": {\n\t\t\t\"aws:MultiFactorAuthPresent\": \"true\"\n\t\t }\n\t\t},\n\t\t\"Principal\": {\n\t\t \"AWS\": [\n\t\t\t\"arn:aws:iam::<AWS_ACCOUNT_ID>:user/user.name2\"\n\t\t ]\n\t\t},\n\t\t\"Effect\": \"Allow\",\n\t\t\"Sid\": \"\"\n\t }\n\t]\n }\n"
}
resource "aws_iam_role_policy_attachment" "aws_iam_role_policy_attachment" {
}
作为人类,我们可以就如何格式化事物以提高可读性做出主观决定,但是hclwrite
只是默认对此字符串使用带引号的字符串形式,在这种情况下,这是一个人类作者可能不会做出的糟糕选择。