Keď sme si v článku Terraform 101 – Začíname stručne vysvetlili základy automatizácie Terraformom, mohli by sme sa vrhnúť na praktickú ukážku toho, v čom nám vie pomôcť.
V predchádzajúcom článku, sme si ukázali, ako nainštalovať vCenter server pomocou Terraformu s využitím CLI vcsa-deploy. Dnes si k inštalácii doplníme vytvorenie datacentra, clustra, adresára pre VMky, pridanie ESXi hosta do clustra a vytvorenie distribuovaného switcha.
Skončíme tak, že vmnic0 bude v štandardnom switchi a vmnic1 v distribuovanom. Čo už je solídny základ pre následnú konfiguráciu.
Úvod
V článku Terraform 101 sme si povedali, že kvôli prehľadnosti kódu, si vytvárame súborovú štruktúru ao súbormi ako providers.tf, main.tf, vars.tf, … Tieto súbory si dnes “zabalíme” ešte do adresárov a vytvoríme tak moduly.
- Modul na vytvorenie vCenter servera
- Modul na vytvorenie základných objektov
- Modul na vytvorenie vDS.
Budeme tak možnosť v prípade potreby, volať jednotlivé moduly samostatne. Pri novom deploymente, to možno využijeme iba pri prípadnom “ladení” konfigurácie vDS, ale pri následných konfiguráciách, sa nám tento návyk rozdeľovania kódu do modulov určite zíjde.
Príklad adresárovej štruktúry:
terraform/ ├── vsphere-bootstrap/ │ ├── providers.tf │ ├── main.tf │ ├── vars.tf │ ├── terraform.tfvars │ ├── secret.auto.tfvars │ └── Modules/ │ ├── basic-infra-objects/ │ ├── main.tf │ ├── vars.tf │ ├── outputs.tf │ ├── vcsa/ │ ├── Template/ │ ├── embedded_vCSA_on_ESXi.json │ ├── main.tf │ ├── vars.tf │ └── vds/ │ ├── main.tf │ ├── vars.tf
Moduly
Základné informácie k modulom:
- premenné (variables), ktoré majú byť použité v module, musia byť vždy deklarované
- ak chceme posunúť hodnotu premennej z rootovského main.tf (pri volaní modulu), musíme mať premennú deklarovanú ako v rootovskom vars.tf tak aj v (child)modulovom vars.tf
- terraform.tfvars (hodnoty premenných) sa automaticky načítava iba z rootovského adresára. Neodporúča sa teda mať terraform.tfvars v modulovom adresári a volať ho explicitne. Default hodnoty však môžu byť zadefinované vo vars.tf modulu, prípadne hodnoty premenných môžu byť zadefinované pri volaní samotného modulu.
- použitie premenej a jej hodnoty z príkazu output v jednom module, je možné použiť ako vstup do druhého modulu. Aby bola splnená 1. podmienka, táto premenná musí byť deklarovaná vo vars.tf modulu, kde bude použitá (nemusí byť deklarovaná aj v rootovskom vars.tf)
Root Module
Poďme si teda ukázať jednottlivé časti kódu.
Deklarácia premenných – vars.tf
variable "template_file_path" {
description = "JSON template file path"
type = string
}
variable "config_file_path" {
description = "vcsa configuration JSON file path"
type = string
}
variable "vcsadeploy_file_path" {
description = "command line file path"
type = string
}
variable "vcsadeploy_log_file_path" {
description = "log file path"
type = string
}
variable "esxi_IP" {
description = "IP address of ESXi"
type = string
}
variable "esxi_pass" {
description = "ESXi password"
type = string
}
variable "esxi_portgroup" {
description = "ESXi portgroup"
type = string
}
variable "esxi_datastore" {
description = "ESXi datastore"
type = string
}
variable "vc_deploy_option" {
description = "vcsa deployment option"
type = string
default = "tiny"
}
variable "vcsa_fqdn" {
description = "vcsa fqdn"
type = string
}
variable "vcsa_IP" {
description = "vcsa network IP"
type = string
}
variable "vcsa_prefix" {
description = "vcsa network refix"
type = number
}
variable "vcsa_gw" {
description = "vcsa network GW"
type = string
}
variable "dns" {
description = "dns server"
type = string
}
variable "vcsa_root_pass" {
description = "vcsa OS pass"
type = string
}
variable "vcsa_sso_pass" {
description = "administrator@vsphre.local pass"
type = string
}
variable "datacenter" {
description = "Name of new Datacenter"
type = string
}
variable "cluster" {
description = "Name of new Cluster"
type = string
}
variable "hosts" {
description = "List of ESXi hosts"
type = list(string)
}
variable "folder" {
description = "Name of new VM folder"
type = string
}
variable "esxi_root" {
description = "Root account"
type = string
default = "root"
}
variable "network_interfaces" {
description = "List of ESXi network interfaces"
type = list(string)
}
variable "vds_name" {
description = "Name of new vDS"
type = string
}
variable "vds_uplinks" {
description = "List of vDS uplinks"
type = list(string)
}
variable "vds_active_uplinks" {
description = "List of Active vDS uplinks"
type = list(string)
}
variable "management_vlan" {
description = "VLAN ID for Management port group"
type = number
Hodnoty premenných – terraform.tfvars
template_file_path = "C:\\Automation\\Terraform\\vsphere-bootstrap\\Modules\\vcsa\\Template\\embedded_vCSA_on_ESXi.json" config_file_path = "C:\\Automation\\Terraform\\vsphere-bootstrap\\Modules\\vcsa\\Template\\vcsa_embedded_vCSA_on_ESXi.json" vcsadeploy_log_file_path = "C:\\Automation\\Terraform\\vsphere-bootstrap\\Modules\\vcsa\\Template\\vcsa_embedded_vCSA_on_ESXi.log" vcsadeploy_file_path = "E:\\vcsa-cli-installer\\win32" esxi_IP = "10.10.100.50" esxi_portgroup = "sPG-100" esxi_datastore = "ds-nesx01-local" vc_deploy_option = "tiny" vcsa_fqdn = "nvcsa01.domain.local" vcsa_IP = "10.10.100.51" vcsa_prefix = "24" vcsa_gw = "10.10.100.1" dns = "10.10.100.10" datacenter = "DC-TF" cluster = "CL-TF" folder = "VM-TF" hosts = [ "nesx01.domain.local", ] network_interfaces = [ "vmnic0", "vmnic1" ] vds_name = "vDS-TF" vds_uplinks = [ "uplink2", "uplink1" ] vds_active_uplinks = [ "uplink2", "uplink1" ] management_vlan = 74
Hodnoty premenných – secret.auto.tfvars – by default terraform automaticky načítava iba hodnoty premenných zo súboru terraform.tfvars. Ak chceme, aby načítaval aj iný .tfvars súbor, potrebujeme do jeho mena zapísať “auto”. V našom prípade som si vyrobil špeciálny súbor, kvôli heslám. Ako načítať heslá z OpenBao alebo Hashicorp Vaultu, si ukážeme v samostatnom článku.
esxi_pass = "Password1" vcsa_root_pass = "Password2" vcsa_sso_pass = "Password3"
Definícia providerov – providers.tf
terraform {
required_providers {
vsphere = {
source = "hashicorp/vsphere"
}
}
}
provider "vsphere" {
user = "administrator@vsphere.local"
password = var.vcsa_sso_pass
vsphere_server = var.vcsa_fqdn
allow_unverified_ssl = true
api_timeout = 10
}
To, ako správne zapísať providera sa dozviete v jeho dokumentácii pod tlačidlom “Use Provider“. vSphere providera udržuje samotná spoločnosť Hashicorp a je dostupný na strákach https://registry.terraform.io/providers/vmware/vsphere/latest/docs.
Volanie modulov a “posúvanie premenných” – main.tf
module "Module_VCSA" {
source = "./Modules/vcsa"
template_file_path = var.template_file_path
config_file_path = var.config_file_path
vcsadeploy_log_file_path = var.vcsadeploy_log_file_path
vcsadeploy_file_path = var.vcsadeploy_file_path
esxi_IP = var.esxi_IP
esxi_pass = var.esxi_pass
esxi_portgroup = var.esxi_portgroup
esxi_datastore = var.esxi_datastore
vc_deploy_option = var.vc_deploy_option
vcsa_fqdn = var.vcsa_fqdn
vcsa_IP = var.vcsa_IP
vcsa_prefix = var.vcsa_prefix
vcsa_gw = var.vcsa_gw
dns = var.dns
vcsa_root_pass = var.vcsa_root_pass
vcsa_sso_pass = var.vcsa_sso_pass
}
module "Module_basic-infra-objects" {
source = "./Modules/basic-infra-objects"
datacenter = var.datacenter
cluster = var.cluster
folder = var.folder
hosts = var.hosts
esxi_root = var.esxi_root
esxi_pass = var.esxi_pass
}
module "Module_vds" {
source = "./Modules/vds"
vds_name = var.vds_name
network_interfaces = var.network_interfaces
vds_vmnic_devices = ["vmnic1"]
vds_uplinks = var.vds_uplinks
vds_active_uplinks = var.vds_active_uplinks
management_vlan = var.management_vlan
datacenter_id = module.Module_basic-infra-objects.vsphere_datacenter_datacenter_moid
host_ids = module.Module_basic-infra-objects.host_ids
}
Child Module – VCSA
Inštaláciu vCenter servera sme si prešli v minulom článku, takže teraz už len vypíšeme obsahy súborov potrebné pre správne spustenie kódu.
Súbor main.tf
resource "local_file" "vcsa_json" {
content = templatefile (
var.template_file_path,
{
esxi_IP = var.esxi_IP,
esxi_pass = var.esxi_pass,
esxi_portgroup = var.esxi_portgroup,
esxi_datastore = var.esxi_datastore,
vc_deploy_option = var.vc_deploy_option,
vcsa_name = element(split(".", var.vcsa_fqdn),0),
vcsa_fqdn = var.vcsa_fqdn,
vcsa_IP = var.vcsa_IP,
vcsa_prefix = var.vcsa_prefix,
vcsa_gw = var.vcsa_gw,
dns = var.dns,
vcsa_root_pass = var.vcsa_root_pass,
vcsa_sso_pass = var.vcsa_sso_pass
}
)
filename = var.config_file_path
}
resource "null_resource" "vcsa_install" {
provisioner "local-exec" {
command = "${var.vcsadeploy_file_path}\\vcsa-deploy install --accept-eula --acknowledge-ceip --no-esx-ssl-verify --log-dir=${var.vcsadeploy_log_file_path} ${var.config_file_path}"
}
}
Deklarácia premenných – vars.tf
variable "template_file_path" {
description = "JSON template file path"
type = string
}
variable "config_file_path" {
description = "vcsa configuration JSON file path"
type = string
}
variable "vcsadeploy_file_path" {
description = "command line file path"
type = string
}
variable "vcsadeploy_log_file_path" {
description = "log file path"
type = string
}
variable "esxi_IP" {
description = "IP address of ESXi"
type = string
}
variable "esxi_pass" {
description = "ESXi password"
type = string
}
variable "esxi_portgroup" {
description = "ESXi portgroup"
type = string
}
variable "esxi_datastore" {
description = "ESXi datastore"
type = string
}
variable "vc_deploy_option" {
description = "vcsa deployment option"
type = string
default = "tiny"
}
variable "vcsa_fqdn" {
description = "vcsa fqdn"
type = string
}
variable "vcsa_IP" {
description = "vcsa network IP"
type = string
}
variable "vcsa_prefix" {
description = "vcsa network refix"
type = number
}
variable "vcsa_gw" {
description = "vcsa network GW"
type = string
}
variable "dns" {
description = "dns server"
type = string
}
variable "vcsa_root_pass" {
description = "vcsa OS pass"
type = string
}
variable "vcsa_sso_pass" {
description = "administrator@vsphre.local pass"
type = string
}
Child Module – Basic-Infra-Objects
Tento module vyrobí objekt Datacenter, v ňom objekt Cluster, objekt Folder pre ukladanie VMiek a zaradí do klástra všetky ESXi hosty, ktoré máme zadefinované.
Všeobecná informácia: Všetky nastavenia použitých “Resource” sa nastavia podľa predvolených hodnôt alebo tak, ako to explicitne zadefinujeme. Takže v “Resource” blokoch definujeme iba to, čo chceme zmeniť oproti predvoleným hodnotám (alebo si nie sme istí, aka hodnota je predvolená). Všetky hodnoty, ktoré Terraform bude nastavovať sa zobrazia po spustení príkazu: terraform plan
main.tf
resource "vsphere_datacenter" "datacenter" {
name = var.datacenter
}
resource "vsphere_compute_cluster" "compute_cluster" {
name = var.cluster
datacenter_id = vsphere_datacenter.datacenter.moid
drs_enabled = true
drs_automation_level = "fullyAutomated"
ha_enabled = true
}
data "vsphere_host_thumbprint" "thumbprint" {
for_each = toset(var.hosts)
address = each.value
insecure = true
}
resource "vsphere_host" "host" {
for_each = toset(var.hosts)
hostname = each.value
username = var.esxi_root
password = var.esxi_pass
thumbprint = data.vsphere_host_thumbprint.thumbprint[each.value].id
cluster = vsphere_compute_cluster.compute_cluster.id
}
resource "vsphere_folder" "folder" {
path = var.folder
type = "vm"
datacenter_id = vsphere_datacenter.datacenter.moid
}
vars.tf
variable "datacenter" {
type = string
}
variable "cluster" {
type = string
}
variable "hosts" {
type = list(string)
}
variable "folder" {
type = string
}
variable "esxi_root" {
type = string
}
variable "esxi_pass" {
type = string
}
Výstupy – outputs.tf – keďže ID Datacentra a ID ESXi hostov, budeme potrebovať pre vytvorenie distribuovaného switcha, ich hodnoty si necháme vygenerovať a uložiť.
output "vsphere_datacenter_datacenter_moid" {
value = vsphere_datacenter.datacenter.moid
}
output "host_ids" {
value = {
for hname, h in vsphere_host.host :
hname => h.id
}
}
Vysvetlenie zápisu value v host_ids si vysvetlíme inokedy. Teraz nám stačí vedieť, že si týmto vyrobíme “mapu” vyzerajúcu nasledovne:
{
"nesx01.psguy.local" = "host-93"
"nesx02.psguy.local" = "host-94"
}
Child Module – vDS
Tento posledný modul nám
- vyrobí distribuovaný switch s dvoma uplinkami (obe nastavené ako aktívne)
- priradí vmnic1 ESXi hostu ako uplink2 vDS switcha
- vyrobí port groupu s názvom “dPG-MGMT” s aktívnym uplinkom uplink2
main.tf
resource "vsphere_distributed_virtual_switch" "vDS" {
name = var.vds_name
datacenter_id = var.datacenter_id
uplinks = var.vds_uplinks
active_uplinks = var.vds_active_uplinks
dynamic "host" {
for_each = var.host_ids
content {
host_system_id = host.value
devices = var.vds_vmnic_devices
}
}
}
resource "vsphere_distributed_port_group" "management" {
name = "dPG-MGMT"
distributed_virtual_switch_uuid = vsphere_distributed_virtual_switch.vDS.id
vlan_id = var.management_vlan
type = "earlyBinding"
active_uplinks = ["uplink2"]
security_policy_override_allowed = false
block_override_allowed = false
live_port_moving_allowed = true
}
To, aby terraform priradil vmnic1 uplinku uplink2, sme docielili zapísaním poradia uplinkov v premenných vds_uplinks a vds_active_uplinks a zároveň zapísaním hodnoty premennej vds_vmnic_devices pri volaní modulu z main.tf v rootovskom module.
vars.tf
variable "network_interfaces" {
type = list(string)
}
variable "host_ids" {
type = map(string)
}
variable "vds_vmnic_devices" {
type = list(string)
}
variable "vds_name" {
type = string
}
variable "datacenter_id" {
type = string
}
variable "vds_uplinks" {
type = list(string)
}
variable "vds_active_uplinks" {
type = list(string)
}
variable "management_vlan" {
type = number
}
outputs.tf
output "vsphere_distributed_virtual_switch_vDS_id" {
value = vsphere_distributed_virtual_switch.vDS.id
}
Po aplikovaní tohto modulu, by sme mohli presunúť VMkernel port zo štandardného switcha na distribuovaný. Najjednoduchšie je to urobiť ručne. Ako to urobiť terraformom, si ukážeme v niektorom z nasledujúcich článkov.
init, plan, apply
Teraz nám už ostáva iba
- zinicializovať adresár s našim projektom – vsphere-boostrap
- spustiť plan
- spustiť apply
Ak by sme chceli destroynuť iba zmeny, ktoré vykonal konkrétny modul, urobíme to príkazom:
terraform.exe destroy -target="module.Module_vds"
V prípade, že chceme znova spustiť konkrétny modul, vymeníme destroy za apply.
Upozornenie: Destroy modulu, ktorý inštaluje vCenter server, vykoná iba zmazanie podkladového súboru vcsa_embedded_vCSA_on_ESXi.json nie samotného vCenter servera. Terraform totiž iba volá CLI command vcsa-deploy. Po destroy zmien vykonaných v tomto module a ich následnom opätovnom aplikovaní, Terraform zavolá vcsa-deploy a ten sa bude snažiť opäť nainštalovať vCenter server s rovnakými parametrami.
Záver
Pevne verím, že čím viac príkladov použitia Terraformu si ukazejeme, tým viac sa vám začína páčiť. V nasledujúcom článku, si vyrobíme samostatné moduly pre vytvorenie nových distribuovaných port group, nových VMiek a podobne.













