# OpenTofu, fork Terraform и parity-status _Advanced · TerraformLab Knowledge Base_ **TL;DR:** В 2023 HashiCorp перевела Terraform на BSL, community форкнула под именем OpenTofu (Linux Foundation). API совместим, HCL тот же, провайдеры те же. Расхождение пошло: OpenTofu добавил state-encryption, `exclude` для for_each; Terraform, Stacks. Matrix-CI: гонять оба, ловить расхождение фич. ## Краткая история - **Авг 2023**, HashiCorp меняет лицензию Terraform с MPL-2.0 на BSL (Business Source License), запрещающую коммерческие альтернативы. - **Сен 2023**, Linux Foundation объявляет OpenTofu, fork последнего MPL-релиза (1.5.x). - **2024**, OpenTofu 1.6, 1.7, фичи добавляются параллельно с Terraform, иногда раньше. - **2025**, обе утилиты живут; community-провайдеры поддерживают обе. Технически на 1.5 они идентичны. С 1.6+, расхождение растёт. ## Что одинаковое - HCL-синтаксис. - Все провайдеры (single source, Registry). - Команды (init, plan, apply, destroy, fmt, validate). - State-format и обратная совместимость state'а, миграция `terraform state` → `tofu state` без конверсии. - Backends (S3, GCS, local, Terraform Cloud в read-only mode для OpenTofu). - Сценарии 95% использования. ## Что разное | Фича | Terraform | OpenTofu | |---|---|---| | `*.tftest.hcl` фреймворк | 1.6+ | 1.6+ | | mock_provider | 1.7+ | 1.7+ | | state encryption at rest | Нет (только backend SSE) | Да (с 1.7, native) | | `exclude` в for_each | Нет | Да (с 1.8) | | Stacks | Да (HCP-only) | Нет | | Terraform Cloud integration | Нативный | Read-only (через OAuth) | | License | BSL | MPL-2.0 | | Provider registry | registry.terraform.io | registry.opentofu.org (зеркало) | ## Migration Сценарий: hcp-free workflow, хочешь уйти от BSL. ```bash # Терраформ устанавливал так: brew install hashicorp/tap/terraform # OpenTofu: brew install opentofu tofu --version ``` Дальше, `tofu` вместо `terraform`: ```bash tofu init tofu plan tofu apply ``` State совместим, `tofu plan` на сырой `terraform.tfstate` от Terraform работает. Никакой конверсии. Lockfile (`.terraform.lock.hcl`) общий. Если стeк использует фичи 1.6+ (например `mock_provider`), сделай тест-прогон. Парность хорошая, но изредка нюансы. ## State encryption OpenTofu 1.7+ умеет шифровать state на стороне клиента **до** записи в backend: ```hcl terraform { encryption { key_provider "aws_kms" "key" { kms_key_id = "arn:aws:kms:..." region = "us-east-1" key_spec = "AES_256" } method "aes_gcm" "method" { keys = key_provider.aws_kms.key } state { method = method.aes_gcm.method } plan { method = method.aes_gcm.method } } } ``` Это плюс: даже если backend (S3) compromised, state в нём зашифрован. Terraform полагается на SSE backend'а, что иногда недостаточно. См. [tf-secrets-in-state](/terraform/kb/tf-secrets-in-state.md). ## exclude в for_each (OpenTofu 1.8) ```hcl variable "envs" { type = set(string) default = ["dev", "stage", "prod", "test"] } resource "aws_s3_bucket" "logs" { for_each = var.envs bucket = "logs-${each.key}" exclude { condition = each.key == "test" } } ``` Удаляет элемент из for_each без переиндексирования. Terraform такого не имеет, нужно `setsubtract(var.envs, ["test"])`. ## Matrix-CI с обеими Самое практичное в неопределённости, гонять оба: ```yaml jobs: plan-terraform: runs-on: ubuntu-latest steps: - uses: hashicorp/setup-terraform@v3 with: { terraform_version: 1.9.8 } - run: terraform init - run: terraform plan plan-opentofu: runs-on: ubuntu-latest steps: - uses: opentofu/setup-opentofu@v1 with: { tofu_version: 1.8.0 } - run: tofu init - run: tofu plan ``` Если что-то расходится, оба job'а покажут. На production-apply выбираешь один (тот, на котором state). Это страховка: если HashiCorp ещё раз поменяет лицензию, у тебя готовый switch. ## Подводные камни - **`registry.terraform.io` vs `registry.opentofu.org`.** Провайдеры те же, но регистры разные. OpenTofu по умолчанию ходит в свой; `~/.terraformrc` или env-var переопределяет. Для air-gapped, оба через mirror. - **Terraform Cloud features.** Sentinel-policy, workspaces, dynamic credentials, все эти TC-фичи в OpenTofu не работают; OpenTofu может только читать state из TC. - **Расхождение версионирования.** Major-version числа не совпадают: OpenTofu 1.7 ≠ Terraform 1.7. Сравнивай feature-by-feature, не по номеру. - **`hcp-terraform` блок в config'е.** OpenTofu понимает синтаксис, но не подключается. На миграционный период оставь как есть, не мешает. - **Tooling-расхождение.** Atlantis, Spacelift, Env0, что-то умеет `tofu`, что-то нет. Перед миграцией проверь. - **Подписи и checksum'ы провайдеров.** OpenTofu использует свои public-keys. Если в `terraform_required_providers` указан explicit hash, он может не совпасть. Решение: убрать lockfile, переинициализировать. - **Community уже разделено.** Issue в одном репозитории может не отражаться в другом. При репорте бага, оба. - **State encryption, opt-in.** Включил, все участники команды должны иметь KMS-доступ. Без KMS-decrypt никто не может работать с state'ом. Заведомо сложнее рекавери после потери ключа. ## Команды ```bash tofu --version ``` Проверь OpenTofu установку. ```bash tofu init ``` Аналог terraform init. ```bash tofu plan ``` Аналог terraform plan. State от Terraform читает. ```bash TF_PLUGIN_CACHE_DIR=~/.tf-cache tofu init ``` Cache провайдеров между tofu/terraform, экономит трафик. ## См. также - [Terraform Stacks, нативная multi-stack оркестрация](/terraform/kb/tf-stacks.md) - [State: память Terraform о созданном](/terraform/kb/tf-state.md) - [Remote state в S3: бакет, DynamoDB lock, encryption](/terraform/kb/tf-remote-backend-s3.md)