Deploy VM's with Terraform in 10min

Page content

Managing VM’s on Hetzner Cloud with Terraform

you may want to manage some vm in the cloud. webgui is nice, but a real nerd needs cli ;)

some notes how to get terraform running with OpenBSD.

add Packages (3min)

$ time doas pkg_add git gmake go terraform

3m18.62s real     0m19.53s user     0m07.73s system

set GO PATH

echo "GOPATH=$HOME/go" >> ~/.profile
echo "export GOPATH" >> ~/.profile
. ./.profile
echo $GOPATH

build terraform provider for hcloud (2min)

As the hcloud is not available for OpenBSD, we have to build it on our own.

$ echo $GOPATH
/home/stoege/go

mkdir -p $GOPATH/src/github.com/hetznercloud
cd $GOPATH/src/github.com/hetznercloud
git clone https://github.com/hetznercloud/terraform-provider-hcloud.git
cd terraform-provider-hcloud
time gmake build

1m19.51s real     0m52.93s user     0m20.08s system

Install Provider (2min)

cd ~
ll /home/stoege/go/bin/

mkdir -p .terraform.d/plugins/registry.terraform.io/hetznercloud/hcloud/1.25.1/openbsd_amd64/
cp go/bin/terraform-provider-hcloud .terraform.d/plugins/registry.terraform.io/hetznercloud/hcloud/1.25.1/openbsd_amd64/

Terraform main.tf

Here, we build two VM’s based on existing snapshots. Learn how to Bootstrap OpenBSD

-> replace “YOUR_TOKEN_YOU_HAVE_CREATED_ON_HETZNER_CLOUD” with your own token ;)

cd ~
mkdir hetzner
cd hetzner

cat << 'EOF' > main.tf
############## Variables ###############
# Token variable
variable "hcloud_token" {
default = "YOUR_TOKEN_YOU_HAVE_CREATED_ON_HETZNER_CLOUD"
}

# Define Hetzner provider
provider "hcloud" {
  token = var.hcloud_token
}

# Create an OpenBSD Server
resource "hcloud_server" "obsd" {
  name        = "obsd"
  image       = "33661841"
  server_type = "cx11"
  location    = "nbg1"
}

# Create an FreeBSD Server
resource "hcloud_server" "fbsd" {
  name        = "fbsd"
  image       = "33811022"
  server_type = "cx11"
  location    = "nbg1"
}


# Output server IPs
output "server_ip_obsd" {
 value = hcloud_server.obsd.ipv4_address
}

output "server_ip_fbsd" {
 value = hcloud_server.fbsd.ipv4_address
}
EOF

versions.tf

TF 13.0 and higher requires a version file

cat << 'EOF' > versions.tf
terraform {
  required_providers {
    hcloud = {
      source = "hetznercloud/hcloud"
    }
  }
  required_version = ">= 0.13"
}
EOF

terraform init

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/hcloud...

Error: Failed to install providers

Could not find required providers, but found possible alternatives:

  hashicorp/hcloud -> hetznercloud/hcloud

If these suggestions look correct, upgrade your configuration with the
following command:
    terraform 0.13upgrade .

terraform upgrade

$ terraform 0.13upgrade .

This command will update the configuration files in the given directory to use
the new provider source features from Terraform v0.13. It will also highlight
any providers for which the source cannot be detected, and advise how to
proceed.

Would you like to upgrade the module in the current directory?
  Only 'yes' will be accepted to confirm.

  Enter a value: yes

-----------------------------------------------------------------------------


Upgrade complete!

terraform init

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Using previously-installed hetznercloud/hcloud v1.25.1

The following providers do not have any version constraints in configuration,
so the latest version was installed.

* hetznercloud/hcloud: version = "~> 1.25.1"

Terraform has been successfully initialized!

Problems ?

SOLVED hcloud provider is missing from the terraform registry

