<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://matthewaerose.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://matthewaerose.com/" rel="alternate" type="text/html" /><updated>2026-06-21T11:30:55-05:00</updated><id>https://matthewaerose.com/feed.xml</id><title type="html">Matthew Rose</title><subtitle>I&apos;m Matthew Rose, a dedicated Site Reliability Engineer (SRE) and  DevOps enthusiast. With a solid foundation in both software development  and system administration, I excel at creating efficient and automated  solutions for complex infrastructure challenges</subtitle><author><name>Matthew Rose</name></author><entry><title type="html">Dockerizing a Next.js App</title><link href="https://matthewaerose.com/2025/02/24/next-js-docker.html" rel="alternate" type="text/html" title="Dockerizing a Next.js App" /><published>2025-02-24T23:39:19-06:00</published><updated>2025-02-24T23:39:19-06:00</updated><id>https://matthewaerose.com/2025/02/24/next-js-docker</id><content type="html" xml:base="https://matthewaerose.com/2025/02/24/next-js-docker.html"><![CDATA[<h1 id="dockerizing-a-nextjs-application-challenges-and-solutions">Dockerizing a Next.js Application: Challenges and Solutions</h1>

<p>Deploying a Next.js application is straightforward with platforms like Vercel and Netlify. However, when you want full control over your deployment and opt for Docker, the available guidance is sparse. This blog post covers the challenges of creating a Docker image for a Next.js application, including the multi-stage build process, differences between local and production environments, and necessary tweaks for the entrypoint script.</p>

<h2 id="multi-stage-build-process">Multi-Stage Build Process</h2>

<p>Using a multi-stage build significantly reduces the final image size and ensures that only necessary files are included in production. The Dockerfile uses the following stages:</p>

<h3 id="1-base-stage">1. Base Stage</h3>
<p>This stage installs system dependencies and prepares the base image:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="w"> </span><span class="s">node:18-alpine</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">base</span>
<span class="k">RUN </span>apk add <span class="nt">--no-cache</span> libc6-compat bash
<span class="k">WORKDIR</span><span class="s"> /app</span>
</code></pre></div></div>

<h3 id="2-dependency-installation">2. Dependency Installation</h3>
<p>This stage installs dependencies while ensuring caching optimizations:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="w"> </span><span class="s">base</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">deps</span>
<span class="k">COPY</span><span class="s"> package.json package-lock.json* entrypoint.sh ./</span>
<span class="k">RUN </span>npm ci <span class="nt">--ignore-scripts</span>
</code></pre></div></div>

<h3 id="3-build-stage">3. Build Stage</h3>
<p>Here, the application is built using the dependencies from the previous stage:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="w"> </span><span class="s">base</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">builder</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">COPY</span><span class="s"> --from=deps /app/node_modules ./node_modules</span>
<span class="k">COPY</span><span class="s"> . .</span>
<span class="k">RUN </span>npx prisma generate <span class="nt">--schema</span><span class="o">=</span>./prisma/schema.prisma
<span class="k">RUN </span><span class="nv">NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY</span><span class="o">=</span><span class="nv">$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY</span> <span class="nv">NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY</span><span class="o">=</span><span class="nv">$NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY</span> npx next build <span class="nt">--no-lint</span>
</code></pre></div></div>

<h3 id="4-runner-stage">4. Runner Stage</h3>
<p>This stage prepares the final image, ensuring a minimal and secure environment:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="w"> </span><span class="s">base</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">runner</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">ENV</span><span class="s"> NODE_ENV=production</span>
<span class="k">RUN </span>addgroup <span class="nt">--system</span> <span class="nt">--gid</span> 1001 nodejs
<span class="k">RUN </span>adduser <span class="nt">--system</span> <span class="nt">--uid</span> 1001 nextjs

<span class="k">COPY</span><span class="s"> --from=builder /app/public ./public</span>
<span class="k">RUN </span><span class="nb">mkdir</span> .next <span class="o">&amp;&amp;</span> <span class="nb">chown </span>nextjs:nodejs .next
<span class="k">COPY</span><span class="s"> --from=builder --chown=nextjs:nodejs /app/.next/standalone ./</span>
<span class="k">COPY</span><span class="s"> --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static</span>
<span class="k">COPY</span><span class="s"> --from=builder --chown=nextjs:nodejs /app/entrypoint.sh ./entrypoint.sh</span>
<span class="k">RUN </span><span class="nb">chmod</span> +x ./entrypoint.sh

