Обновление файлов terraform (.tf) с использованием Python - PullRequest
0 голосов
/ 14 февраля 2020

Я пытаюсь обновить шаблоны terraform, используя python, хотя у меня возникают некоторые проблемы при попытке найти модуль (например, PyYAML для обновления файлов yml).

Моя цель - обновить имеющиеся файлы terraform в репо git и создайте запрос на извлечение, который при объединении вызовет конвейер для реализации изменений.

Пример .tf-файла:

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" {
}

Хотите добавить арн из user.name2 в этом файле.

1 Ответ

0 голосов
/ 15 февраля 2020

В настоящее время нет Python реализации базового синтаксиса HCL Terraform, который может поддерживать хирургическое изменение существующего файла, потому что для этого требуется сохранение информации, которую типичный парсер HCL не сохранит, такой как комментарии и порядок аргументов внутри блоков .

Другой вариант - использовать менее часто используемую функцию Terraform, которая называется Override Files , которая позволяет вам писать новый файл, который переопределяет часть существующего файла. Этот механизм существует специально для такого рода ситуаций, когда вы хотите использовать контент из файла, написанного людьми, но переопределить его часть содержимым, сгенерированным машиной.

Если вы взяли код данного примера как 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 синтаксис , он легко генерировать из любого языка, имеющего сериализатор JSON, включая Python. Сам Terraform прочитает оба файла и примет assume_role_policy из файла переопределения как перезаписывающий соответствующий файл в базовом файле, как описано в Поведение слияния . Затем вы можете оставить исходный файл HCL без изменений, избегая проблем с вмешательством в решения о форматировании, ориентированные на человека в этом файле.


Реализация Go HCL, которая является канонической и является той, которая Сам Terraform использует пакет 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": ""
      }
    ]
  }
`

Однако выходные данные этой программы демонстрируют один из ограничения автоматического создания c языка, который в первую очередь предназначен для написания и чтения людьми:

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 только что по умолчанию использовал строковую форму в кавычках для этой строки, что в данном случае является плохим выбором, который автор, вероятно, не сделал бы.

...