From e24ed2f4c22499c39ef71953606d98a2bfdce8aa Mon Sep 17 00:00:00 2001
From: Martin Goik <goik@hdm-stuttgart.de>
Date: Sun, 14 Apr 2024 22:11:26 +0200
Subject: [PATCH] Hetzner terraform intro

---
 Doc/Common/glossary.xml                       |  10 +
 Doc/Sdi/CloudProvider/Terra/.gitignore        |   5 +
 Doc/Sdi/CloudProvider/Terra/010Hello/main.tf  |  19 +
 .../Terra/020HelloSshPublicKey/main.tf        |  38 ++
 .../Terra/030HelloSshOutput/main.tf           |  47 ++
 .../Terra/040HelloSshSecretApiToken/main.tf   |  44 ++
 .../secrets.auto.tfvars.template              |   1 +
 .../040HelloSshSecretApiToken/variables.tf    |   4 +
 Doc/Sdi/CloudProvider/gettingStarted.xml      | 614 ++++++++++++++++++
 .../{Cloud => FileCloud}/Fig/lightning.png    | Bin
 Doc/Sdi/{Cloud => FileCloud}/cloud.xml        |   0
 Doc/Sdi/Hetzner/gettingStarted.xml            | 256 --------
 Doc/lectures.xml                              |   4 +-
 13 files changed, 784 insertions(+), 258 deletions(-)
 create mode 100644 Doc/Sdi/CloudProvider/Terra/.gitignore
 create mode 100644 Doc/Sdi/CloudProvider/Terra/010Hello/main.tf
 create mode 100644 Doc/Sdi/CloudProvider/Terra/020HelloSshPublicKey/main.tf
 create mode 100644 Doc/Sdi/CloudProvider/Terra/030HelloSshOutput/main.tf
 create mode 100644 Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/main.tf
 create mode 100644 Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/secrets.auto.tfvars.template
 create mode 100644 Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/variables.tf
 create mode 100644 Doc/Sdi/CloudProvider/gettingStarted.xml
 rename Doc/Sdi/{Cloud => FileCloud}/Fig/lightning.png (100%)
 rename Doc/Sdi/{Cloud => FileCloud}/cloud.xml (100%)
 delete mode 100644 Doc/Sdi/Hetzner/gettingStarted.xml

diff --git a/Doc/Common/glossary.xml b/Doc/Common/glossary.xml
index cae963a33..0dc21dce5 100644
--- a/Doc/Common/glossary.xml
+++ b/Doc/Common/glossary.xml
@@ -967,6 +967,16 @@
       </glossdef>
     </glossentry>
 
+    <glossentry xml:id="glo_VCS">
+      <glossterm><acronym>VCS</acronym></glossterm>
+
+      <glossdef>
+        <para><link
+        xlink:href="https://en.wikipedia.org/wiki/Version_control">Version
+        control system</link></para>
+      </glossdef>
+    </glossentry>
+
     <glossentry xml:id="glo_Vnc">
       <glossterm><acronym>Vnc</acronym></glossterm>
 