terraform plan

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # hcloud_server.fbsd will be created
  + resource "hcloud_server" "fbsd" {
      + backup_window = (known after apply)
      + backups       = false
      + datacenter    = (known after apply)
      + id            = (known after apply)
      + image         = "33811022"
      + ipv4_address  = (known after apply)
      + ipv6_address  = (known after apply)
      + ipv6_network  = (known after apply)
      + keep_disk     = false
      + location      = "nbg1"
      + name          = "fbsd"
      + server_type   = "cx11"
      + status        = (known after apply)
    }

  # hcloud_server.obsd will be created
  + resource "hcloud_server" "obsd" {
      + backup_window = (known after apply)
      + backups       = false
      + datacenter    = (known after apply)
      + id            = (known after apply)
      + image         = "33661841"
      + ipv4_address  = (known after apply)
      + ipv6_address  = (known after apply)
      + ipv6_network  = (known after apply)
      + keep_disk     = false
      + location      = "nbg1"
      + name          = "obsd"
      + server_type   = "cx11"
      + status        = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

terraform apply (1min)

$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # hcloud_server.fbsd will be created
  + resource "hcloud_server" "fbsd" {
      + backup_window = (known after apply)
      + backups       = false
      + datacenter    = (known after apply)
      + id            = (known after apply)
      + image         = "33811022"
      + ipv4_address  = (known after apply)
      + ipv6_address  = (known after apply)
      + ipv6_network  = (known after apply)
      + keep_disk     = false
      + location      = "nbg1"
      + name          = "fbsd"
      + server_type   = "cx11"
      + status        = (known after apply)
    }

  # hcloud_server.obsd will be created
  + resource "hcloud_server" "obsd" {
      + backup_window = (known after apply)
      + backups       = false
      + datacenter    = (known after apply)
      + id            = (known after apply)
      + image         = "33661841"
      + ipv4_address  = (known after apply)
      + ipv6_address  = (known after apply)
      + ipv6_network  = (known after apply)
      + keep_disk     = false
      + location      = "nbg1"
      + name          = "obsd"
      + server_type   = "cx11"
      + status        = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

hcloud_server.fbsd: Creating...
hcloud_server.obsd: Creating...
hcloud_server.fbsd: Still creating... [10s elapsed]
hcloud_server.obsd: Still creating... [10s elapsed]
hcloud_server.fbsd: Still creating... [20s elapsed]
hcloud_server.obsd: Still creating... [20s elapsed]
hcloud_server.obsd: Still creating... [30s elapsed]
hcloud_server.fbsd: Still creating... [30s elapsed]
hcloud_server.obsd: Creation complete after 36s [id=10805887]
hcloud_server.fbsd: Still creating... [40s elapsed]
hcloud_server.fbsd: Still creating... [50s elapsed]
hcloud_server.fbsd: Creation complete after 54s [id=10805888]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

server_ip_fbsd = 157.90.230.237
server_ip_obsd = 157.90.230.238

Terraform Show

$ terraform show

# hcloud_server.fbsd:
resource "hcloud_server" "fbsd" {
    backups      = false
    datacenter   = "nbg1-dc3"
    id           = "10805888"
    image        = "33811022"
    ipv4_address = "157.90.230.237"
    ipv6_address = "2a01:4f8:1c1c:7805::1"
    ipv6_network = "2a01:4f8:1c1c:7805::/64"
    keep_disk    = false
    location     = "nbg1"
    name         = "fbsd"
    server_type  = "cx11"
    status       = "running"
}

# hcloud_server.obsd:
resource "hcloud_server" "obsd" {
    backups      = false
    datacenter   = "nbg1-dc3"
    id           = "10805887"
    image        = "33661841"
    ipv4_address = "157.90.230.238"
    ipv6_address = "2a01:4f8:1c1c:77f8::1"
    ipv6_network = "2a01:4f8:1c1c:77f8::/64"
    keep_disk    = false
    location     = "nbg1"
    name         = "obsd"
    server_type  = "cx11"
    status       = "running"
}


Outputs:

server_ip_fbsd = "157.90.230.237"
server_ip_obsd = "157.90.230.238"

Terraform Cleanup

$ terraform destroy
hcloud_server.obsd: Refreshing state... [id=10805887]
hcloud_server.fbsd: Refreshing state... [id=10805888]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # hcloud_server.fbsd will be destroyed
  - resource "hcloud_server" "fbsd" {
      - backups      = false -> null
      - datacenter   = "nbg1-dc3" -> null
      - firewall_ids = [] -> null
      - id           = "10805888" -> null
      - image        = "33811022" -> null
      - ipv4_address = "157.90.230.237" -> null
      - ipv6_address = "2a01:4f8:1c1c:7805::1" -> null
      - ipv6_network = "2a01:4f8:1c1c:7805::/64" -> null
      - keep_disk    = false -> null
      - labels       = {} -> null
      - location     = "nbg1" -> null
      - name         = "fbsd" -> null
      - server_type  = "cx11" -> null
      - status       = "running" -> null
    }

  # hcloud_server.obsd will be destroyed
  - resource "hcloud_server" "obsd" {
      - backups      = false -> null
      - datacenter   = "nbg1-dc3" -> null
      - firewall_ids = [] -> null
      - id           = "10805887" -> null
      - image        = "33661841" -> null
      - ipv4_address = "157.90.230.238" -> null
      - ipv6_address = "2a01:4f8:1c1c:77f8::1" -> null
      - ipv6_network = "2a01:4f8:1c1c:77f8::/64" -> null
      - keep_disk    = false -> null
      - labels       = {} -> null
      - location     = "nbg1" -> null
      - name         = "obsd" -> null
      - server_type  = "cx11" -> null
      - status       = "running" -> null
    }

Plan: 0 to add, 0 to change, 2 to destroy.

Changes to Outputs:
  - server_ip_fbsd = "157.90.230.237" -> null
  - server_ip_obsd = "157.90.230.238" -> null

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

hcloud_server.fbsd: Destroying... [id=10805888]
hcloud_server.obsd: Destroying... [id=10805887]
hcloud_server.fbsd: Destruction complete after 0s
hcloud_server.obsd: Destruction complete after 0s

Destroy complete! Resources: 2 destroyed.

sha256: dd6f6fd10ab3bd3bdb1637748557ea4374a69af0239c471aae471a77fece8c85