Spec-Zone.ru › OpenTofu 1.11

Синтаксис конфигурации JSON

Большинство конфигураций OpenTofu записываются с использованием собственного синтаксиса языка OpenTofu, разработанного так, чтобы его было относительно легко читать и изменять.

OpenTofu также поддерживает альтернативный синтаксис, совместимый с JSON. Этот синтаксис полезен при программной генерации частей конфигурации, поскольку для подготовки генерируемых файлов конфигурации можно использовать существующие библиотеки JSON.

Синтаксис JSON определён на основе собственного синтаксиса. Всё, что можно выразить в собственном синтаксисе, можно выразить и в синтаксисе JSON, однако некоторые конструкции сложнее представить в JSON из-за ограничений грамматики JSON.

OpenTofu ожидает собственный синтаксис в файлах с суффиксом .tf или .tofu и синтаксис JSON в файлах с суффиксом .tf.json или .tofu.json.

Низкоуровневый синтаксис JSON, как и собственный синтаксис, определён в терминах спецификации под названием HCL. Для работы с OpenTofu необязательно знать все подробности синтаксиса HCL или его отображения в JSON, поэтому на этой странице кратко описаны наиболее важные различия между собственным синтаксисом и синтаксисом JSON. Если вам интересно, полное описание синтаксиса JSON для HCL можно найти в его спецификации.

Приоритет расширений​

Если в каталоге есть файлы .tf.json и .tofu.json с одинаковым базовым именем, OpenTofu отдаст приоритет файлу .tofu.json и проигнорирует файл .tf.json. Например:

  • Если в одном каталоге есть foo.tf.json и foo.tofu.json, OpenTofu загрузит только foo.tofu.json и проигнорирует foo.tf.json.

Это гарантирует, что файлы .tofu.json всегда имеют приоритет над файлами .tf.json, если присутствуют оба варианта. Такая возможность может быть полезна авторам модулей, которые хотят, чтобы их модули поддерживали и OpenTofu, и Terraform.

Структура файла JSON​

Корневым элементом любой конфигурации OpenTofu на основе JSON является объект JSON. Его свойства соответствуют типам блоков верхнего уровня языка OpenTofu. Например:

Блок кода
{
  "variable": {
    "example": {
      "default": "hello"
    }
  }
}

Каждое свойство объекта верхнего уровня должно соответствовать имени одного из ожидаемых типов блоков верхнего уровня. Типы блоков, для которых требуются метки, например variable выше, представлены вложенным объектом для каждого уровня меток. Для блоков resource требуются две метки, поэтому необходимы два уровня вложенности:

Блок кода
{
  "resource": {
    "aws_instance": {
      "example": {
        "instance_type": "t2.micro",
        "ami": "ami-abc123"
      }
    }
  }
}

После вложенных объектов, представляющих метки, ещё один вложенный объект описывает тело самого блока. В приведённых выше примерах указаны аргумент default для variable "example", а также аргументы instance_type и ami для resource "aws_instance" "example".

Вместе эти два файла конфигурации эквивалентны следующим блокам в собственном синтаксисе:

Блок кода
variable "example" {
  default = "hello"
}

resource "aws_instance" "example" {
  instance_type = "t2.micro"
  ami           = "ami-abc123"
}

Правила преобразования в JSON немного различаются для каждого типа блока верхнего уровня (см. приведённые ниже исключения для отдельных типов блоков), однако в большинстве случаев применяются следующие общие правила:

  • Объект JSON, представляющий тело блока, содержит свойства, соответствующие именам аргументов или типам вложенных блоков.

  • Если свойство соответствует аргументу, который в собственном синтаксисе принимает произвольные выражения, значение свойства преобразуется в выражение, как описано ниже в разделе Преобразование выражений. Для аргументов, которые не принимают произвольные выражения, интерпретация значения свойства зависит от аргумента, как описано далее на этой странице в разделе исключений для отдельных типов блоков.

  • Если имя свойства соответствует ожидаемому типу вложенного блока, значение интерпретируется, как описано ниже в разделе Преобразование вложенных блоков, если далее на этой странице в разделе исключений для отдельных типов блоков не указано иное.

Преобразование выражений​

Поскольку грамматика JSON не позволяет представить весь синтаксис выражений языка OpenTofu, значения JSON, интерпретируемые как выражения, преобразуются следующим образом:

