<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thorsten Hans</title>
    <description>The latest articles on DEV Community by Thorsten Hans (@thorstenhans).</description>
    <link>https://dev.to/thorstenhans</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F254942%2Fc5aed224-0d2f-4664-8e69-218ae2f83d4c.jpeg</url>
      <title>DEV Community: Thorsten Hans</title>
      <link>https://dev.to/thorstenhans</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thorstenhans"/>
    <language>en</language>
    <item>
      <title>Run WebAssembly on DigitalOcean Kubernetes with SpinKube - In 4 Easy Steps</title>
      <dc:creator>Thorsten Hans</dc:creator>
      <pubDate>Wed, 27 Mar 2024 14:37:39 +0000</pubDate>
      <link>https://dev.to/fermyon/run-webassembly-on-digitalocean-kubernetes-with-spinkube-in-4-easy-steps-2lcl</link>
      <guid>https://dev.to/fermyon/run-webassembly-on-digitalocean-kubernetes-with-spinkube-in-4-easy-steps-2lcl</guid>
      <description>&lt;p&gt;&lt;a href="https://www.digitalocean.com/"&gt;DigitalOcean&lt;/a&gt; is a cloud infrastructure provider catering to developers, offering scalable virtual servers, storage solutions, networking services, and managed Kubernetes clusters. It simplifies application deployment, management, and scaling through its intuitive user interface and CLI (&lt;code&gt;doctl&lt;/code&gt;), allowing developers to efficiently utilize cloud resources for their projects. &lt;/p&gt;

&lt;p&gt;As part of this article, you'll learn how to&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Provision a new Kubernetes cluster on DigitalOcean&lt;/li&gt;
&lt;li&gt;Deploy &lt;a href="https://www.spinkube.dev/"&gt;SpinKube&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a simple WebAssembly app using Spin 💫 and Rust 🦀&lt;/li&gt;
&lt;li&gt;Deploy the Spin App to Kubernetes using an OCI compliant registry&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What is SpinKube
&lt;/h2&gt;

&lt;p&gt;SpinKube is an open-source project that allows you to run WebAssembly workloads on top of Kubernetes. Essentially, SpinKube is a stack that consists of the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/spinkube/spin-operator"&gt;Spin Operator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spinkube/containerd-shim-spin"&gt;containerd-shim-spin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/spinkube/runtime-class-manager"&gt;Runtime Class Manager&lt;/a&gt; (formerly &lt;a href="https://kwasm.sh/"&gt;KWasm&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to running WebAssembly workloads side-by-side with containers, you can run workloads more efficiently and with higher density. Because of WebAssembly’s portability, you can run the same WebAssembly workload on different architectures to reduce cloud spendings even more by using - for example - arm64 worker nodes 🚀.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At KubeCon EU 2024 - The application for contributing SpinKube to the Cloud Native Computing Foundation (CNCF) has been filed. &lt;a href="https://www.youtube.com/watch?v=tu8a-GefJL8"&gt;Click here to watch the keynote recording&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along this article, the following software must be installed on your local machine: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Spin CLI (&lt;code&gt;spin&lt;/code&gt;)

&lt;ul&gt;
&lt;li&gt;See &lt;a href="https://developer.fermyon.com/spin/v2/install"&gt;installation instructions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;kube&lt;/code&gt; plugin for &lt;code&gt;spin&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Install it via &lt;code&gt;spin plugins install kube&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Rust 

&lt;ul&gt;
&lt;li&gt;See &lt;a href="https://www.rust-lang.org/tools/install"&gt;installation instructions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;wasm32-wasi&lt;/code&gt; target

&lt;ul&gt;
&lt;li&gt;Install it via &lt;code&gt;rustup target add wasm32-wasi&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A DigitalOcean Account &amp;amp; the DigitalOcean CLI (&lt;code&gt;doctl&lt;/code&gt;)

&lt;ul&gt;
&lt;li&gt;See &lt;a href="https://docs.digitalocean.com/reference/doctl/how-to/install/"&gt;installation instructions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The Kubernetes CLI (&lt;code&gt;kubectl&lt;/code&gt;)

&lt;ul&gt;
&lt;li&gt;See &lt;a href="https://kubernetes.io/docs/tasks/tools/#kubectl"&gt;installation instructions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The Helm CLI (&lt;code&gt;helm&lt;/code&gt;)

&lt;ul&gt;
&lt;li&gt;See &lt;a href="https://helm.sh/docs/intro/install/"&gt;installation instructions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Although the DigitalOcean account itself is free, you have to provide payment information and you will be charged for allocating resources and renting services - such as the  Managed Kubernetes Cluster we will create as part of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. Provision a Managed Kubernetes Cluster in DigitalOcean
&lt;/h2&gt;

&lt;p&gt;Although the DigitalOcean Control Panel is an easy to grasp UI for provisioning cloud resources, we will use &lt;code&gt;doctl&lt;/code&gt; CLI and its &lt;code&gt;doctl kubernetes cluster create&lt;/code&gt; command to provision the managed Kubernetes cluster. Execute the following command, to provision a cluster consisting of two worker nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Variables&lt;/span&gt;
&lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fra1
&lt;span class="nv"&gt;node_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;s-4vcpu-8gb-intel

&lt;span class="c"&gt;# Create a new Kubernetes cluster&lt;/span&gt;
doctl kubernetes cluster create my-spinkube-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$location&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--count&lt;/span&gt; 2 &lt;span class="se"&gt;\ &lt;/span&gt; 
  &lt;span class="nt"&gt;--size&lt;/span&gt; &lt;span class="nv"&gt;$node_size&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-current-context&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;DigitalOcean provides different regions and machine sizes for managed Kubernetes clusters. Use &lt;code&gt;doctl kubernetes options regions&lt;/code&gt; to get a list of all regions. By running&lt;br&gt;
&lt;code&gt;doctl kubernetes options sizes&lt;/code&gt; you get a list of all supported machine sizes for Kubernetes worker nodes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the managed Kubernetes cluster is provisioned, we can double-check if the corresponding context has been added to &lt;code&gt;kubectl&lt;/code&gt; and is marked as the current context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all contexts of kubectlkubectl config get-contexts&lt;/span&gt;
CURRENT   NAME                  CLUSTER 
&lt;span class="k"&gt;*&lt;/span&gt;         my-spinkube-cluster   my-spinkube-cluster 
          k3d-wasm-cluster      k3d-wasm-cluster    

&lt;span class="c"&gt;# Set current context to my-spinkube-cluster (if not already)&lt;/span&gt;
kubectl config use-context my-spinkube-cluster
Switched to context &lt;span class="s2"&gt;"my-spinkube-cluster"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Install SpinKube
&lt;/h2&gt;

&lt;p&gt;On top of its core components, SpinKube depends on &lt;a href="https://cert-manager.io"&gt;cert-manager&lt;/a&gt;. &lt;code&gt;cert-Manager&lt;/code&gt; is responsible for provisioning and managing TLS certificates that are used by the admission webhook system of the Spin Operator. Let’s install &lt;code&gt;cert-manager&lt;/code&gt; and &lt;code&gt;KWasm&lt;/code&gt; using the commands shown here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install cert-manager CRDs&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.crds.yaml

&lt;span class="c"&gt;# Add Helm repositories jetstack and KWasm&lt;/span&gt;
helm repo add jetstack https://charts.jetstack.io
helm repo add kwasm http://kwasm.sh/kwasm-operator

&lt;span class="c"&gt;# Update Helm repositories&lt;/span&gt;
helm repo update

&lt;span class="c"&gt;# Install cert-manager using Helm&lt;/span&gt;
helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  cert-manager jetstack/cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; v1.14.4

&lt;span class="c"&gt;# Install KWasm operator&lt;/span&gt;
helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  kwasm-operator kwasm/kwasm-operator &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; kwasm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; kwasmOperator.installerImage&lt;span class="o"&gt;=&lt;/span&gt;ghcr.io/spinkube/containerd-shim-spin/node-installer:v0.13.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;KWasm will install &lt;code&gt;containerd-shim-spin&lt;/code&gt; on Kubernetes nodes annotated with &lt;code&gt;kwasm.sh/kwasm-node=true&lt;/code&gt;. For the sake of this article, we will annotate all nodes of our cluster using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Annotate all Kubernetes nodes with kwasm.sh/kwasm-node=true&lt;/span&gt;
kubectl annotate node &lt;span class="nt"&gt;--all&lt;/span&gt; kwasm.sh/kwasm-node&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can install the Spin Operator on the Kubernetes cluster along with necessary Custom Resource Definitions (CRDs), the &lt;code&gt;RuntimeClass&lt;/code&gt; and the &lt;code&gt;SpinAppExecutor&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install SpinKube CRDs&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.crds.yaml

&lt;span class="c"&gt;# Install a RuntimeClass for wasmtime-spin-v2&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.runtime-class.yaml

&lt;span class="c"&gt;# Install the containerd-spin-shim SpinAppExecutor&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.shim-executor.yaml

&lt;span class="c"&gt;# Install Spin Operator with Helm&lt;/span&gt;
helm &lt;span class="nb"&gt;install &lt;/span&gt;spin-operator &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; spin-operator &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; 0.1.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  oci://ghcr.io/spinkube/charts/spin-operator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Create a Spin App
&lt;/h2&gt;

&lt;p&gt;Having SpinKube installed on our managed Kubernetes cluster, we can build a simple Spin App for verification purposes. We will use the &lt;code&gt;http-rust&lt;/code&gt; template and apply some small changes to the app generated by &lt;code&gt;spin&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a new Spin App&lt;/span&gt;
spin new &lt;span class="nt"&gt;-t&lt;/span&gt; http-rust &lt;span class="nt"&gt;--accept-defaults&lt;/span&gt; hello-do-k8s

&lt;span class="c"&gt;# Move into the Spin App&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;hello-do-k8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify the implementation ( in &lt;code&gt;./src/lib.rs&lt;/code&gt;) to match the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;spin_sdk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;IntoResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;spin_sdk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;http_component&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[http_component]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;handle_hello_do_k8s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;IntoResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Handling request to {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="nf"&gt;.header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"spin-full-url"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"content-type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/plain"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from DigitalOcean Kubernetes"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, we expect our Spin App to respond on incoming HTTP requests with an HTTP &lt;code&gt;200&lt;/code&gt;, a Content-Type of &lt;code&gt;text/plain&lt;/code&gt; and payload of &lt;code&gt;Hello from DigitalOcean Kubernetes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Compile the Spin App using &lt;code&gt;spin build&lt;/code&gt; and use &lt;code&gt;spin up&lt;/code&gt; to test your app locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build the Spin App&lt;/span&gt;
spin build

&lt;span class="c"&gt;## snip &lt;/span&gt;
Finished release &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;8.92s
Finished building all Spin components

&lt;span class="c"&gt;# Run the Spin App locally&lt;/span&gt;
spin up
Logging component stdio to &lt;span class="s2"&gt;".spin/logs/"&lt;/span&gt;Serving http://127.0.0.1:3000
Available Routes:
  hello-do-k8s: http://127.0.0.1:3000 &lt;span class="o"&gt;(&lt;/span&gt;wildcard&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# You can terminate the app at any time using CTRL+C&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test the Spin App locally, by sending an HTTP request to &lt;code&gt;localhost:3000&lt;/code&gt; from within a new terminal instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Send a HTTP GET request to localhost:3000&lt;/span&gt;
