Working with Terraform

Figure 978. What's it all about? Slide presentation

Quote:

Terraform is an infrastructure as code tool that lets you build, change, and version cloud and on-prem resources safely and efficiently.


Figure 979. Terraform resources Slide presentation

Figure 980. Hetzner API token Slide presentation
  • Access you cloud project using the Hetzner GUI interface.

  • Go to Security --> API Tokens --> Generate API token

  • Provide a name and hit Generate API token.

  • Copy the generated token's value and store it in a secure location e.g. a password manager.

    Caution

    The Hetzner GUI blocks future access to the token.


Figure 981. Minimal Terraform configuration Slide presentation
# Define Hetzner cloud provider
terraform {
  required_providers {
    hcloud = {
      source = "hetznercloud/hcloud"
    }
  }
  required_version = ">= 0.13"
}

# Configure the Hetzner Cloud API token
provider "hcloud" {
  token = "your_api_token_goes_here"
}

# Create a server
resource "hcloud_server" "helloServer" {
  name         = "hello"
  image        =  "debian-12"
  server_type  =  "cx11"   
}

Figure 982. Terraform init Slide presentation
$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hetznercloud/hcloud...
- Installing hetznercloud/hcloud v1.46.1...
- Installed hetznercloud/hcloud v1.46.1 (signed by a HashiCorp partner, key ID 5219EACB3A77198B)

...
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above.  ...

Figure 983. Terraform plan Slide presentation
$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions ...
  + create

Terraform will perform the following actions:

  # hcloud_server.helloServer will be created
  + resource "hcloud_server" "helloServer" {
      + allow_deprecated_images    = false
      + backup_window              = (known after apply)
  ...
    }

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

Figure 984. Terraform apply Slide presentation
$ terraform apply
...
Plan: 1 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.helloServer: Creating...
hcloud_server.helloServer: Still creating... [10s elapsed]
hcloud_server.helloServer: Creation complete after 14s [id=45822789]

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

Figure 985. Credentials by E-Mail Slide presentation
Your server "hello" was created!

You can access your server with the following credentials:
 
IPv4	128.140.108.60
IPv6	2a01:4f8:1c1c:8e3a::/64
User	root
Password	rJ3pNvJXbqMp3XNTvFdq

You will be prompted to change your password on your first login.

To improve security, we recommend that you add an SSH key when creating a server.

Figure 986. Problems: 😟 Slide presentation
  • Firewall blocks ssh server access:

    $ ssh root@128.140.108.60
    ssh: connect to host 128.140.108.60 port 22: Connection refused

    Access by Vnc console login only

  • IP and (initial) credentials by email 😱

Solution:

  1. Add firewall inbound ssh access rule.

  2. Configure ssh public key login.


Figure 987. ssh access, firewall Slide presentation
resource "hcloud_firewall" "sshFw" {
  name = "ssh-firewall"
  rule {
    direction = "in"
    protocol  = "tcp"
    port      = "22"
    source_ips = ["0.0.0.0/0", "::/0"]
  }
}
              ...
resource "hcloud_server" "helloServer" {
              ...
  firewall_ids = [hcloud_firewall.sshFw.id]
}

Figure 988. ssh access, public key Slide presentation
resource "hcloud_ssh_key" "loginUser" {
  name       = "goik@hdm-stuttgart.de"
  public_key = file("~/.ssh/id_ed25519.pub")
}
              ...
resource "hcloud_server" "helloServer" {
              ...
  ssh_keys     = [hcloud_ssh_key.loginUser.id]
}

Figure 989. Apply ssh key access Slide presentation
$ terraform apply

  # hcloud_firewall.sshFw will be created
  + resource "hcloud_firewall" "sshFw" {
       ...
  # hcloud_server.helloServer will be created
  + resource "hcloud_server" "helloServer" {
       ...
  # hcloud_ssh_key.goik will be created
  + resource "hcloud_ssh_key" "loginUser" {
       ...
Plan: 3 to add, 0 to change, 0 to destroy.
       ...
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Figure 990. Output data details #1/2 Slide presentation

See terraform output documentation:

File outputs.tf Result
output "hello_ip_addr" {
  value       = hcloud_server.helloServer.ipv4_address
  description = "The server's IPv4 address"
}

output "hello_datacenter" {
  value       = hcloud_server.helloServer.datacenter
  description = "The server's datacenter"
}
$ terraform output
hello_datacenter = "nbg1-dc3"
hello_ip_addr = "159.69.152.37"
$ terraform output hello_ip_addr
"159.69.152.37"

Figure 991. Output data details #2/2 Slide presentation
File outputs.tf terraform output -json
output "hello_ip_addr" {
  value       = hcloud_server.helloServer.ipv4_address
  description = "The server's IPv4 address"
}

output "hello_datacenter" {
  value       = hcloud_server.helloServer.datacenter
  description = "The server's datacenter"
}
{
  "hello_datacenter": {
    "sensitive": false,
    "type": "string",
    "value": "nbg1-dc3"
  },
  "hello_ip_addr": {
    "sensitive": false,
    "type": "string",
    "value": "159.69.152.37"
  }
}

Figure 992. Problem 2: VCS and visible provider API token 😱 Slide presentation

Versioned file main.tf:

...
provider "hcloud" { token = "xdaGfz9LmwO8SWkg ... "}
...

Solution:

  • Declare a variable hcloud_token in a variables.tf file

  • Add a non-versioned file secrets.auto.tfvars.

  • Optional: Provide a versioned secrets.auto.tfvars.template documenting file


Figure 993. Solution Slide presentation

Declaring hcloud_token in variables.tf:

variable "hcloud_token" {  # See secret.auto.tfvars
  nullable = false
  sensitive = true
}

Defining hcloud_token's value in secrets.auto.tfvars:

hcloud_token="xdaGfz9LmwO8SWkg ... "

Using hcloud_token in main.tf:

provider "hcloud" { token = var.hcloud_token }

Example in secrets.auto.tfvars.template:

hcloud_token="your_api_token_goes_here"

exercise No. 3

Incrementally creating a base system

Q:

Follow the subsequent steps creating basic server based on Terraform:

  1. Start from Figure 981, “Minimal Terraform configuration ” adding a ssh inbound firewall rule. Enter your Hetzner provider token and create the server.

    On success you'll receive an e-mail containing your server's IP address and the root user's password for ssh login. Why does this happen? Log in to your server.

  2. Subject your configuration to version control in a Git project. Versioning the previous Terraform configuration might expose your cloud provider's API token. Circumvent this problem by following the steps outlined in Figure 993, “Solution ”.

  3. Ditch unsafe (and tedious) ssh password login in favour of public/private key access.

    Tip

    Create a resource "hcloud_ssh_key" ... and read your hcloud_server documentation regarding ssh public key configuration.

    On success you should be able to log in using your ssh private key.

  4. Currently when executing the terraform apply command both your newly created server's IP and data center location are not being shown. Add an outputs.tf file containing two corresponding output entries.

    On success when executing terraform apply you should see something like:

    terraform apply
    ...
    hcloud_server.helloServer: Still creating... [10s elapsed]
    hcloud_server.helloServer: Creation complete after 14s [id=46961197]
    
    Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
    
    Outputs:
    
    hello_datacenter = "hel1-dc2"
    hello_ip_addr = "95.217.154.104"