<span class="k">USER</span><span class="s"> nextjs</span>
<span class="k">EXPOSE</span><span class="s"> 3000</span>
<span class="k">ENV</span><span class="s"> PORT=3000</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["/app/entrypoint.sh"]</span>
<span class="k">CMD</span><span class="s"> ["node", "server.js"]</span>
</code></pre></div></div>

<h2 id="using-the-image-locally-vs-in-production">Using the Image Locally vs. In Production</h2>

<h3 id="local-development">Local Development</h3>
<p>For local development, you can build and run the container with:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build <span class="nt">-t</span> nextjs-app <span class="nb">.</span>
docker run <span class="nt">--env-file</span><span class="o">=</span>.env <span class="nt">-p</span> 3000:3000 nextjs-app
</code></pre></div></div>

<p>This ensures that environment variables are correctly injected at runtime.</p>

<h3 id="production-deployment">Production Deployment</h3>
<p>In production, the image should be pushed to a registry and deployed with an orchestration tool like Kubernetes or a simple Docker Compose setup:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>
<span class="na">services</span><span class="pi">:</span>
  <span class="na">app</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">your-registry/nextjs-app:latest</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">3000:3000"</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">DATABASE_URL</span><span class="pi">:</span> <span class="s2">"</span><span class="s">your-database-url"</span>
      <span class="na">NODE_ENV</span><span class="pi">:</span> <span class="s2">"</span><span class="s">production"</span>
</code></pre></div></div>

<h2 id="the-importance-of-the-entrypoint-script">The Importance of the EntryPoint Script</h2>

<p>Next.js does not automatically replace <code class="language-plaintext highlighter-rouge">NEXT_PUBLIC_</code> environment variables at runtime. To work around this, we modify the <code class="language-plaintext highlighter-rouge">.next</code> build output dynamically using <code class="language-plaintext highlighter-rouge">entrypoint.sh</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nb">test</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY</span><span class="s2">"</span>
<span class="nb">test</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY</span><span class="s2">"</span>
find /app/.next <span class="se">\(</span> <span class="nt">-type</span> d <span class="nt">-name</span> .git <span class="nt">-prune</span> <span class="se">\)</span> <span class="nt">-o</span> <span class="nt">-type</span> f <span class="nt">-print0</span> | xargs <span class="nt">-0</span> <span class="nb">sed</span> <span class="nt">-i</span> <span class="s2">"s#CLERK_PUBLISHABLE_KEY#</span><span class="nv">$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY</span><span class="s2">#g"</span>
find /app/.next <span class="se">\(</span> <span class="nt">-type</span> d <span class="nt">-name</span> .git <span class="nt">-prune</span> <span class="se">\)</span> <span class="nt">-o</span> <span class="nt">-type</span> f <span class="nt">-print0</span> | xargs <span class="nt">-0</span> <span class="nb">sed</span> <span class="nt">-i</span> <span class="s2">"s#STRIPE_PUBLISHABLE_KEY#</span><span class="nv">$NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY</span><span class="s2">#g"</span>
<span class="nb">exec</span> <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
</code></pre></div></div>