curl &lt;span class="nt"&gt;-iX&lt;/span&gt; GET http://localhost:3000
HTTP/1.1 200 OK
content-type: text/plain
transfer-encoding: chunked
&lt;span class="nb"&gt;date&lt;/span&gt;: Tue, 25 Mar 2024 11:40:28 GMT
Hello from DigitalOcean Kubernetes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Deploy the Spin App via OCI compliant registry
&lt;/h2&gt;

&lt;p&gt;Spin Apps are distributed as OCI artifacts, meaning we can choose from many different container registries for distributing them. For demonstration purposes, we will use &lt;a href="https://ttl.sh/"&gt;ttl.sh&lt;/a&gt; (an anonymous and ephemeral Docker image registry) in which the tag of an artifact defines how long the artifact remains available (TTL).&lt;/p&gt;

&lt;p&gt;We use the &lt;code&gt;spin registry push&lt;/code&gt; command for distributing Spin Apps through OCI compliant registries.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can also use the &lt;code&gt;spin registry login&lt;/code&gt; command to authenticate with private registries and distribute your Spin Apps without making them publicly available at all.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build the Spin App&lt;/span&gt;
spin build

&lt;span class="c"&gt;# Publish the Spin App as OCI artifact&lt;/span&gt;
&lt;span class="c"&gt;# The artifact will remain accessible for 24 hours (24h tag)&lt;/span&gt;
spin registry push ttl.sh/hello-do-k8s:24h
Pushing app to the
Pushed with digest sha256:b77b7cd7644be0b32613cbec1be5049eb96c7e536377165c4c08e1467c4087b2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we deploy our Spin App to the managed Kubernetes cluster running on DigitalOcean. Again, we use the &lt;code&gt;spin&lt;/code&gt; CLI to scaffold the necessary Kubernetes manifests and pipe them to &lt;code&gt;kubectl&lt;/code&gt; which will interact with Kubernetes API server and provision the &lt;code&gt;SpinApp&lt;/code&gt; custom resource (CR):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deploy Spin App to Kubernetes&lt;/span&gt;
spin kube scaffold &lt;span class="nt"&gt;-f&lt;/span&gt; ttl.sh/hello-do-k8s:24h | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
spinapp.core.spinoperator.dev/hello-do-k8s created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s use &lt;code&gt;kubectl&lt;/code&gt; and take a closer look at what we got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List Spin Apps in the default namespacekubectl get spinapps&lt;/span&gt;
NAME            READY   DESIRED   EXECUTOR
hello-do-k8s    2       2         containerd-shim-spin

&lt;span class="c"&gt;# List Deploymentskubectl get deployments&lt;/span&gt;
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
hello-do-k8s    2/2     2            2           103s

&lt;span class="c"&gt;# List Pods (wide)kubectl get pods -o wide&lt;/span&gt;
NAME                             READY   STATUS     AGE     IP
hello-do-k8s-665676d5bd-9gt4s    1/1     Running    2m14s   10.42.0.7
hello-do-k8s-665676d5bd-5fjpm    1/1     Running    2m14s   10.42.1.7

&lt;span class="c"&gt;# List all Services and their Endpointskubectl get services,endpoints&lt;/span&gt;
NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;   AGE
service/hello-do-k8s    ClusterIP   10.43.75.164    &amp;lt;none&amp;gt;        80/TCP    4m7s

NAME                      ENDPOINTS                   AGE
endpoints/hello-do-k8s    10.42.0.7:80,10.42.1.7:80   4m7s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the Spin Operator takes care of all the Kubernetes primitives for running and accessing our Spin App.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Spin App running on Kubernetes
&lt;/h2&gt;

&lt;p&gt;Let’s configure port forwarding, to access the Spin App using &lt;code&gt;curl&lt;/code&gt; from our local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start port-forwarding&lt;/span&gt;
kubectl port-forward services/hello-do-k8s 8080:80
Forwarding from 127.0.0.1:8080 -&amp;gt; 80
Forwarding from &lt;span class="o"&gt;[&lt;/span&gt;::1]:8080 -&amp;gt; 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a new terminal instance and send an HTTP request to &lt;code&gt;locahost:8080&lt;/code&gt; and verify receiving the expected response back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Send an HTTP GET request to localhost:8080&lt;/span&gt;
curl &lt;span class="nt"&gt;-iX&lt;/span&gt; GET http://localhost:8080
HTTP/1.1 200 OK
content-type: text/plain
transfer-encoding: chunked
&lt;span class="nb"&gt;date&lt;/span&gt;: Tue, 25 Mar 2024 11:48:21 GMT
Hello from DigitalOcean Kubernetes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleaning up the Cloud Resources
&lt;/h2&gt;

&lt;p&gt;To remove the cloud infrastructure we created as part of this article from your DigitalOcean account, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Delete the DigitalOcean managed Kubernetes cluster&lt;/span&gt;
doctl kubernetes cluster delete my-spinkube-cluster &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By deploying SpinKube onto your managed Kubernetes clusters in DigitalOcean, you can run WebAssembly workloads right beside containers. As WebAssembly apps are way smaller than containers, you can run more workloads on the same amount of compute resources. As you’ve learned in this article, the deployment of SpinKube is simple and requires only a couple of steps.&lt;/p&gt;

&lt;p&gt;If you want to learn more about SpinKube, consider reading the &lt;a href="https://www.spinkube.dev/docs/overview/"&gt;SpinKube documentation&lt;/a&gt;. There are plenty of tutorials, like, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.spinkube.dev/docs/spin-operator/tutorials/scaling-with-hpa/"&gt;Scaling Spin App With Horizontal Pod Autoscaling (HPA)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.spinkube.dev/docs/spin-operator/tutorials/scaling-with-keda/"&gt;Scaling Spin App With Kubernetes Event-Driven Autoscaling (KEDA)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webassembly</category>
      <category>kubernetes</category>
      <category>cloud</category>
      <category>containers</category>
    </item>
    <item>
      <title>Building Serverless Apps with Spin and htmx</title>
      <dc:creator>Thorsten Hans</dc:creator>
      <pubDate>Mon, 05 Feb 2024 15:33:00 +0000</pubDate>
      <link>https://dev.to/fermyon/building-serverless-apps-with-spin-and-htmx-1p3l</link>
      <guid>https://dev.to/fermyon/building-serverless-apps-with-spin-and-htmx-1p3l</guid>
      <description>&lt;p&gt;Hi, &lt;/p&gt;

&lt;p&gt;In this article, I’ll walk you through the process of building serverless applications using the power of WebAssembly with &lt;a href="https://developer.fermyon.com/spin/v2/index"&gt;Fermyon Spin&lt;/a&gt; and &lt;a href="https://htmx.org/"&gt;htmx&lt;/a&gt;. For demonstration purposes, we will build a simple shopping list. We will implement the serverless backend in Rust. For building the frontend, we’ll start with a simple HTML page, which we will enhance using htmx.&lt;/p&gt;

&lt;p&gt;Before we dive into implementing the sample application, we will ensure that everybody is on track and quickly recap what Fermyon Spin and htmx actually are.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Fermyon Spin
&lt;/h2&gt;

&lt;p&gt;Fermyon Spin (Spin) is an &lt;a href="https://github.com/fermyon/spin"&gt;open-source framework&lt;/a&gt; for building and running event-driven, serverless applications based on WebAssembly (Wasm).&lt;/p&gt;

&lt;p&gt;Developers can build applications using a wide variety of different programming languages (basically developers can choose from all languages that could be compiled to Wasm). Spin uses Wasm because the core value propositions of Wasm perfectly address common non-functional requirements that we face when building distributed applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Near-native runtime performance&lt;/li&gt;
&lt;li&gt;Blazingly fast cold start times (speaking about milliseconds here) that allow scale-down to zero&lt;/li&gt;
&lt;li&gt;Strict sandboxing and secure isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At Fermyon we're obsessed when it comes to developer productivity. The &lt;code&gt;spin&lt;/code&gt; CLI and the language-specific SDKs ensure unseen developer productivity in the realm of WebAssembly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is htmx
&lt;/h2&gt;

&lt;p&gt;htmx is a lightweight JavaScript library built for developers, facilitating progressive enhancements in web applications. By seamlessly updating specific parts of a webpage without necessitating a complete reload, htmx empowers us to create dynamic and interactive user experiences effortlessly. Its simplicity and performance-oriented approach, utilizing HTML attributes for defining behavior, make it a valuable tool for augmenting existing projects or crafting new frontends.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sample application
&lt;/h2&gt;

&lt;p&gt;For demonstration purposes, we will create a simple shopping list to illustrate how Spin and a simple frontend crafted with htmx could be integrated. From an architectural point of view, our shopping list consists of three major components, as shown in the diagram below.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Persistence: We use SQLite as a database to persist the items of our shopping list&lt;/li&gt;
&lt;li&gt;Backend: We will build an HTTP API using Rust and Spin&lt;/li&gt;
&lt;li&gt;Frontend: The frontend consists of HTML, CSS, and progressive enhancements provided by htmx&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff75svn2zm5bpje4y5iqy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff75svn2zm5bpje4y5iqy.png" alt="Overall Architecture for the Spin &amp;amp; htmx Shopping List" width="765" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the source of sample application on GitHub at &lt;a href="https://github.com/ThorstenHans/spin-htmx"&gt;https://github.com/ThorstenHans/spin-htmx&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;To follow along with the sample explained in this article, you need to have the following tools installed on your machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rust - See &lt;a href="https://www.rust-lang.org/tools/install"&gt;installation instructions for Rust&lt;/a&gt; (I am currently using Rust version &lt;code&gt;1.75.0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;wasm32-wasi&lt;/code&gt; compilation target - You can install it using the &lt;code&gt;rustup target add wasm32-wasi&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;spin&lt;/code&gt; CLI - See &lt;a href="https://developer.fermyon.com/spin/v2/install"&gt;installation instructions for Spin&lt;/a&gt; (I am currently using &lt;code&gt;spin&lt;/code&gt; version &lt;code&gt;2.1.0&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Creating the app skeleton
&lt;/h3&gt;

&lt;p&gt;First, we will use the &lt;code&gt;spin&lt;/code&gt; CLI to create our Spin application and add the following components to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app&lt;/code&gt;: A Spin component based on the &lt;code&gt;static-fileserver&lt;/code&gt; template which will serve our frontend&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api&lt;/code&gt;: A Spin component based on the &lt;code&gt;http-rust&lt;/code&gt; template, which will serve the HTTP API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the &lt;code&gt;spin&lt;/code&gt; CLI is built with productivity in mind, generating the entire boilerplate is quite easy, as you can see in this snippet:&lt;/p&gt;

&lt;p&gt;Because the &lt;code&gt;spin&lt;/code&gt; CLI is built with productivity in mind, generating the entire boilerplate is quite easy, as you can see in this snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create an empty Spin app called shopping-list&lt;/span&gt;
spin new &lt;span class="nt"&gt;-t&lt;/span&gt; http-empty shopping-list
Description: A shopping list built with Spin and htmx

&lt;span class="c"&gt;# Move into the app directory&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;shopping-list

&lt;span class="c"&gt;# Create the app component&lt;/span&gt;
spin add &lt;span class="nt"&gt;-t&lt;/span&gt; static-fileserver app
HTTP path: /...
Directory containing the files to serve: assets

&lt;span class="c"&gt;# Create the frontend-asset folder&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;assets

