<?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: Jonathon Belotti</title>
    <description>The latest articles on DEV Community by Jonathon Belotti (@thundergolfer).</description>
    <link>https://dev.to/thundergolfer</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F281503%2Ff29d1c2d-54ee-49e9-add8-1d669a25f206.jpeg</url>
      <title>DEV Community: Jonathon Belotti</title>
      <link>https://dev.to/thundergolfer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thundergolfer"/>
    <language>en</language>
    <item>
      <title>Stable Diffusion Pokémon Cards</title>
      <dc:creator>Jonathon Belotti</dc:creator>
      <pubDate>Sun, 08 Jan 2023 20:56:06 +0000</pubDate>
      <link>https://dev.to/thundergolfer/stable-diffusion-pokemon-cards-45dn</link>
      <guid>https://dev.to/thundergolfer/stable-diffusion-pokemon-cards-45dn</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/mQsMuM8d4Qc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is a fun demo of a full-stack ML app. It takes your text prompt as input and uses three models to produce four sample Pokémon card images — the models are StableDiffusion, an RNN for Pokémon name generation, and a basic OpenCV background removal model. &lt;/p&gt;

&lt;p&gt;This app is an example of what can be done these days by what I'm calling &lt;em&gt;model stacking&lt;/em&gt;, for lack of a better name. It's become so easy to stick together ML models, often without training many or all of them yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;demo link:&lt;/strong&gt; &lt;a href="https://modal-labs-example-text-to-pokemon-fastapi-app.modal.run/"&gt;modal-labs-example-text-to-pokemon-fastapi-app.modal.run/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cloud platform:&lt;/strong&gt; &lt;a href="https://modal.com/"&gt;modal.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code is here: &lt;a href="https://github.com/modal-labs/modal-examples/tree/main/06_gpu_and_ml/text-to-pokemon"&gt;github.com/modal-labs/modal-examples/tree/main/06_gpu_and_ml/text-to-pokemon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Be aware that in the video the prompts used are previously seen and cached. Unseen prompt generations take 30-120 seconds)&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>svelte</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Transcribe any podcast in 1 minute with serverless Python</title>
      <dc:creator>Jonathon Belotti</dc:creator>
      <pubDate>Mon, 28 Nov 2022 22:06:03 +0000</pubDate>
      <link>https://dev.to/thundergolfer/transcribe-any-podcast-in-1-minute-with-serverless-python-13pd</link>
      <guid>https://dev.to/thundergolfer/transcribe-any-podcast-in-1-minute-with-serverless-python-13pd</guid>
      <description>&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1597344306008055815-987" src="https://platform.twitter.com/embed/Tweet.html?id=1597344306008055815"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1597344306008055815-987');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1597344306008055815&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;In late September &lt;a href="https://openai.com/research/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; publicly released a shockingly good speech-to-text AI model. Pretty soon after that I began working on using it to make a podcast transcriber tool. I'm a big podcast listener (probably 10+ hrs a week) and I'm keen to hear any feedback you have on the tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://modal-labs-whisper-pod-transcriber-fastapi-app.modal.run/#/" rel="noopener noreferrer"&gt;modal-labs-whisper-pod-transcriber-fastapi-app.modal.run&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's some videos showing how it works.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/12058921/199637855-d98bcabe-bff4-433b-a58f-1e309d69e14d.mp4" rel="noopener noreferrer"&gt;&lt;strong&gt;Video showing the transcription of &lt;em&gt;Serial season 2 episode 1&lt;/em&gt; in just 62 seconds&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/12058921/199637370-1cb1e070-8f60-4cc6-8c51-dc42bebcf29d.mp4" rel="noopener noreferrer"&gt;&lt;strong&gt;Video showing how to go from a transcript segment back to the original audio&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're interested in the technical details, you can &lt;a href="https://twitter.com/bernhardsson/status/1597344306008055815?s=20&amp;amp;t=P0RTA8EWP3ztnQ8vYA3e9Q" rel="noopener noreferrer"&gt;read more in a &lt;strong&gt;blog post&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is built only with &lt;code&gt;.py&lt;/code&gt; code. No YAML, C++, Dockerfile, nothing like that, just Python.&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>Using Terraform to make my many side-projects 'pick up and play'</title>
      <dc:creator>Jonathon Belotti</dc:creator>
      <pubDate>Mon, 14 Jun 2021 11:45:10 +0000</pubDate>
      <link>https://dev.to/thundergolfer/using-terraform-to-make-my-many-side-projects-pick-up-and-play-aca</link>
      <guid>https://dev.to/thundergolfer/using-terraform-to-make-my-many-side-projects-pick-up-and-play-aca</guid>
      <description>&lt;p&gt;&lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; is an infrastructure-as-code technology providing a way to describe cloud infrastructure like AWS S3 buckets and SQS queues as code objects and manage the full lifecycle of that AWS infrastructure programmatically. It has very recently reached the 1.0 version milestone. Like &lt;code&gt;git&lt;/code&gt;, learning Terraform is not easy and using Terraform is not fun, but it's so obviously better than alternatives that it has become a 'no-brainer' choice in my software engineering toolkit. I had to learn Terraform for work and use it extensively there, but I also now also put in the effort to use it from day one in any side project that runs in the cloud. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/thundergolfer/example-bazel-monorepo/tree/master/infrastructure"&gt;thundergolfer/example-bazel-monorepo&lt;/a&gt; is a public repository where I have an &lt;code&gt;infrastructure/&lt;/code&gt; top-level folder with all the project's Terraform, and there's at least a few private repositories where I'm doing the same. Although some might have the reaction that the effort involved in writing and maintaining Terraform for multiple side-projects is overkill, Terraform's value proposition is really well suited to my side-project needs.&lt;/p&gt;