<p>This ensures that all <code class="language-plaintext highlighter-rouge">NEXT_PUBLIC_</code> variables are correctly populated when the container starts.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Creating a Docker image for a Next.js application involves several challenges, from efficient multi-stage builds to ensuring proper runtime environment variable handling. By following these steps, you can build and deploy a streamlined, secure, and production-ready Next.js container that functions seamlessly in both local and cloud environments.</p>]]></content><author><name>Matthew Rose</name></author><category term="docker" /><category term="nextjs" /><category term="webdev" /><category term="ai-written" /><summary type="html"><![CDATA[Dockerizing a Next.js Application: Challenges and Solutions]]></summary></entry><entry><title type="html">Supercharge your Command Line</title><link href="https://matthewaerose.com/2023/09/10/uplevel-your-cli.html" rel="alternate" type="text/html" title="Supercharge your Command Line" /><published>2023-09-10T00:39:19-05:00</published><updated>2023-09-10T00:39:19-05:00</updated><id>https://matthewaerose.com/2023/09/10/uplevel-your-cli</id><content type="html" xml:base="https://matthewaerose.com/2023/09/10/uplevel-your-cli.html"><![CDATA[<h1 id="supercharge-your-command-line-with-these-10-tools-and-tips">Supercharge your command line with these 10 tools and tips</h1>
<p>I was screen sharing with a colleague the other day and realized how far <em>I’ve</em> come with my CLI usage.
I’d like to share with ya’ll the tips and tricks shared with me that helped me get to where I am now, plus some new ones I’ve picked up along the way 😉</p>

<p>These tools and techniques will reduce context switching and help you achieve your goal faster.</p>

<blockquote>
  <p>Note: These tools and tips will be Linux-y. For Windows, use WSL.</p>
</blockquote>

<h3 id="tldr">TL;DR</h3>
<p>Context switching is bad. Stay on the CLI and get more work done.</p>

<h1 id="0-tip-dont-use-the-default-cli">0. tip&gt; Don’t use the default CLI!</h1>
<p>Obviously, for ephemeral environments, you get what you get and it is <em>often</em> not worth it to configure.</p>

<p>But for your personal or work laptop?
Definitely explore the non-default options</p>

<p>Here are my favorites for the big three Operating Systems.</p>

<h2 id="mac">Mac</h2>
<p><code class="language-plaintext highlighter-rouge">terminal</code> is fine for a quick and dirty job. But you should make time to explore <a href="https://iterm2.com/"><code class="language-plaintext highlighter-rouge">iTerm2</code></a>.</p>

<p>Highly configurable, pretty, and full featured.</p>

<h2 id="windows">Windows</h2>
<p>Ye olde windows has come a <strong>long</strong> way since the days of CMD prompt.</p>

<p>The new <a href="https://apps.microsoft.com/store/detail/windows-terminal/9N0DX20HK701">Windows terminal</a> is a full featured terminal with some eye popping features out of the box. It has tabs! It features Azure Cloud shell! Powershell! All the Shells!</p>

<p>And then when you add Windows Subsystem for Linux (WSL)? 😍</p>

<p>You <strong>must</strong> try this terminal!</p>

<h2 id="linux">Linux</h2>
<p>Mac and Windows have been my jam for sometime now. But last I checked, <a href="https://konsole.kde.org/">konsole</a> was still the ‘terminal du jour’.</p>

<p>Lots of features and fun to use.</p>

<h1 id="1-tool-asdf">1. tool&gt; <code class="language-plaintext highlighter-rouge">asdf</code></h1>
<p><a href="https://asdf-vm.com/">LINK</a></p>
<blockquote>
  <p>Manage all your runtime versions with one tool!</p>
</blockquote>

<p>Yes, we have <code class="language-plaintext highlighter-rouge">apt</code> for debian or <code class="language-plaintext highlighter-rouge">yum</code> for centos/RHEL, or <code class="language-plaintext highlighter-rouge">brew</code> for MacOS.
These are <em>fine</em> for long lived CLI applications. But what about languages and tools?
Have you ever been in a situation where you had multiple versions of <code class="language-plaintext highlighter-rouge">go</code> that you needed to switch between?
Fine, use <code class="language-plaintext highlighter-rouge">gvm</code>. But then you have another project you need to switch between multiple versions of <code class="language-plaintext highlighter-rouge">node</code>. 
And then add <code class="language-plaintext highlighter-rouge">ruby</code>, then <code class="language-plaintext highlighter-rouge">rust</code>, then <code class="language-plaintext highlighter-rouge">ad nauseam</code>…</p>

<p><code class="language-plaintext highlighter-rouge">asdf</code> is <em>the</em> tool and language version manager to use.</p>

<p>Check out the full list of plugins <a href="https://github.com/asdf-vm/asdf-plugins#plugin-list">here</a></p>

