# 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>`.