# Infrastructure as Code-workshop med Rett i prod đ Denne workshopen gir en intro til infrastructure as code (IaC) med [terraform](https://www.terraform.io/). Se slides i [docs](/docs). NB! Denne workshopen krever at enkelte ressurser er satt opp for Ă„ bruke egne domenenavn. Dersom du skal gĂ„ gjennom workshopen pĂ„ egenhĂ„nd vil ikke alt fungere. ## FĂžr du starter 1. InstallĂ©r `az` og `terraform`, `npm` og `node`, f.eks. ved hjelp av [brew](https://brew.sh/) om du bruker macOS: `brew install azure-cli terraform node@16`. Sjekk at terraform versjonen din er minst `v1.0.0` ved Ă„ kjĂžre `terraform version`, og at node versjonen er minst `v16.0.0` ved Ă„ kjĂžre `node --version`. 1. Det kan vĂŠre lurt Ă„ installere en plugin i editoren din. VS Code har f.eks. extensionen "Hashicorp Terraform". Alternativt bruke et JetBrains IDE som IntelliJ med pluginen "HashiCorp Terraform / HCL language support". 1. Klon repoet med git 1. Om du har tid fĂžr workshopen starter, kan det vĂŠre lurt Ă„ navigere til `frontend/` og kjĂžre `npm ci`. 1. Du vil fĂ„ utlevert credentials (et brukernavn og passord) for Ă„ fĂ„ tilgang til Azure-portalen. GĂ„ til `portal.azure.com` og logg inn med de credentials du har fĂ„tt utdelt. 1. NĂ„r du har kommet til Azure-portalen, sjekk at det stĂ„r "Bekk Terraform Workshop" Ăžverst til hĂžyre. Dersom det ikke gjĂžr det, trykk pĂ„ profilbildet ditt (Ăžverst til hĂžyre), deretter "Switch directory" og velg "Bekk Terraform Workshop" pĂ„ siden du kommer til. 1. Dersom du allerede har en bruker i Azure-portalen, trykk pĂ„ profilbildet Ăžverstt til hĂžyre og trykk "Sign in with a different account". Logg inn med credentials du har fĂ„tt utdelt. 1. Skriv `az login` i terminalen for Ă„ logge inn i Azure. NĂ„r det er gjort kan du bruke `az account show` til Ă„ sjekke at du er logget pĂ„, og at du bruker `iac-workshop`-subscriptionen. Dersom det ikke stemmer kan du bruke `az account set -s iac-workshop` for Ă„ bytte subscription, verifiser etterpĂ„ med `az account show`. Du er nĂ„ klar til Ă„ starte! ## Terraform Dette repoet har tre mapper: `frontend/`, `backend/` og `infrastructure/`. De to fĂžrste inneholder koden for hhv. frontenden og backenden, og er med slik at du kan deploye en full app som faktisk fungerer. `infrastructure/` er den mappen som inneholder terraform-koden, og alle kommandoer og nye filer du lager skal ligge i denne mappen, men mindre noe annet er spesifisert. I `infrastructure/`-mappen er det forelĂžpig ikke sĂ„ mye: * I `terraform.tf` beskrives hvilke *providers* du trenger, og konfigurasjonen av disse. En provider kan sammenliknes med et bibliotek/*library* fra andre programmeringssprĂ„k. `azurerm` er en slik provider, som definerer ressurser i Azure du kan bruke og oversetter til riktige API-kall nĂ„r du kjĂžrer koden. * `main.tf` inneholder noen konstanter i `locals`-blokken, som kan brukes i programmet. Merk at `locals` kan defineres i en hvilken som helst terraform-fil, og vĂŠre globalt tilgjengelige i alle andre filer. `main.tf` inneholder ogsĂ„ en definisjon av ressursgruppen som skal opprettes. * `variables.tf` inneholder variable som gis til terraform. `variable` likner litt pĂ„ `locals`, men disse kan spesifiseres og overskrives nĂ„r terraform kjĂžres, f.eks. ved Ă„ gi et ekstra argument pĂ„ kommandolinjen. Det er litt tungvint Ă„ spesifisere variable pĂ„ kommandolinjen, sĂ„ vi kommer tilbake til hvordan vi kan gjĂžre dette enklere. `location` er den eneste variabelen som er definert forelĂžpig, og den har fĂ„tt en default-verdi, sĂ„ det er ikke noe vi trenger Ă„ gjĂžre noe med forelĂžpig. Det var mye tekst, la oss gĂ„ videre til godsakene! 1. FĂžr du kan provisjonere infrastruktur med terraform, mĂ„ du initialisere providerne som er spesifisert i `terraform.tf`. Dette kan du gjĂžre ved Ă„ kjĂžre `terraform init` (husk Ă„ kjĂžre den fra `infrastructure/`-mappen!). Dette gjĂžr ingen endringer pĂ„ infrastrukturen, men oppretter bl.a. `.terraform`-mappen. `terraform init` mĂ„ kjĂžres pĂ„ nytt om du Ăžnsker Ă„ installere eller oppgradere providers. **NB!** `.terraform` mĂ„ ikke committes til git, da den kan inneholde sensitiv informasjon. 1. NĂ„r terraform er initialisert kan vi provisjonere infrastruktur ved Ă„ kjĂžre `terraform apply`. FĂžrst vil terraform gi en oversikt over endringene som blir gjort. Her opprettes en ressursgruppe i Azure og en random string, `id`, som brukes for Ă„ automatisk gi unike navn pĂ„ ressursene vi skal opprette, f.eks. ressursgruppen. Skriv `yes` nĂ„r terraform spĂžr om du er sikker pĂ„ om du vil fortsette. 1. Dersom alt gikk fint kan du finne navnet pĂ„ ressursgruppen i en av de siste linjene i outputen: ```output azurerm_resource_group.rg: Creation complete after 1s [id=/subscriptions/9539bc24-8692-4fe2-871e-3733e84b1b73/resourceGroups/iac-workshop-xxxxxxxx] ``` Det er den siste delen (`iac-workshop-xxxxxxxx`) vi er interessert i. Dette er navnet pĂ„ ressursgruppen, og `xxxxxxxx` vil vĂŠre den tilfeldige strengen som ble generert. 1. GĂ„ til [Azure-portalen](https://portal.azure.com/), og sjekk at du kan finne ressursgruppen din. Den skal (forelĂžpig) vĂŠre tom. ## Backend Backend-koden bygget til et Docker-image, som lastes opp i GitHub package registry. Vi skal nĂ„ sette opp en Azure Container Group som laster ned imaget og kjĂžrer det som en container. 1. Opprett en ny fil `backend.tf` i `infrastructure/`. 1. Opprett en `locals` blokk i `backend.tf` med fĂžlgende innhold: ```terraform locals { server_port = "8080" mgmt_port = "8090" } ``` Her opprettes to konstanter `server_port` og `mgmt_port`, som vi kan referere til senere, f.eks. ved Ă„ skrive `local.server_port`. Verdiene som er gitt er ikke tilfeldige, og samsvarer med det som stĂ„r i `backend/src/main/resources/application.properties`. 1. Vi trenger ogsĂ„ en ny variabel, `backend_image`, i `variables.tf`. Den kan defineres slik: ```terraform variable "backend_image" { type = string description = "The Docker image to run for the backend" } ``` Her deklareres en variabel, `backend_image`, og den mĂ„ vĂŠre av typen `string`. Vi kan referere til denne variablen ved Ă„ skrive `var.backend_image`. 1. Dersom du nĂ„ kjĂžrer `terraform apply` vil du bli bedt om Ă„ oppgi variabelen. Den vil vĂŠre `ghcr.io/bekk/iac-workshop-backend:latest`. 1. Vi trenger ikke oppgi variabelen hver gang, for vi kan nemlig putte den i en egen fil som leses automatisk av terraform. Opprett en ny fil `variables.auto.tfvars` med linjen: ```terraform backend_image = "ghcr.io/bekk/iac-workshop-backend:latest" ``` Dersom du nĂ„ kjĂžrer `terraform apply` pĂ„ nytt vil du ikke lenger trenge Ă„ skrive inn variabelen. 1. Videre skal vi generere en tilfeldig streng som applikasjonen trenger for Ă„ generere JWT-tokens. Det kan vi gjĂžre slik: ```terraform resource "random_password" "jwt-secret" { length = 64 special = false lower = true upper = true number = true } ``` Denne strengen kan vi senere bruke ved Ă„ referere til `random_password.jwt-secret.result`. 1. Til sist mĂ„ vi opprette en Azure Container Group som faktisk oppretter backenden: ```terraform resource "azurerm_container_group" "backend" { resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location name = "${local.resource_prefix}backend" ip_address_type = "Public" dns_name_label = local.unique_id_raw os_type = "Linux" container { name = "backend" image = var.backend_image cpu = "1" memory = "1" ports { port = local.server_port protocol = "TCP" } secure_environment_variables = { "JWT_SECRET" = random_password.jwt-secret.result } readiness_probe { http_get { path = "/actuator/health" port = local.mgmt_port scheme = "Http" } } } } ``` Det var en del kode! La oss brekke det opp litt, og se pĂ„ det viktigste: * Vi oppretter her en ressurs av typen `azurerm_container_group`, og kaller denne ressursen for `backend` i fĂžrste linje. Merk at `backend` er kun et internt navn i terraform, og ikke navnet ressursen fĂ„r i Azure! Det gjĂžr at vi senere kan referere til ressursen med `azurerm_container_group.backend`. * Videre gir vi en del argumenter til ressursblokken. F.eks. setter vi `resource_group_name` og `location` til Ă„ tilsvare ressursgruppen som ble opprettet i `main.tf`. `name` settes ved Ă„ konkatenere konstanten `local.resource_prefix` (ogsĂ„ fra `main.tf`) med strengen `backend`, slik at ressursnavnet i Azure blir `iac-workshop-backend`. * `container`-blokken er veldig lik en container-definisjon i Kubernetes (selv om syntaksen er ulik). Merk spesielt at vi setter `image` til variabelen vi definerte i et tidligere steg. Azure vil laste ned imaget for oss (gitt at imaget er Ă„pent tilgjengelig) og kjĂžre det. `ports`-blokken definerer hvilken port applikasjonen skal vĂŠre tilgjengelig pĂ„. `readiness_probe`-blokken definerer et endepunkt som kan brukes til Ă„ sjekke at applikasjonen kjĂžrer som den skal, akkurat som i Kubernetes. 1. NĂ„ skal vi ha det som trengs for Ă„ provisjonere opp backenden med `terraform apply`. Dette vil ta litt tid fĂžrste gangen det gjĂžres. Sjekk etterpĂ„ at du finner en ressurs av typen "Container instances" i ressursgruppen din som heter `iac-workshop-backend`. 1. Dersom du klikker pĂ„ ressursen vil du fĂ„ en oversikt over noen egenskaper, bl.a. FQDN. Det stĂ„r for "Fully Qualified Domain Name" og er domenenavnet for backenden. Ettersom applikasjonen kjĂžrer pĂ„ port `8080` mĂ„ vi legge det pĂ„ i tillegg for Ă„ koble til. Den fulle addressen blir dermed `xxxxxxx.westeurope.azurecontainer.io:8080`, der `xxxxxxxx` fortsatt er den unike id-en generert av terraform. Denne siden vil gi en feilmelding fordi det ikke er et endepunkt definert av applikasjonen der, men om du gĂ„r til en av applikasjonens definerte endepunkt, f.eks. `xxxxxxxx.westeurope.azurecontainer.io:8080/api/articles` bĂžr du fĂ„ en JSON-respons for en artikkel med tittel "Hello World". 1. Det er litt tungvint Ă„ klikke seg gjennom Azure-portalen for Ă„ finne domenenavnet til appen, sĂ„ vi kan definere output i terraform i stedet. Opprett en ny fil `outputs.tf`, med fĂžlgende kode: ```terraform output "backend_url" { value = "http://${azurerm_container_group.backend.fqdn}:${local.server_port}" } ``` Her lager vi en ny `output`, `backend_url`, som bestĂ„r av `fqdn` pĂ„ `backend` ressursen, og portnummeret. 1. KjĂžr `terraform apply` pĂ„ nytt, og sjekk at du fĂ„r `backend_url` liknende dette: ```output Outputs: backend_url = "http://xxxxxxxx.westeurope.azurecontainer.io:8080" ``` Det var backenden! Dersom du nĂ„ fĂ„r en god respons fra `http://xxxxxxxx.westeurope.azurecontainer.io:8080/api/articles` og `backend_url` som output kan du gĂ„ videre til frontenden. ## Frontend Vi skal bruke Azure Blob Storage til Ă„ hoste statiske filer frontend-filer. Forenklet sett skal vi bruke en Azure Storage Account som en tradisjonell webserver. FĂžrst skal vi opprette en ny storage account: 1. Opprett en ny fil, `frontend.tf`, og legg til fĂžlgende kode og erstatt `<ressursgruppenavn>` og `<ressursgrupperegion>` med riktige verdier ved Ă„ bruke ressursgruppe-ressursen som er opprettet tidligere. (Hint: hvordan er dette gjort for andre ressurser vi har allerede har opprettet?) ```terraform resource "azurerm_storage_account" "web" { name = local.unique_id_sanitized resource_group_name = <ressursgruppenavn> location = <ressursgrupperegion> account_tier = "Standard" account_replication_type = "LRS" allow_nested_items_to_be_public = true enable_https_traffic_only = false min_tls_version = "TLS1_2" } ``` Her skjer det igjen litt forskjellig: * Vi har opprettet en ressurs av typen `azurerm_storage_account` som har (det interne) navnet `web`. * `name` er satt til `local.unique_id_sanitized`, som er definert i `main.tf`. Navnet pĂ„ en storage account mĂ„ vĂŠre globalt unikt, dvs. ingen storage accounts i Azure kan ha det samme navnet, dermed mĂ„ vi ha et navn som inneholder en unik id som reduserer sjansen for at noen andre har samme navn. * `allow_nested_items_to_be_public` er verdt Ă„ merke seg. Denne tillater at hvem som helst kan fĂ„ tilgang til blobs (filer) i storage accounten, sĂ„ lenge de kjenner URL-en. Normalt vil det vĂŠre lurt Ă„ sette denne `false`, men her Ăžnsker vi at andre kan fĂ„ tilgang og setter den til `true`, slik at vi kan bruke den til Ă„ statiske filer for en allment tilgjengelig nettside. * `enable_https_traffic_only` er vanligvis lurt Ă„ sette til `true`, men forelĂžpig skal vi ikke sette opp HTTPS (det kan du gjĂžre i ekstraoppgavene pĂ„ slutten). 1. KjĂžr `terraform apply`. NĂ„r storage accounten er opprettet kan du sjekke i ressursgruppen din at du finner en ressurs av typen "Storage account" med navn `iacworkshopxxxxxxxx`. 1. For Ă„ bruke storage accounten til en server for statiske filer mĂ„ vi skru pĂ„ "static website" featuren. For Ă„ fĂ„ til det mĂ„ vi legge til en ny `static_website`-blokk inni `azurerm_storage_account`-blokken: ```terraform resource "azurerm_storage_account" "web" { // Samme argumenter som i forrige kodeblokk static_website { index_document = "index.html" } } ``` Hva skjer her? * Ved Ă„ legge til `static_website`-blokken vil det opprettes en *storage account container* som vi kan legge filer i. Dette er ikke det samme som en Kubernetes container. En storage account container er en gruppering av *blobs*, og en blob er en fil som kan vĂŠre pĂ„ et hvilket som helst format, f.eks. tekst eller bilder. Denne storage account containeren vil hete `$web`. * `index_document`-argumentet spesifiserer navnet pĂ„ filen som brukes til nĂ„r det kommer en request til rot-URL-en. 1. KjĂžr `terraform apply`. GĂ„ sĂ„ til Azure-portalen, klikk deg inn pĂ„ storage accounten og klikk pĂ„ "Containers" i menyen til venstre. 1. Hva blir sĂ„ URL-en til denne storage accounten? La oss lage en ny `output` i `outputs.tf`: ```terraform output "storage_account_web_url" { value = azurerm_storage_account.web.primary_web_endpoint } ``` Her lager vi en ny `output`, som referer til `azurerm_storage_account.web` som vi nettopp opprettet. URL-en er definert i `primary_web_endpoint`. Du bĂžr da fĂ„ en URL i outputen som likner pĂ„ dette: `https://iacworkshopxxxxxxxx.z6.web.core.windows.net`. Dersom du gĂ„r dit vil du fĂ„ feilmeldingen "The requested content does not exist.". Hvorfor det? Vi har jo ikke lastet opp noen filer enda! Mer om det straks! I dette steget har vi opprettet en ny storage account, med en storage account container `$web`. Dersom du har klart Ă„ fĂ„ en URL som `storage_account_web_url`-output og fĂ„r en feilmelding nĂ„r du gĂ„r til URL-en i nettleseren er du klar til neste steg. ## Frontend deploy For at brukere skal kunne se og lage poster i Bekkium mĂ„ frontenden opp. Vi skal her bygge filene manuelt lokalt fĂžrst (tilsvarende byggsteget i en CI/CD pipeline), og bruke terraform til Ă„ laste disse opp i storage accounten. For Ă„ kunne nĂ„ de statiske i nettleseren, mĂ„ vi deploye filene i storage account containeren `$web`. 1. For Ă„ rette frontenden til riktig backend mĂ„ du sette miljĂžvariabelen `REACT_APP_BACKEND_URL` for bygget. Denne mĂ„ vĂŠre outputen `backend_url` som du fĂ„r nĂ„r du kjĂžrer `terraform apply` (den skal se ut omtrent som `http://xxxxxxxx.westeurope.azurecontainer.io:8080`), pluss `/api` som postfiks. Kommandoen du mĂ„ kjĂžre fra `frontend/`-mappen blir dermed omtrent slik: ```sh # Bash (macOS/Linux/WSL) npm ci && REACT_APP_BACKEND_URL="http://xxxxxxxx.westeurope.azurecontainer.io:8080/api" npm run build # Powershell 7 (Windows) $env:REACT_APP_BACKEND_URL="http://xxxxxxxx.westeurope.azurecontainer.io:8080/api" npm ci npm run build ``` Dersom alt gikk bra ligger nĂ„ den ferdigbygde frontenden i `frontend/build/`-mappen, klar for bruk i senere steg. 1. For Ă„ hjelpe deg litt pĂ„ vei har vi definert noen lokale variable some blir nyttige, disse kan puttes pĂ„ toppen av `frontend.tf`. ```terraform locals { frontend_dir = "${path.module}/../frontend" frontend_files = fileset(local.frontend_dir, "**") frontend_src = { for fn in local.frontend_files : fn => filemd5("${local.frontend_dir}/${fn}") if(length(regexall("(node_modules/.*)|build/.*", fn)) == 0) } mime_types = { ".gif" = "image/gif" ".html" = "text/html" ".ico" = "image/vnd.microsoft.icon" ".jpeg" = "image/jpeg" ".jpg" = "image/jpeg" ".js" = "text/javascript" ".json" = "application/json" ".map" = "application/json" ".png" = "image/png" ".txt" = "text/plain" } } ``` 1. NĂ„r frontend-filene er bygget i `frontend/build` er de klare for opplastning. Vi skal nĂ„ laste opp hver enkelt fil som en blob til `$web` containeren. Dette er prima use case for [lĂžkker](https://www.terraform.io/docs/language/meta-arguments/for_each.html). For at filene skal tolkes riktig mĂ„ MIME-typen vĂŠre rett. Vi har allerede definert de nĂždvendige typene i `local.mime_types` map-et. Bruk [`lookup`-funksjonen](https://www.terraform.io/docs/language/functions/lookup.html) for Ă„ setter `content_type` til riktig MIME-type. (Hint: `regex("\\.[^.]+$", basename(each.value))` gir deg filendingen, og default kan vĂŠre `null`). ```terraform resource "azurerm_storage_blob" "payload" { // Vi trenger kun ferdige statiske filer for_each = fileset("${local.frontend_dir}/build", "**") name = each.value storage_account_name = azurerm_storage_account.web.name storage_container_name = "$web" type = "Block" source = "${local.frontend_dir}/build/${each.value}" content_md5 = filemd5("${local.frontend_dir}/build/${each.value}") content_type = <sett inn riktig MIME-type> } ``` 1. KjĂžr `terraform apply`. Dersom alt gĂ„r fint, skal du nĂ„ se en nettside dersom du navigerer til URL-en for storage accounten (`storage_account_web_url` output-variabelen). Dersom nettsiden fungerer er du ferdig med dette steget. Terraform er ikke nĂždvendigvis den beste mĂ„ten Ă„ deploye kode pĂ„, men vi har tatt det med her for Ă„ vise at det er _mulig_. I et reelt scenario ville man kanskje Ăžnske Ă„ gjĂžre det pĂ„ andre mĂ„ter. ## DNS Til slutt skal vi sette opp et eget domene for appen. Denne gangen har vi satt opp domenet `cloudlabs-azure.no` og appen din skal fĂ„ et subdomene pĂ„ `xxxxxxxx.cloudlabs-azure.no`. 1. Opprett filen `dns.tf`. Og legg til fĂžlgende kode: ```terraform locals { assumed_storage_account_web_host = "${local.unique_id_sanitized}.z6.web.core.windows.net" } ``` 1. Videre har vi lagd satt opp de nĂždvendige, delte ressursene for domenet `cloudlabs-azure.no` i ressursgruppen `workshop-admin`. Vi mĂ„ referere til disse ressursene for Ă„ lage et subdomene. Det gjĂžr vi ved Ă„ opprette fĂžlgende `data`-blokk: ```terraform data "azurerm_dns_zone" "cloudlabs_azure_no" { name = "cloudlabs-azure.no" resource_group_name = "workshop-admin" } ``` 1. Til slutt mĂ„ vi lage subdomenet. Det gjĂžr vi ved Ă„ opprette en CNAME record, som peker fra navnet pĂ„ subdomenet til URL-en til storage accounten. Det kan vi gjĂžre slik: ```terraform resource "azurerm_dns_cname_record" "www" { zone_name = data.azurerm_dns_zone.cloudlabs_azure_no.name resource_group_name = data.azurerm_dns_zone.cloudlabs_azure_no.resource_group_name ttl = 60 name = local.unique_id_raw record = local.assumed_storage_account_web_host } ``` * Legg merke til at `resource_group_name` her blir `workshop-admin`, og ikke ressursgruppen du tidligere har opprettet. Dette er fordi alle DNS-ressursene mĂ„ ligge i samme ressursgruppe. * `name` her blir navnet pĂ„ subdomenet, i vĂ„rt tilfelle den unike ID-en `xxxxxxxx` som terraform har generert for deg, og `record` er URL-en til den statiske nettsiden i storage accounten. 1. KjĂžr `terraform apply`. Du kan sjekke at dette ble opprettet riktig ved Ă„ gĂ„ til `workshop-admin` ressursgruppen i Azure-portalen. Trykke pĂ„ ressursen som heter `cloudlabs-azure.no` og sjekke at det er opprettet en CNAME record, med samme navn som din unike id (`xxxxxxxx`). 1. NĂ„ mĂ„ vi oppdatere `azurerm_storage_account` ressursen i `frontend.tf` slik at den aksepterer requests med det nye domenenavnet. Storage accounten mĂ„ nĂ„ provisjoneres opp etter at DNS-recorden er klar, hvis ikke vil det ikke fungere. Det kan vi ordne ved Ă„ legge in et [`depends_on`-array](https://www.terraform.io/docs/language/meta-arguments/depends_on.html). ```terraform resource "azurerm_storage_account" "web" { // Argumentene fra tidligere er uforandret custom_domain { name = local.web_hostname use_subdomain = false } // Legg til depends_on her. } ``` 1. Lag en ny output-variabel, `frontend_url` som gir oss den nye URL-en til frontenden. 1. KjĂžr `terraform apply` og gĂ„ til URL-en du fĂ„r i output. Dersom du fĂ„r den nye nye URL-en som output (den skal se ca. slik ut: `http://xxxxxxx.cloudlabs-azure.no`) og den fungerer, er du ferdig. Bra jobba! đ ## Ekstra Du har nĂ„ fĂ„tt hobbyprosjeketet ditt ut i prod! đ Hvis du har tid til overs sĂ„ har vi noen ekstraoppgaver du kan prĂžve deg pĂ„. Du kan selv velge hvilke du vil gjĂžre, de fleste er ikke i en spesiell rekkefĂžlge. ### Slett ressursene du har opprettet Dersom du Ăžnsker Ă„ slette alle ressursene kan du kjĂžre `terraform destroy`. Dette vil fjerne alle ressursene i Azure, og nullstille terraform-tilstanden. Dersom du Ăžnsker Ă„ opprette ressursene pĂ„ nytt kan du kjĂžre `terraform apply` igjen, og alle ressursene vil opprettes pĂ„ nytt. Merk at ettersom all tilstanden slettes av `terraform destroy`, vil den unike id-en bli generert pĂ„ nytt av terraform. Dermed blir ogsĂ„ ressursgruppenavnet og URL-ene nye. **NB!** `terraform destroy` vil ugjenopprettelig slette data som ikke er definert av terraform. F.eks. data i databaser, hemmeligheter i key vaults eller brukeropplastede filer i en storage account. I denne workshopen er det trygt, men vĂŠr forsiktig om du bruker terraform til faktiske applikasjoner. ### Les om terraform-provideren for Azure Her kan du slĂ„ opp de ulike ressursene vi har brukt, og prĂžve Ă„ finne forklaringen pĂ„ ressursblokker eller argumenter du ikke forstĂ„r. Dokumentasjonen finner du [her](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs). ### Sett opp en database Backenden stĂžtter fĂžlgende databaser: H2, MSSQL, MySQL og PostgreSQL. Som standard [benyttes H2](./backend/src/main/resources/application.properties) (in-memory database). Finn ut hvordan man konfigurerer en alternativ database via miljĂžvariabler, samt hvordan man provisjonerer en med Terraform (f.eks. [`azurerm_postgresql_server`](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/postgresql_server). ### SlĂ„ pĂ„ HTTPS for backend Det kan vĂŠre nyttig med HTTPS. Det enkleste er en lĂžsning som hĂ„ndterer HTTPS sertifikater automatisk for oss, f.eks. ved Ă„ spinne opp en ny container i Azure Container Instances som fungerer som en *reverse proxy* og tar seg av dette. Caddy kan brukes som reverse proxy. Container-imaget `caddy` inneholder alt du trenger, og kjĂžres ved Ă„ bruke kommandoen `caddy reverse-proxy --from <ekstern-aci-url> --to <intern-backend-url>` nĂ„r containeren skal startes. Du vil ogsĂ„ trenge Ă„ konfigurere et `volume` for containeren, der Caddy-instansen kan lagre data. Dette gjĂžres enklest ved Ă„ lage en file share i en storage account (`azurerm_storage_share`). Konfigurer port `80` og `433` for containeren. OppdatĂ©r `backend_url` outputen til Ă„ bruke `https` og fjern portspesifikasjonen (den vil da automatisk bruke `443`). Test at det fungerer ved Ă„ sjekke at du fĂ„r suksessfull respons fra `https://xxxxxxxx.cloudlabs-azure.no/api/articles`. Videre bĂžr man bygge frontenden pĂ„ nytt (etterfulgt av en ny `terraform apply`), med ny `REACT_APP_BACKEND_URL` til Ă„ bruke HTTPS fremfor HTTP for Ă„ unngĂ„ advarsler om og problemer med [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content). Kommandoen for Ă„ bygge frontenden bĂžr nĂ„ se omtrent slik ut: ```sh npm ci && REACT_APP_BACKEND_URL="https://xxxxxxxx.cloudlabs-azure.no/api" npm build ``` Nyttige lenker: * [Azure Container Instance](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/container_group) * [Storage Account](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account) og [file share](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share) ### SlĂ„ pĂ„ HTTPS for frontend med eget domene For Ă„ gjĂžre dette steget mĂ„ HTTPS fungere for backend fĂžrst. Storage accounten stĂžtter HTTPS ut av boksen med sitt eget domene (typisk `<storage-account-navn>.z6.web.core.windows.net`), men om vi skal ha HTTPS for eget domene blir det komplisert. Det finnes flere mĂ„ter Ă„ gjĂžre dette pĂ„, men her skal vi sette opp en CDN som hĂ„ndterer sertifikatet for oss. Terraform-dokumentasjonen for [`azurerm_cdn_endpoint_custom_domain`](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_endpoint_custom_domain) har et godt eksempel pĂ„ hvordan en CDN kan settes opp med eget domene. I tillegg mĂ„ du bruke underressursen `cdn_managed_https` for Ă„ fĂ„ HTTPS for domenet. Merk at nĂ„r du skrur pĂ„ HTTPS vil `terraform apply` kjĂžre til sertifikater er ferdig provisjonert av Azure - dette kan ta opp mot en time, i denne workshopen anbefaler vi at du gjĂžr dette sist. Du kan nĂ„ ogsĂ„ sette `enable_https_traffic_only` til `true` for storage accounten. ### GjĂžre endringer pĂ„ applikasjonene 1. Lag en fork av dette repoet (bruk knappen Ăžverst til hĂžyre), og lag en fork som ligger under din egen bruker. URL-en til det nye repoet blir da `https://github.com/<ditt-github-brukernavn>/iac-workshop`. 1. GĂ„ til din fork av dette repoet. Her mĂ„ du gjĂžre noen instillinger: 1. GĂ„ til `Actions` i menyen. Her skal du skru pĂ„ GitHub Actions for ditt repo, slik at du fĂ„r automatiserte bygg av frontend og backend. Byggene (GitHub kaller dette "workflows") vil bare kjĂžre dersom det er gjort endringer i hhv. `frontend/` eller `backend/` mappene i repoet. 1. NĂ„r automatiserte bygg er skrudd pĂ„, mĂ„ vi kjĂžre dem manuelt fĂžrste gang. For hvert av byggene, trykk pĂ„ "Run workflow" for Ă„ kjĂžre koden. Du trenger ikke laste ned artifaktene som lages av bygget, det gjĂžres automatisk nĂ„r koden kjĂžres. 1. NĂ„r frontend-bygget er ferdig, kan du se at artifakten er lastet opp pĂ„ `https://github.com/<ditt-github-brukernavn>/iac-workshop/releases`. 1. Backend-bygget legges i ditt private image-registry. Det bygde Docker-imaget kan du finne pĂ„ `https://github.com/<ditt-github-brukernavn>?tab=packages>`.