# VM Resource Best Practices ## Критичные параметры для устойчивого создания VM ### 1. JSON Параметры - ОБЯЗАТЕЛЬНО использовать `jsonencode()` **❌ НЕПРАВИЛЬНО:** ```text resource "nubes_vm" "web" { access_port_list = "22,80,443" # ОШИБКА: не JSON! access_ip_list = "" # ОШИБКА: пустая строка! } ``` **✅ ПРАВИЛЬНО:** ```text resource "nubes_vm" "web" { access_port_list = jsonencode(["22", "80", "443"]) access_ip_list = jsonencode(["1.2.3.4", "10.0.0.0/8"]) # Если нужен доступ отовсюду - не указывайте параметр: # access_ip_list не указан = доступ с 0.0.0.0/0 } ``` ### 2. Валидация на этапе plan Провайдер автоматически проверит: - ✅ `access_port_list` - валидный JSON массив - ✅ `access_ip_list` - валидный JSON массив (если указан) - ✅ `vm_cpu`, `vm_ram`, `vm_disk` >= 1 - ✅ `image_vm` - один из разрешённых образов **Ошибка при некорректном формате:** ``` Error: Invalid JSON Array │ │ with nubes_vm.web, │ on main.tf line 10: │ 10: access_port_list = "22,80" │ │ Value must be a valid JSON array: invalid character ',' after top-level value ``` ### 3. Default значения | Параметр | Default | Поведение | |----------|---------|-----------| | `access_ip_list` | `["0.0.0.0/0"]` | Если не указан или пустой - доступ отовсюду | | `deletion_protection` | `true` | При `terraform destroy` ресурс удаляется только из state, не из облака | ### 4. Специальные значения #### `ip_space_name` - `"no-needed"` - не выделять внешний IP - Любое другое значение - имя ipSpace для публикации VIP #### `access_ip_list` - **Не указан** → доступ с 0.0.0.0/0 - `jsonencode(["1.2.3.4"])` → доступ только с указанного IP - `jsonencode(["10.0.0.0/8", "192.168.0.0/16"])` → доступ с нескольких подсетей ### 5. Lifecycle операции #### Create 1. Создаётся instance в API 2. Создаётся operation "create" 3. Отправляются все параметры (включая defaults) 4. **Важно**: Если `access_ip_list` не задан, отправляется `["0.0.0.0/0"]` 5. Запускается выполнение через `/run` 6. Polling до завершения (статус `running`) #### Update (Modify) Модифицируемые поля: - `vm_cpu` - можно увеличивать/уменьшать - `vm_ram` - можно увеличивать/уменьшать - `vm_disk` - **только увеличение!** (нельзя уменьшить) - `ip_space_name` - изменение внешнего IP - `access_ip_list` - изменение белого списка - `access_port_list` - изменение портов - `need_add_zabbix_template` - включить/выключить мониторинг #### Delete - **С `deletion_protection = true`** (default): Ресурс удаляется из Terraform state, остаётся в облаке - **С `deletion_protection = false`**: Отправляется операция `suspend` (карантин 14 дней), затем ресурс удаляется из state ### 6. Известные ловушки (из HAR анализа) #### Проблема: VM создалась, но FW зависает **Причина**: Пустая строка в `accessIpList` или `accessPortList` **Решение**: Провайдер теперь автоматически заменяет: - Пустой `access_ip_list` → `["0.0.0.0/0"]` - Пустой `vm_disk` → параметр не отправляется #### Проблема: ColdFusion Type Mismatch Error **Причина**: Сервер получил массив вместо GUID или наоборот **Решение**: Провайдер использует динамическое мапирование параметров по `Code` (как в Postgres ресурсе) ### 7. Debugging При проблемах с созданием VM проверьте: 1. **Terraform logs** (уровень TRACE): ```bash TF_LOG=TRACE terraform apply ``` 2. **Параметры операции** в выводе: ``` submitVMOperationParams: sending accessIpList (ID 413) = ["0.0.0.0/0"] ``` 3. **Статус операции** в Read: ``` Operation Status: isSuccessful=false, isInProgress=true Stages: [{"stage":"Добавление правил FW","isSuccessful":false}] ``` ### 8. Пример корректной конфигурации ```text resource "nubes_vm" "app_server" { display_name = "app-prod-01" description = "Application server" vapp_uid = nubes_vapp.my_app.id vm_name = "app-prod-01" # Ресурсы (обязательно >= 1) vm_cpu = 2 vm_ram = 4 vm_disk = 50 # GB, можно только увеличивать # Образ (проверяется валидатором) image_vm = "Ubuntu_22-20G" # SSH доступ user_login = "ubuntu" user_public_key = file("~/.ssh/id_ed25519.pub") # Firewall (JSON массивы!) access_port_list = jsonencode(["22", "80", "443"]) access_ip_list = jsonencode(["1.2.3.4/32", "10.0.0.0/8"]) # Внешний IP ip_space_name = "k8s-3.ext.nubes.ru" # или "no-needed" # Мониторинг need_add_zabbix_template = true # Cloud-init (опционально) cloud_init = <<-EOT #cloud-config packages: - nginx runcmd: - systemctl start nginx EOT # Защита от удаления deletion_protection = true # default, можно не указывать } ``` ## Миграция с предыдущих версий Если у вас есть конфигурации без JSON валидации: 1. **Проверьте все `access_port_list` и `access_ip_list`** 2. **Оберните в `jsonencode()`** 3. **Запустите `terraform plan`** - валидаторы покажут ошибки 4. **Исправьте до применения** Провайдер версии ≥ v1.1.0 отклонит некорректные конфигурации на этапе plan, что **предотвратит зависание на FW правилах**.