JSON Интерпретация в языке OpenTofu
Логическое значение Литеральное значение bool.
Число Литеральное значение number.
Строка Разбирается как шаблон строки, а затем вычисляется, как описано ниже.
Объект Каждое значение свойства преобразуется согласно этой таблице, в результате чего получается значение object(...) с подходящими типами атрибутов.
Массив Каждый элемент преобразуется согласно этой таблице, в результате чего получается значение tuple(...) с подходящими типами элементов.
Null Литеральное значение null.

Если в месте, где ожидаются произвольные выражения, встречается строка JSON, её значение сначала разбирается как [шаблон строки][], а затем вычисляется для получения окончательного результата.

Если данный шаблон состоит только из одной последовательности интерполяции, результат его выражения используется напрямую, без предварительного преобразования в строку. Это позволяет использовать в синтаксисе JSON выражения, результатом которых являются значения нестрокового типа:

Блок кода
{
  "output": {
    "example": {
      "value": "${aws_instance.example}"
    }
  }
}

Объявленное выше output "example" имеет в качестве значения объект, представляющий указанный блок ресурса aws_instance, а не строковое значение. Это особое поведение не применяется, если в шаблоне присутствуют какие-либо литеральные или управляющие последовательности; в таких случаях всегда создаётся строковое значение.

Преобразование вложенных блоков​

Если свойство объекта JSON названо в соответствии с типом вложенного блока, его значение представляет один или несколько блоков этого типа. Значением свойства должен быть объект JSON или массив JSON.

Самый простой случай — представление одного блока заданного типа, если для этого типа не требуются метки, как в случае вложенного блока lifecycle внутри блоков resource:

Блок кода
{
  "resource": {
    "aws_instance": {
      "example": {
        "lifecycle": {
          "create_before_destroy": true
        }
      }
    }
  }
}

Приведённый выше пример эквивалентен следующей конфигурации в собственном синтаксисе:

Блок кода
resource "aws_instance" "example" {
  lifecycle {
    create_before_destroy = true
  }
}

Если для типа вложенного блока требуется одна или несколько меток либо можно указать несколько блоков одного типа, преобразование становится немного сложнее. Например, для вложенного типа блока provisioner внутри блоков resource требуется метка, указывающая используемый исполнитель, а порядок блоков исполнителей важен для определения порядка операций.

В следующем примере на собственном синтаксисе показан блок resource с несколькими исполнителями разных типов:

Блок кода
resource "aws_instance" "example" {
  # (resource configuration omitted for brevity)

  provisioner "local-exec" {
    command = "echo 'Hello World' >example.txt"
  }
  provisioner "file" {
    source      = "example.txt"
    destination = "/tmp/example.txt"
  }
  provisioner "remote-exec" {
    inline = [
      "sudo install-something -f /tmp/example.txt",
    ]
  }
}

Чтобы сохранить порядок этих блоков, в качестве непосредственного значения свойства, представляющего этот тип блока, необходимо использовать массив JSON, как в следующем эквиваленте приведённого выше примера на JSON:

Блок кода
{
  "resource": {
    "aws_instance": {
      "example": {
        "provisioner": [
          {
            "local-exec": {
              "command": "echo 'Hello World' >example.txt"
            }
          },
          {
            "file": {
              "source": "example.txt",
              "destination": "/tmp/example.txt"
            }
          },
          {
            "remote-exec": {
              "inline": ["sudo install-something -f /tmp/example.txt"]
            }
          }
        ]
      }
    }
  }
}

Каждый элемент массива provisioner — это объект с одним свойством, имя которого представляет метку соответствующего блока provisioner. Для типов блоков, которым требуется несколько меток, этот шаблон чередования вложенных массивов и объектов можно использовать для каждого дополнительного уровня.

Если для типа вложенного блока требуются метки, но порядок не имеет значения, массив можно опустить и указать только один объект, имена свойств которого соответствуют уникальным меткам блоков. В простых случаях это допустимо как сокращённая форма приведённой выше записи, однако чередование массивов и объектов — наиболее универсальный подход. При систематическом преобразовании собственного синтаксиса в JSON мы рекомендуем использовать наиболее универсальную форму, чтобы гарантировать точное сохранение смысла конфигурации.

Свойства для комментариев​