<h1 id="2-tool-fzf">2. tool&gt; <code class="language-plaintext highlighter-rouge">fzf</code></h1>
<p><a href="https://github.com/junegunn/fzf">LINK</a></p>
<blockquote>
  <p>It’s an interactive Unix filter for command-line that can be used with any list; files, command history, processes, hostnames, bookmarks, git commits, etc.</p>
</blockquote>

<p>This is a blazing fast filter. So what, right? WRONG!</p>

<p>When you have a lot of things to search through, like files, command history, commits, processes, etc, you don’t want to get kicked out of the zone by a slow tool.</p>

<p>This will help you stay <em>in it</em> and get your work done faster.</p>

<blockquote>
  <p>PRO TIP: Use fzf with back search (ctrl + r) for an unbeatable back search experience!</p>
</blockquote>

<h1 id="3-tip-dont-be-afraid-to-try-out-different-shells">3. tip&gt; Don’t be afraid to try out different shells!</h1>
<p>There are so many shells out there to try out. Just because your OS came with <code class="language-plaintext highlighter-rouge">sh</code>, doesn’t mean you have to stick with it!</p>

<p>Here are a handful, in no particular order</p>

<h2 id="zsh-and-the-oh-my-zsh-framework">zsh and the oh-my-zsh framework</h2>
<p><a href="https://ohmyz.sh/">LINK</a></p>

<p>Not a shell! Sorry!
But <code class="language-plaintext highlighter-rouge">oh-my-zsh</code> takes the Z shell to the next level.</p>

<p>If you are on a Mac, you gotta try this out. (zsh is the default shell for mac)
And if you aren’t on a mac, take zsh (and oh-my-zsh) out for a spin.</p>

<h2 id="fish">fish</h2>
<p><a href="https://github.com/fish-shell/fish-shell">LINK</a></p>

<p>The friendly interactive shell!</p>

<h2 id="nushell">nushell</h2>
<p><a href="https://github.com/nushell/nushell?ref=itsfoss.com">LINK</a></p>
<blockquote>
  <p>A new type of shell</p>
</blockquote>

<h1 id="4-tool-ripgrep">4. tool&gt; ripgrep</h1>
<p><a href="https://github.com/BurntSushi/ripgrep">LINK</a></p>

<p>Like <code class="language-plaintext highlighter-rouge">grep</code> but faster.</p>

<p>ripgrep is written in Rust and is amazing at recursive search.</p>

<p>Checkout some of the tool comparisons <a href="https://github.com/BurntSushi/ripgrep#quick-examples-comparing-tools">here</a> and see the speed difference.</p>

<h1 id="5-tools-jq-and-yq">5. tools&gt; jq and yq</h1>
<p><a href="">jq LINK</a></p>

<p><a href="">yq LINK</a></p>

<p>JSON and YAML have become popular formats for storing, sending, and communicating large amounts of information. Most CLI tools output in JSON or YAML, or have options to do so. Both AWS and Azure CLI tools (<code class="language-plaintext highlighter-rouge">aws</code> and <code class="language-plaintext highlighter-rouge">az</code>) output to JSON by default and both support YAML.</p>

<p><code class="language-plaintext highlighter-rouge">jq</code> and <code class="language-plaintext highlighter-rouge">yq</code> allow you to slice and dice that output, making it easy to get at the parts that are important to you.</p>

<p><code class="language-plaintext highlighter-rouge">jq</code> has this to say about itself:</p>
<blockquote>
  <p>jq is like sed for JSON data - you can use it to slice and filter and map and transform structured data with the same ease that sed, awk, grep and friends let you play with text.</p>
</blockquote>

<h1 id="6-tool-gh">6. tool&gt; gh</h1>

<p><a href="https://cli.github.com/">LINK</a></p>

<p>Keeping with the theme of “staying in it”, <code class="language-plaintext highlighter-rouge">gh</code> is github on the CLI.</p>

<p>Don’t switch between browser and CLI to create a PR. Create one directly from the CLI!</p>

<p>Review PRs from your CLI!</p>

<p>Check for issues!</p>

<p>Clone repos!</p>

<p>Make a fork!</p>

<p>Whatever you do, don’t waste time by opening your a web browser and going ‘clicky clicky’ with your mouse.</p>