&lt;span class="c"&gt;# Create the API component&lt;/span&gt;
spin add &lt;span class="nt"&gt;-t&lt;/span&gt; http-rust api
Description: Shopping list API
HTTP path: /api/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having our Spin app bootstrapped and added all necessary components, we can move on and take care of the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparing the database
&lt;/h3&gt;

&lt;p&gt;Although Spin takes care of running the database behind the scenes, we must ensure that the desired component(s) can interact with the database. This is necessary because &lt;strong&gt;Wasm is secure by default&lt;/strong&gt; and Wasm modules must explicitly request permissions to use or interact with different resources/capabilities.&lt;/p&gt;

&lt;p&gt;This might sound like a complicated task in the first place, but Spin makes this super easy. All we have to do is update the application manifest (&lt;code&gt;spin.toml&lt;/code&gt;) and add the &lt;code&gt;sqlite_databases&lt;/code&gt; configuration property to the desired component(s). In our case, the &lt;code&gt;api&lt;/code&gt; component is the only one, that should be able to interact with the database. That said, update the &lt;code&gt;[component.api]&lt;/code&gt; section in &lt;code&gt;spin.toml&lt;/code&gt; to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[component.api]&lt;/span&gt;
&lt;span class="py"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"api/target/wasm32-wasi/release/api.wasm"&lt;/span&gt;
&lt;span class="py"&gt;allowed_outbound_hosts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="py"&gt;sqlite_databases&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we have to lay out our database using a simple DDL script. We create a new file called &lt;code&gt;migration.sql&lt;/code&gt; in the root folder of our application and add the following content to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;ITEMS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the update to &lt;code&gt;spin.toml&lt;/code&gt; and our custom &lt;code&gt;migration.sql&lt;/code&gt; file, we have prepared everything regarding the database. We could move on and take care of the serverless. backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the serverless backend
&lt;/h2&gt;

&lt;p&gt;Our serverless backend exposes three different endpoints that we’ll use later from within the frontend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP GET at &lt;code&gt;/api/items&lt;/code&gt; : to retrieve all items of the shopping list&lt;/li&gt;
&lt;li&gt;HTTP POST at &lt;code&gt;/api/items&lt;/code&gt;: to add a new item to the shopping list by providing a proper JSON payload as the request body&lt;/li&gt;
&lt;li&gt;HTTP DELETE at &lt;code&gt;/api/items/:id&lt;/code&gt;: to delete an existing item from the shopping list using its identifier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Spin SDK for Rust comes with batteries included to create full-fledged HTTP APIs. We use the &lt;code&gt;Router&lt;/code&gt; structure to layout our API and handle incoming requests as part of the function decorated with the &lt;code&gt;#[http_component]&lt;/code&gt; macro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;spin_sdk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;http_component&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;spin_sdk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;IntoResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[http_component]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;handle_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;IntoResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="nf"&gt;.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;add_new&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_all&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="nf"&gt;.delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/items/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delete_one&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="nf"&gt;.handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we dive into implementing the handlers, we add some 3rd party crates to simplify working with JSON and creating HTML fragments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Move into the API folder&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;api

&lt;span class="c"&gt;# Add serde and serde_json&lt;/span&gt;
cargo add serde &lt;span class="nt"&gt;-F&lt;/span&gt; derive
cargo add serde_json

&lt;span class="c"&gt;# Add build_html&lt;/span&gt;
cargo add build_html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those commands will download the 3rd party dependencies and add them to the &lt;code&gt;Cargo.toml&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the &lt;code&gt;POST&lt;/code&gt; endpoint
&lt;/h3&gt;

&lt;p&gt;To add a new item to the shopping list, we want to issue &lt;code&gt;POST&lt;/code&gt; requests from the frontend to the API and send the new item as JSON payload using the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Buy milk"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we create the corresponding API model as a simple &lt;code&gt;struct&lt;/code&gt; and decorate it with &lt;code&gt;Deserialize&lt;/code&gt; from the &lt;code&gt;serde&lt;/code&gt; crate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;serde&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having our model in place, we can implement the &lt;code&gt;add_new&lt;/code&gt; handler. We can offload the act of actually deserializing the payload to the &lt;code&gt;http&lt;/code&gt; crate, by precisely specifying the request type (here &lt;code&gt;http::Request&amp;lt;Json&amp;lt;Item&amp;gt;&amp;gt;&lt;/code&gt;). Additionally, we use &lt;code&gt;Connection&lt;/code&gt; and &lt;code&gt;Value&lt;/code&gt; from &lt;code&gt;spin_sdk::sqlite&lt;/code&gt; to securely construct the TSQL command for storing the new item in the database. Finally, we return an HTTP &lt;code&gt;200&lt;/code&gt; along with a &lt;code&gt;HX-Trigger&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;Later, when implementing the frontend, we’ll use the value of the &lt;code&gt;HX-Trigger&lt;/code&gt; header, when implementing the frontend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;spin_sdk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sqlite&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;spin_sdk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;add_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;IntoResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="nf"&gt;.into_body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open_default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="py"&gt;.value&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
  &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"INSERT INTO ITEMS (VALUE) VALUES (?)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HX-Trigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"newItem"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementing the &lt;code&gt;GET&lt;/code&gt; endpoint
&lt;/h3&gt;

&lt;p&gt;Next on our list is implementing the handler to retrieve all shopping list items from the database. Before we dive into the implementation of the handler, let’s revisit our &lt;code&gt;Item&lt;/code&gt; struct and extend it, to match the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Deserialize,&lt;/span&gt; &lt;span class="nd"&gt;Serialize)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;#[serde(skip_deserializing)]&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of returning plain values as JSON, our API will create response bodies as HTML fragments (&lt;code&gt;Content-Type: text/html&lt;/code&gt;) with htmx enhancements.&lt;/p&gt;

&lt;p&gt;To achieve this, we must specify how a single item (an instance of the &lt;code&gt;Item&lt;/code&gt; structure) is represented as HTML. The &lt;code&gt;build_html&lt;/code&gt; create provides the &lt;code&gt;Html&lt;/code&gt; trait, which we can use to lay out how an item should look like in HTML. Let’s implement the &lt;code&gt;Html&lt;/code&gt; trait for our custom type&lt;br&gt;
&lt;code&gt;Item&lt;/code&gt; as shown here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;build_html&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ContainerType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HtmlContainer&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Html&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;to_html_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ContainerType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;.with_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"item-{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
      &lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="nf"&gt;.with_container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nn"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ContainerType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nf"&gt;.with_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
          &lt;span class="nf"&gt;.with_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;.with_container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nn"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ContainerType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nf"&gt;.with_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"delete-item"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hx-delete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/items/{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
          &lt;span class="p"&gt;])&lt;/span&gt;
          &lt;span class="nf"&gt;.with_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"❌"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;.to_html_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On top of creating an HTML representation of the actual item, the snippet also contains a &lt;code&gt;hx-delete&lt;/code&gt; attribute. We add this attribute to the delete-icon, to allow users to delete a particular item directly from the shopping list.&lt;/p&gt;

&lt;p&gt;The handler implementation (&lt;code&gt;get_all&lt;/code&gt;) reads all items from the database, constructs a new instance of &lt;code&gt;Item&lt;/code&gt; for every record retrieved, and transforms every instance into an HTML string by invoking &lt;code&gt;to_html_string&lt;/code&gt;. Finally, we join all HTML representations together into a bigger HTML fragment and construct a proper HTTP response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;IntoResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open_default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;row_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT ID, VALUE FROM ITEMS ORDER BY ID DESC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row_set&lt;/span&gt;
    &lt;span class="nf"&gt;.rows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="py"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="py"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"VALUE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="nf"&gt;.to_html_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.reduce&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{} {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementing the &lt;code&gt;DELETE&lt;/code&gt; endpoint
&lt;/h3&gt;