&lt;p&gt;The two most important benefits of Terraform for side-projects are the declarative specification of resources and their interrelations, and the fast spin-up-then-teardown workflow. I develop side-projects in spurts and often get busy with the rest of my life, leaving a side project alone for months. If I set up a project's AWS resources in the web UI, known as the &lt;em&gt;'click ops'&lt;/em&gt; approach, and then ditched the project for a while I would definitely be lost upon return. I'd have to click around AWS's pretty unpleasant web UI searching for security groups and S3 bucket policies, and importantly I'd have to figure out which resources were missing because they were too expensive to leave around doing nothing. Nearly all modern developers have the experience of accidentally leaving on an expensive EC2 instance to sit idle. Without Terraform, I find this situation is much more likely to happen. &lt;em&gt;With&lt;/em&gt; Terraform, I can quickly set up a script that can delete and recreate the project's entire cloud infrastructure setup in around 10 minutes, with essentially no effort. Such a script is used like this:&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;# Destroy all expensive AWS resources.&lt;/span&gt;
&lt;span class="c"&gt;# Takes ~10 minutes because some resource, such as EKS clusters, are slow to delete.&lt;/span&gt;
./scripts/cloud.sh down
&lt;span class="c"&gt;# Recreate the project's infrastructure&lt;/span&gt;
&lt;span class="c"&gt;# Takes ~10 minutes because some resources, such as EKS clusters or ALBs, are slow to create.&lt;/span&gt;
./scripts/cloud.sh up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a weekend I may use this kind of script across multiple side-projects, spinning up and spinning down expensive resources such as EKS clusters, Application Load Balancers, Apache Spark clusters, and GPU-enabled VMs. &lt;/p&gt;

&lt;p&gt;This saves money, but even better saves a hell of a lot of time and toil that would be involved if I didn't have Terraform setup and instead had to 'click ops' my way through infrastructure maintenance.&lt;/p&gt;

&lt;p&gt;To give a proper test of my claim that the up-front investment in doing Terraform pays off, I jumped back into a private repo that I hadn't touched &lt;em&gt;for over six months,&lt;/em&gt; and went about spinning up its Kubernetes cluster and submitting one of the repo's applications to the cluster for execution. It doesn't have a script with &lt;code&gt;cloud.sh down&lt;/code&gt; or &lt;code&gt;cloud.sh up&lt;/code&gt; available, but using the README instructions I'd written down way back then and plain &lt;code&gt;terragrunt&lt;/code&gt;, I was able to spin up the cluster and run an application in just over 20 minutes, and 13 minutes of that was just waiting for AWS to create the resources. 🏎&lt;/p&gt;

&lt;p&gt;Here's a run down of that ~22 minutes, from 4:21PM to 4:43PM. For some context, this side-project is concerned with getting hands-on with the various batch-pipeline or workflow systems used in data engineering (think Spotify's Luigi, Lyft's Flyte, Apache Airflow, Kedro, etc). &lt;/p&gt;

&lt;h3&gt;
  
  
  Begin ⏱  ~ &lt;strong&gt;4:21PM&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 4:21PM - Begin&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;infrastructure/aws/k8s &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; terragrunt apply