<h1 id="7-tool-broot">7. tool&gt; broot</h1>

<p><a href="https://github.com/Canop/broot">LINK</a></p>

<p>like <code class="language-plaintext highlighter-rouge">tree</code> but waaaay better</p>

<p>This is for visually representing directory structures in the CLI.</p>

<p>No more are the days of <code class="language-plaintext highlighter-rouge">ls ... cd ... ls ... cd ... ls ... etc etc etc</code> !</p>

<p>Simply run <code class="language-plaintext highlighter-rouge">br</code> and see a beautiful, <em>compact</em>, structure that is easy to navigate and use.</p>

<h1 id="8-tip-the-pipe-is-your-friend">8. tip&gt; the pipe is your friend!</h1>

<p><a href="https://www.redhat.com/sysadmin/pipes-command-line-linux">ARTICLE</a></p>

<p>You hear about the mythical “one-liner”. A command that can perform a complex task in a single line. This would be <em>impossible</em> without the pipe!</p>

<p>Introducing… <code class="language-plaintext highlighter-rouge">|</code></p>

<p>If you are wondering where that is on your keyboard, on a standard QWERTY keyboard, it is above the <code class="language-plaintext highlighter-rouge">return</code> key and shares the key with <code class="language-plaintext highlighter-rouge">\</code>. Created by holding down <code class="language-plaintext highlighter-rouge">shift</code>.</p>

<p><code class="language-plaintext highlighter-rouge">shift + \</code> == <code class="language-plaintext highlighter-rouge">|</code></p>

<h2 id="what-does-it-do">What does it do?!?!</h2>
<p>It chains commands together.</p>

<p>Let’s take <code class="language-plaintext highlighter-rouge">jq</code> as an example</p>

<p>This command will output a file with <code class="language-plaintext highlighter-rouge">cat</code> and “pipe” it to <code class="language-plaintext highlighter-rouge">jq</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat </span>some-file.json | jq <span class="s1">'.'</span>
<span class="o">{</span>
    <span class="s2">"hello"</span>:<span class="s2">"world"</span>
<span class="o">}</span>
</code></pre></div></div>

<p>the <em>output</em> of <code class="language-plaintext highlighter-rouge">cat</code> is fed as the <em>input</em> to <code class="language-plaintext highlighter-rouge">jq</code></p>

<p>You can continue chaining in this fashion until you’ve achieved the desired result.
example,</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat </span>some-file.json | jq <span class="s1">'.'</span> | <span class="nb">grep</span> <span class="nt">-n</span> hello
2: <span class="s2">"hello"</span>: <span class="s2">"world"</span>
</code></pre></div></div>

<p>and speaking of <code class="language-plaintext highlighter-rouge">cat</code>, ditch it for…</p>

<h1 id="9-tool-bat">9. tool&gt; bat</h1>

<p><a href="https://github.com/sharkdp/bat">LINK</a></p>

<blockquote>
  <p>a cat clone with wings</p>
</blockquote>

<p><code class="language-plaintext highlighter-rouge">bat</code> is similar to <code class="language-plaintext highlighter-rouge">cat</code> in that it will show you the content of the file you ask it to. But unlike <code class="language-plaintext highlighter-rouge">cat</code>, it provides the following</p>
<ul>
  <li>syntax highlighting</li>
  <li>git integration</li>
  <li>visible whitespace</li>
</ul>

<p>Plus, it has <a href="https://github.com/sharkdp/bat#integration-with-other-tools">integrations</a> with some of the tools we listed here!</p>

<h1 id="10-tool-fd">10. tool&gt; fd</h1>

<p><a href="https://github.com/sharkdp/fd">LINK</a></p>

<blockquote>
  <p>an alternative to <code class="language-plaintext highlighter-rouge">find</code></p>
</blockquote>

<p>While <code class="language-plaintext highlighter-rouge">find</code> is, and will probably remain, the most powerful CLI search tool, it can’t hurt to have something that is a little more intuitive and user friendly. Check it out at the link above!</p>

<h1 id="conclusion">Conclusion</h1>
<p>I hope you learned something cool from this post!</p>