&lt;p&gt;The last endpoint we have to implement is responsible for deleting an item based on its identifier. We do some housekeeping here to ensure that requests contain an identifier and ensure that the identifier provided is a valid &lt;code&gt;i64&lt;/code&gt;. If that’s the case, we delete the corresponding record from the database and return an empty response with status code 200 (which is done via &lt;code&gt;Response::default()&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;delete_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;IntoResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing identifier"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="py"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nf"&gt;.status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unexpected identifier format"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open_default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;

  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DELETE FROM ITEMS WHERE ID = ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error while deleting item: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error while deleting item"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, our serverless backend is done and we can move on and take care of the frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the Frontend
&lt;/h2&gt;

&lt;p&gt;We’ll keep the frontend as simple as possible. All sources will remain in the &lt;code&gt;assets&lt;/code&gt; folder (you remember, we specified that as the source for the &lt;code&gt;static-fileserver&lt;/code&gt; component when creating the Spin app at the very beginning).&lt;/p&gt;

&lt;p&gt;First, let’s create all necessary files and bring in our 3rd party dependencies (&lt;code&gt;htmx&lt;/code&gt; and the &lt;code&gt;json-enc&lt;/code&gt; extension):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Move into the assets folder&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;assets

&lt;span class="c"&gt;# Create the HTML file&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;index.html

&lt;span class="c"&gt;# Download the pre-coded CSS file&lt;/span&gt;
curl &lt;span class="nt"&gt;-o&lt;/span&gt; style.css &lt;span class="se"&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/ThorstenHans/spin-htmx/main/app/style.css

&lt;span class="c"&gt;# Download the latest version of htmx&lt;/span&gt;
curl &lt;span class="nt"&gt;-o&lt;/span&gt; htmx.min.js &lt;span class="se"&gt;\&lt;/span&gt;
  https://unpkg.com/browse/htmx.org@1.9.10/dist/htmx.min.js

&lt;span class="c"&gt;# Download the latest version of json-enc&lt;/span&gt;
curl &lt;span class="nt"&gt;-o&lt;/span&gt; json-enc.js &lt;span class="se"&gt;\&lt;/span&gt;
  https://unpkg.com/htmx.org@1.9.10/dist/ext/json-enc.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a starting point, we use a fairly simple HTML page. Please note that the page is already linking the stylesheet (&lt;code&gt;&amp;lt;link rel=...&amp;gt;&lt;/code&gt;) as part of the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Shopping List&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Shopping List | &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;powered by Spin &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; htmx&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"slug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This is a simple shopping list for demonstrating the combination of
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://htmx.org/"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;htmx&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; and
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://developer.fermyon.com"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Fermyon Spin&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;.
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"all-items"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;aside&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Add a new item to the shopping list&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Add Item"&lt;/span&gt; &lt;span class="na"&gt;maxlength=&lt;/span&gt;&lt;span class="s"&gt;"36"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Add&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;footer&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Made with 💜 by&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://www.fermyon.com"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Fermyon&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Find the code on&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"xxx"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;GitHub&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding htmx to the app
&lt;/h3&gt;

&lt;p&gt;First, we have to ensure htmx and the &lt;code&gt;json-enc&lt;/code&gt; extension are brought into context correctly. Update the &lt;code&gt;index.html&lt;/code&gt; file and add the following &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags before the closing &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/htmx.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"json-enc.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Loading and displaying items
&lt;/h3&gt;

&lt;p&gt;Let’s have a look at loading and displaying the items on our shopping list now!&lt;/p&gt;

&lt;p&gt;We can use the &lt;code&gt;hx-get&lt;/code&gt; attribute to issue an HTTP GET request to &lt;code&gt;/api/items&lt;/code&gt; for loading all items. Using the &lt;code&gt;hx-target&lt;/code&gt; attribute, we specify &lt;strong&gt;where&lt;/strong&gt; the received HTML fragment should be placed. We control &lt;strong&gt;how&lt;/strong&gt; the HTML fragment is treated with the &lt;code&gt;hx-swap&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;Last, but not least, we use the &lt;code&gt;hx-trigger&lt;/code&gt; attribute to determine &lt;strong&gt;when&lt;/strong&gt; HMTX should issue the HTTP GET request. We update the HTML, and modify the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; node to match the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/api/items"&lt;/span&gt;
      &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#all-items"&lt;/span&gt;
      &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHtml"&lt;/span&gt;
      &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"load"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deleting an item
&lt;/h3&gt;

&lt;p&gt;The HTML fragment for every item contains an icon and a &lt;code&gt;hx-delete&lt;/code&gt; attribute. Users can press the delete icon, to delete the corresponding item. Although we could issue the HTTP DELETE request immediately, we can prevent users from accidentally deleting items by adding a confirmation dialog. (Although there are fancier UI representations possible, we’ll keep it simple here and use the standard confirmation dialog provided by the browser.)&lt;/p&gt;

&lt;p&gt;Because we place all items inside of &lt;code&gt;#all-items&lt;/code&gt;, we can add &lt;code&gt;hx-&lt;/code&gt; attributes on that particular container, to control the actual delete behavior. We customize the message shown in the confirmation dialog, using the &lt;code&gt;hx-confirm&lt;/code&gt; attribute. To specify which child node should be manipulated when the delete operation is confirmed, we use the &lt;code&gt;hx-target&lt;/code&gt; attribute. Again, we use &lt;code&gt;hx-swap&lt;/code&gt; to specify that we want to remove the corresponding &lt;code&gt;.item&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;Let’s update the &lt;code&gt;&amp;lt;div id="all-items"&amp;gt;&lt;/code&gt; node as shown in the following snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"all-items"&lt;/span&gt;
     &lt;span class="na"&gt;hx-confirm=&lt;/span&gt;&lt;span class="s"&gt;"Do you really want to delete this item?"&lt;/span&gt;
     &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .item"&lt;/span&gt;
     &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML swap:1s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding a new item
&lt;/h3&gt;

&lt;p&gt;Our frontend contains a simple &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; which allows users to add new items to the shopping list. Upon submitting the form, we want to issue an HTTP POST request to &lt;code&gt;/api/items&lt;/code&gt; and send the provided text as JSON to our serverless backend. Once an item has been added, we want our UI to refresh and display the updated shopping list.&lt;/p&gt;

&lt;p&gt;Our serverless backend expects to receive the payload for adding new items as JSON. Because of this, we have added the JSON extension for htmx (&lt;code&gt;json-enc.js&lt;/code&gt;) to our frontend project.&lt;/p&gt;

&lt;p&gt;We can alter the default behavior of htmx (because it normally sends the data from HTML forms as &lt;code&gt;form-data&lt;/code&gt;) by adding the &lt;code&gt;hx-ext="json-enc"&lt;/code&gt; attribute to the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; node.&lt;/p&gt;

&lt;p&gt;Additionally, we will update the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; definition by adding the &lt;code&gt;hx-post&lt;/code&gt; and &lt;code&gt;hx-swap&lt;/code&gt; attributes. We specify the &lt;code&gt;hx-on::after-request&lt;/code&gt; attribute, to control what should happen after the request has been processed. Modify the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; node to look like shown here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/api/items"&lt;/span&gt;
     &lt;span class="na"&gt;hx-ext=&lt;/span&gt;&lt;span class="s"&gt;"json-enc"&lt;/span&gt;
     &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
     &lt;span class="na"&gt;hx-on::after-request=&lt;/span&gt;&lt;span class="s"&gt;"this.reset()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding those four attributes to the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tag, we control how and where the form data is sent. On top of that, the form is reset after the request has been processed. Finally, we have to&lt;br&gt;
reload the shopping list once the request has finished.&lt;/p&gt;

&lt;p&gt;Do you remember the response that we sent from the backend when a new item has been added to the database? As part of the response, we sent an HTTP header called &lt;code&gt;HX-Trigger&lt;/code&gt; with the value &lt;code&gt;newItem&lt;/code&gt;. We use this value and instruct htmx to reload the list of all items. All we have to do in the frontend is alter the &lt;code&gt;hx-trigger&lt;/code&gt; attribute on &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; and add our “custom event” as a trigger. Update the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; tag to match the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/api/items"&lt;/span&gt;
     &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"load, newItem from:body"&lt;/span&gt;
     &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#all-items"&lt;/span&gt;
     &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running the application locally
&lt;/h3&gt;

&lt;p&gt;Now that we have implemented everything, we can test our app. To do so, we move into the project folder (the folder containing the &lt;code&gt;spin.toml&lt;/code&gt; file) and use the &lt;code&gt;spin&lt;/code&gt; CLI for compiling and running both components (frontend and backend).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build the application&lt;/span&gt;
&lt;span class="c"&gt;# Actually, only the backend has to be compiled... &lt;/span&gt;
&lt;span class="c"&gt;# Spin is smart enough to recognize that&lt;/span&gt;
spin build

Building component api with &lt;span class="sb"&gt;`&lt;/span&gt;cargo build &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-wasi &lt;span class="nt"&gt;--release&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
Working directory: &lt;span class="s2"&gt;"./api"&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;

&lt;span class="c"&gt;# Start the app on your local machine&lt;/span&gt;
&lt;span class="c"&gt;# by specifying the --sqlite option, we tell Spin to execute the migration.sql &lt;/span&gt;
&lt;span class="c"&gt;# upon starting&lt;/span&gt;
spin up &lt;span class="nt"&gt;--sqlite&lt;/span&gt; @migration.sql

Logging component stdio to &lt;span class="s2"&gt;".spin/logs/"&lt;/span&gt;
Storing default SQLite data to &lt;span class="s2"&gt;".spin/sqlite_db.db"&lt;/span&gt;
Serving http://127.0.0.1:3000
Available Routes:
 api: http://127.0.0.1:3000/api &lt;span class="o"&gt;(&lt;/span&gt;wildcard&lt;span class="o"&gt;)&lt;/span&gt;
 app: http://127.0.0.1:3000 &lt;span class="o"&gt;(&lt;/span&gt;wildcard&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the output of &lt;code&gt;spin up&lt;/code&gt; outlines, we can access the shopping list by browsing to &lt;a href="http://127.0.0.1:3000/"&gt;http://127.0.0.1:3000&lt;/a&gt;, which should display the serverless shopping list like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50holps9zemo5vbpsnaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50holps9zemo5vbpsnaz.png" alt="Shopping List powered by Spin &amp;amp; htmx" width="800" height="591"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying to Fermyon Cloud
&lt;/h3&gt;

&lt;p&gt;Having our shopping list tested locally, we can take it one step further and deploy it to Fermyon Cloud. To deploy our app using the &lt;code&gt;spin&lt;/code&gt; CLI, we must log in with Fermyon Cloud. We can login,&lt;br&gt;
by following the instructions provided by the &lt;code&gt;spin cloud login&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Deploying to Fermyon Cloud is as simple as running the &lt;code&gt;spin cloud deploy&lt;/code&gt; command from within the root folder of our project. Because our app relies on a SQLite database, we must provide a unique name for the database inside of our Fermyon Cloud account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deploy to Fermyon Cloud&lt;/span&gt;
spin cloud deploy

Uploading shopping-list version 0.1.0 to Fermyon Cloud...
Deploying...
App &lt;span class="s2"&gt;"shopping-list"&lt;/span&gt; accesses a database labeled &lt;span class="s2"&gt;"default"&lt;/span&gt;
Would you like to &lt;span class="nb"&gt;link &lt;/span&gt;an existing database or create a new database?: 
Create a new database and &lt;span class="nb"&gt;link &lt;/span&gt;the app to it

What would you like to name your database? 
Note: This name is used when managing your database at the account level.
The app &lt;span class="s2"&gt;"shopping-list"&lt;/span&gt; will refer to this database by the label &lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Other apps can use different labels to refer to the same database.: shopping

Waiting &lt;span class="k"&gt;for &lt;/span&gt;application to become ready........ 
ready

Available Routes:
 api: https://shopping-list-1jcjqxxq.fermyon.app/api &lt;span class="o"&gt;(&lt;/span&gt;wildcard&lt;span class="o"&gt;)&lt;/span&gt;
 app: https://shopping-list-1jcjqxxq.fermyon.app &lt;span class="o"&gt;(&lt;/span&gt;wildcard&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the initial deployment has been finished, we must ensure that our &lt;code&gt;ITEMS&lt;/code&gt; table is created in the database that was created by Fermyon Cloud. To do so, we can use the &lt;code&gt;spin cloud sqlite execute&lt;/code&gt; command as shown in the next snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all SQLite databases in your Fermyon Cloud account&lt;/span&gt;
spin cloud sqlite list
+-------------------------------+
| App       Label      Database |
+&lt;span class="o"&gt;===============================&lt;/span&gt;+
| shopping  default    shopping |
+-------------------------------+

&lt;span class="c"&gt;# Apply migration.sql to the shopping database&lt;/span&gt;
spin cloud sqlite execute &lt;span class="nt"&gt;--database&lt;/span&gt; shopping @migration.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fermyon Cloud automatically assigns a unique domain to our application, we can customize the domain and even bring a custom domain using the &lt;a href="https://cloud.fermyon.com/"&gt;Fermyon Cloud portal&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we walked through building a truly serverless application based on Spin and htmx. Although our shopping list is quite simple, it demonstrates how we can combine both to build a real app. Looking at both, Spin and htmx have at least one thing in common:&lt;/p&gt;

&lt;p&gt;They reduce complexity!&lt;/p&gt;

&lt;p&gt;With htmx we can build dynamic frontends without having to learn the idioms and patterns of full-blown, big Single Page Application (SPA) frameworks such as Angular. We can declare our intentions directly in HTML by adding a set of htmx attributes to HTML tags.&lt;/p&gt;

&lt;p&gt;With Spin we can focus on solving functional requirements and choose from a wide variety of programming languages to do so. Integration with services like databases, key-value stores, message brokers, or even Large Language Models (LLMs) is amazingly smooth and&lt;br&gt;
requires no Ops at all. &lt;/p&gt;

&lt;p&gt;On top of that, the &lt;code&gt;spin&lt;/code&gt; CLI and the language-specific SDKs provide amazing developer productivity, that is unmatched in the context of WebAssembly.&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>htmx</category>
      <category>tutorial</category>
      <category>rust</category>
    </item>
    <item>
      <title>Terraform - The definitive guide for Azure enthusiasts</title>
      <dc:creator>Thorsten Hans</dc:creator>
      <pubDate>Mon, 21 Oct 2019 20:27:52 +0000</pubDate>
      <link>https://dev.to/thorstenhans/terraform-the-definitive-guide-for-azure-enthusiasts-2nn9</link>
      <guid>https://dev.to/thorstenhans/terraform-the-definitive-guide-for-azure-enthusiasts-2nn9</guid>
      <description>&lt;p&gt;&lt;strong&gt;This post has been published initially on my blog at &lt;a href="https://thorsten-hans.com"&gt;thorsten-hans.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Terraform&lt;/em&gt; is a product in the &lt;em&gt;Infrastructure as Code (IaC)&lt;/em&gt; space, it has been created by &lt;a href="https://www.hashicorp.com/"&gt;HashiCorp&lt;/a&gt;. With &lt;em&gt;Terraform&lt;/em&gt; you can use a single language to describe your infrastructure in code. This guide explains the core concepts of &lt;em&gt;Terraform&lt;/em&gt; and essential basics that you need to spin up your first Azure environments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is Infrastructure as Code (IaC)&lt;/li&gt;
&lt;li&gt;
What is Terraform

&lt;ul&gt;
&lt;li&gt;Terraform Providers&lt;/li&gt;
&lt;li&gt;The HashiCorp Configuration Language (HCL)&lt;/li&gt;
&lt;li&gt;Previewing changes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Setting up Terraform

&lt;ul&gt;
&lt;li&gt;Terraform Extension for Code&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
DataTypes in HCL

&lt;ul&gt;
&lt;li&gt;Booleans&lt;/li&gt;
&lt;li&gt;Strings&lt;/li&gt;
&lt;li&gt;Lists&lt;/li&gt;
&lt;li&gt;Maps&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;How does a Terraform project look like&lt;/li&gt;
&lt;li&gt;
Variables in HCL

&lt;ul&gt;
&lt;li&gt;Referencing Variables&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Outputs in HCL&lt;/li&gt;
&lt;li&gt;Overrides&lt;/li&gt;
&lt;li&gt;
The Azure Provider

&lt;ul&gt;
&lt;li&gt;Authenticating mechanisms offered by the Azure Provider&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Your first Azure Environment with Terraform&lt;/li&gt;
&lt;li&gt;Destroying Terraform Environments&lt;/li&gt;
&lt;li&gt;The Terraform Lifecycle&lt;/li&gt;
&lt;li&gt;Download the code from GitHub&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Infrastructure as Code (IaC)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Infrastructure as Code (IaC)&lt;/em&gt; is the process of describing infrastructural components such as servers, services, or databases using a programming language. Once all infrastructural requirements are described in code, that code can be stored in &lt;em&gt;source control&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source control&lt;/em&gt; means that the infrastructure is &lt;em&gt;versioned&lt;/em&gt;, &lt;em&gt;transparent&lt;/em&gt;, &lt;em&gt;documented&lt;/em&gt;, &lt;em&gt;testable&lt;/em&gt;, &lt;em&gt;mutable&lt;/em&gt; and &lt;em&gt;discoverable&lt;/em&gt;. Once the first &lt;em&gt;version&lt;/em&gt; is stored in &lt;em&gt;source control&lt;/em&gt;, all team members see which infrastructure is required to bring a project alive. Everyone sees which configuration settings are required to make -for example- the database perform as good as expected. &lt;/p&gt;

&lt;p&gt;Changes are done to the infrastructure exist as dedicated &lt;em&gt;commits&lt;/em&gt; -or more significant changes as &lt;em&gt;pull requests&lt;/em&gt;- and will be &lt;em&gt;reviewed&lt;/em&gt; by peers before being merged into the latest version. The code is also easy to test and on top, &lt;em&gt;Continuous Integration (CI)&lt;/em&gt; builds can execute existing tests automatically with no human interaction. Having tests means errors in the infrastructure configuration are spotted earlier.&lt;/p&gt;

&lt;p&gt;Also, having code in a repository is 100% more documentation as if all necessary information is stored in just one human brain. This is also a critical risk reduction for every project. &lt;em&gt;Let's elaborate on risk reduction a bit:&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Before Infrastructure as Code, John -the smart guy everyone has on his/her software projects- was responsible for maintaining the infrastructure. John gave his best to describe all essential infrastructure parts and their configuration values. However, modern software projects evolve. Services have to be added or removed from the overall architecture; things must be scaled dynamically and/or manually based on external events. So there is a good chance that John has some critical information only in his head... Maybe has Tim -the guy who sits next to John- some tribal knowledge about the Redis configuration, but would you bet on it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;That said, everyone on the team knows John. Everyone trusts him. However, every teammate is afraid when John wants to take a couple of weeks off and go on vacation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Look at your current project team and try to spot &lt;em&gt;John&lt;/em&gt;. Everybody knows situations or constellations like these. To be clear: &lt;strong&gt;It's not Johns fault&lt;/strong&gt;. It's the fact that the entire organization isn't using &lt;em&gt;IaC&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Having &lt;em&gt;Infrastructure as Code&lt;/em&gt; would remove that critical path.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Terraform
&lt;/h2&gt;

&lt;p&gt;Now, knowing what &lt;em&gt;Infrastructure as Code&lt;/em&gt; is, it's time to look at &lt;em&gt;Terraform&lt;/em&gt; itself. &lt;em&gt;Terraform&lt;/em&gt; is currently the best tool to implement &lt;em&gt;IaC&lt;/em&gt;. And it doesn't matter if you're using &lt;em&gt;Azure&lt;/em&gt;, &lt;em&gt;Azure Stack&lt;/em&gt; or other vendors as a target for your infrastructure. With &lt;em&gt;Terraform&lt;/em&gt; you can create, modify and destroy environments safely and efficiently.&lt;/p&gt;

&lt;p&gt;For me, these are the three significant benefits offered by &lt;em&gt;Terraform&lt;/em&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It works with almost every environment (On-Demand and On-Premises)&lt;/li&gt;
&lt;li&gt;You've to learn only a single and simple language&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Terraform&lt;/em&gt; can preview changes before applying them&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's take a closer look at those three benefits now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terraform Providers
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;HashiCorp&lt;/em&gt; created a small, yet powerful tool which can talk to numerous platforms using a flexible provider model. Vendors like &lt;em&gt;Microsoft&lt;/em&gt; expose functionalities as APIs, and the corresponding &lt;em&gt;Terraform&lt;/em&gt; provider is responsible for making those APIs accessible to you. If you ask yourself which platforms &lt;em&gt;Terraform&lt;/em&gt; is supporting, go and check the &lt;a href="https://www.terraform.io/docs/providers/index.html"&gt;list of providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, means dealing with different APIs, that each platform supports different features?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yes&lt;/strong&gt;. Take &lt;em&gt;Azure Functions&lt;/em&gt; as an example. You can quickly query information about an existing &lt;em&gt;Azure Functions&lt;/em&gt; instance or create/modify/destroy another &lt;em&gt;Azure Function&lt;/em&gt; instance using the  &lt;em&gt;Azure provider for Terraform&lt;/em&gt;. However, if you talk to a local &lt;em&gt;VMWare Cluster&lt;/em&gt;, you can't interact with &lt;em&gt;Azure Functions&lt;/em&gt; because &lt;em&gt;VMWare&lt;/em&gt; doesn't have a first-class citizen of type &lt;em&gt;Azure Function&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The HashiCorp Configuration Language (HCL)
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;HashiCorp Configuration Language (HCL)&lt;/em&gt; is a small domain-specific language which is based on &lt;em&gt;JSON&lt;/em&gt;. The &lt;em&gt;HashiCorp&lt;/em&gt; team removed some language-specific plumbings to make us a bit more productive by saving some keystrokes.&lt;/p&gt;

&lt;p&gt;On the other side, &lt;em&gt;Terraform&lt;/em&gt; adds some powerful interpolation features to &lt;em&gt;HCL&lt;/em&gt;, which you'll use and love every day. You'll dive into &lt;em&gt;HCL&lt;/em&gt; later in this guide. However, take the following short snippet as sneak-peek to see how &lt;em&gt;Terraform&lt;/em&gt; scripts look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_redis_cache"&lt;/span&gt; &lt;span class="s2"&gt;"sample"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tf-redis-basic"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_resource_group.test.location}"&lt;/span&gt;
  &lt;span class="nx"&gt;resource_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_resource_group.test.name}"&lt;/span&gt;
  &lt;span class="nx"&gt;capacity&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;family&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"C"&lt;/span&gt;
  &lt;span class="nx"&gt;sku_name&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Basic"&lt;/span&gt;
  &lt;span class="nx"&gt;enable_non_ssl_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.redis_enable_non_ssl}"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.all_tags}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Previewing changes
&lt;/h3&gt;

&lt;p&gt;Being able to preview changes before they are applied to a platform is the most significant benefit offered by &lt;em&gt;Terraform&lt;/em&gt; in day-to-day business. You can think of it as the &lt;code&gt;git status&lt;/code&gt; of &lt;em&gt;IaC&lt;/em&gt;. You describe your infrastructure in &lt;em&gt;HCL&lt;/em&gt; and use the handy &lt;code&gt;terraform plan&lt;/code&gt; command to see what would happen if that &lt;em&gt;Terraform&lt;/em&gt; script gets &lt;strong&gt;applied&lt;/strong&gt; to the chosen platform.&lt;/p&gt;

&lt;p&gt;Besides those three major benefits, &lt;em&gt;Terraform&lt;/em&gt; offers things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;centralized state management&lt;/li&gt;
&lt;li&gt;implicit dependency resolution&lt;/li&gt;
&lt;li&gt;parallelization of execution&lt;/li&gt;
&lt;li&gt;reusable modules&lt;/li&gt;
&lt;li&gt;terraform module registry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and many more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Terraform
&lt;/h2&gt;

&lt;p&gt;Setting up &lt;em&gt;Terraform&lt;/em&gt; is quite smooth. &lt;em&gt;Terraform&lt;/em&gt; can be used on every popular operating system. It can be downloaded directly from the &lt;a href="https://www.terraform.io/downloads.html"&gt;official website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On &lt;em&gt;macOS&lt;/em&gt; you can install it also using &lt;em&gt;homebrew&lt;/em&gt; by executing &lt;code&gt;brew install terraform&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On &lt;em&gt;Windows&lt;/em&gt; you can install it using &lt;em&gt;chocolatey&lt;/em&gt; by executing &lt;code&gt;choco install terraform&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The installation can be verified by executing the &lt;code&gt;terraform&lt;/code&gt; command. &lt;em&gt;Terraform&lt;/em&gt; should now show all available &lt;code&gt;subcommands&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gUSMG1Lc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-setup-verification.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gUSMG1Lc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-setup-verification.png" alt="A working Terraform installation" width="800" height="817"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Terraform Extension for Code
&lt;/h3&gt;

&lt;p&gt;There is an extension for &lt;em&gt;Visual Studio Code&lt;/em&gt; called &lt;a href="https://github.com/mauve/vscode-terraform.git"&gt;Terraform (by Mikael Olenfalk)&lt;/a&gt;. Once installed Code can do &lt;em&gt;syntax highlighting&lt;/em&gt;, &lt;em&gt;code completion&lt;/em&gt;, &lt;em&gt;IntelliSense&lt;/em&gt; and on top of that you can drill through your resource graph using a nice visual tree.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8qDbSThh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-vscode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8qDbSThh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-vscode.png" alt="Terraform in VSCode" width="501" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  DataTypes in HCL
&lt;/h2&gt;

&lt;p&gt;The introduction already mentioned that &lt;em&gt;HashiCorp Configuration Language (HCL)&lt;/em&gt; is a relatively simple language. This simplicity continues when we dive deeper in &lt;em&gt;HCL DataTypes&lt;/em&gt;. Currently &lt;em&gt;HCL&lt;/em&gt; knows four different data types that you'll use to craft your scripts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Booleans
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;boolean&lt;/code&gt; in &lt;em&gt;HCL&lt;/em&gt; must have a value. The value can either be &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;. &lt;em&gt;HCL&lt;/em&gt; has no &lt;strong&gt;native&lt;/strong&gt; support for booleans; instead, it converts every &lt;code&gt;string&lt;/code&gt; into a &lt;code&gt;boolean&lt;/code&gt; if possible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"storace_account_enable_firewall"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strings
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;string&lt;/code&gt; in &lt;em&gt;HCL&lt;/em&gt; is either a single line of text or text that spreads over multiple lines. The following snippet shows, how those two kinds of &lt;code&gt;string&lt;/code&gt; can be defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"single_line_string"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"value"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"multi_line_string"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
This value spreads
over several lines.
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When receiving &lt;code&gt;string&lt;/code&gt; values, &lt;em&gt;HCL&lt;/em&gt; inspects the value and converts it to a &lt;code&gt;number&lt;/code&gt; or a &lt;code&gt;boolean&lt;/code&gt; if possible (as mentioned earlier). However, there are some important caveats that every &lt;em&gt;Terraform&lt;/em&gt; user needs to know. You can read more about those &lt;a href="https://www.terraform.io/docs/configuration/variables.html#booleans"&gt;caveats here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lists
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;list&lt;/code&gt; in &lt;em&gt;HCL&lt;/em&gt; is a collection of &lt;code&gt;string&lt;/code&gt; values which are indexed by &lt;code&gt;numbers&lt;/code&gt;. Lists in &lt;em&gt;HCL&lt;/em&gt; are &lt;em&gt;zero-based&lt;/em&gt; (so the first index of a list is always &lt;code&gt;0&lt;/code&gt; in &lt;em&gt;HCL&lt;/em&gt;). It's a typed JSON &lt;code&gt;Array&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"simple_list"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"list"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"development"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"staging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Maps
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;map&lt;/code&gt; in &lt;em&gt;HCL&lt;/em&gt; is a dictionary of &lt;code&gt;string&lt;/code&gt; values, indexed by &lt;code&gt;string&lt;/code&gt; keys. You can think of it as a regular JavaScript &lt;code&gt;object&lt;/code&gt;. Defining a &lt;code&gt;map&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"custom_tags"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"map"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;author&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Thorsten"&lt;/span&gt;
    &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How does a Terraform project look like
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Terraform&lt;/em&gt; projects are easy to understand. Every folder is a valid &lt;em&gt;Terraform&lt;/em&gt; project if it contains at least a single &lt;code&gt;.tf&lt;/code&gt; or &lt;code&gt;.tf.json&lt;/code&gt; file. &lt;em&gt;(Yes you can write your scripts in plain old JSON, but my advice is to stick with &lt;code&gt;.tf&lt;/code&gt; files)&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;However, if you have multiple &lt;code&gt;.tf&lt;/code&gt; files in a folder, files are processed in alphabetical order. While processing, &lt;code&gt;.tf&lt;/code&gt; files are merely appended together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variables in HCL
&lt;/h2&gt;

&lt;p&gt;In &lt;em&gt;Terraform&lt;/em&gt; &lt;code&gt;variables&lt;/code&gt; can be specified to make scripts more flexible and dynamic. Variables are either created directly inside of regular &lt;code&gt;.tf&lt;/code&gt; scripts or they could be organized in dedicated &lt;code&gt;variables.tf&lt;/code&gt; files. There is no real best practice here because each &lt;em&gt;Terraform&lt;/em&gt; projects differ in both: size and complexity.&lt;/p&gt;

&lt;p&gt;Because of &lt;em&gt;Terraform's&lt;/em&gt; implicit dependency resolutions, &lt;code&gt;variables&lt;/code&gt; are always available, no matter where they end up in the final script. To keep things organized, we'll start with a dedicated &lt;code&gt;variable&lt;/code&gt; file. In such a situation, I choose names like &lt;code&gt;variables.tf&lt;/code&gt;. &lt;code&gt;frontend-variables.tf&lt;/code&gt; or &lt;code&gt;backend-variables.tf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;variable&lt;/code&gt; has a reasonably simple schema in &lt;em&gt;HCL&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"storage_account_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"thorstensstorage"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"provide a unique name for the Storage Account"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"storage_account_location"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"name of the Azure datacenter where the Storage Account should be generated"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every variable requires a value at runtime. There are &lt;strong&gt;five ways&lt;/strong&gt; how the value of a variable could be specified in &lt;em&gt;Terraform&lt;/em&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The actual value of the variable is provided by the &lt;code&gt;default&lt;/code&gt; property as part of the variable definition as shown above for &lt;code&gt;storage_account_name&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The variable &lt;code&gt;storage_account_location&lt;/code&gt; has no &lt;code&gt;default&lt;/code&gt; value. When &lt;code&gt;terraform apply&lt;/code&gt; or &lt;code&gt;terraform plan&lt;/code&gt; is executed, a simple wizard will ask for a value.&lt;/li&gt;
&lt;li&gt;Actual variable values could also be specified using &lt;code&gt;Environment Variables&lt;/code&gt;. This is an excellent approach to build servers or scenarios where &lt;em&gt;Terraform&lt;/em&gt; scripts are applied without human interaction. &lt;code&gt;Environment Variables&lt;/code&gt; are pulled if their name follows the schema &lt;code&gt;TF_VAR_{variable_name}&lt;/code&gt; (&lt;code&gt;TF_VAR_storage_account_location&lt;/code&gt; in this example)&lt;/li&gt;
&lt;li&gt;Values can be specified by passing arguments to the &lt;code&gt;terraform apply&lt;/code&gt; command. This approach is great for development time or -if required due to limitations- for unattended execution contexts such as build servers&lt;/li&gt;
&lt;li&gt;The last and most convenient method to specify variable values are so-called &lt;code&gt;.tfvars&lt;/code&gt; files. &lt;code&gt;.tfvars&lt;/code&gt; files should never go to source control. In real-world scenarios, it's often required to pass some sensitive data into &lt;em&gt;Terraform&lt;/em&gt; scripts (Think of Service Principal credentials for example). My projects normally contain a &lt;code&gt;values.tfvars.template&lt;/code&gt; file which is explicitly added to &lt;em&gt;git&lt;/em&gt; and tells other teammates which values should be defined. Providing concrete values for the sample &lt;code&gt;variables&lt;/code&gt; above could look like this.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;storage_account_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"storagethorsten"&lt;/span&gt;
&lt;span class="nx"&gt;storage_account_location&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"West Europe"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Referencing Variables
&lt;/h3&gt;

&lt;p&gt;Referencing variables in &lt;em&gt;Terraform&lt;/em&gt; scripts is done by using the &lt;em&gt;Terraform&lt;/em&gt; interpolation syntax. Both variables that were defined above are used in the following sample to provide essential metadata for an &lt;em&gt;Azure Storage Account&lt;/em&gt;. The following script contains &lt;em&gt;HCL&lt;/em&gt; keywords that weren't explained yet. Don't worry about those for now. The concept of using &lt;em&gt;variables&lt;/em&gt; is essential for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_storage_account"&lt;/span&gt; &lt;span class="s2"&gt;"storageacc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"exports${var.storage_account_name}"&lt;/span&gt;
  &lt;span class="nx"&gt;resource_group_name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"thh"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.storage_account_location}"&lt;/span&gt;
  &lt;span class="nx"&gt;account_tier&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Standard"&lt;/span&gt;
  &lt;span class="nx"&gt;account_replication_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GRS"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute &lt;code&gt;terraform plan&lt;/code&gt; and see how the interpolations construct runtime values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8rsbUWlP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8rsbUWlP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-1.png" alt="Terraform plan - Interpolated configuration values" width="800" height="859"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Outputs in HCL
&lt;/h2&gt;

&lt;p&gt;Every &lt;em&gt;Terraform&lt;/em&gt; script has to read data from &lt;code&gt;resources&lt;/code&gt;. Data like &lt;em&gt;Connection Strings&lt;/em&gt;, &lt;em&gt;IP addresses&lt;/em&gt; or &lt;em&gt;DNS names&lt;/em&gt; from items that are created as part of the script itself. This can be achieved using so-called &lt;code&gt;outputs&lt;/code&gt; in &lt;em&gt;HCL&lt;/em&gt;. The definition syntax is quite similar to &lt;code&gt;variable&lt;/code&gt; definitions. For smaller projects, my advice is to put all outputs into a single file called &lt;code&gt;all_outputs.tf&lt;/code&gt;. A simple &lt;code&gt;output&lt;/code&gt; that grabs the &lt;code&gt;primary access key&lt;/code&gt; from the &lt;em&gt;Azure Storage Account&lt;/em&gt; specified above may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"storage_account_access_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_storage_account.storageacc.primary_access_key}"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The storage account's primary access key"&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;value&lt;/code&gt; of the &lt;code&gt;output&lt;/code&gt; is queried from the &lt;em&gt;Azure Storage Account&lt;/em&gt;, once again the &lt;code&gt;resource&lt;/code&gt; is referenced by interpolation. To identify the custom &lt;code&gt;resource&lt;/code&gt;, the combination of the type &lt;code&gt;azurerm_storage_account&lt;/code&gt; and the custom, unique name &lt;code&gt;storageacc&lt;/code&gt; is used for identification. When the &lt;code&gt;resource&lt;/code&gt; is identified, the &lt;em&gt;exported&lt;/em&gt; property is referenced by the name (here &lt;code&gt;primary_access_key&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The official &lt;em&gt;Azure&lt;/em&gt; provider documentation is providing a list of all exported attributes. Check the documentation of &lt;code&gt;azurerm_storage_account&lt;/code&gt; &lt;a href="https://www.terraform.io/docs/providers/azurerm/r/storage_account.html#attributes-reference"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Besides &lt;code&gt;description&lt;/code&gt;, the &lt;code&gt;output&lt;/code&gt; scheme defines another important property called &lt;code&gt;sensitive&lt;/code&gt;. If the output is marked as sensitive, &lt;em&gt;Terraform&lt;/em&gt; won't write the actual value to logs. &lt;code&gt;sensitive&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt; in the definition above for demonstration purpose.&lt;/p&gt;

&lt;p&gt;Execute the &lt;em&gt;Terraform&lt;/em&gt; script using &lt;code&gt;terraform apply&lt;/code&gt; and check the log messages for the &lt;code&gt;storage_account_access_key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8ju5eeXv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8ju5eeXv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-2.png" alt="Terraform Outputs - printed by terraform apply" width="800" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overrides
&lt;/h2&gt;

&lt;p&gt;As said, all &lt;code&gt;.tf&lt;/code&gt; files within a &lt;em&gt;Terraform&lt;/em&gt; project are appended together. &lt;em&gt;Overrides&lt;/em&gt; behave a bit differently, they're loaded at the end, and their values are merged into existing configurations instead of being simply appended. &lt;em&gt;Overrides&lt;/em&gt; are a great solution to change simple properties without actually changing the configuration itself.&lt;/p&gt;

&lt;p&gt;Typical scenarios for &lt;em&gt;Overrides&lt;/em&gt; are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build servers&lt;/li&gt;
&lt;li&gt;temporary modifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Override&lt;/em&gt; files must be named &lt;code&gt;override.tf&lt;/code&gt; or end with &lt;code&gt;_override.tf&lt;/code&gt;. If multiple &lt;em&gt;Override&lt;/em&gt; files are present, they're merged in alphabetical order. For example, consider the following &lt;em&gt;Azure App Service&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_app_service"&lt;/span&gt; &lt;span class="s2"&gt;"webapi"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sample-api"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; can be modified by defining &lt;code&gt;override.tf&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_app_service"&lt;/span&gt; &lt;span class="s2"&gt;"webapi"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"override-sample-api"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Azure Provider
&lt;/h2&gt;

&lt;p&gt;The real power of &lt;em&gt;Terraform&lt;/em&gt; is defined by the actual provider that is used. Luckily, the Azure provider is a compelling one. Besides creating, modifying or deleting resources, existing resources (including those, that were not created by &lt;em&gt;Terraform&lt;/em&gt;) could be used as a data source, and their values can quickly be brought into every &lt;em&gt;Terraform&lt;/em&gt; scripts. &lt;/p&gt;

&lt;p&gt;Bevor we're diving deeper into resources and data sources, a new &lt;em&gt;Terraform&lt;/em&gt; project must be created, and the &lt;em&gt;Azure&lt;/em&gt; provider has to be configured. Create a new folder &lt;code&gt;azure-sample&lt;/code&gt; and a new file called &lt;code&gt;main.tf&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"azurerm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"=1.22.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To download the desired provider, you've to execute &lt;code&gt;terraform init&lt;/code&gt; in the project's folder. The &lt;code&gt;terraform providers&lt;/code&gt; command can be executed in any project to list all providers used in the current project.&lt;/p&gt;

&lt;p&gt;Without further configuration, the &lt;em&gt;Azure&lt;/em&gt; provider will reuse existing authentication from &lt;a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest"&gt;Azure CLI&lt;/a&gt;. The project runs in the security context provided by the local &lt;code&gt;az&lt;/code&gt; installation. All modifications are applied to the currently selected &lt;em&gt;Azure Subscription&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;You can verify this in Azure CLI using &lt;code&gt;az account list&lt;/code&gt;. &lt;em&gt;Terraform&lt;/em&gt; uses the &lt;code&gt;default&lt;/code&gt; subscription. You can change the subscription in &lt;code&gt;az&lt;/code&gt; by executing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az account &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--subscription&lt;/span&gt; 00000000-0000-0000-0000-000000000000
&lt;span class="c"&gt;# replace 00000000-0000-0000-0000-000000000000 with your subscription ID&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authenticating mechanisms offered by the Azure Provider
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Azure&lt;/em&gt; provider supports four different kinds of authentication mechanisms. Depending on your security implementation, you've to select the proper mechanism for your needs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authenticating to Azure using the Azure CLI&lt;/li&gt;
&lt;li&gt;Authenticating to Azure using a Service Principal and a Client Secret&lt;/li&gt;
&lt;li&gt;Authenticating to Azure using a Service Principal and a Client Certificate&lt;/li&gt;
&lt;li&gt;Authenticating to Azure using Managed Service Identity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Authenticating to Azure using Azure CLI&lt;/em&gt; is excellent to get started, however, you should switch to an authentication which is independent from &lt;code&gt;az&lt;/code&gt; early.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The other guy on the team may not have a local instance of &lt;code&gt;az&lt;/code&gt; or think of the build server. You should not add &lt;code&gt;az&lt;/code&gt; as a dependency, except for local development.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Authenticating to Azure using a &lt;em&gt;Service Principal (SP)&lt;/em&gt; is more convenient. To configure this kind of authentication, either a combination of &lt;code&gt;ClientId&lt;/code&gt; and &lt;code&gt;ClientSecret&lt;/code&gt; or -for Service Principal identification by a certificate- the combination of &lt;code&gt;client certificate password&lt;/code&gt; and &lt;code&gt;client certificate path&lt;/code&gt; is required.&lt;/p&gt;

&lt;p&gt;To authenticate to Azure using a &lt;em&gt;Managed Service Identity (MSI)&lt;/em&gt;, the &lt;code&gt;use_msi&lt;/code&gt; variable must be set to &lt;code&gt;true&lt;/code&gt; and a &lt;code&gt;msi_endpoint&lt;/code&gt; could optionally be specified. Last but not least the actual &lt;em&gt;Azure Environment&lt;/em&gt; can be specified on the provider. You can chose between &lt;code&gt;public&lt;/code&gt; (default), &lt;code&gt;usgovernment&lt;/code&gt;, &lt;code&gt;german&lt;/code&gt; or &lt;code&gt;china&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of putting those sensitive data into &lt;code&gt;.tf&lt;/code&gt; files either &lt;code&gt;.tfvars&lt;/code&gt; files or &lt;code&gt;Environment Variables&lt;/code&gt; should be used. The &lt;em&gt;Azure Provider&lt;/em&gt; excepts the names of those environment variables to follow a strict schema &lt;code&gt;ARM_{variablename}&lt;/code&gt;. (eg.:  &lt;code&gt;ARM_ENVIRONMENT&lt;/code&gt; or &lt;code&gt;ARM_USE_MSI&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;No matter which kind of "authentication mechanism" used, &lt;code&gt;ARM_ENVIRONMENT&lt;/code&gt; and &lt;code&gt;ARM_TENANT_ID&lt;/code&gt; and &lt;code&gt;ARM_SUBSCRIPTION_ID&lt;/code&gt; should always be specified.&lt;/p&gt;

&lt;p&gt;To keep things simple, this guide will stick with tokens being acquired by &lt;em&gt;Azure CLI&lt;/em&gt;. But &lt;em&gt;environment&lt;/em&gt;, &lt;em&gt;tenant&lt;/em&gt; and &lt;em&gt;subscription&lt;/em&gt; will be pinned by using &lt;code&gt;Environment Variables&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ARM_ENVIRONMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ARM_TENANT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;00000000-0000-0000-0000-000000000000
&lt;span class="c"&gt;# replace 00000000-0000-0000-0000-000000000000 with your Tenant ID&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ARM_SUBSCRIPTION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;11111111-1111-1111-1111-111111111111
&lt;span class="c"&gt;# replace 11111111-1111-1111-1111-111111111111 with your Subscription ID&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, those &lt;code&gt;export&lt;/code&gt; commands are not required if the suggested &lt;em&gt;Azure Subscription&lt;/em&gt; matches the current one chosen one in &lt;em&gt;Azure CLI&lt;/em&gt;, but it's a good practice and critical to understanding. Especially if &lt;em&gt;Terraform&lt;/em&gt; will be executed without human interaction (eg. on the build server).&lt;/p&gt;

&lt;p&gt;For further information on how to configure the different authentication mechanisms, check out the &lt;a href="https://www.terraform.io/docs/providers/azurerm/index.html"&gt;official provider documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your first Azure Environment with Terraform
&lt;/h2&gt;

&lt;p&gt;Having the provider configuration in place, it's time to dig into Azure specific resources and data sources. Everything in Azure belongs to a &lt;em&gt;Resource Group&lt;/em&gt; so let's get started with such a &lt;em&gt;Resource Group&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"azurerm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"=1.22.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"location"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"westeurope"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Specify a location see: az account list-locations -o table"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"tags"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"map"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"A list of tags associated to all resources"&lt;/span&gt;

  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;maintained_by&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_resource_group"&lt;/span&gt; &lt;span class="s2"&gt;"resg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-group"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.location}"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.tags}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far so good. Verify what &lt;em&gt;Terraform&lt;/em&gt; would do in Azure with &lt;code&gt;terraform plan&lt;/code&gt;. Before applying it to Azure, some refactorings are required, to ensure our project remains clean and readable. First, all variable should be moved to a dedicated &lt;code&gt;global_variables.tf&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;#global_variables.tf&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"location"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"westeurope"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Specify a location see: az account list-locations -o table"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"tags"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"map"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"A list of tags associated to all resources"&lt;/span&gt;

  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;maintained_by&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your local &lt;code&gt;main.tf&lt;/code&gt; should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"azurerm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"=1.22.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_resource_group"&lt;/span&gt; &lt;span class="s2"&gt;"resg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-group"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.location}"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.tags}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although the &lt;code&gt;tags&lt;/code&gt; variable is specified in &lt;code&gt;global_variables.tf&lt;/code&gt;, you should always specify critical variables using &lt;code&gt;.tfvars&lt;/code&gt; files (keep in mind that those will not go to source control!). Add &lt;code&gt;local.tfvars&lt;/code&gt; and provide specify &lt;code&gt;tags&lt;/code&gt; as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Thorsten Hans"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify, use &lt;code&gt;terraform plan -var-file=local.tfvars&lt;/code&gt; now. Terraform should print something matching the following picture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VFdiEdna--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VFdiEdna--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-3.png" alt="Terraform Plan: Merged Tags" width="602" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;tags&lt;/code&gt; variable isn't overwritten, the values from &lt;code&gt;default&lt;/code&gt; and those from &lt;code&gt;local.tfvars&lt;/code&gt; are merged (in case of looking at a &lt;code&gt;map&lt;/code&gt; variable). However, if both maps contain the same &lt;code&gt;key&lt;/code&gt;, the &lt;code&gt;value&lt;/code&gt; from the &lt;code&gt;.tfvars&lt;/code&gt; file is used.&lt;/p&gt;

&lt;p&gt;Next, add a &lt;code&gt;all_outputs.tf&lt;/code&gt; file. This file will query essential, resource-independent data from Azure once resources are applied or modified. For now, the name of the &lt;em&gt;Azure Subscription&lt;/em&gt; will be queried and written to the console once the script will be applied.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;#all_outputs.tf&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_subscription"&lt;/span&gt; &lt;span class="s2"&gt;"current"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"target_azure_subscription"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${data.azurerm_subscription.current.display_name}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's apply this state to the cloud!&lt;/p&gt;

&lt;p&gt;Execute &lt;code&gt;terraform apply -var-file=local.tfvars&lt;/code&gt; and confirm the execution plan by answering &lt;em&gt;Terraforms&lt;/em&gt; confirmation-question with &lt;code&gt;yes&lt;/code&gt;. (You can also prevent &lt;em&gt;Terraform&lt;/em&gt; from asking for confirmation by adding the &lt;code&gt;--auto-approve&lt;/code&gt; flag). Once finished, &lt;em&gt;Terraform&lt;/em&gt; will print the name of the modified &lt;em&gt;Azure Subscription&lt;/em&gt; to the console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R9FxtQv6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R9FxtQv6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-4.png" alt="Terraform apply - for the very first time" width="800" height="681"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! But having only a &lt;em&gt;Resource Group&lt;/em&gt; being deployed to Azure solves no need. For demonstration purpose, extend the script and deploy an instance of &lt;em&gt;Application Insights&lt;/em&gt;. Add the following to &lt;code&gt;main.tf&lt;/code&gt;;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_application_insights"&lt;/span&gt; &lt;span class="s2"&gt;"ai"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-ai"&lt;/span&gt;
  &lt;span class="nx"&gt;resource_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_resource_group.resg.name}"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_resource_group.resg.location}"&lt;/span&gt;
  &lt;span class="nx"&gt;application_type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Web"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.tags}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When working with &lt;em&gt;Application Insights&lt;/em&gt;, the &lt;code&gt;instrumentation_key&lt;/code&gt; is critical. It has to be provided to any resource which should write application-specific logs using &lt;em&gt;Application Insights&lt;/em&gt;. Add an &lt;code&gt;output&lt;/code&gt; and query the &lt;code&gt;instrumentation_key&lt;/code&gt; in &lt;code&gt;all_outputs.tf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"instrumentation_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_application_insights.ai.instrumentation_key}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute &lt;code&gt;terraform plan -var-file=local.tfvars&lt;/code&gt; again and verify, that only &lt;strong&gt;one&lt;/strong&gt; resource will be added. &lt;em&gt;Terraform&lt;/em&gt; looks at the currently deployed resources in &lt;em&gt;Azure&lt;/em&gt; and verifies that all properties are still matching those described in your script. If so, no action is required for that resource(s).&lt;/p&gt;

&lt;p&gt;If the changes look good, go ahead and apply them by invoking &lt;code&gt;terraform apply -var-file=local.tfvars --auto-approve&lt;/code&gt;. Finally &lt;em&gt;Terraform&lt;/em&gt; should display the following result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rvk1w9XG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rvk1w9XG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-5.png" alt="Terraform apply - Application Insights has been created" width="797" height="820"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last, but not least, the actual &lt;em&gt;Azure App Service&lt;/em&gt; and the underlying &lt;em&gt;Azure App Service Plan&lt;/em&gt; have to be created to complete the sample. Both resources expose a vast of properties, which have to be set depending on the kind of &lt;em&gt;App Service / App Service Plan&lt;/em&gt; you want to create. Again, the official documentation helps to spot and to understand all those properties. This sample will create a &lt;em&gt;Linux App Service Plan&lt;/em&gt; and a &lt;em&gt;App Service for Containers&lt;/em&gt;. For demonstration purpose, a plain &lt;em&gt;NGINX Docker Image&lt;/em&gt; will be deployed to the &lt;em&gt;App Service&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To ensure flexibility, several configuration properties should be set by variables. Add a &lt;code&gt;frontend.variables.tf&lt;/code&gt; and provide the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"appservice_plan_tier"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Standard"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Specify the SKU tier for the app service plan"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"appservice_plan_size"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"S1"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Specify the SKU size for the app service plan"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"appservice_plan_kind"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Linux"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Specify the kind for the app service plan (Linux, FunctionApp or Windows)"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"appservice_always_on"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"boolean"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Specify if the app service should be always online"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"appservice_docker_image"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx:alpine"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Specify the Docker image that should be deployed to the app service"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having the variables in place, the actual resource (&lt;em&gt;App Service Plan&lt;/em&gt;) goes to &lt;code&gt;main.tf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_app_service_plan"&lt;/span&gt; &lt;span class="s2"&gt;"appsvcplan"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-app-svc-plan"&lt;/span&gt;
  &lt;span class="nx"&gt;resource_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_resource_group.resg.name}"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_resource_group.resg.location}"&lt;/span&gt;
  &lt;span class="nx"&gt;kind&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.appservice_plan_kind}"&lt;/span&gt;
  &lt;span class="nx"&gt;reserved&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.tags}"&lt;/span&gt;

  &lt;span class="nx"&gt;sku&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;tier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.appservice_plan_tier}"&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.appservice_plan_size}"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An &lt;em&gt;Azure App Service Plan&lt;/em&gt; without an actual &lt;em&gt;App Service&lt;/em&gt; is useless. Add the following resource to &lt;code&gt;main.tf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_app_service"&lt;/span&gt; &lt;span class="s2"&gt;"appsvc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-app-linux-app-svc"&lt;/span&gt;
  &lt;span class="nx"&gt;resource_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_resource_group.resg.name}"&lt;/span&gt;
  &lt;span class="nx"&gt;app_service_plan_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_app_service_plan.appsvcplan.id}"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_resource_group.resg.location}"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.tags}"&lt;/span&gt;

  &lt;span class="nx"&gt;app_settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;WEBSITES_ENABLE_APP_SERVICE_STORAGE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;site_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;always_on&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.appservice_docker_image}"&lt;/span&gt;
    &lt;span class="nx"&gt;linux_fx_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DOCKER|${var.appservice_docker_image}"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did you recognize that all required variables were already specified in the previous snippet? If not, verify their existence. To verify our deployment once it has been applied, add another the public DNS name of the &lt;em&gt;Azure App Service&lt;/em&gt; as &lt;code&gt;output&lt;/code&gt; to &lt;code&gt;global.outputs.tf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"appservice_dns_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${azurerm_app_service.appsvc.default_site_hostname}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the runtime configuration has to be specified in &lt;code&gt;local.tfvars&lt;/code&gt; as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;appservice_plan_tier&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Basic"&lt;/span&gt;
&lt;span class="nx"&gt;appservice_plan_size&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"B1"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute &lt;code&gt;terraform plan -var-file=local.tfvars&lt;/code&gt; to preview the upcoming changes. If everything looks good, apply the changes using &lt;code&gt;terraform apply -var-file=local.tfvars --auto-approve&lt;/code&gt;. &lt;em&gt;Terraform&lt;/em&gt; will now print the public DNS name as part of all &lt;code&gt;output&lt;/code&gt; variables to the console. Open that URL using your favorite browser. You should see the beautiful &lt;em&gt;NGINX Welcome Page&lt;/em&gt; as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--18odKhUt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--18odKhUt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-6.png" alt="NGINX on an Azure App Service deployed by Terraform" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cool!&lt;/p&gt;

&lt;p&gt;But did you recognize the values specified in the &lt;code&gt;.tfvars&lt;/code&gt; file? Those differ from the default values defined in &lt;code&gt;frontend_variables.tf&lt;/code&gt;. Imagine, that you recognize a bigger load as expected on the &lt;em&gt;App Service&lt;/em&gt;, so let's scale up to the &lt;em&gt;App Service Plan&lt;/em&gt; to &lt;code&gt;Standard&lt;/code&gt; and &lt;code&gt;S1&lt;/code&gt; as initially defined as default values. Change &lt;code&gt;local.tfvars&lt;/code&gt; to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Thorsten Hans"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;appservice_plan_tier&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Standard"&lt;/span&gt;
&lt;span class="nx"&gt;appservice_plan_size&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"S1"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;terraform apply&lt;/code&gt; also prints the execution plan before actually modifying the target, you can use &lt;code&gt;terraform apply -var-file=local.tfvars&lt;/code&gt; and preview the upcoming changes before they are applied. Now you should see that &lt;em&gt;Terraform&lt;/em&gt; will &lt;strong&gt;modify&lt;/strong&gt; exactly one resource - the &lt;em&gt;Azure App Service Plan&lt;/em&gt;. It's even more precisely. It tells you exactly which properties it'll change. If it looks good, go ahead and confirm the changes.&lt;/p&gt;

&lt;p&gt;That wasn't the only issue in our script. We've also forgotten to set the &lt;code&gt;instrumentation_key&lt;/code&gt; on the &lt;em&gt;Azure App Service&lt;/em&gt;. To demonstrate &lt;em&gt;Terraform&lt;/em&gt; state management, let's set the &lt;code&gt;instrumentation_key&lt;/code&gt; on the &lt;em&gt;Azure App Service&lt;/em&gt; manually using &lt;em&gt;Azure CLI&lt;/em&gt;. You'll use &lt;code&gt;terraform output&lt;/code&gt; to query actual information from Azure and finally set the &lt;code&gt;appsetting&lt;/code&gt; manually using the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform output instrumentation_key
&lt;span class="c"&gt;# will print the Instrumentation ID (GUID) to the terminal&lt;/span&gt;
&lt;span class="c"&gt;# 22222222-2222-2222-2222-222222222222&lt;/span&gt;

terraform output appservice_dns_name
&lt;span class="c"&gt;# will print the entire DNS name of the web app to the terminal&lt;/span&gt;
&lt;span class="c"&gt;# terraform-app-linux-app-svc.azurewebsites.net&lt;/span&gt;

&lt;span class="c"&gt;#!! HERE WE NEED ONLY the SUBDOMAIN&lt;/span&gt;

az webapp config appsettings &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--resource-group&lt;/span&gt; terraform-group
     &lt;span class="nt"&gt;--name&lt;/span&gt; terraform-app-linux-app-svc
     &lt;span class="nt"&gt;--settings&lt;/span&gt; &lt;span class="nv"&gt;INSTRUMENTATION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22222222-2222-2222-2222

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having the &lt;code&gt;appsettings&lt;/code&gt; updated, move on and execute &lt;code&gt;terraform plan -var-file=local.tfvars&lt;/code&gt;. &lt;em&gt;Terraform&lt;/em&gt; recognized that an untracked change has happened to the &lt;em&gt;Azure App Service&lt;/em&gt;. It suggests to change the &lt;code&gt;appsettings&lt;/code&gt; back to the value specified in the &lt;em&gt;Terraform&lt;/em&gt; script - which is not existing indeed. We want to keep the &lt;em&gt;App Service&lt;/em&gt; as it is, for now, so cancel the script at this point.&lt;/p&gt;

&lt;p&gt;No worries you can reuse the &lt;code&gt;output&lt;/code&gt; of one resource as &lt;code&gt;variable&lt;/code&gt; in another, but that requires to have &lt;em&gt;Modules&lt;/em&gt; in your &lt;em&gt;Terraform&lt;/em&gt; script, so definitive content for another article on &lt;em&gt;Terraform&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Destroying Terraform Environments
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Terraform&lt;/em&gt; is also able to destroy entire environments it has created previously. Just execute &lt;code&gt;terraform destroy&lt;/code&gt; inside of the project's folder and after reviewing the execution plan -and confirming- &lt;em&gt;Terraform&lt;/em&gt; will destroy all resources. Once finished, the &lt;em&gt;Azure Subscription&lt;/em&gt; should be clean again.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Terraform Lifecycle
&lt;/h2&gt;

&lt;p&gt;Now that you've created, modified and destroyed resources in &lt;em&gt;Azure&lt;/em&gt; using &lt;em&gt;Terraform&lt;/em&gt;, you covered all aspects of the single developer &lt;em&gt;Terraform&lt;/em&gt; workflow. Several actions -like creating resources- has been executed quite often and I hope you memorized those basics already. To visualize it again, here the single developer &lt;em&gt;Terraform Lifecycle&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EgKVVj6z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EgKVVj6z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thorsten-hans.com/assets/images/posts/2019/terraform-guide-7.png" alt="The single Developer Terraform Lifecycle" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Download the code from GitHub
&lt;/h2&gt;

&lt;p&gt;The entire &lt;em&gt;Terraform&lt;/em&gt; project is available on &lt;a href="https://github.com/ThorstenHans/terraform-guide-for-azure-enthusiasts"&gt;GitHub&lt;/a&gt;. Browse through it and use it to grasp even more knowledge on &lt;em&gt;Terraform&lt;/em&gt; in an &lt;em&gt;Azure&lt;/em&gt; world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed reading &lt;em&gt;Terraform - The definitive guide for Azure enthusiasts&lt;/em&gt;. If you made it through the post, you gained a ton of knowledge about &lt;em&gt;Terraform&lt;/em&gt;, and you made some necessary steps with the &lt;em&gt;Azure&lt;/em&gt; Provider for Terraform. Having this introduction in place, I'll publish more advanced posts on &lt;em&gt;Terraform&lt;/em&gt; and &lt;em&gt;Infrastructure as Code&lt;/em&gt; in the upcoming weeks and months.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Infrastructure as Code&lt;/em&gt; is something every developer and IT-Pro should care about. Modern applications are way more complex than those five years ago. Developers, teams, and organizations are combining cloud services from different vendors to build the best user experience for their customers. With &lt;em&gt;IaC&lt;/em&gt; and &lt;em&gt;Terraform&lt;/em&gt; you can manage the jungle of services and make that essential knowledge discoverable and collaborate on it. However, keep in mind that moving a real-world project or perhaps an entire organization on the &lt;em&gt;IaC&lt;/em&gt; and &lt;em&gt;Terraform&lt;/em&gt; track isn't an easy task!&lt;/p&gt;

&lt;p&gt;If you need further assistance on that journey, reach out. I would be thrilled to help.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>iac</category>
      <category>infrastructureascode</category>
    </item>
  </channel>
</rss>