...
aws_eks_cluster.demo: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;10s elapsed]
aws_eks_cluster.demo: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;20s elapsed]
...
&lt;span class="c"&gt;# Go get a ☕️&lt;/span&gt;
aws_eks_cluster.demo: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;4m10s elapsed]
...
&lt;span class="c"&gt;# Finished! ✅&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-east-2"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLUSTER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"foobar-demo-cluster"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; aws eks &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; update-kubeconfig &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLUSTER_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# 4:34 - Test cluster access&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get nodes
NAME                                      STATUS   ROLES    AGE     VERSION
ip-10-0-0-65.us-east-2.compute.internal   Ready    &amp;lt;none&amp;gt;   4m34s   v1.18.9-eks-d1db3c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  13 minutes later... ⏱  ~ &lt;strong&gt;4:35PM&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I had to wait for AWS a bit, but now the cluster is active. Let's setup some basic cluster functionality:&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;# 4:35PM&lt;/span&gt;
&lt;span class="c"&gt;# Apply some YAMl&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; dashboard/
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; metrics-server/
&lt;span class="c"&gt;# Now let's spin up Luigi, a batch pipeline management system&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--show-toplevel&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;workflows_and_pipelines/luigi
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; k8s-objects/
&lt;span class="c"&gt;# Done. Now let's create a cronjob that runs a basic Luigi pipeline every 15 mins&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;k8s-objects/workflow_cronjobs/
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; hello_luigi.yaml
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create job &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cronjob/hello-luigi-wf adhoc-run
&lt;span class="c"&gt;# Check that it's all been created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  YAML Applied ⏱ ~ &lt;strong&gt;4:41PM&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Sweet, I've now run a Luigi pipeline in the fresh Kubernetes cluster. (I spent some time reading the notes in my READMEs to check I'd done everything correctly, so there's a gap in time between commands)&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;# 4:41PM&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods
NAME                      READY   STATUS      RESTARTS   AGE
adhoc-run-kc2tp           0/1     Completed   0          7s
luigid-5689dfd84d-tl7dt   1/1     Running     0          2m4s
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl logs adhoc-run-kc2tp
DEBUG: Checking &lt;span class="k"&gt;if &lt;/span&gt;demo.HelloWorldTask&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;249&lt;span class="o"&gt;)&lt;/span&gt; is &lt;span class="nb"&gt;complete&lt;/span&gt;
/tmp/Bazel.runfiles_w_tfrz7e/runfiles/pypi/pypi__luigi/luigi/worker.py:409: UserWarning: Task demo.HelloWorldTask&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;249&lt;span class="o"&gt;)&lt;/span&gt; without outputs has no custom &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; method
  is_complete &lt;span class="o"&gt;=&lt;/span&gt; task.complete&lt;span class="o"&gt;()&lt;/span&gt;
INFO: Informed scheduler that task   demo.HelloWorldTask_249_fab3ce8be8   has status   PENDING
INFO: Done scheduling tasks
INFO: Running Worker with 1 processes
DEBUG: Asking scheduler &lt;span class="k"&gt;for &lt;/span&gt;work...
DEBUG: Pending tasks: 1
INFO: &lt;span class="o"&gt;[&lt;/span&gt;pid 7] Worker Worker&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;salt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;264513973, &lt;span class="nv"&gt;workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1, &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;adhoc-run-kc2tp, &lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root, &lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7&lt;span class="o"&gt;)&lt;/span&gt; running   demo.HelloWorldTask&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;249&lt;span class="o"&gt;)&lt;/span&gt;
INFO: &lt;span class="o"&gt;[&lt;/span&gt;pid 7] Worker Worker&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;salt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;264513973, &lt;span class="nv"&gt;workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1, &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;adhoc-run-kc2tp, &lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root, &lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;done      &lt;/span&gt;demo.HelloWorldTask&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;249&lt;span class="o"&gt;)&lt;/span&gt;
DEBUG: 1 running tasks, waiting &lt;span class="k"&gt;for &lt;/span&gt;next task to finish
INFO: Informed scheduler that task   demo.HelloWorldTask_249_fab3ce8be8   has status   DONE
DEBUG: Asking scheduler &lt;span class="k"&gt;for &lt;/span&gt;work...
DEBUG: Done
DEBUG: There are no more tasks to run at this &lt;span class="nb"&gt;time
&lt;/span&gt;INFO: Worker Worker&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;salt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;264513973, &lt;span class="nv"&gt;workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1, &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;adhoc-run-kc2tp, &lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root, &lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7&lt;span class="o"&gt;)&lt;/span&gt; was stopped. Shutting down Keep-Alive thread
INFO:
&lt;span class="o"&gt;=====&lt;/span&gt; Luigi Execution Summary &lt;span class="o"&gt;=====&lt;/span&gt;

Scheduled 1 tasks of which:
&lt;span class="k"&gt;*&lt;/span&gt; 1 ran successfully:
    - 1 demo.HelloWorldTask&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;249&lt;span class="o"&gt;)&lt;/span&gt;

This progress looks :&lt;span class="o"&gt;)&lt;/span&gt; because there were no failed tasks or missing dependencies

&lt;span class="o"&gt;=====&lt;/span&gt; Luigi Execution Summary &lt;span class="o"&gt;=====&lt;/span&gt;