diff --git a/Doc/Sdi/CloudProvider/Terra/.gitignore b/Doc/Sdi/CloudProvider/Terra/.gitignore
new file mode 100644
index 000000000..e1518f8a3
--- /dev/null
+++ b/Doc/Sdi/CloudProvider/Terra/.gitignore
@@ -0,0 +1,5 @@
+.terraform
+.terraform.*
+terraform.tfstate
+terraform.tfstate.backup
+secrets.auto.tfvars
\ No newline at end of file
diff --git a/Doc/Sdi/CloudProvider/Terra/010Hello/main.tf b/Doc/Sdi/CloudProvider/Terra/010Hello/main.tf
new file mode 100644
index 000000000..3fdd5a0a8
--- /dev/null
+++ b/Doc/Sdi/CloudProvider/Terra/010Hello/main.tf
@@ -0,0 +1,19 @@
+terraform {               # Define Hetzner cloud provider
+  required_providers {
+    hcloud = {
+      source = "hetznercloud/hcloud"
+    }
+  }
+  required_version = ">= 0.13"
+}
+
+provider "hcloud" {       # Configure the Hetzner Cloud API token
+  token = "your_api_token_goes_here"
+}
+
+resource "hcloud_server" "helloServer" {  # Create a server
+  name         = "hello"
+  image        =  "debian-12"
+  server_type  =  "cx11"
+  location     =  "nbg1"
+}
\ No newline at end of file
diff --git a/Doc/Sdi/CloudProvider/Terra/020HelloSshPublicKey/main.tf b/Doc/Sdi/CloudProvider/Terra/020HelloSshPublicKey/main.tf
new file mode 100644
index 000000000..8058c1349
--- /dev/null
+++ b/Doc/Sdi/CloudProvider/Terra/020HelloSshPublicKey/main.tf
@@ -0,0 +1,38 @@
+terraform {
+  required_providers {
+    hcloud = {
+      source = "hetznercloud/hcloud"
+    }
+  }
+  required_version = ">= 0.13"
+}
+
+provider "hcloud" {
+  token = "your_api_token_goes_here"
+}
+
+resource "hcloud_firewall" "sshFw" {
+  name = "ssh-firewall"
+  rule {
+    direction = "in"
+    protocol  = "tcp"
+    port      = "22"
+    source_ips = [
+      "0.0.0.0/0",
+      "::/0"
+    ]
+  }
+}
+resource "hcloud_ssh_key" "goik" {
+  name       = "goik@hdm-stuttgart.de"
+  public_key = file("~/.ssh/id_ed25519.pub")
+}
+# Create server
+resource "hcloud_server" "helloServer" {
+  name         = "hello"
+  image        =  "debian-12"
+  server_type  =  "cx11"
+  location     =  "nbg1"
+  ssh_keys     = [hcloud_ssh_key.goik.id]
+  firewall_ids = [hcloud_firewall.sshFw.id]
+}
diff --git a/Doc/Sdi/CloudProvider/Terra/030HelloSshOutput/main.tf b/Doc/Sdi/CloudProvider/Terra/030HelloSshOutput/main.tf
new file mode 100644
index 000000000..55bbc2677
--- /dev/null
+++ b/Doc/Sdi/CloudProvider/Terra/030HelloSshOutput/main.tf
@@ -0,0 +1,47 @@
+terraform {
+  required_providers {
+    hcloud = {
+      source = "hetznercloud/hcloud"
+    }
+  }
+  required_version = ">= 0.13"
+}
+
+provider "hcloud" {
+  token = "your_api_token_goes_here"
+}
+
+resource "hcloud_firewall" "sshFw" {
+  name = "ssh-firewall"
+  rule {
+    direction = "in"
+    protocol  = "tcp"
+    port      = "22"
+    source_ips = [
+      "0.0.0.0/0",
+      "::/0"
+    ]
+  }
+}
+resource "hcloud_ssh_key" "goik" {
+  name       = "goik@hdm-stuttgart.de"
+  public_key = file("~/.ssh/id_ed25519.pub")
+}
+# Create server
+resource "hcloud_server" "helloServer" {
+  name         = "hello"
+  image        =  "debian-12"
+  server_type  =  "cx11"
+  location     =  "nbg1"
+  ssh_keys     = [hcloud_ssh_key.goik.id]
+  firewall_ids = [hcloud_firewall.sshFw.id]
+}
+
+output "hello_id" {
+  value = hcloud_server.helloServer.id
+}
+output "hello_ip_addr" {
+  value       = hcloud_server.helloServer.ipv4_address
+  description = "The server's IPv4 address"
+}
+
diff --git a/Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/main.tf b/Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/main.tf
new file mode 100644
index 000000000..91b7263f9
--- /dev/null
+++ b/Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/main.tf
@@ -0,0 +1,44 @@
+terraform {
+  required_providers {
+    hcloud = {
+      source = "hetznercloud/hcloud"
+    }
+  }
+  required_version = ">= 0.13"
+}
+
+provider "hcloud" {
+  token = var.hcloud_token
+}
+
+resource "hcloud_firewall" "sshFw" {
+  name = "ssh-firewall"
+  rule {
+    direction = "in"
+    protocol  = "tcp"
+    port      = "22"
+    source_ips = [
+      "0.0.0.0/0",
+      "::/0"
+    ]
+  }
+}
+
+resource "hcloud_ssh_key" "goik" {
+  name       = "goik@hdm-stuttgart.de"
+  public_key = file("~/.ssh/id_ed25519.pub")
+}
+
+resource "hcloud_server" "helloServer" {
+  name         = "hello"
+  image        =  "debian-12"
+  server_type  =  "cx11"
+  location     =  "nbg1"
+  ssh_keys     = [hcloud_ssh_key.goik.id]
+  firewall_ids = [hcloud_firewall.sshFw.id]
+}
+
+output "hello_ip_addr" {
+  value       = hcloud_server.helloServer.ipv4_address
+  description = "The server's IPv4 address"
+}
diff --git a/Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/secrets.auto.tfvars.template b/Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/secrets.auto.tfvars.template
new file mode 100644
index 000000000..5929da087
--- /dev/null
+++ b/Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/secrets.auto.tfvars.template
@@ -0,0 +1 @@
+hcloud_token="your_api_token_goes_here"
diff --git a/Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/variables.tf b/Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/variables.tf
new file mode 100644
index 000000000..3eefa6804
--- /dev/null
+++ b/Doc/Sdi/CloudProvider/Terra/040HelloSshSecretApiToken/variables.tf
@@ -0,0 +1,4 @@
+variable "hcloud_token" {  # See secret.auto.tfvars
+  nullable = false
+  sensitive = true
+}
\ No newline at end of file
diff --git a/Doc/Sdi/CloudProvider/gettingStarted.xml b/Doc/Sdi/CloudProvider/gettingStarted.xml
new file mode 100644
index 000000000..991beb142
--- /dev/null
+++ b/Doc/Sdi/CloudProvider/gettingStarted.xml
@@ -0,0 +1,614 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter annotations="slide" version="5.1" xml:id="sdi_cloudProvider"
+         xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xila="http://www.w3.org/2001/XInclude/local-attributes"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         xmlns:trans="http://docbook.org/ns/transclusion"
+         xmlns:svg="http://www.w3.org/2000/svg"
+         xmlns:m="http://www.w3.org/1998/Math/MathML"
+         xmlns:html="http://www.w3.org/1999/xhtml"
+         xmlns:db="http://docbook.org/ns/docbook">
+  <title>Cloud provider</title>
+
+  <section xml:id="sdi_cloudProvider_webAdminGui">
+    <title><orgname xlink:href="https://hetzner.com">Hetzner</orgname> cloud
+    administration GUI</title>
+
+    <figure xml:id="sdi_cloudProvider_webAdminGui_createSshKeyPair">
+      <title>Preliminary: Create an <command>ssh</command> key pair</title>
+
+      <screen>sdiuser@martin-pc-dachboden:~$ <command
+          xlink:href="https://linux.die.net/man/1/ssh-keygen">ssh-keygen</command> -t ed25519 <co
+          linkends="sdi_cloudProvider_webAdminGui_createSshKeyPair-1"
+          xml:id="sdi_cloudProvider_webAdminGui_createSshKeyPair-1-co"/>
+Generating public/private ed25519 key pair.
+Enter file in which to save the key (/home/sdiuser/.ssh/id_ed25519): 
+Created directory '/home/sdiuser/.ssh'.
+Enter passphrase (empty for no passphrase): <co
+          linkends="sdi_cloudProvider_webAdminGui_createSshKeyPair-2"
+          xml:id="sdi_cloudProvider_webAdminGui_createSshKeyPair-2-co"/>
+Enter same passphrase again: 
+Your identification has been saved in /home/sdiuser/.ssh/id_ed25519 <co
+          linkends="sdi_cloudProvider_webAdminGui_createSshKeyPair-3"
+          xml:id="sdi_cloudProvider_webAdminGui_createSshKeyPair-3-co"/>
+Your public key has been saved in /home/sdiuser/.ssh/id_ed25519.pub <co
+          linkends="sdi_cloudProvider_webAdminGui_createSshKeyPair-4"
+          xml:id="sdi_cloudProvider_webAdminGui_createSshKeyPair-4-co"/></screen>
+
+      <calloutlist role="slideExclude">
+        <callout arearefs="sdi_cloudProvider_webAdminGui_createSshKeyPair-1-co"
+                 xml:id="sdi_cloudProvider_webAdminGui_createSshKeyPair-1">
+          <para>Create an elliptic rather than default <abbrev>RSA</abbrev>
+          type key.</para>
+        </callout>
+
+        <callout arearefs="sdi_cloudProvider_webAdminGui_createSshKeyPair-2-co"
+                 xml:id="sdi_cloudProvider_webAdminGui_createSshKeyPair-2">
+          <para>Security aware folks will choose a decent passphrase
+          protecting the private key being generated.</para>
+        </callout>
+
+        <callout arearefs="sdi_cloudProvider_webAdminGui_createSshKeyPair-3-co"
+                 xml:id="sdi_cloudProvider_webAdminGui_createSshKeyPair-3">
+          <para>The generated private key.</para>
+        </callout>
+
+        <callout arearefs="sdi_cloudProvider_webAdminGui_createSshKeyPair-4-co"
+                 xml:id="sdi_cloudProvider_webAdminGui_createSshKeyPair-4">
+          <para>The generated public key.</para>
+
+          <note>
+            <para>Different implementations like e.g. <command
+            xlink:href="https://www.putty.org">putty</command> may use
+            different key storage formats.</para>
+          </note>
+        </callout>
+      </calloutlist>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_webAdminGui_hetznerSignUp">
+      <title>Create a <orgname>Hetzner</orgname> account</title>
+
+      <itemizedlist>
+        <listitem>
+          <para>Sign up at <link
+          xlink:href="https://accounts.hetzner.com/signUp">https://accounts.hetzner.com/signUp</link>
+          using an account name of your choice.</para>
+        </listitem>
+
+        <listitem>
+          <para>Optionally: Activate 2-factor authentication.</para>
+        </listitem>
+
+        <listitem>
+          <para>You may validate your account by ID card or similar. No
+          payment required!</para>
+        </listitem>
+
+        <listitem>
+          <para>Publish your <orgname>Hetzner</orgname> account's username to
+          your SDI course's group at <link
+          xlink:href="https://learn.mi.hdm-stuttgart.de">https://learn.mi.hdm-stuttgart.de</link>.</para>
+        </listitem>
+      </itemizedlist>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_webAdminGui_accessProject">
+      <title>Access your project space</title>
+
+      <para>Upon confirmation your <orgname>Hetzner</orgname> project space
+      sdi_gxy (e.g. sdi_g01 corresponding to group 1) should be
+      accessible.</para>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_webAdminGui_">
+      <title>Create a server</title>
+
+      <informaltable border="0">
+        <tr>
+          <td valign="top"><orderedlist>
+              <listitem>
+                <para>Create a default firewall allowing <command
+                xlink:href="https://linux.die.net/man/8/ping">ping</command>
+                and <command
+                xlink:href="https://linux.die.net/man/1/ssh">ssh</command></para>
+              </listitem>
+
+              <listitem>
+                <para><productname>Ubuntu</productname> latest</para>
+              </listitem>
+
+              <listitem>
+                <para>Shared vCPU / x86 / CX11 (<link
+                xlink:href="https://www.hetzner.com/cloud/#pricing">the
+                cheapest</link>)</para>
+              </listitem>
+
+              <listitem>
+                <para>Add your personal <command>ssh</command> public key from
+                <xref
+                linkend="sdi_cloudProvider_webAdminGui_createSshKeyPair"/></para>
+              </listitem>
+            </orderedlist></td>
+
+          <td valign="top"><orderedlist continuation="continues">
+              <listitem>
+                <para>Omit volume, labels and cloud config</para>
+              </listitem>
+
+              <listitem>
+                <para>Note the <guimenuitem>Networking</guimenuitem> /
+                <guisubmenu>Public IPv4</guisubmenu> address for later
+                reference</para>
+              </listitem>
+
+              <listitem>
+                <para>Click »Create &amp; Buy now«</para>
+              </listitem>
+            </orderedlist></td>
+        </tr>
+      </informaltable>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_webAdminGui_accessServer">
+      <title>Access your server</title>
+
+      <itemizedlist>
+        <listitem>
+          <para>Ping your server:</para>
+
+          <note>
+            <para>The IP 91.107.232.156 serves just as a sample value
+            irrespective of your individual actual server IP.</para>
+          </note>
+
+          <screen>sdiuser:~$ ping 91.107.232.156
+PING 91.107.232.156 (91.107.232.156) 56(84) bytes of data.
+64 bytes from 91.107.232.156: icmp_seq=1 ttl=49 time=18.3 ms
+64 bytes from 91.107.232.156 ...</screen>
+        </listitem>
+
+        <listitem>
+          <para>Login via <command>ssh</command>:</para>
+
+          <screen>ssh root@91.107.232.156</screen>
+        </listitem>
+      </itemizedlist>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_webAdminGui_updateServer">
+      <title>Update and reboot</title>
+
+      <orderedlist>
+        <listitem>
+          <para>apt update</para>
+        </listitem>
+
+        <listitem>
+          <para>apt upgrade</para>
+        </listitem>
+
+        <listitem>
+          <para>reboot</para>
+        </listitem>
+      </orderedlist>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_webAdminGui_installNginx">
+      <title>Install a web server</title>
+
+      <screen>root@topsy:~# apt install nginx</screen>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_webAdminGui_localHttpAccess">
+      <title>Check local <acronym>http</acronym> web access</title>
+
+      <screen>root@topsy:~# wget -O - 91.107.232.156
+--2024-04-07 18:59:13--  http://91.107.232.156/
+Connecting to 91.107.232.156:80... connected.
+&lt;html&gt;
+&lt;head&gt;
+&lt;title&gt;Welcome to nginx!&lt;/title&gt; ...</screen>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_cloudAdminGui_externHttp">
+      <title>External <acronym>http</acronym> web access</title>
+
+      <para>Point your browser to http://91.107.232.156.</para>
+
+      <screen>sdiuser:~$ telnet 91.107.232.156 80
+Trying 91.107.232.156...</screen>
+
+      <para>Why is there no answer?</para>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_cloudAdminGui_allowHttp">
+      <title>Add port 80 / <acronym>http</acronym> firewall rule</title>
+
+      <screen>sdiuser:~$ telnet 91.107.232.156 80
+Trying 91.107.232.156...
+Connected to 91.107.232.156.
+Escape character is '^]'</screen>
+
+      <para>Congrats: External Browser access is working now!</para>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_cloudAdminGui_cleanUp">
+      <title>Cleaning up!</title>
+
+      <caution>
+        <para>This is about <emphasis role="red">$$$ MONEY
+        $$$</emphasis></para>
+      </caution>
+
+      <itemizedlist>
+        <listitem>
+          <para>Delete your server including the IPv4 address.</para>
+        </listitem>
+
+        <listitem>
+          <para>You may delete your firewall</para>
+        </listitem>
+      </itemizedlist>
+    </figure>
+  </section>
+
+  <section xml:id="sdi_cloudProvider_terra">
+    <title>Working with <productname
+    xlink:href="https://www.terraform.io">Terraform</productname></title>
+
+    <figure xml:id="sdi_cloudProvider_terra_pledge">
+      <title>What's it all about?</title>
+
+      <para><link
+      xlink:href="https://developer.hashicorp.com/terraform/intro">Quote:</link></para>
+
+      <para><quote><productname>Terraform</productname> is an infrastructure
+      as code tool that lets you build, change, and version cloud and
+      <abbrev>on-prem</abbrev> resources safely and
+      efficiently.</quote></para>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_installSoftware">
+      <title><productname>Terraform</productname> resources</title>
+
+      <itemizedlist>
+        <listitem>
+          <para><link
+          xlink:href="https://developer.hashicorp.com/terraform/intro#why-terraform">Why
+          <productname>Terraform</productname>?</link></para>
+        </listitem>
+
+        <listitem>
+          <para><link
+          xlink:href="https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli">Install
+          <productname>Terraform</productname></link>.</para>
+        </listitem>
+      </itemizedlist>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_minimalConfig">
+      <title>Minimal <productname>Terraform</productname>
+      configuration</title>
+
+      <programlisting language="terraform"># Define Hetzner cloud provider
+terraform {
+  required_providers {
+    hcloud = {
+      source = "hetznercloud/hcloud"
+    }
+  }
+  required_version = "&gt;= 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"   
+  location     =  "nbg1"
+}</programlisting>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_init">
+      <title><productname>Terraform</productname> <command
+      xlink:href="https://developer.hashicorp.com/terraform/cli/commands/init">init</command></title>
+
+      <programlisting language="terraform">$ terraform <command
+          xlink:href="https://developer.hashicorp.com/terraform/cli/commands/init">init</command>
+
+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.  ...</programlisting>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_plan">
+      <title><productname>Terraform</productname> <command
+      xlink:href="https://developer.hashicorp.com/terraform/cli/commands/plan">plan</command></title>
+
+      <programlisting language="terraform">$ terraform <command
+          xlink:href="https://developer.hashicorp.com/terraform/cli/commands/plan">plan</command>
+
+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.</programlisting>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_apply">
+      <title><productname>Terraform</productname> <command
+      xlink:href="https://developer.hashicorp.com/terraform/cli/commands/apply">apply</command></title>
+
+      <programlisting language="terraform">$ terraform <command
+          xlink:href="https://developer.hashicorp.com/terraform/cli/commands/apply">apply</command>
+...
+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: <emphasis role="red">yes</emphasis>
+
+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.</programlisting>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_hello_email">
+      <title><productname>Credentials by E-Mail</productname></title>
+
+      <screen>Your server "hello" was created!
+
+You can access your server with the following credentials:
+ 
+IPv4	<emphasis role="red">128.140.108.60</emphasis>
+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.</screen>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_hello_problems">
+      <title><productname>Problems</productname>: 😟</title>
+
+      <itemizedlist>
+        <listitem>
+          <para>Firewall blocks <command
+          xlink:href="https://linux.die.net/man/1/ssh">ssh</command> server
+          access:</para>
+
+          <screen>$ ssh root@<emphasis role="red">128.140.108.60</emphasis>
+ssh: connect to host 128.140.108.60 port 22: Connection refused</screen>
+
+          <para>Access by <xref linkend="glo_Vnc"/> <link
+          xlink:href="https://docs.hetzner.com/cloud/servers/getting-started/vnc-console">console
+          login</link> only</para>
+        </listitem>
+
+        <listitem>
+          <para>IP and (initial) credentials by email 😱</para>
+        </listitem>
+      </itemizedlist>
+
+      <para>Solution:</para>
+
+      <orderedlist>
+        <listitem>
+          <para>Add firewall inbound <command
+          xlink:href="https://linux.die.net/man/1/ssh">ssh</command> access
+          rule.</para>
+        </listitem>
+
+        <listitem>
+          <para>Configure <command
+          xlink:href="https://linux.die.net/man/1/ssh">ssh</command> public
+          key login.</para>
+        </listitem>
+      </orderedlist>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_hello_sshAccessFw">
+      <title><command
+      xlink:href="https://linux.die.net/man/1/ssh">ssh</command> access,
+      firewall</title>
+
+      <programlisting language="terraform">resource "hcloud_firewall" "<emphasis
+          role="red">sshFw</emphasis>" {
+  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.<emphasis role="red">sshFw</emphasis>.id]
+}</programlisting>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_hello_sshAccessKey">
+      <title><command
+      xlink:href="https://linux.die.net/man/1/ssh">ssh</command> access,
+      public key</title>
+
+      <programlisting language="terraform">resource "hcloud_ssh_key" "<emphasis
+          role="red">goik</emphasis>" {
+  name       = "goik@hdm-stuttgart.de"
+  public_key = file("~/.ssh/id_ed25519.pub")
+}
+              ...
+resource "hcloud_server" "helloServer" {
+              ...
+  ssh_keys     = [hcloud_ssh_key.<emphasis role="red">goik</emphasis>.id]
+}</programlisting>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_hello_sshApply">
+      <title>Apply <command
+      xlink:href="https://linux.die.net/man/1/ssh">ssh</command> key
+      access</title>
+
+      <screen>$ 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" "goik" {
+       ...
+Plan: 3 to add, 0 to change, 0 to destroy.
+       ...
+Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
+</screen>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_hello_sshApplyOutputValuesDetails">
+      <title>Output data details</title>
+
+      <glosslist>
+        <glossentry>
+          <glossterm><command>terraform</command> <option
+          xlink:href="https://developer.hashicorp.com/terraform/language/values/outputs">output</option></glossterm>
+
+          <glossdef>
+            <programlisting language="properties">hello_id = "45839978"
+hello_ip_addr = "128.140.108.60"</programlisting>
+          </glossdef>
+        </glossentry>
+
+        <glossentry>
+          <glossterm><command>terraform</command> <option
+          xlink:href="https://developer.hashicorp.com/terraform/language/values/outputs">output</option>
+          <option>hello_ip_addr</option></glossterm>
+
+          <glossdef>
+            <screen>"128.140.108.60"</screen>
+          </glossdef>
+        </glossentry>
+
+        <glossentry>
+          <glossterm><command>terraform</command> <option
+          xlink:href="https://developer.hashicorp.com/terraform/language/values/outputs">output</option>
+          <option>-json</option></glossterm>
+
+          <glossdef>
+            <programlisting language="json">{
+  "hello_id": {"sensitive": false, "type": "string", "value": "45839978"},
+  "hello_ip_addr": {"sensitive": false, "type": "string", "value": "128.140.108.60"}
+}</programlisting>
+          </glossdef>
+        </glossentry>
+      </glosslist>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_hello_sshProblemApiToken">
+      <title><productname>Problem 2</productname>: <xref linkend="glo_VCS"/>
+      and visible provider API token 😱</title>
+
+      <para>Versioned file <filename>main.tf</filename>:</para>
+
+      <programlisting language="terraform">...
+provider "hcloud" { token = "xdaGfz9LmwO8SWkg ... "}
+...</programlisting>
+
+      <para>Solution:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para>Declare a <link
+          xlink:href="https://developer.hashicorp.com/terraform/language/values/variables">variable</link>
+          <varname>hcloud_token</varname> in a
+          <filename>variables.tf</filename> file</para>
+        </listitem>
+
+        <listitem>
+          <para>Add a non-versioned file
+          <filename>secrets.auto.tfvars</filename>.</para>
+        </listitem>
+
+        <listitem>
+          <para>Optional: Provide a versioned
+          <filename>secrets.auto.tfvars.template</filename> file</para>
+        </listitem>
+      </itemizedlist>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_hello_sshProblemApiTokenSolve">
+      <title>Solution</title>
+
+      <informaltable border="0">
+        <tr>
+          <td valign="top"><para>Declaring <emphasis
+          role="red"><varname>hcloud_token</varname></emphasis> in
+          <filename>variables.tf</filename>:</para><programlisting
+          language="terraform">variable "<emphasis role="red">hcloud_token</emphasis>" {  # See secret.auto.tfvars
+  nullable = false
+  sensitive = true
+}</programlisting></td>
+
+          <td valign="top"><para>Defining <emphasis
+          role="red"><varname>hcloud_token</varname></emphasis>'s value in
+          <filename>secrets.auto.tfvars</filename>:</para><programlisting
+          language="terraform"><emphasis role="red">hcloud_token</emphasis>="xdaGfz9LmwO8SWkg ... "</programlisting></td>
+        </tr>
+
+        <tr>
+          <td valign="top"><para>Using <emphasis
+          role="red"><varname>hcloud_token</varname></emphasis> in
+          <filename>main.tf</filename>:</para><programlisting
+          language="terraform">provider "hcloud" { token = var.<emphasis
+                role="red">hcloud_token</emphasis> }</programlisting></td>
+
+          <td valign="top"><para>Example in
+          <filename>secrets.auto.tfvars.template:</filename></para><programlisting
+          language="terraform"><emphasis role="red">hcloud_token</emphasis>="your_api_token_goes_here"</programlisting></td>
+        </tr>
+      </informaltable>
+    </figure>
+
+    <figure xml:id="sdi_cloudProvider_terra_hello_kownHostsDuplicateProblem">
+      <title>Duplicate known_hosts entry on re-creating server</title>
+
+      <screen>$ ssh root@128.140.108.60
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
+Someone could be eavesdropping on you right now (<emphasis role="red">man-in-the-middle attack</emphasis>)!</screen>
+    </figure>
+  </section>
+</chapter>
diff --git a/Doc/Sdi/Cloud/Fig/lightning.png b/Doc/Sdi/FileCloud/Fig/lightning.png
similarity index 100%
rename from Doc/Sdi/Cloud/Fig/lightning.png
rename to Doc/Sdi/FileCloud/Fig/lightning.png
diff --git a/Doc/Sdi/Cloud/cloud.xml b/Doc/Sdi/FileCloud/cloud.xml
similarity index 100%
rename from Doc/Sdi/Cloud/cloud.xml
rename to Doc/Sdi/FileCloud/cloud.xml
diff --git a/Doc/Sdi/Hetzner/gettingStarted.xml b/Doc/Sdi/Hetzner/gettingStarted.xml
deleted file mode 100644
index d3b3392ba..000000000
--- a/Doc/Sdi/Hetzner/gettingStarted.xml
+++ /dev/null
@@ -1,256 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<chapter annotations="slide" version="5.1" xml:id="sdi_hetzner"
-         xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink"
-         xmlns:xila="http://www.w3.org/2001/XInclude/local-attributes"
-         xmlns:xi="http://www.w3.org/2001/XInclude"
-         xmlns:trans="http://docbook.org/ns/transclusion"
-         xmlns:svg="http://www.w3.org/2000/svg"
-         xmlns:m="http://www.w3.org/1998/Math/MathML"
-         xmlns:html="http://www.w3.org/1999/xhtml"
-         xmlns:db="http://docbook.org/ns/docbook">
-  <title>Using the <orgname xlink:href="https://hetzner.com">Hetzner</orgname>
-  cloud</title>
-
-  <section xml:id="sdi_hetzner_getstarted">
-    <title>Getting started</title>
-
-    <figure xml:id="sdi_hetzner_getstarted_createSshKeyPair">
-      <title>Create an <command>ssh</command> key pair</title>
-
-      <screen>sdiuser@martin-pc-dachboden:~$ <command
-          xlink:href="https://linux.die.net/man/1/ssh-keygen">ssh-keygen</command> -t ed25519 <co
-          linkends="sdi_hetzner_getstarted_createSshKeyPair-1"
-          xml:id="sdi_hetzner_getstarted_createSshKeyPair-1-co"/>
-Generating public/private ed25519 key pair.
-Enter file in which to save the key (/home/sdiuser/.ssh/id_ed25519): 
-Created directory '/home/sdiuser/.ssh'.
-Enter passphrase (empty for no passphrase): <co
-          linkends="sdi_hetzner_getstarted_createSshKeyPair-2"
-          xml:id="sdi_hetzner_getstarted_createSshKeyPair-2-co"/>
-Enter same passphrase again: 
-Your identification has been saved in /home/sdiuser/.ssh/id_ed25519 <co
-          linkends="sdi_hetzner_getstarted_createSshKeyPair-3"
-          xml:id="sdi_hetzner_getstarted_createSshKeyPair-3-co"/>
-Your public key has been saved in /home/sdiuser/.ssh/id_ed25519.pub <co
-          linkends="sdi_hetzner_getstarted_createSshKeyPair-4"
-          xml:id="sdi_hetzner_getstarted_createSshKeyPair-4-co"/></screen>
-
-      <calloutlist role="slideExclude">
-        <callout arearefs="sdi_hetzner_getstarted_createSshKeyPair-1-co"
-                 xml:id="sdi_hetzner_getstarted_createSshKeyPair-1">
-          <para>Create an elliptic rather than default <abbrev>RSA</abbrev>
-          type key.</para>
-        </callout>
-
-        <callout arearefs="sdi_hetzner_getstarted_createSshKeyPair-2-co"
-                 xml:id="sdi_hetzner_getstarted_createSshKeyPair-2">
-          <para>Security aware folks will choose a decent passphrase
-          protecting the private key being generated.</para>
-        </callout>
-
-        <callout arearefs="sdi_hetzner_getstarted_createSshKeyPair-3-co"
-                 xml:id="sdi_hetzner_getstarted_createSshKeyPair-3">
-          <para>The generated private key.</para>
-        </callout>
-
-        <callout arearefs="sdi_hetzner_getstarted_createSshKeyPair-4-co"
-                 xml:id="sdi_hetzner_getstarted_createSshKeyPair-4">
-          <para>The generated public key.</para>
-
-          <note>
-            <para>Different implementations like e.g. <command
-            xlink:href="https://www.putty.org">putty</command> may use
-            different key storage formats.</para>
-          </note>
-        </callout>
-      </calloutlist>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_hetznerSignUp">
-      <title>Create a <orgname>Hetzner</orgname> account</title>
-
-      <itemizedlist>
-        <listitem>
-          <para>Sign up at <link
-          xlink:href="https://accounts.hetzner.com/signUp">https://accounts.hetzner.com/signUp</link>
-          using an account name of your choice.</para>
-        </listitem>
-
-        <listitem>
-          <para>Optionally: Activate 2-factor authentication.</para>
-        </listitem>
-
-        <listitem>
-          <para>You may validate your account by ID card or similar. No
-          payment required!</para>
-        </listitem>
-
-        <listitem>
-          <para>Publish your <orgname>Hetzner</orgname> account's username to
-          your SDI course's group at <link
-          xlink:href="https://learn.mi.hdm-stuttgart.de">https://learn.mi.hdm-stuttgart.de</link>.</para>
-        </listitem>
-      </itemizedlist>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_accessProject">
-      <title>Access your project space</title>
-
-      <para>Upon confirmation your <orgname>Hetzner</orgname> project space
-      sdi_gxy (e.g. sdi_g01 corresponding to group 1) should be
-      accessible.</para>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_">
-      <title>Create a server</title>
-
-      <informaltable border="0">
-        <tr>
-          <td valign="top"><orderedlist>
-              <listitem>
-                <para>Create a default firewall allowing <command
-                xlink:href="https://linux.die.net/man/8/ping">ping</command>
-                and <command
-                xlink:href="https://linux.die.net/man/1/ssh">ssh</command></para>
-              </listitem>
-
-              <listitem>
-                <para><productname>Ubuntu</productname> latest</para>
-              </listitem>
-
-              <listitem>
-                <para>Shared vCPU / x86 / CX11 (<link
-                xlink:href="https://www.hetzner.com/cloud/#pricing">the
-                cheapest</link>)</para>
-              </listitem>
-
-              <listitem>
-                <para>Add your personal <command>ssh</command> public key from
-                <xref
-                linkend="sdi_hetzner_getstarted_createSshKeyPair"/></para>
-              </listitem>
-            </orderedlist></td>
-
-          <td valign="top"><orderedlist continuation="continues">
-              <listitem>
-                <para>Omit volume, labels and cloud config</para>
-              </listitem>
-
-              <listitem>
-                <para>Note the <guimenuitem>Networking</guimenuitem> /
-                <guisubmenu>Public IPv4</guisubmenu> address for later
-                reference</para>
-              </listitem>
-
-              <listitem>
-                <para>Click »Create &amp; Buy now«</para>
-              </listitem>
-            </orderedlist></td>
-        </tr>
-      </informaltable>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_accessServer">
-      <title>Access your server</title>
-
-      <itemizedlist>
-        <listitem>
-          <para>Ping your server:</para>
-
-          <note>
-            <para>The IP 91.107.232.156 serves just as a sample value
-            irrespective of your individual actual server IP.</para>
-          </note>
-
-          <screen>sdiuser:~$ ping 91.107.232.156
-PING 91.107.232.156 (91.107.232.156) 56(84) bytes of data.
-64 bytes from 91.107.232.156: icmp_seq=1 ttl=49 time=18.3 ms
-64 bytes from 91.107.232.156 ...</screen>
-        </listitem>
-
-        <listitem>
-          <para>Login via <command>ssh</command>:</para>
-
-          <screen>ssh root@91.107.232.156</screen>
-        </listitem>
-      </itemizedlist>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_updateServer">
-      <title>Update and reboot</title>
-
-      <orderedlist>
-        <listitem>
-          <para>apt update</para>
-        </listitem>
-
-        <listitem>
-          <para>apt upgrade</para>
-        </listitem>
-
-        <listitem>
-          <para>reboot</para>
-        </listitem>
-      </orderedlist>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_installNginx">
-      <title>Install a web server</title>
-
-      <screen>root@topsy:~# apt install nginx</screen>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_localHttpAccess">
-      <title>Check local <acronym>http</acronym> web access</title>
-
-      <screen>root@topsy:~# wget -O - 91.107.232.156
---2024-04-07 18:59:13--  http://91.107.232.156/
-Connecting to 91.107.232.156:80... connected.
-&lt;html&gt;
-&lt;head&gt;
-&lt;title&gt;Welcome to nginx!&lt;/title&gt; ...</screen>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_externHttp">
-      <title>External <acronym>http</acronym> web access</title>
-
-      <para>Point your browser to http://91.107.232.156.</para>
-
-      <screen>sdiuser:~$ telnet 91.107.232.156 80
-Trying 91.107.232.156...</screen>
-
-      <para>Why is there no answer?</para>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_allowHttp">
-      <title>Add port 80 / <acronym>http</acronym> firewall rule</title>
-
-      <screen>sdiuser:~$ telnet 91.107.232.156 80
-Trying 91.107.232.156...
-Connected to 91.107.232.156.
-Escape character is '^]'</screen>
-
-      <para>Congrats: External Browser access is working now!</para>
-    </figure>
-
-    <figure xml:id="sdi_hetzner_getstarted_cleanUp">
-      <title>Cleaning up!</title>
-
-      <caution>
-        <para>This is about <emphasis role="red">$$$ MONEY
-        $$$</emphasis></para>
-      </caution>
-
-      <itemizedlist>
-        <listitem>
-          <para>Delete your server including the IPv4 address.</para>
-        </listitem>
-
-        <listitem>
-          <para>You may delete your firewall</para>
-        </listitem>
-      </itemizedlist>
-    </figure>
-  </section>
-</chapter>
diff --git a/Doc/lectures.xml b/Doc/lectures.xml
index e965786b3..9fa55b7f5 100644
--- a/Doc/lectures.xml
+++ b/Doc/lectures.xml
@@ -169,7 +169,7 @@
 
     <xi:include href="Sdi/Package/package.xml" xpointer="element(/1)"/>
 
-    <xi:include href="Sdi/Hetzner/gettingStarted.xml" xpointer="element(/1)"/>
+    <xi:include href="Sdi/CloudProvider/gettingStarted.xml" xpointer="element(/1)"/>
 
     <xi:include href="Sdi/GettingStarted/gettingStarted.xml"
                 xpointer="element(/1)"/>
@@ -180,7 +180,7 @@
 
     <xi:include href="Sdi/Apache/apache.xml" xpointer="element(/1)"/>
 
-    <xi:include href="Sdi/Cloud/cloud.xml" xpointer="element(/1)"/>
+    <xi:include href="Sdi/FileCloud/cloud.xml" xpointer="element(/1)"/>
 
     <xi:include href="Sdi/Samba/samba.xml" xpointer="element(/1)"/>
 
-- 
GitLab