Terraform Cloud Project Bootcamp with Andrew Brown —
2.6.0 Terraform Cloud and Multi Homes

Gwen Leigh
5 min readOct 17, 2023

This article is part of my Terraform journey with Terraform Bootcamp by Andrew Brown and Andrew Bayko with Chris Williams and Shala.

My wild career adventure into Cloud Computing continues with Andrews, and I highly, highly recommend you to come join us if you are interested. Check out Andrews’ free youtube learning contents. You can also buy some paid courses here to support their good cause.

Agenda

Video here: 2 6 0 Terraform Cloud and Multi Homes

Issue #51 Goals

  • ✅ 1) Move our state back to Terraform Cloud with local execution
  • ✅ 2) Provision more than one terra home

Workflow

  • ✅ 1) Switch back to Terraform Cloud
  • ✅ 2) Configure multi homes
  • ✅ 3) Nested variables

1. Switch back to Terraform Cloud

We have been developing and executing locally in Gitpod workspace. In this last episode, Andrew and Andrew walk us through the migration process to move back from the local environment to ‘Terraform Cloud while executing locally’.

First, we go to Terraform Cloud and set our Default Execution Mode as Local. At the project level (our terra-house), set the Execution Mode as Custom > Local.

We have to be able to connect to the remote Terraform Cloud from local Gitpod workspace. So let’s bring back the configuration. Uncomment the cloud block inside terraform{} in main.tf.

terraform {
...
cloud {
organization = "mariachiinajar"
workspaces {
name = "terra-house"
}
}
}

Andrew updates gitpod.yml.

tasks:
- name: terraform
env:
TF_LOG=DEBUG
before: |
cd $PROJECT_ROOT
source ./bin/install_terraform_cli
source ./bin/generate_tfrc_credentials
source ./bin/set_tf_alias
+ source ./bin/build_provider

Andrew runs terraform init and terraform apply and it works.

2. Configure multi homes

Andrew creates another terratowns_home using the same module (terrahome_aws). The configuration is the same as before. Now that we are deploying homes for real, you can change the town to the one you like amongst cooker-cove, gamers-grotto, the-nomad-pad, video-valley, and melomaniac-mansion. Interesting things to note are:

  • module name can be anything. What matters is the path to the source.
  • As every resource has to have a unique name, the first home (home) and second home (home_voices)have different names.
  • There are better was to create multiple instances of the same resource. This could be a micro project to further our Terraform skills.
module "home_nomadiachi_hosting" {
source = "./modules/terrahome_aws"
user_uuid = var.teacherseat_user_uuid
public_path = var.nomadiachi.public_path
content_version = var.nomadiachi.content_version
}

resource "terratowns_home" "home" {
name = "Nomadiachi's nomadic life"
description = <<DESCRIPTION
Mariachi works nomadically so popping in and checking out here and there.
Pictures of some memorable moments, quick snapshots of days in and days out.
Come join my nomadinary journey? :D
DESCRIPTION
domain_name = module.home_nomadiachi_hosting.domain_name
town = "the-nomad-pad"
content_version = var.nomadiachi.content_version
}

module "home_voices_hosting" {
source = "./modules/terrahome_aws"
user_uuid = var.teacherseat_user_uuid
public_path = var.voices.public_path
content_version = var.voices.content_version
}

resource "terratowns_home" "home_voices" {
name = "Voices that keep me going"
description = <<DESCRIPTION
Reading is a mental nomadism. Reading quotes is traveling through million different minds and angles.
There were many moments and days I entertained quitting.
But there were voices that give me nudges and pushes (and punches and smashes... and slaps and !@#$@$).
I'm still here and keep moving forward.
Come have a look who are those with the badass voices. lol
DESCRIPTION
domain_name = module.home_voices_hosting.domain_name
town = "the-nomad-pad"
content_version = var.voices.content_version
}

Now Andrew reorganises the assets for homes. The folder structure is:

public
├── home1
│ ├── assets
│ │ ├── ...
│ │ └── image.jpg
│ ├── index.html
│ └── error.html
├── home2
│ ├── assets
│ │ ├── ...
│ │ └── image.jpg
│ ├── index.html
└── └── error.html

In modules/terrahome_aws/variables.tf, change the variable name as below.

- variable "index_html_filepath" {
- description = "File path for index.html"
+ variable "public_path" {
+ description = "The file path for the public directory"

In modules/terrahome_aws/resource-storage.tf, modify the paths using the public_path variable both aws_s3_object.index_html and error_html.

# resource-storage.tf

resource "aws_s3_object" "index_html" {
bucket = aws_s3_bucket.website_bucket.bucket
key = "error.html"
- source = "${path.root}/public/index.html"
+ source = "${var.public_path}/index.html"
content_type = "text/html"

- etag = filemd5("${path.root}/public/index.html")
+ etag = filemd5("${var.public_path}/index.html")
}

3. Nested variables

Andrew decides to reorganise the variables using the nested variable structure. In Terraform, we can achieve this using object or map.

// variables.tf at root

variable "nomadiachi" {
type = object({
public_path = string
content_version = number
})
}

variable "voices" {
type = object({
public_path = string
content_version = number
})
}

Now we can use the nested variables as below.

// main.tf at root

module "home_nomadiachi_hosting" {
source = "./modules/terrahome_aws"
user_uuid = var.teacherseat_user_uuid
public_path = var.nomadiachi.public_path
content_version = var.nomadiachi.content_version
}

Andrew changes the module name to terrahome_aws so we make the changes accordingly across the files where the module is referenced.

// outputs.tf

module "home_nomadiachi_hosting" {
- source = "./modules/terrahouse_aws"
+ source = "./modules/terrahome_aws"
user_uuid = var.teacherseat_user_uuid
public_path = var.nomadiachi.public_path
content_version = var.nomadiachi.content_version
}

Andrew updates resourceHouseUpdate function in main.go. (45:32)

  • Add
 // parse response JSON
var responseData map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&responseData); err != nil {
return diag.FromErr(err)
}
  • Replace

// StatusOK = 200 HTTP Response Code
if resp.StatusCode != http.StatusOK {
- return diag.FromErr(fmt.Errorf("failed to update home resource, status_code: %d, status: %s", resp.StatusCode, resp.Status))
+ return diag.FromErr(fmt.Errorf("failed to create home resource, status_code: %d, status: %s, body: %s", resp.StatusCode, resp.Status, responseData))
}

Then run:

./bin/build_provider
terraform init

Andrew runs terraform init and terraform apply and the resources are running on TerraTowns. In case you run into error, compare your code against Andrew’s source code here.

My homes are running in the Nomad Pad town!

📑 Notes

  • With nested variables, we can organise the structure of variables more neatly.
  • The term “interpolation” means embedding variables within other strings.
  • If you are looking to further your terraform skills, you can try refactoring the multihomes using, for example, for_each or map function.

Resources

Bootcamp

--

--

Gwen Leigh

Cloud Engineer to be. Actively building my profile, network and experience in the cloud space. .