diff --git a/course/providers.hcl b/course/providers.hcl index 624476d..7ec20d5 100644 --- a/course/providers.hcl +++ b/course/providers.hcl @@ -2,7 +2,7 @@ resource "chapter" "providers" { title = "Providers" tasks = { - install_provider = resource.task.install_provider + install_provider = resource.task.install_provider provider_configuration = resource.task.provider_configuration } @@ -16,10 +16,10 @@ resource "chapter" "providers" { } resource "task" "install_provider" { - prerequisites = resource.chapter.workflow.tasks != null ? values(resource.chapter.workflow.tasks).*.id : [] + prerequisites = resource.chapter.workflow.tasks != null ? values(resource.chapter.workflow.tasks).*.meta.id : [] config { - user = "root" + user = "root" target = variable.terraform_target } @@ -27,12 +27,12 @@ resource "task" "install_provider" { description = "The vault provider is added to the code" check { - script = file("checks/providers/install_provider/provider_added") + script = file("checks/providers/install_provider/provider_added") failure_message = "The \"hashicorp/vault\" provider was not added to required_providers" } solve { - script = file("checks/providers/install_provider/solve") + script = file("checks/providers/install_provider/solve") timeout = 120 } } @@ -41,7 +41,7 @@ resource "task" "install_provider" { description = "The vault provider is installed" check { - script = file("checks/providers/install_provider/provider_installed") + script = file("checks/providers/install_provider/provider_installed") failure_message = "the vault provider was not correctly initialized" } } @@ -53,7 +53,7 @@ resource "task" "provider_configuration" { ] config { - user = "root" + user = "root" target = variable.terraform_target } @@ -61,7 +61,7 @@ resource "task" "provider_configuration" { description = "The provider configuration is added" check { - script = file("checks/providers/provider_configuration/configuration_added") + script = file("checks/providers/provider_configuration/configuration_added") failure_message = "The provider configuration does not specify the Vault address" } diff --git a/course/state.hcl b/course/state.hcl index 97ced3b..142d002 100644 --- a/course/state.hcl +++ b/course/state.hcl @@ -3,8 +3,8 @@ resource "chapter" "state" { tasks = { viewing_state = resource.task.viewing_state - list_state = resource.task.list_state - show_state = resource.task.show_state + list_state = resource.task.list_state + show_state = resource.task.show_state } page "viewing_state" { @@ -21,10 +21,10 @@ resource "chapter" "state" { } resource "task" "viewing_state" { - prerequisites = resource.chapter.providers.tasks != null ? values(resource.chapter.providers.tasks).*.id : [] + prerequisites = resource.chapter.providers.tasks != null ? values(resource.chapter.providers.tasks).*.meta.id : [] config { - user = "root" + user = "root" target = variable.terraform_target } @@ -36,7 +36,7 @@ resource "task" "viewing_state" { } check { - script = file("checks/state/viewing_state/show_command") + script = file("checks/state/viewing_state/show_command") failure_message = "The terraform show command was not used to view the state" } @@ -49,7 +49,7 @@ resource "task" "viewing_state" { description = "The state is in a machine-readable format" check { - script = file("checks/state/viewing_state/json_flag") + script = file("checks/state/viewing_state/json_flag") failure_message = "The terraform state was not viewed in a machine-readable format such as JSON" } } @@ -61,7 +61,7 @@ resource "task" "list_state" { ] config { - user = "root" + user = "root" target = variable.terraform_target } @@ -69,7 +69,7 @@ resource "task" "list_state" { description = "The state for all resources is listed" check { - script = file("checks/state/list_state/list_command") + script = file("checks/state/list_state/list_command") failure_message = "The terraform state list command was not used" } @@ -85,7 +85,7 @@ resource "task" "show_state" { ] config { - user = "root" + user = "root" target = variable.terraform_target } @@ -93,7 +93,7 @@ resource "task" "show_state" { description = "The state of the Vault Docker container was shown" check { - script = file("checks/state/show_state/show_command") + script = file("checks/state/show_state/show_command") failure_message = "The terraform state show command was not used to view the state for docker_container.vault" } diff --git a/course/workflow.hcl b/course/workflow.hcl index 5bc3dc1..a4c18cb 100644 --- a/course/workflow.hcl +++ b/course/workflow.hcl @@ -2,10 +2,10 @@ resource "chapter" "workflow" { title = "Terraform workflow" tasks = { - terraform_init = resource.task.terraform_init - terraform_plan = resource.task.terraform_plan - terraform_apply = resource.task.terraform_apply - update_resources = resource.task.update_resources + terraform_init = resource.task.terraform_init + terraform_plan = resource.task.terraform_plan + terraform_apply = resource.task.terraform_apply + update_resources = resource.task.update_resources terraform_destroy = resource.task.terraform_destroy } @@ -31,10 +31,10 @@ resource "chapter" "workflow" { } resource "task" "terraform_init" { - prerequisites = resource.chapter.installation.tasks != null ? values(resource.chapter.installation.tasks).*.id : [] + prerequisites = resource.chapter.installation.tasks != null ? values(resource.chapter.installation.tasks).*.meta.id : [] config { - user = "root" + user = "root" target = variable.terraform_target } @@ -42,12 +42,12 @@ resource "task" "terraform_init" { description = "The terraform_basics working directory is initialized" check { - script = file("checks/workflow/terraform_init/init_command") + script = file("checks/workflow/terraform_init/init_command") failure_message = "'terraform init' command was not used to initialize the working directory" } solve { - script = file("checks/workflow/terraform_init/solve") + script = file("checks/workflow/terraform_init/solve") timeout = 120 } } @@ -56,7 +56,7 @@ resource "task" "terraform_init" { description = "The terraform lock file has been created" check { - script = file("checks/workflow/terraform_init/dependency_lock_file") + script = file("checks/workflow/terraform_init/dependency_lock_file") failure_message = "'.terraform.lock.hcl' file does not exist" } } @@ -65,7 +65,7 @@ resource "task" "terraform_init" { description = "The Docker provider is initialized" check { - script = file("checks/workflow/terraform_init/docker_provider") + script = file("checks/workflow/terraform_init/docker_provider") failure_message = "the docker provider was not correctly initialized" } } @@ -77,7 +77,7 @@ resource "task" "terraform_plan" { ] config { - user = "root" + user = "root" target = variable.terraform_target } @@ -85,7 +85,7 @@ resource "task" "terraform_plan" { description = "Use the terraform plan command" check { - script = file("checks/workflow/terraform_plan/plan_command") + script = file("checks/workflow/terraform_plan/plan_command") failure_message = "'terraform plan' command was not used to preview changes" } @@ -101,20 +101,20 @@ resource task "terraform_apply" { ] config { - user = "root" + user = "root" target = variable.terraform_target } condition "apply_command" { description = "Use the terraform apply command" - + check { - script = file("checks/workflow/terraform_apply/apply_command") + script = file("checks/workflow/terraform_apply/apply_command") failure_message = "'terraform apply' command was not used to apply changes" } solve { - script = file("checks/workflow/terraform_apply/solve") + script = file("checks/workflow/terraform_apply/solve") timeout = 300 } } @@ -123,16 +123,16 @@ resource task "terraform_apply" { description = "The Terraform state contains the Docker image" check { - script = file("checks/workflow/terraform_apply/state_image") + script = file("checks/workflow/terraform_apply/state_image") failure_message = "docker_image.vault not found in terraform state" } } - condition "state_container" { + condition "state_container" { description = "The Terraform state contains the Docker container" check { - script = file("checks/workflow/terraform_apply/state_container") + script = file("checks/workflow/terraform_apply/state_container") failure_message = "docker_container.vault not found in terraform state" } } @@ -141,7 +141,7 @@ resource task "terraform_apply" { description = "The Docker image is created" check { - script = file("checks/workflow/terraform_apply/docker_image") + script = file("checks/workflow/terraform_apply/docker_image") failure_message = "the docker \"vault\" image with tag \"1.12.6\" was not pulled" } } @@ -150,7 +150,7 @@ resource task "terraform_apply" { description = "The Docker container is running" check { - script = file("checks/workflow/terraform_apply/docker_container") + script = file("checks/workflow/terraform_apply/docker_container") failure_message = "the docker container named \"terraform-basics-vault\" is not running" } } @@ -162,7 +162,7 @@ resource "task" "update_resources" { ] config { - user = "root" + user = "root" target = variable.terraform_target } @@ -170,12 +170,12 @@ resource "task" "update_resources" { description = "Change the version of the vault image" check { - script = file("checks/workflow/update_resources/update_code") + script = file("checks/workflow/update_resources/update_code") failure_message = "The version of the vault image has not been updated to 1.12.7" } solve { - script = file("checks/workflow/update_resources/solve") + script = file("checks/workflow/update_resources/solve") timeout = 300 } } @@ -184,7 +184,7 @@ resource "task" "update_resources" { description = "The Terraform state is updated" check { - script = file("checks/workflow/update_resources/state_changed") + script = file("checks/workflow/update_resources/state_changed") failure_message = "The Terraform state does not contain the updated resources" } } @@ -193,7 +193,7 @@ resource "task" "update_resources" { description = "The Docker image is updated" check { - script = file("checks/workflow/update_resources/docker_image") + script = file("checks/workflow/update_resources/docker_image") failure_message = "the docker 'vault' image with tag '1.12.7' was not pulled" } } @@ -202,7 +202,7 @@ resource "task" "update_resources" { description = "The Docker container is running" check { - script = file("checks/workflow/update_resources/docker_container") + script = file("checks/workflow/update_resources/docker_container") failure_message = "the docker container named 'terraform-basics-vault' is not running" } } @@ -214,7 +214,7 @@ resource "task" "terraform_destroy" { ] config { - user = "root" + user = "root" target = variable.terraform_target } @@ -222,7 +222,7 @@ resource "task" "terraform_destroy" { description = "Use the terraform destroy command" check { - script = file("checks/workflow/terraform_destroy/destroy_command") + script = file("checks/workflow/terraform_destroy/destroy_command") failure_message = "'terraform destroy' command was not used to clean up the environment" } @@ -235,7 +235,7 @@ resource "task" "terraform_destroy" { description = "The Terraform state is empty" check { - script = file("checks/workflow/terraform_destroy/state_empty") + script = file("checks/workflow/terraform_destroy/state_empty") failure_message = "the terraform state is not empty" } } @@ -244,7 +244,7 @@ resource "task" "terraform_destroy" { description = "The Docker container is removed" check { - script = file("checks/workflow/terraform_destroy/docker_container") + script = file("checks/workflow/terraform_destroy/docker_container") failure_message = "the docker container named 'terraform-basics-vault' is still running" } } @@ -253,7 +253,7 @@ resource "task" "terraform_destroy" { description = "The Docker container is removed" check { - script = file("checks/workflow/terraform_destroy/docker_image") + script = file("checks/workflow/terraform_destroy/docker_image") failure_message = "the docker 'vault' image with tag '1.12.7' was not removed" } } diff --git a/packer/files/install.sh b/packer/files/install.sh new file mode 100644 index 0000000..cc8889e --- /dev/null +++ b/packer/files/install.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -e + +export DEBIAN_FRONTEND=noninteractive + +echo "waiting 180 seconds for cloud-init to update /etc/apt/sources.list" +timeout 180 /bin/bash -c \ + 'until stat /var/lib/cloud/instance/boot-finished 2>/dev/null; do echo waiting ...; sleep 1; done' + +apt-get update && apt-get -y upgrade +apt-get -y install \ + apt-transport-https \ + ca-certificates \ + software-properties-common \ + sudo \ + unzip \ + git \ + curl \ + jq \ + vim + +# Install Docker +curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | sudo apt-key add - +add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \ + $(lsb_release -cs) \ + stable" + +apt-get update && apt-get install -y docker-ce + +# Install Jumppad +curl -s -L https://github.com/jumppad-labs/jumppad/releases/download/${JUMPPAD_VERSION}/jumppad_${JUMPPAD_VERSION}_linux_x86_64.tar.gz | tar -xz +mv jumppad /usr/local/bin/jumppad +chmod +x /usr/local/bin/jumppad + +cp /tmp/resources/jumppad-connector.service /etc/systemd/system/jumppad-connector.service + +systemctl daemon-reload +systemctl enable jumppad-connector.service + +# Pre-pull docker images +for IMAGE in $JUMPPAD_IMAGES; do + docker pull $IMAGE +done \ No newline at end of file diff --git a/packer/files/jumppad-connector.service b/packer/files/jumppad-connector.service new file mode 100644 index 0000000..aecd05e --- /dev/null +++ b/packer/files/jumppad-connector.service @@ -0,0 +1,9 @@ +[Unit] +Description=Jumppad Connector + +[Service] +WorkingDirectory=/root +ExecStart=/usr/local/bin/start-connector + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/packer/files/start-connector b/packer/files/start-connector new file mode 100644 index 0000000..c1c24da --- /dev/null +++ b/packer/files/start-connector @@ -0,0 +1,31 @@ +#!/bin/bash -e + +HOSTNAME=$(hostname) + +# Generate CA certificates for the Jumppad connector +/usr/local/bin/jumppad connector generate-certs \ + --non-interactive \ + --ca \ + --dns-name \ + /root/.jumppad/certs/ + +# Generate leaf certificates for the Jumppad connector +/usr/local/bin/jumppad connector generate-certs \ + --non-interactive \ + --root-ca /root/.jumppad/certs/root.cert \ + --root-key /root/.jumppad/certs/root.key \ + --leaf \ + --dns-name ${HOSTNAME},localhost,*.jumppad.dev,localhost:30001,localhost:30002 \ + --ip-address 127.0.0.1,::1 \ + /root/.jumppad/certs/ + +# Start the Jumppad connector +jumppad connector run \ + --non-interactive \ + --grpc-bind=:30001 \ + --http-bind=:30002 \ + --api-bind=:30003 \ + --root-cert-path=/root/.jumppad/certs/root.cert \ + --server-cert-path=/root/.jumppad/certs/leaf.cert \ + --server-key-path=/root/.jumppad/certs/leaf.key \ + --log-level=debug \ No newline at end of file diff --git a/packer/image.pkr.hcl b/packer/image.pkr.hcl deleted file mode 100644 index 43545de..0000000 --- a/packer/image.pkr.hcl +++ /dev/null @@ -1,25 +0,0 @@ -packer { - required_plugins { - googlecompute = { - version = ">= 1.1.1" - source = "github.com/hashicorp/googlecompute" - } - } -} - -source "googlecompute" "instruqt_terraform" { - project_id = "jumppad" - source_image = "debian-10-buster-v20230711" - ssh_username = "packer" - zone = "europe-west1-b" -} - -build { - sources = ["sources.googlecompute.instruqt_terraform"] - - provisioner "shell" { - script = "script.sh" - pause_before = "10s" - timeout = "600s" - } -} diff --git a/packer/main.pkr.hcl b/packer/main.pkr.hcl new file mode 100644 index 0000000..5a298e9 --- /dev/null +++ b/packer/main.pkr.hcl @@ -0,0 +1,63 @@ +variable "jumppad_version" { + type = string +} + +variable "jumppad_images" { + type = list(string) + default = [] +} + +variable "project_id" { + type = string +} + +variable "region" { + type = string + default = "europe-west1" +} + +variable "zone" { + type = string + default = "europe-west1-d" +} + +packer { + required_plugins { + googlecompute = { + source = "github.com/hashicorp/googlecompute" + version = "~> 1" + } + } +} + +source "googlecompute" "jumppad" { + project_id = var.project_id + region = var.region + zone = var.zone + + image_family = "jumppad" + image_name = regex_replace("terraform-workshop-${var.jumppad_version}", "[^a-zA-Z0-9_-]", "-") + + source_image_family = "ubuntu-2204-lts" + machine_type = "n1-standard-4" + disk_size = 20 + + ssh_username = "root" +} + +build { + sources = ["source.googlecompute.jumppad"] + + provisioner "file" { + source = "files" + destination = "/tmp/resources" + } + + provisioner "shell" { + script = "files/install.sh" + environment_vars = [ + "JUMPPAD_VERSION=${trimprefix(var.jumppad_version, "v")}", + "JUMPPAD_IMAGES=${join(" ", var.jumppad_images)}" + ] + } +} \ No newline at end of file diff --git a/packer/main.pkrvars.hcl b/packer/main.pkrvars.hcl new file mode 100644 index 0000000..097f9f9 --- /dev/null +++ b/packer/main.pkrvars.hcl @@ -0,0 +1,9 @@ +jumppad_images = [ + "ghcr.io/rpardini/docker-registry-proxy:0.6.4", + "ghcr.io/jumppad-labs/terraform-workshop:v0.3.3", + "ghcr.io/jumppad-labs/docs:v0.4.0", +] + +jumppad_version = "0.7.0" + +project_id = "jumppad" \ No newline at end of file