diff --git a/SwitchEngines/README.md b/SwitchEngines/README.md
index 039df9774923ceca2405e293b8e9dfd8d8f55bcc..2265f49631fcd08638d5ae0c6ed8c7a065351cdb 100644
--- a/SwitchEngines/README.md
+++ b/SwitchEngines/README.md
@@ -41,31 +41,29 @@ Please, refer to your OS documentation for the proper way to do so
      v1.1.4. Skip the TF "Quick start tutorial" (Docker).
   1. [OpenStack
      CLI](https://docs.openstack.org/newton/user-guide/common/cli-install-openstack-command-line-clients.html)
-     version >= 5.7.0. It is recommended to install your original distribution
+     version >= 6.0.0. It is recommended to install your original distribution
      package named like `python-openstackclient`.
-  1. Grab or create a new project in your OpenStack account. The placeholder
-     used in this exercise is `<your-project-name>` with value `Cloud-MA-IaC`.
-  1. On the OpenStack Horizon dashboard, go to your project page, then
-     1. create new [Application
+  1. On the OpenStack dashboard, create a new project. The placeholder used in
+     this exercise is `<your-project-name>` with value `Cloud-MA-IaC`.
+  1. Go to your project page, then create new [Application
         Credentials](https://engines.switch.ch/horizon/identity/application_credentials/)
-        and save it as `~/.config/openstack/clouds.yaml`. With SwitchEngines, the
-        cloud name to use for this is `engines`. :warning: App credentials might
-        not work for some commands. A template is also available in repo file
-        [`conf/clouds.yaml.api_cred`](conf/clouds.yaml.api_cred). Alternatively,
+        and save it as `~/.config/openstack/clouds.yaml`. With SwitchEngines,
+        the cloud name to use for this is `engines`. :warning: App credentials
+        might not work for some commands. A template is also available in this
+        repo file
+        [`conf/clouds.yaml.app_cred`](conf/clouds.yaml.app_cred). Alternatively,
         you can use your [API
         credentials](https://engines.switch.ch/horizon/project/api_access/clouds.yaml)
-        with explicit project name/ID -- you'll have to add your API `password` from
-        your profile page's ["Credentials"
+        with explicit project name/ID -- you'll have to add your API
+        `password` from your profile page's ["Credentials"
         tab](https://engines.admin.switch.ch/users/profile). A template is
-        available in repo file
-        [`conf/clouds.yaml.app_cred`](conf/clouds.yaml.app_cred).
-    <!-- 1. download your [RC -->
-    <!--    file](https://engines.switch.ch/horizon/project/api_access/openrc/) and -->
-    <!--    install it (it asks for your API password): -->
-    <!--    ``` shell -->
-    <!--    $ source <your-project>-openrc.sh -->
-    <!--    ``` -->
-  1. Verify that your credentials are OK (:warning: it might reveal secrets!):
+        available in this repo file
+        [`conf/clouds.yaml.api_cred`](conf/clouds.yaml.app_cred). :warning:
+        Avoid mixing different authentication schemes in `clouds.yaml` or from
+        the environment (via sourcing so called OpenStack RC files).
+  1. Verify that your credentials are OK (:warning: it might reveal secrets!
+     If you have just one cloud configured, you can drop the switch
+     `--os-cloud=engines`, else adapt accordingly):
   ``` shell
   lcl$ $ openstack --os-cloud=engines [application] credential list
   ```
@@ -74,7 +72,7 @@ Please, refer to your OS documentation for the proper way to do so
 
 **Goal:** instruct TF to handle a single OpenStack instance.
 
-<a name="image-query"></a>Find out the smallest image to use for a Debian server:
+Find out the smallest image to use for a Debian server:
 
 ``` shell
 lcl$ openstack --os-cloud=engines image list --public --status=active --sort-column=Size -c ID -c Name -c Size --long
@@ -86,7 +84,7 @@ lcl$ openstack --os-cloud=engines image list --public --status=active --sort-col
 ```
 
 :bulb: We use the first ID found for the placeholder `<your-image-ID>`. In
-SwitchEngines this is 1.5GB.
+SwitchEngines, that has a size of ~1.5GB.
 
 Find out the smallest instance *flavor* that acommodates our Debian image.
 
@@ -106,6 +104,9 @@ Create a "sandbox" directory on your local machine
 in HCL language), the infrastructure *definition* file, with the following
 content (replace all `<...>` tokens with actual values):
 
+:bulb: Application credentials shall match those in file
+`~/.config/openstack/clouds.yaml`.
+
 ``` hcl
 terraform {
   required_version = ">= 0.14.9"
@@ -122,7 +123,7 @@ provider "openstack" {
   project_domain_id = "<your-project-ID>"
   application_credential_id     = "<your-app-cred-ID>"
   application_credential_secret = "<your-app-cred-secret>"
-  region = "ZH"
+  region = "<your-region>"
 }
 
 resource "openstack_compute_instance_v2" "app_server" {
@@ -229,8 +230,7 @@ Do you want to perform these actions?
 
   Enter a value: yes
 
-openstack_compute_instance_v2.app_server: Creating...
-openstack_compute_instance_v2.app_server: Still creating... [10s elapsed]
+...
 openstack_compute_instance_v2.app_server: Creation complete after 18s [id=f70aef53-51f9-4d12-a4a9-fe9a6cc5a58f]
 
 Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
@@ -295,7 +295,7 @@ not, why? We'll deal with that in Task #4 below.
 Now, stop the running instance (its ID is shown above ;-):
 
 ``` shell
-lcl$ $ openstack server stop f70aef53-51f9-4d12-a4a9-fe9a6cc5a58f
+lcl$ openstack --os-cloud=engines server stop f70aef53-51f9-4d12-a4a9-fe9a6cc5a58f
 ```
 
 wait some seconds and test again:
@@ -310,114 +310,254 @@ resource. So, let's refresh it, and check again:
 
 ``` shell
 lcl$ terraform refresh
-aws_instance.app_server: Refreshing state... [id=i-0155ba9d77ee0a854]
-lcl$ terraform show | grep instance_state
-    instance_state                       = "stopped"
+...
+lcl$ terraform show | grep power_state
+    power_state         = "shutoff"
 ```
 
 Ah-ha!
 
+Hold on a second: our TF plan does not (explicitly) specify the desired status
+of a resource. What happens if we reapply the plan? Lets' try:
 
-### Task #4: change your infrastructure ###
+``` shell
+lcl$ terraform apply -auto-approve
+openstack_compute_instance_v2.app_server: Refreshing state... [id=...]
+...
+Terraform will perform the following actions:
+...
+      ~ power_state         = "shutoff" -> "active"
+...
+Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
 
-Indeed, the instance doesn't have a (floating) *public* IP address, so we need
-to get one and associate it to our instance with the following snippet added
-to `main.tf`:
+lcl$ terraform show | grep power_state
+    power_state         = "active"
+```
 
-``` hcl
-resource "openstack_networking_floatingip_v2" "fip_1" {
-  pool = "public"
-}
+:warning: Apply was run in `-auto-approve` (non interactive) mode which
+assumes "yes" to all questions. Use with care!
+
+From the above commands' output we see that
+  * the local state is refreshed before doing anything,
+  * 1 change was applied: the instance has been switched on,
+  * OpenStack instances have an "active" default `power_state` :-)
 
-resource "openstack_compute_floatingip_associate_v2" "fip_1" {
-  # we use var/obj notation here ${x.y}
-  floating_ip = "${openstack_networking_floatingip_v2.fip_1.address}"
-  instance_id = "${openstack_compute_instance_v2.app_server.id}"
-  # to avoid getting "Resource not found"
-  wait_until_associated = true
-}
-```
 
-Let's see what happens:
+### Task #4: change your infrastructure ###
+
+**Goal:** modify the resource created before, and learn how to apply changes
+to a Terraform project.
+
+Replace the resource's `image_id` in `main.tf` with the second one found from
+the catalog query done above -- it should be a "Debian Bullseye 11
+(SWITCHengines)". Before applying our new plan, let's see what TF thinks of
+it:
+
 ``` shell
-lcl$ terraform apply -auto-approve
-openstack_compute_instance_v2.app_server: Refreshing state... [id=f70aef53-51f9-4d12-a4a9-fe9a6cc5a58f]
-...
+lcl$ terraform plan -out=change-image-ID.tfplan
+openstack_compute_instance_v2.app_server: Refreshing state... [id=1edad9e2-5459-4980-bbc5-a0c5b65bfb0d]
+
 Terraform will perform the following actions:
 
-  # openstack_compute_floatingip_associate_v2.fip_1 will be created
-  + resource "openstack_compute_floatingip_associate_v2" "fip_1" {
-      + floating_ip           = (known after apply)
-      ...
-    }
+  # openstack_compute_instance_v2.app_server will be updated in-place
+  ~ resource "openstack_compute_instance_v2" "app_server" {
+        id                  = "1edad9e2-5459-4980-bbc5-a0c5b65bfb0d"
+      ~ image_id            = "8674f1a5-f7d9-4975-af0b-d2e9e33c9152" -> "54ee4d6e-9155-4698-ab2b-45d9067e8e8e"
+        name                = "TF-managed"
+        tags                = []
+        # (13 unchanged attributes hidden)
 
-  # openstack_networking_floatingip_v2.fip_1 will be created
-  + resource "openstack_networking_floatingip_v2" "fip_1" {
-      + address    = (known after apply)
-      ...
+        # (1 unchanged block hidden)
     }
 
-Plan: 2 to add, 0 to change, 0 to destroy.
-openstack_networking_floatingip_v2.fip_1: Creating...
-openstack_networking_floatingip_v2.fip_1: Creation complete after 9s [id=81f7690f-e024-4eb8-bbbc-98a242c3b0c3]
-openstack_compute_floatingip_associate_v2.fip_1: Creating...
-openstack_compute_floatingip_associate_v2.fip_1: Creation complete after 6s [id=86.119.32.210/f70aef53-51f9-4d12-a4a9-fe9a6cc5a58f/]
+Plan: 0 to add, 1 to change, 0 to destroy.
+...
+Saved the plan to: change-image-ID.tfplan
+
+To perform exactly these actions, run the following command to apply:
+    terraform apply "change-image-ID.tfplan"
 ```
 
-Here we go. First, observe how TF had to refresh its local status before
-applying the new plan. Then, now the instance got the public IP address shown
-at the bottom of the command's output. However, a last bit is still missing,
-because the "default" *security group* only allows outbound traffic. Thus we
-need something like:
+:bulb: Remarks:
+  * The change we want to apply is *not* destructive.
+  * We saved our plan. :question: Why? It is not really necessary in a simple
+    scenario like ours, however a more complex IaC workflow might require plan
+    artifacts to be programmatically validated and versioned.
+
+Apply the saved plan:
+``` shell
+lcl$ terraform apply change-image-ID.tfplan
+$ terraform apply change-image-ID.tfplan
+...
+Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
+```
+
+:bulb: What? Not asking for confirmation? Indeed, a *saved* plan is intended for
+automated workflows! Moreover, a saved plan will come handy for rolling back a
+broken infrastructure to the last working setup.
+
+:question: What if we did not save our plan, and called a plain apply command?
+Would the result be the same?
+
+
+### Task #5: input variables ###
+
+**Goal:** make a TF plan more flexible via input variables.
+
+Our original plan has all its content hard-coded. Let's make it more flexible
+with some input variables stored in a separate `variables.tf` file inside your
+TF sandbox:
 
 ``` hcl
-resource "openstack_compute_instance_v2" "app_server" {
-  ... # as above
- security_groups = ["default", "secgroup_tf"]
+variable "instance_name" {
+  description = "Value of the instance's name tag"
+  type        = string
+  default     = "AnotherAppServerInstance"
 }
+```
 
-resource "openstack_networking_secgroup_v2" "secgroup_tf" {
-  name        = "secgroup_tf"
-}
+Then modify the `main.tf` as follows:
 
-resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_1" {
-  direction         = "ingress"
-  ethertype         = "IPv4"
-  protocol          = "tcp"
-  port_range_min    = 22
-  port_range_max    = 22
-  remote_ip_prefix  = "0.0.0.0/0"
-  security_group_id = "${openstack_networking_secgroup_v2.secgroup_tf.id}"
+``` hcl
+resource "openstack_compute_instance_v2" "app_server" {
+  ...
+  metadata = {
+    my_instance_name = var.instance_name
+  }
 }
+```
 
-resource "openstack_networking_floatingip_v2" "fip_1" ...
+Apply the changes:
+``` shell
+lcl$ terraform apply -auto-approve
+
+Plan: 0 to add, 1 to change, 0 to destroy.
 ...
+Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
 ```
 
-Now, our instance is reachable via SSH (only!). But can we login? The reply is
-in Task #7.
+You should see the new tag added to the "Metadata" section of the Horizon
+dashboard:
+`https://engines.switch.ch/horizon/project/instances/<instance-ID>/`.
+
+:bulb: **Exercise:** input variables can also be passed on the `apply` command
+line. Find how to do that with another different value for the variable
+`instance_name`. :question: Would this last change be persistent if we rerun a
+plain `terraform apply`?
 
-### Task #5: input variables ###
 
 ### Task #6: queries with outputs ###
 
-### Task #7: SSH provisioning with Cloud-Init ###
+**Goal:** use output values to query a provisioned infrastructure.
+
+We have seen in the previous tasks that the infrastructure's status can be
+displayed via `terraform show`: a rather clumsy way, if you just want to
+extract some specific information. A better programmatic way of querying your
+infrastructure makes use of "outputs". In your sandbox, put the following in a
+file called `outputs.tf`:
 
-@@@@@ OLD part below@@@@
+``` hcl
+output "instance_id" {
+  description = "ID of the instance"
+  value       = openstack_compute_instance_v2.app_server.id
+}
+```
 
-Also, prepare a `~/terraform/OpenStack/outputs.tf` file with the appropriate
-contents to get the instance's public IP address (via `instance_public_ip`).
+We have declared two outputs. As usual with TF, before querying their
+associated values, we need to apply the changes:
 
+``` shell
+lcl$ terraform apply -auto-approve
+...
+Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
+
+Outputs:
 
+instance_id = "93914f14-e521-4cc1-acfe-046bc3fa31be"
+```
 
-Then apply!
+So, we already got the needed information, but, within a workflow, it is more
+practical to do something like:
 
 ``` shell
-lcl$ terraform apply
+lcl$ terraform output instance_id
 ```
 
-Verify that you can SSH as user `debian` into your instance:
+:bulb: **Exercise:** Add an output item to display the instance metadata tag
+`my_instance_name`.
 
+:question: What if the `my_instance_name` tag is changed outside TF? Try f.i.:
 ``` shell
-lcl$ ssh debian@$(terraform output -raw instance_public_ip) -i ../tf-cloud-init
+lcl$ openstack --os-cloud=engines server set \
+       --property my_instance_name="Foo-Bar" 93914f14-e521-4cc1-acfe-046bc3fa31be
+```
+
+:question: What must be done to have TF respect that external change?
+
+:question: How to revert an external change via TF?
+
+
+### Task #7: networking and SSH provisioning with Cloud-Init ###
+
+**Goal:** use Cloud-Init to provision an SSH access to your TF-managed
+instance.
+
+Did you try to SSH into the instance you created via TF? It cannot work,
+because we did not instructed TF about networking, login, users, keys or
+anything else. **This is left entirely to you as an exercise.** With reference
+to the official TF documentation for the [OpenStack
+provider](https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/), you shall:
+
+  1. Destroy your infrastructure. Guess how ;-)
+  1. Create an SSH key pair `tf-cloud-init`.
+  1. Create a new cloud-init file
+     `~/terraform/OpenStack/scripts/add-ssh.yaml` with the following content:
+     ``` yaml
+     #cloud-config
+     #^^^^^^^^^^^^
+     # DO NOT TOUCH the first line!
+     ---
+     groups:
+       - debian: [root, sys]
+       - terraform
+
+     users:
+       - default
+       - name: terraform
+         gecos: terraform
+         primary_group: terraform
+         groups: users, admin
+         ssh_authorized_keys:
+           - <your-SSH-pub-key-on-one-line>
+     ```
+     :warning: **Mind that the first line of this file must spell exactly
+     `#cloud-config`**!
+  1. Modify the `main.tf` as follows:
+     1. add a resource block for a custom v2 network *security group*;
+     1. add resource blocks for v2 *security group rules* allowing ICMP ping
+        and TCP ingress ports 22 from anywhere with any egress port open. These
+        resources shall reference your security group;
+     1. add a resource block for a v2 *floating IP* from the public pool;
+     1. add a resource block for a v2 *floating IP association* referencing
+        your floating IP resource and your compute instance resource;
+     1. extend your `"app_server"` resource block to:
+        1. reference your custom security group as well as the default one,
+        1. associate a public IP address,
+        1. include the above cloud-init file,
+     1. add a TF output `"instance_public_ip"`.
+
+:bulb: In the above instruction, "reference" means that the keys shall take
+values dynamically from variable expansion, like:
+``` hcl
+  key = ${<object.attribute...>}
+```
+
+When done, *validate* your new plan and *apply* it. Verify that you can ping
+and connect via SSH as user `terraform` into your instance:
+
+``` shell
+lcl$ ping $(terraform output -raw instance_public_ip)
+...
+lcl$ ssh terraform@$(terraform output -raw instance_public_ip) -i /path/to/private/key/tf-cloud-init
+...
 ```
diff --git a/SwitchEngines/conf/clouds.yaml.api_cred b/SwitchEngines/conf/clouds.yaml.api_cred
index 32777c9fafb5db941279219a1b7f23faa557c2dc..a07c76529d6a255ed977c698a24ce19b87bd9f00 100644
--- a/SwitchEngines/conf/clouds.yaml.api_cred
+++ b/SwitchEngines/conf/clouds.yaml.api_cred
@@ -1,10 +1,13 @@
-# Project-agnostic -- API credentials
+# -*- mode: yaml -*-
+# OpenStack conf file with *API* credentials --
+# <https://engines.switch.ch/horizon/project/api_access>
 clouds:
   engines:
     auth:
-      auth_url: https://keystone.cloud.switch.ch:5000/v3
+      auth_url: "https://keystone.cloud.switch.ch:5000/v3"
       username: "<your-user-name>"
-      password: "<your-password"
+      password: "<your-password>"
+      # project_name: "<your-project>"
       user_domain_name: "Default"
     interface: "public"
     identity_api_version: 3
diff --git a/SwitchEngines/conf/clouds.yaml.app_cred b/SwitchEngines/conf/clouds.yaml.app_cred
index 6021f0b6538e2121529afabb5a4b61da36752ca1..40a5b98cb781e27675a0604669d6fc58340ca113 100644
--- a/SwitchEngines/conf/clouds.yaml.app_cred
+++ b/SwitchEngines/conf/clouds.yaml.app_cred
@@ -1,12 +1,12 @@
-# Project-specific -- application credentials.
+# -*- mode: yaml -*-
+# OpenStack conf file with *application* credentials.
 clouds:
-  Cloud-MA-IaC:
+  engines:
     auth:
-      auth_url: https://keystone.cloud.switch.ch:5000/v3
+      auth_url: "https://keystone.cloud.switch.ch:5000/v3"
       application_credential_id: "<your-app-cred-ID>"
       application_credential_secret: "<your-app-cred-secret>"
-      user_domain_name: Default
-      project_domain_name: Default
     interface: "public"
+    region: "<your-region>"
     identity_api_version: 3
     auth_type: "v3applicationcredential"
diff --git a/SwitchEngines/main.tf.advnc b/SwitchEngines/main.tf.advnc
index c5ace7adaf62c2938ad383b304302acd56fd4f13..d1fe209a99075405e0512e77a60d8307b3b6e30c 100644
Binary files a/SwitchEngines/main.tf.advnc and b/SwitchEngines/main.tf.advnc differ
diff --git a/SwitchEngines/main.tf.basic b/SwitchEngines/main.tf.basic
index 3bcab00726c6a6ca1041f482cd1eb821b1778087..e6b51428b8bd34f1b83ff0bb76996660d441a302 100644
--- a/SwitchEngines/main.tf.basic
+++ b/SwitchEngines/main.tf.basic
@@ -1,12 +1,12 @@
+# -*- mode: terraform -*-
 terraform {
-  required_version = ">= 0.14.9"
   required_providers {
     openstack = {
       source  = "terraform-provider-openstack/openstack"
-      # version = "~> 1.35.0"
       version = "~> 1.48.0"
     }
   }
+  required_version = ">= 0.15.0"
 }
 
 # Configure the OpenStack Provider
@@ -15,7 +15,7 @@ provider "openstack" {
   project_domain_id = "<your-project-ID>"
   application_credential_id     = "<your-app-cred-ID>"
   application_credential_secret = "<your-app-cred-secret>"
-  region = "LS"
+  region = "<your-region>"
 }
 
 resource "openstack_compute_instance_v2" "app_server" {
@@ -23,30 +23,4 @@ resource "openstack_compute_instance_v2" "app_server" {
   image_id        = "<your-image-ID>"
   flavor_name     = "<your-flavor>"
   key_pair        = "TF_lab"
-  security_groups = ["default", "secgroup_tf"]
-}
-
-resource "openstack_networking_secgroup_v2" "secgroup_tf" {
-  name        = "secgroup_tf"
-}
-
-resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_1" {
-  direction         = "ingress"
-  ethertype         = "IPv4"
-  protocol          = "tcp"
-  port_range_min    = 22
-  port_range_max    = 22
-  remote_ip_prefix  = "0.0.0.0/0"
-  security_group_id = "${openstack_networking_secgroup_v2.secgroup_tf.id}"
-}
-
-resource "openstack_networking_floatingip_v2" "fip_1" {
-  pool = "public"
-}
-
-resource "openstack_compute_floatingip_associate_v2" "fip_1" {
-  floating_ip = "${openstack_networking_floatingip_v2.fip_1.address}"
-  instance_id = "${openstack_compute_instance_v2.app_server.id}"
-  # to avoid getting "Resource not found"
-  wait_until_associated = true
 }
diff --git a/SwitchEngines/outputs.tf b/SwitchEngines/outputs.tf
index 87c3f8461141a6cc8e49d008c9acb3229e799c16..0019196b6f6cbdf52fec3bb2055c8685cfa26627 100644
Binary files a/SwitchEngines/outputs.tf and b/SwitchEngines/outputs.tf differ
diff --git a/SwitchEngines/scripts/add-ssh.yaml b/SwitchEngines/scripts/add-ssh.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..88c19b119b7827e97b4dd87d6c33c44259d631c7
--- /dev/null
+++ b/SwitchEngines/scripts/add-ssh.yaml
@@ -0,0 +1,16 @@
+#cloud-config
+#^^^^^^^^^^^^
+# DO NOT TOUCH the first line!
+---
+groups:
+  - debian: [root, sys]
+  - terraform
+
+users:
+  - default
+  - name: terraform
+    gecos: terraform
+    primary_group: terraform
+    groups: users, admin
+    ssh_authorized_keys:
+      - <your-SSH-pub-key-on-one-line>
diff --git a/SwitchEngines/variables.tf b/SwitchEngines/variables.tf
new file mode 100644
index 0000000000000000000000000000000000000000..7f6dfe50702df019f653d84e6535e157aafe84bc
--- /dev/null
+++ b/SwitchEngines/variables.tf
@@ -0,0 +1,6 @@
+# -*- mode: terraform -*-
+variable "instance_name" {
+  description = "Value of the instance's name tag"
+  type        = string
+  default     = "TF-Lab-AppServerInstance"
+}