<p>Go out there, try something new, and start learning how to take full advantage of the most powerful tool on your computer… the Command Line Interface!</p>]]></content><author><name>Matthew Rose</name></author><category term="cli" /><category term="computing" /><category term="expert" /><summary type="html"><![CDATA[Supercharge your command line with these 10 tools and tips I was screen sharing with a colleague the other day and realized how far I’ve come with my CLI usage. I’d like to share with ya’ll the tips and tricks shared with me that helped me get to where I am now, plus some new ones I’ve picked up along the way 😉]]></summary></entry><entry><title type="html">Using the Terraform Console</title><link href="https://matthewaerose.com/terraform/console/2023/08/31/talking-to-the-locals.html" rel="alternate" type="text/html" title="Using the Terraform Console" /><published>2023-08-31T00:39:19-05:00</published><updated>2023-08-31T00:39:19-05:00</updated><id>https://matthewaerose.com/terraform/console/2023/08/31/talking-to-the-locals</id><content type="html" xml:base="https://matthewaerose.com/terraform/console/2023/08/31/talking-to-the-locals.html"><![CDATA[<h1 id="what-is-the-terraform-console">What is the Terraform Console?</h1>
<p>The Terraform Console provides a local environment to interrogate state and prove out methods to interact with that state during the plan and apply phases.</p>

<h2 id="why-is-it-useful-what-problems-does-it-solve">Why is it useful? What problems does it solve?</h2>
<p>There are three phases to writing Terraform: development, plan, apply.
The plan and apply phase are Terraform interacting with the state file, whether in a speculative or performative manner.
The development phase, however, is entirely under our (developers) control.
One of the frustrating issues with moving from the development to the plan phase is correcting errors with our own code or adjusting the interactions  between previously declared cloud resources.
The Terraform Console can help alleviate these issues!</p>

<p>Let’s check it out!</p>

<h2 id="how-to-get-started-with-the-terraform-console">How to get started with the Terraform Console</h2>
<blockquote>
  <p><strong><em>NOTE</em></strong></p>

  <p>Use the examples from <a href="https://github.com/matthewaerose/terraform-examples">here</a> to get started fast!</p>
</blockquote>

<h3 id="the-most-basic-way">The <em>most</em> basic way</h3>
<p>In an empty directory (or a directory with no Terraform files), run the command</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>terraform console
</code></pre></div></div>
<p>That is it! You should see the console prompt show up after you type the previous command. The whole thing looks like</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kephalos@dev:~/an-empty-directory$ terraform console
&gt;
</code></pre></div></div>

<p>where <code class="language-plaintext highlighter-rouge">&gt;</code> is the console prompt.</p>

<h3 id="terraform-console-in-real-life">Terraform Console in Real life</h3>
<p>In real life, you’ll already have Terraform files where you want to use the console. Your Terraform should be <strong>well-formed</strong>. The console performs some validation before starting. Some errors will still allow the console to start but others will require you to correct them before proceeding</p>

<p>Once you have well-formed files, you need to <code class="language-plaintext highlighter-rouge">init</code> the providers. This downloads any providers defined so that you can start using them.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>terraform init
</code></pre></div></div>

<p>Once that has finished, you can run</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>terraform console
</code></pre></div></div>

<p>Here is a video of how it should look. This example uses the code found in my <a href="https://github.com/matthewaerose/terraform-examples">example repo</a>.</p>

<script async="" id="asciicast-605819" src="https://asciinema.org/a/605819.js"></script>

<p>Now we can start using the console!</p>

<h2 id="using-the-terraform-console-to-interrogate-the-locals">Using the Terraform Console to interrogate the locals</h2>
<p>Like a beat cop on patrol, the console can interrogate the locals. The <code class="language-plaintext highlighter-rouge">locals{}</code> block, that is! 😅</p>

<p>Anyways, if you want to follow along, fork my <a href="https://github.com/matthewaerose/terraform-examples">example repo</a></p>
<blockquote>
  <p><strong><em>NOTE</em></strong></p>

  <p>You can use the <code class="language-plaintext highlighter-rouge">gh</code> cli to do this easily!</p>
  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gh repo fork matthewaerose/terraform-examples
</code></pre></div>  </div>
  <p> </p>
</blockquote>