Хотя мы не рекомендуем вручную редактировать файлы конфигурации с синтаксисом JSON — этот формат в первую очередь предназначен для программной генерации и обработки, — внутри объектов JSON, представляющих тела блоков, допускается ограниченная форма комментариев, задаваемых с помощью специального имени свойства:

Блок кода
{
  "resource": {
    "aws_instance": {
      "example": {
        "//": "This instance runs the scheduled tasks for backup",

        "instance_type": "t2.micro",
        "ami": "ami-abc123"
      }
    }
  }
}

Во всех объектах, представляющих тело блока, свойства с именем "//" полностью игнорируются OpenTofu. Это исключение не распространяется на объекты, которые интерпретируются как выражения: в этом случае такое свойство будет интерпретировано как атрибут типа объекта с именем "//".

Это специальное имя свойства также можно использовать в корне файла конфигурации на основе JSON. Так можно, например, указать, какая программа создала файл.

Блок кода
{
  "//": "This file is generated by generate-outputs.py. DO NOT HAND-EDIT!",

  "output": {
    "example": {
      "value": "${aws_instance.example}"
    }
  }
}

Исключения для отдельных типов блоков​

Некоторые аргументы в блоках определённых типов обрабатываются OpenTofu особым образом, поэтому их преобразование в синтаксис JSON не соответствует описанным выше общим правилам. В следующих подразделах описаны специальные правила преобразования, применяемые к каждому типу блока верхнего уровня.

Блоки resource и data​

Некоторые метааргументы типов блоков resource и data принимают прямые ссылки на объекты или литеральные ключевые слова. В JSON ссылка или ключевое слово задаются строкой JSON без дополнительных пробелов или символов вокруг неё.

Например, метааргумент provider принимает ссылку <PROVIDER>.<ALIAS> на конфигурацию провайдера. В собственном синтаксисе она указывается без кавычек, а в синтаксисе JSON должна быть представлена строкой:

Блок кода
{
  "resource": {
    "aws_instance": {
      "example": {
        "provider": "aws.foo"
      }
    }
  }
}

Особая обработка применяется к следующим метааргументам:

  • provider: одна строка, как показано выше
  • depends_on: массив строк, содержащих ссылки на именованные сущности, например ["aws_instance.example"].
  • ignore_changes внутри блока lifecycle: если задано значение all, необходимо указать одну строку "all". В противном случае необходимо использовать массив строк JSON, содержащих ссылки на свойства, например ["ami"].

Особая обработка также применяется к аргументу type любого блока connection, расположенного непосредственно внутри блока resource или вложенного в блоки provisioner: заданная строка интерпретируется буквально, а не разбирается и вычисляется как шаблон строки.

Блоки variable​

Все аргументы внутри блоков variable преобразуются в JSON нестандартным образом:

  • type: строка с выражением типа, например "string" или "list(string)".
  • default: литеральное значение JSON, которое можно преобразовать в указанный тип. Строки в этом значении воспринимаются буквально и не интерпретируются как шаблоны строк.
  • description: литеральная строка JSON, которая не интерпретируется как шаблон.
Блок кода
{
  "variable": {
    "example": {
      "type": "string",
      "default": "hello"
    }
  }
}

Блоки output​

Аргументы description и sensitive интерпретируются как литеральные значения JSON. Строка description не интерпретируется как шаблон строки.

Аргумент value интерпретируется как выражение.

Блок кода
{
  "output": {
    "example": {
      "value": "${aws_instance.example}"
    }
  }
}

Блоки locals​

Значением свойства объекта JSON, представляющего тип блока locals, должен быть объект JSON, имена свойств которого соответствуют именам объявляемых локальных значений:

Блок кода
{
  "locals": {
    "greeting": "Hello, ${var.name}"
  }
}

Значение каждого из этих вложенных свойств интерпретируется как выражение.

Блоки module​

Метааргумент providers необходимо задать как объект JSON, свойства которого — это краткие адреса провайдеров, доступных дочернему модулю, а значения — адреса провайдеров, используемых текущим модулем; и те и другие задаются литеральными строками:

Блок кода
{
  "module": {
    "example": {
      "source": "hashicorp/consul/azurerm",
      "version": "= 1.0.0",
      "providers": {
        "aws": "aws.usw1"
      }
    }
  }
}

Блоки provider​

Метааргументы alias и version необходимо задавать литеральными строками. Их значения не интерпретируются как шаблоны строк.

