dmacvicar / terraform-provider-libvirt

Terraform provider to provision infrastructure with Linux's KVM using libvirt
Apache License 2.0
1.54k stars 457 forks source link

Automatically start or poweroff machine if `running` is changed during func resourceLibvirtDomainUpdate #1010

Open ghuntley opened 1 year ago

ghuntley commented 1 year ago

Hey @dmacvicar!

It looks like the status change of running doesn't result in the machine being shut down (gracefully) or started up.

https://github.com/dmacvicar/terraform-provider-libvirt/blob/9260f4ce9ba2e24e98c4b7970f85790c5436cf7d/libvirt/resource_libvirt_domain.go#L685

I'm opening up this GitHub issue to discuss behaviour before opening the pull request and to seek feedback!

func resourceLibvirtDomainUpdate {

// add this
    if d.HasChange("running") {
        if true -> issue virConn.StartMachine.
        if false -> issue virConn.PowerOffMachine (should it be a soft shutdown or a forced power off?)
        if _ -> unknown... do what?
    }

use case

Via coder_workspace (https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace) when Coder creates a machine then the agent sets the value to "start". When Coder is stopping the machine (it's an tf apply/update operation) it sets the value to other things.

What I'd like to do is to listen to this and when running = false the machine is turned off else turned on.

  running = data.coder_workspace.me.transition == "start" ? true : false

The value of running is being flipped between true/false as needed by Coder but the logic of what to do with this is missing in the provider thus a machine remains running within kvm.

main.tf

terraform {
  required_providers {
    coder = {
      source  = "coder/coder"
      version = "~> 0.6.17"
    }
    libvirt = {
      source  = "dmacvicar/libvirt"
      version = "~> 0.7.1"
    }
  }
}

resource "libvirt_cloudinit_disk" "init" {
  name           = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}.init.iso"
  meta_data      = <<EOF

  $env:CODER_AGENT_TOKEN="${coder_agent.main.token}"

  ${coder_agent.main.init_script}
  EOF

  pool           = "default"

  provisioner "local-exec" {
    # mkfs.iso is used to generate the ISO image but isn't installed by
    # default in the Coder image so we need to install it
    command = "apk add cdrkit"
  }
}

# instance the provider
provider "libvirt" {
  uri = "qemu:///system"
}

data "coder_workspace" "me" {
}

# We fetch the latest ubuntu release image from their mirrors
resource "libvirt_volume" "qcow2" {
  name   = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}.qcow2"
  pool   = "default"
  source = "/var/lib/libvirt/images/win11-template.qcow2"
  format = "qcow2"
}

data "coder_parameter" "admin_password" {
  name = "Administrator password for logging in via RDP"
  description = "Must meet Windows password complexity requirements: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/password-must-meet-complexity-requirements#reference"
  default = "Hunter2!Hunter2"
  mutable = true
}

# Create the machine
resource "libvirt_domain" "coder" {
  name   = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
  memory = "8096"

  # count = data.coder_workspace.me.transition == "start" ? 1 : 0
  running = data.coder_workspace.me.transition == "start" ? true : false

  cpu {
    mode = "host-passthrough"
  }

  firmware = "/run/libvirt/nix-ovmf/OVMF_CODE.fd"
  nvram {
    file = "/var/lib/libvirt/qemu/nvram/win11_VARS.fd"
  }

  tpm {
    backend_type    = "emulator"
    backend_version = "2.0"
  }

  network_interface {
    network_name = "default"
  }

  cloudinit = libvirt_cloudinit_disk.init.id

  disk {
    volume_id = libvirt_volume.qcow2.id
  }

  graphics {
    type        = "spice"
    listen_type = "address"
    autoport    = true
  }
}

resource "coder_agent" "main" {
  arch = "amd64"
  os   = "windows"

  login_before_ready = false

  startup_script = <<EOF
    # Set admin password and enable admin user (must be in this order)
    Get-LocalUser -Name "Administrator" | Set-LocalUser -Password (ConvertTo-SecureString -AsPlainText "${data.coder_parameter.admin_password.value}" -Force)
    Get-LocalUser -Name "Administrator" | Enable-LocalUser

    # Enable auto login
    Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultUserName" -Value "Administrator"
    Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultPassword" -Value "${data.coder_parameter.admin_password.value}"
    Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "AutoAdminLogon" -Value 1

    Rename-Computer -NewName "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"

    # Enable RDP
    Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -name "fDenyTSConnections" -value 0

    # Enable RDP through Windows Firewall
    Enable-NetFirewallRule -DisplayGroup "Remote Desktop"

    choco feature enable -n=allowGlobalConfirmation
EOF

}
luc-phan commented 9 months ago

Possible duplicate of #622.