doc: добавление документации по созданию вм через tf

This commit is contained in:
2026-02-03 13:59:34 +03:00
parent d175ee9238
commit db37cf8832
11 changed files with 409 additions and 5 deletions
+36
View File
@@ -0,0 +1,36 @@
# ---> Terraform
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
# *.tfvars
*.tfvars.json
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc
.terraform.lock.hcl
+120
View File
@@ -0,0 +1,120 @@
# Развёртывание ВМ в VMware Cloud Director через tf
Используется **OpenTofu** (`tofu`). Конфигурация создаёт виртуальные машины в vCD: можно задать несколько ВМ, размеры CPU/RAM/дисков и сеть.
---
## Быстрый старт
1. **Установите OpenTofu** ([opentofu.org](https://opentofu.org)).
2. **Задайте переменные окружения** (доступ к vCD):
```bash
export TF_VAR_vmware_username="ваш_пользователь_vCD"
export TF_VAR_vmware_password="ваш_пароль_vCD"
```
Пароль администратора ВМ можно не задавать — тогда он сгенерируется автоматически (см. `tools.tf`). Чтобы задать свой: `export TF_VAR_admin_password="ваш_пароль"`.
3. **Отредактируйте `terraform.tfvars`** — укажите нужный vDC, имя vApp, параметры ВМ (см. раздел «Конфигурация» ниже).
4. **Разверните инфраструктуру:**
```bash
tofu init
tofu plan
tofu apply
```
---
## Конфигурация
### Переменные окружения
Обязательные для доступа к vCD:
- `TF_VAR_vmware_username` — пользователь VMware Cloud Director
- `TF_VAR_vmware_password` — пароль VMware Cloud Director
Опционально: `TF_VAR_admin_password` — пароль администратора на ВМ (если не задан, генерируется автоматически).
```
vcds = {
"vdcconfiguration1" = {
url = "https://ngcloud.ru/api"
org = "dev"
vdc = "dev"
network_routed = "dev-mgmt-network"
servers = {
"vappname01" = {
"vmname01" = {
cpu = "4"
memory = "4096"
osDisk = "16384"
dataDisk = {
"1" = "30720"
"2" = "15360"
}
network = {
mode = "auto"
ip = ""
}
catalog_name = "dev"
template_name = "RockyLinux_9-16G-customize"
}
}
}
}
}
```
Переменная задаётся в `terraform.tfvars`; схема — в [var.tf](var.tf), использование — [provider.tf](provider.tf), [vapp.tf](vapp.tf), [vm.tf](vm.tf), [local.tf](local.tf).
*** Ключ vcds — произвольное имя контура (можно несколько vDC в одной конфигурации) ***
```
vcds = {
"test" = {
...
}
}
```
*** Настройка vDC ***
```
url = URL API Cloud Director (https://<хост>/api)
org = имя организации
vdc = имя vDC
network_routed = имя routed-сети, куда подключаются ВМ
```
*** Настройка vApp (ключи внутри servers — имена vApp) ***
```
servers = {
"testservers" = { ... } # первый vApp
"testvapp2" = { ... } # второй vApp
}
```
*** Настройка сервера (параметры ВМ) ***
```
"testservers" = {
"test01" = {
cpu = кол-во ядер CPU
memory = объём RAM (МБ)
osDisk = размер системного диска (МБ)
dataDisk = { # ключ — unit, значение — размер в МБ
"1" = "30720"
"2" = "15360"
}
network = { # mode "auto" (IP из пула) или "manual" (указать ip)
mode = "auto"
ip = ""
}
network = mode "auto" (IP из пула) или "manual" (указать ip)
mode = "auto"
ip = ""
catalog_name = каталог с шаблоном
template_name = имя шаблона образа
# опционально (cloud-init):
init_username = "deploy"
ssh_authorized_keys = ["ssh-ed25519 AAAA..."]
}
```
+39
View File
@@ -0,0 +1,39 @@
locals {
#######################
admin_password = var.admin_password != "" ? var.admin_password : random_password.admin_pass.result
#######################
# Получаем объект с ключами в виде имен серверов и содержимым всего,
# что мы хотим видеть при передаче из vars
servers = merge([
for vcd_key, vcd_config in var.vcds :
merge([
for server_group, servers in vcd_config.servers :
{
for server_name, server_config in servers :
"${server_name}.${server_group}" => {
vcd_key = vcd_key
server_group = server_group
server_name = server_name
config = server_config
network_routed = vcd_config.network_routed
role = server_group
}
}
]...)
]...)
# Получаем объект с ключами в виде сервера-uidдиска,
# и содержимым в виде его сайзинга
disks = merge([
for serverName, serverConfig in local.servers:
{
for diskUid, diskSize in serverConfig.config.dataDisk:
"${serverName}-${diskUid}" => {
vcd_key = serverConfig.vcd_key
vm_name = serverName
unit_num = diskUid
disk_size = diskSize
}
}
]...)
}
+37
View File
@@ -0,0 +1,37 @@
terraform {
required_providers {
vcd = {
source = "vmware/vcd"
version = "3.14.1"
}
random = {
source = "hashicorp/random"
version = "3.5.1"
}
local = {
source = "hashicorp/local"
version = "2.5.1"
}
}
}
# Почему такая конфигурация не будет работать
# https://github.com/hashicorp/terraform/issues/24476
#
# Но решили тут
# https://github.com/opentofu/opentofu/issues/2123
provider "vcd" {
alias = "al"
for_each = var.vcds
user = var.vmware_username
password = var.vmware_password
org = each.value.org
vdc = each.value.vdc
url = each.value.url
max_retry_timeout = "10"
allow_unverified_ssl = "true"
}
+22
View File
@@ -0,0 +1,22 @@
#cloud-config
hostname: ${hostname}
package_update: true
package_upgrade: false
packages:
- curl
- git
- jq
users:
- name: ${username}
sudo: "ALL=(ALL) NOPASSWD:ALL"
groups: "sudo"
shell: /bin/bash
ssh_authorized_keys:
%{ if length(ssh_authorized_keys) > 0 ~}
%{ for key in ssh_authorized_keys ~}
- ${key}
%{ endfor ~}
%{ else ~}
[]
%{ endif ~}
+39
View File
@@ -0,0 +1,39 @@
#######################################################
# Импортируемые переменные из TF_VAR
# vmware_username = ""
# vmware_password = ""
#######################################################
vcds = {
"test" = {
url = "https://sandbox.nubes.ru/api"
org = "dev"
vdc = "dev"
network_routed = "dev-mgmt-network"
servers = {
"testservers" = {
"test01" = {
cpu = "1"
memory = "1024"
osDisk = "35000"
dataDisk = {
"1" = "10000"
"2" = "5480"
}
network = {
mode = "auto"
ip = ""
}
catalog_name = "dev"
template_name = "Ubuntu_24-20G"
init_username = "deploy"
ssh_authorized_keys = [
"ssh-ed25519 *********"
]
}
}
}
}
}
+5
View File
@@ -0,0 +1,5 @@
# Генерируем пароль для админского пользователя
resource "random_password" "admin_pass" {
length = 20
special = false
}
+19
View File
@@ -0,0 +1,19 @@
resource "vcd_vapp" "vapp" {
for_each = var.vcds
provider = vcd.al[each.key]
name = each.key
power_on = false
}
resource "vcd_vapp_org_network" "routed_network" {
for_each = vcd_vapp.vapp
provider = vcd.al[each.key]
vapp_name = each.value.name
org_network_name = var.vcds[each.key].network_routed
reboot_vapp_on_removal = true
}
+42
View File
@@ -0,0 +1,42 @@
####################################################
variable "vmware_username" {
type = string
default = ""
}
variable "vmware_password" {
type = string
default = ""
}
####################################################
# Пароль от ВМ (если пустой — подставляется сгенерированный из tools.tf)
variable "admin_password" {
type = string
default = ""
}
####################################################
variable "vcds" {
type = map(object({
url = string
org = string
vdc = string
network_routed = string
servers = map(map(object({
cpu = string
memory = string
osDisk = string
catalog_name = string
template_name = string
dataDisk = map(string)
network = object({
mode = string
ip = string
})
# cloud-init: пользователь и SSH-ключи (опционально)
init_username = optional(string, "deploy")
ssh_authorized_keys = optional(list(string), [])
})))
}))
}
####################################################
+84
View File
@@ -0,0 +1,84 @@
resource "vcd_vapp_vm" "vm" {
for_each = local.servers
provider = vcd.al[each.value.vcd_key]
vapp_name = vcd_vapp.vapp[each.value.vcd_key].name
name = each.key
computer_name = each.key
catalog_name = each.value.config["catalog_name"]
template_name = each.value.config["template_name"]
memory = each.value.config["memory"]
cpus = each.value.config["cpu"]
network {
type = "org"
name = each.value.network_routed
ip_allocation_mode = lookup(each.value.config["network"], "mode", "auto") == "manual" ? "MANUAL" : "POOL"
is_primary = true
# Устанавливаем IP, только если режим "manual"
ip = lookup(each.value.config["network"], "mode", "auto") == "manual" ? lookup(each.value.config["network"], "ip", null) : null
}
override_template_disk {
bus_type = "paravirtual"
size_in_mb = each.value.config["osDisk"]
bus_number = 0
unit_number = 0
}
customization {
enabled = true
#force = var.forceRecustomization || !contains(var.servers, var.servers[count.index])
#force = var.forceRecustomization
force = false
allow_local_admin_password = true
auto_generate_password = false
admin_password = local.admin_password
}
set_extra_config {
key = "disk.EnableUUID"
value = "TRUE"
}
guest_properties = {
"user-data" = base64encode(templatefile("${path.module}/templates/cloudinit.yaml", {
hostname = each.key
username = each.value.config["init_username"]
ssh_authorized_keys = each.value.config["ssh_authorized_keys"]
}))
}
# Игноирование изменений дисков (PVC - Named Disks)
lifecycle {
ignore_changes = [
disk, # Игнорирует изменения всех дисков
]
}
depends_on = [
vcd_vapp_org_network.routed_network
]
}
resource "vcd_vm_internal_disk" "vmdisk" {
for_each = local.disks
provider = vcd.al[each.value.vcd_key]
vapp_name = vcd_vapp.vapp[each.value.vcd_key].name
vm_name = each.value.vm_name
bus_type = "paravirtual"
bus_number = 0
unit_number = each.value.unit_num
size_in_mb = each.value.disk_size
allow_vm_reboot = true
depends_on = [
vcd_vapp_vm.vm
]
}