Skip to content

Manage VMs with Terraform

Terraform can create, resize and delete VMs through the console's IaC API (/api/iac/vms) over HTTPS. Nothing is installed on the console or the hosts. Terraform authenticates with an API key.

Before you start

  • Terraform 1.3 or later, on a machine that can reach the console's URL.
  • An API key with Full scope, owned by a user who can manage VMs (for example Super Admin, Tenant Admin or Compute Admin). Use an expiry, and treat the key like a password.
  • A template that already exists in the Content Library, for example rocky9.

Configuration

This uses the community Mastercard/restapi provider, so there's nothing to build.

providers.tf:

terraform {
  required_version = ">= 1.3"
  required_providers {
    restapi = {
      source  = "Mastercard/restapi"
      version = "~> 1.20"
    }
  }
}

provider "restapi" {
  uri                  = var.mfcloud_url
  insecure             = var.mfcloud_insecure
  write_returns_object = true
  id_attribute         = "vm_name"

  headers = {
    X-API-Key    = var.mfcloud_api_key
    Content-Type = "application/json"
  }

  create_method  = "POST"
  update_method  = "PUT"
  destroy_method = "DELETE"
}

variables.tf:

variable "mfcloud_url" {
  type        = string
  description = "Console URL, e.g. https://console.example.com"
}

variable "mfcloud_api_key" {
  type      = string
  sensitive = true
}

variable "mfcloud_insecure" {
  type    = bool
  default = false # true only for a self-signed console certificate
}

variable "vms" {
  type = map(object({
    template_name = string
    vcpus         = number
    memory_mb     = number
    disk_gb       = optional(number, 20)
    target_host   = optional(string, "auto")
    network_name  = optional(string, "default")
    os_type       = optional(string, "linux") # linux | windows
  }))
  default = {}
}

main.tf:

resource "restapi_object" "vm" {
  for_each     = var.vms
  path         = "/api/iac/vms"
  id_attribute = "vm_name"

  data = jsonencode({
    vm_name       = each.key
    template_name = each.value.template_name
    vcpus         = each.value.vcpus
    memory_mb     = each.value.memory_mb
    disk_gb       = each.value.disk_gb
    target_host   = each.value.target_host
    network_name  = each.value.network_name
    os_type       = each.value.os_type
  })
}

output "vms" {
  value = { for name, obj in restapi_object.vm : name => jsondecode(obj.api_response) }
}

terraform.tfvars (keep it out of version control):

mfcloud_url     = "https://console.example.com"
mfcloud_api_key = "paste-your-key"

vms = {
  "tf-web-01" = { template_name = "rocky9", vcpus = 2, memory_mb = 2048, disk_gb = 30 }
  "tf-web-02" = { template_name = "rocky9", vcpus = 2, memory_mb = 2048, disk_gb = 30 }
}

Run it

terraform init
terraform plan
terraform apply
terraform output vms    # placement, sizing and guest IP as the console reports them

terraform destroy deletes the VMs.

Things to know

  • Create is asynchronous. apply returns once the console has accepted and recorded the VM; the guest finishes building in the background. Watch Recent Tasks in the console. The guest IP appears in terraform output once the guest agent reports it.
  • Placement. target_host = "auto" lets the console place the VM, the same as Automatic in the deploy dialog. Set a host IP to pin it.
  • Changed in place: vcpus, memory_mb, network_name.
  • Needs a replace: template_name, disk_gb, target_host, os_type. The console ignores a change to one of these on an existing VM, so replace the VM explicitly:

    terraform apply -replace='restapi_object.vm["tf-web-01"]'
    
  • Quotas and protection apply. Creates go through the same quota, tenant limit and license checks as the console. A VM with delete protection on refuses destroy.

  • State. For team use, keep state in a remote backend (S3, Terraform Cloud and so on), not on a laptop.

See also