Блок кода
{
  "provider": {
    "aws": [
      {
        "region": "us-east-1"
      },
      {
        "alias": "usw1",
        "region": "us-west-1"
      }
    ]
  }
}

Блоки terraform​

Параметры внутри блоков terraform обычно интерпретируются буквально, за исключением блоков backend и encryption, которые поддерживают выражения. Другие параметры не принимают ссылки на именованные объекты или вызовы функций, поэтому строковые значения не обрабатываются как шаблоны строк.

Блок backend​

Поскольку в каждом блоке terraform допускается только один блок backend, для его представления можно использовать компактное преобразование блока: вложенный объект с одним свойством, имя которого обозначает тип бэкенда.

Блок кода
{
  "terraform": {
    "required_version": ">= 0.12.0",
    "backend": {
      "s3": {
        "region": "us-west-2",
        "bucket": "acme-tofu-states"
      }
    }
  }
}

Блок encryption​

Как показано в документации по шифрованию, блок encryption поддерживает следующие дочерние блоки:

  • key_provider
  • method
  • state
  • plan
  • remote_state_data_sources

Блоки key_provider и method могут ссылаться на другие именованные блоки, например переменные, тогда как блоки state, plan и remote_state_data_sources могут ссылаться только на другие блоки внутри блока encryption.

Блок key_provider может ссылаться на переменные, чтобы динамически передавать сведения о ключе:

Блок кода
{
  "variable": {
    "state_plan_passphrase": {
      "type": "string",
      "default": "myultrasecretpassphrase1!"
    }
  },
  "terraform": {
    "encryption": {
      "key_provider": {
        "pbkdf2": {
          "state_plan": {
            "passphrase": "${var.state_plan_passphrase}"
          }
        }
      }
    }
  }
}

Блок method может ссылаться на блоки key_provider с помощью статических строковых ссылок или обычных интерполированных выражений Terraform:

Блок кода
{
  "terraform": {
    "encryption": {
      "key_provider": {
        "pbkdf2": {
          "state_plan": {
            "passphrase": "myultrasecretpassphrase1!"
          }
        }
      },
      "method": {
        "aes_gcm": {
          "my_key_for_state": {
            "keys": "${key_provider.pbkdf2.state_plan}"
          },
          "my_key_for_plan": {
            "keys": "key_provider.pbkdf2.state_plan"
          }
        }
      }
    }
  }
}

Блоки state, plan и remote_state_data_sources могут ссылаться только на блоки method с помощью строковых ссылок. Это не выражения, а только ссылки на методы, поэтому они вычисляются не так, как выражения в OpenTofu:

Блок кода
{
  "variable": {
    "remote_state_passphrase": {
      "type": "string",
      "default": "mysecrettestpassword!"
    },
    "state_plan_passphrase": {
      "type": "string",
      "default": "mysecrettestpassword2!"
    }
  },
  "terraform": {
    "encryption": {
      "key_provider": {
        "pbkdf2": {
          "remote_state": {
            "passphrase": "remotestateultrasecretpassphrase!1"
          },
          "state_plan": {
            "passphrase": "stateplanultrasecretpassphrase!1"
          }
        }
      },
      "method": {
        "aes_gcm": {
          "remote_state": {
            "keys": "${key_provider.pbkdf2.remote_state_key}"
          },
          "state_plan": {
            "keys": "key_provider.pbkdf2.state_plan"
          }
        },
        "unencrypted": {
          "unencrypted": {}
        }
      },
      "state": {
        "method": "method.aes_gcm.state_plan",
        "fallback": {
          "method": "method.unencrypted.unencrypted"
        }
      },
      "plan": {
        "method": "method.aes_gcm.state_plan",
        "fallback": {
          "method": "method.unencrypted.unencrypted"
        }
      },
      "remote_state_data_sources": {
        "default": {
          "method": "method.aes_gcm.remote_state"
        },
        "remote_state_data_source": {
          "another_state": {
            "method": "method.aes_gcm.remote_state"
          }
        }
      }
    }
  },
  "data": {
    "terraform_remote_state": {
      "another_state": {
        "backend": "local",
        "config": {
          "path": "<path to an encrypted state>"
        }
      }
    }
  }
}

Copyright (c) The OpenTofu Authors
Copyright (c) 2014 HashiCorp, Inc.
Mozilla Public License, version 2.0
https://opentofu.org/docs/v1.11/language/syntax/json/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API