<h3 id="talking-to-the-locals">Talking to the locals</h3>
<p>Let’s change directory into the basic example and start our console. Once started, let’s see what the <code class="language-plaintext highlighter-rouge">locals</code> block has to offer.</p>

<p>Given a file that has the following content</p>
<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># locals.tf</span>
<span class="nx">locals</span> <span class="p">{</span>
    <span class="nx">this_is_a_object</span> <span class="p">=</span> <span class="p">{</span>
        <span class="nx">foo</span> <span class="p">=</span> <span class="s2">"bar"</span>
        <span class="nx">a_num</span> <span class="p">=</span> <span class="mf">78.9</span>
        <span class="nx">a_bool</span> <span class="p">=</span> <span class="kc">true</span>
        <span class="nx">the_list</span> <span class="p">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]</span>
        <span class="nx">the_map</span> <span class="p">=</span> <span class="p">{</span>
            <span class="nx">foo</span> <span class="p">=</span> <span class="s2">"bar"</span>
            <span class="nx">baz</span> <span class="p">=</span> <span class="s2">"bat"</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="nx">this_is_a_num</span> <span class="p">=</span> <span class="mf">123.987</span>
    <span class="nx">this_is_a_string</span> <span class="p">=</span> <span class="s2">"hello, world!"</span>
    <span class="nx">this_is_interpolated</span> <span class="p">=</span> <span class="s2">"</span><span class="k">${</span><span class="kd">local</span><span class="p">.</span><span class="nx">this_is_a_string</span><span class="k">}</span><span class="s2"> The number is </span><span class="k">${</span><span class="kd">local</span><span class="p">.</span><span class="nx">this_is_a_num</span><span class="k">}</span><span class="s2">"</span>
<span class="p">}</span>
</code></pre></div></div>

<p>we can ask the console for any of that content.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kephalos@dev:~/git/terraform-examples/0-basic-framework<span class="nv">$ </span>terraform console
<span class="o">&gt;</span> local.this-is-a-num
123.987
<span class="o">&gt;</span> local.this-is-a-string
<span class="s2">"hello, world!"</span>
<span class="o">&gt;</span> local.this-is-interpolated
<span class="s2">"hello, world! The number is 123.987"</span>
<span class="o">&gt;</span> local.this-is-a-object.foo
<span class="s2">"bar"</span>
<span class="o">&gt;</span> local.this-is-a-object[<span class="s2">"foo"</span><span class="o">]</span>
<span class="s2">"bar"</span>
<span class="o">&gt;</span> <span class="nb">exit</span>
</code></pre></div></div>

<p>Notice how objects can use the dot operator or by index for keys</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> local.this-is-a-object.foo
<span class="s2">"bar"</span>
<span class="o">&gt;</span> local.this-is-a-object[<span class="s2">"foo"</span><span class="o">]</span>
<span class="s2">"bar"</span>
</code></pre></div></div>

<p>Here is a video of what that looks like. 
<script async="" id="asciicast-605826" src="https://asciinema.org/a/605826.js"></script></p>

<h2 id="what-else-can-i-do-with-the-terraform-console">What else can I do with the Terraform Console?</h2>
<p>Besides asking the locals who they are, you can do the following:</p>
<ul>
  <li>look at static or previously retrieved data blocks or outputs
    <ul>
      <li>“previously retrieved” means having gone through the plan/apply process</li>
    </ul>
  </li>
  <li>use any of the built-in language functions
    <ul>
      <li>like <code class="language-plaintext highlighter-rouge">merge</code>, <code class="language-plaintext highlighter-rouge">concat</code>, or any function found <a href="https://developer.hashicorp.com/terraform/language/functions">here</a></li>
    </ul>
  </li>
  <li>create and iterate over lists, maps, and objects</li>
</ul>

<p><strong>Stay tuned for the next post on outputs and data blocks!</strong></p>]]></content><author><name>Matthew Rose</name></author><category term="terraform" /><category term="console" /><category term="terraform" /><category term="console" /><category term="cli" /><summary type="html"><![CDATA[What is the Terraform Console? The Terraform Console provides a local environment to interrogate state and prove out methods to interact with that state during the plan and apply phases.]]></summary></entry></feed>