HelloWorldTask says: Hello world!
foo equals 249
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Done ⏱ ~ &lt;strong&gt;4:43PM&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;So to sum that up, I went from having nothing for my side-project set up in AWS to having a Kubernetes cluster with the basic metrics and dashboard, a proper IAM-linked &lt;code&gt;ServiceAccount&lt;/code&gt; support for a smooth IAM experience in K8s, and &lt;a href="https://github.com/spotify/luigi"&gt;Luigi&lt;/a&gt; deployed so that I could then run a Luigi workflow using an ad-hoc run of a &lt;code&gt;CronJob&lt;/code&gt;. That's quite remarkable to me. All that took &lt;em&gt;hours&lt;/em&gt; to figure out and define when I first did it, over six months ago. &lt;/p&gt;

&lt;p&gt;If I hadn't used Terraform, I'd have to have jumped into the shitty, confusing AWS user interface and clicked around to recreate the cluster, it's networking configuration, and it's IAM configuration. Even if I got it working there's a high chance it would have been different from my previous configuration and not worked with other code and configuration in the project. I'd estimate that in just this one spin-up-spin-down cycle I've saved almost two hours of my time.&lt;/p&gt;

&lt;p&gt;Now that I've run that to validate my argument for Terraform, let's tie off loose ends.&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="nv"&gt;$ &lt;/span&gt;terragrunt destroy
...
...
Plan: 0 to add, 0 to change, 19 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only &lt;span class="s1"&gt;'yes'&lt;/span&gt; will be accepted to confirm.

  Enter a value: &lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;span class="c"&gt;# --- WAIT FOR AWS TO TEAR DOWN ---&lt;/span&gt;
&lt;span class="c"&gt;# Done! 💯&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Complement your Terraform with good notes
&lt;/h3&gt;

&lt;p&gt;Terraform cannot capture &lt;em&gt;everything&lt;/em&gt; about your environment setup, so I aim to write down any non-trivial detail, either in comments around the Terraform or in READMEs. &lt;/p&gt;

&lt;p&gt;Here's some examples:&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;# These values are a real pain to get at the moment. Hopefully Terraform makes it easier in future.&lt;/span&gt;
&lt;span class="c"&gt;# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html&lt;/span&gt;
thumbprint_list &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="c"&gt;# ⚠️ This value *might* need to be updated whenever cluster is destroyed and recreated.&lt;/span&gt;
  &lt;span class="c"&gt;# ⚠️ Any IAM Roles using the OIDC outputs do need to be updated.&lt;/span&gt;
  &lt;span class="c"&gt;# ⚠️ See README.md in this folder.&lt;/span&gt;
  &lt;span class="s2"&gt;"9988776655aabbcc0b539foobar6bb7f3b02e22da2b1122334455aabbcc"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and in that &lt;a href="http://readme.md"&gt;README.md&lt;/a&gt; I have:&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="k"&gt;**&lt;/span&gt;NOTE:&lt;span class="k"&gt;**&lt;/span&gt; Setting this up is a pain, but worth it. IAM-linked ServiceAccounts are much nicer to use than having the cluster role &lt;span class="k"&gt;do &lt;/span&gt;_everything_
              or have the cluster role-assume into other roles.

&lt;span class="k"&gt;**&lt;/span&gt;Setup:&lt;span class="k"&gt;**&lt;/span&gt;           

1. &lt;span class="sb"&gt;`&lt;/span&gt;aws eks describe-cluster &lt;span class="nt"&gt;--name&lt;/span&gt; workflows-and-pipelines-demo-cluster &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"cluster.identity.oidc.issuer"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="sb"&gt;`&lt;/span&gt;

2. Follow the &lt;span class="s2"&gt;"AWS Management Console"&lt;/span&gt; instructions here: https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After six months, it's very easy to get lost in your own work and want to tear your hair out. The idea is that these comments are there for me right when I hit a 'wait, what next?' moment or 'huh?' moment and need guidance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convinced?
&lt;/h2&gt;

&lt;p&gt;After exercising this system in one of my side-projects, I'm certainly convinced. Kubernetes is a 500lb gorilla of a system, IAM is a regular headache, and Spotify's Luigi is complex enough itself, but in about 20 minutes I was able to get it all set up to the point where I could run some basic code I wrote over half a year and a half-dozen side projects ago. I really think this is a no-brainer in terms of side-project maintenance and cost management.&lt;/p&gt;




&lt;p&gt;Originally posted at: &lt;a href="https://thundergolfer.com/devops/side-projects/software-engineering/2021/02/24/using-terraform-in-side-projects/"&gt;https://thundergolfer.com/devops/side-projects/software-engineering/2021/02/24/using-terraform-in-side-projects/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>sideprojects</category>
      <category>devops</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
