<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Michael Ethridge on Medium]]></title>
        <description><![CDATA[Stories by Michael Ethridge on Medium]]></description>
        <link>https://medium.com/@michael_ethridge?source=rss-65eed4f9f723------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*gDYeskfQKnBFslzDo2fDZA.jpeg</url>
            <title>Stories by Michael Ethridge on Medium</title>
            <link>https://medium.com/@michael_ethridge?source=rss-65eed4f9f723------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 18 May 2026 06:36:47 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@michael_ethridge/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Workstation Setup for Terraform]]></title>
            <link>https://medium.com/@michael_ethridge/workstation-setup-for-terraform-fffcd15180ae?source=rss-65eed4f9f723------2</link>
            <guid isPermaLink="false">https://medium.com/p/fffcd15180ae</guid>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[pre-commit]]></category>
            <category><![CDATA[1password]]></category>
            <category><![CDATA[git]]></category>
            <dc:creator><![CDATA[Michael Ethridge]]></dc:creator>
            <pubDate>Fri, 31 Jan 2025 23:00:02 GMT</pubDate>
            <atom:updated>2025-02-01T01:29:00.883Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*GiRtxEW0qVIL2muS" /><figcaption>Photo by <a href="https://unsplash.com/@agforl24?utm_source=medium&amp;utm_medium=referral">Tai Bui</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Also available on my blog <a href="https://www.404-code-not-found.com/posts/workstation-setup-for-terraform/">here</a>.</p><h3>Introduction</h3><p>In my last post, I covered how I set up <a href="https://medium.com/@michael_ethridge/vs-code-setup-for-terraform-17c3abee704c">VSCode for Terraform</a>. The second most popular question is how I set up my workstation for Terraform development.</p><p>As with most things in the tech world, there are many ways to do things. The following is the way that works for me. I am always looking for ways to improve my workflow, so if you have suggestions, please let me know.</p><p>Additionally, I am a Mac user, so some of the tools I use are harder to use on Windows. However, the tools can be used in WSL or DevContainers if you use either of those tools.</p><p>On a Mac, I use <a href="https://brew.sh/">Homebrew</a> to install my tools. Also, Since we have to store all our code in a version control system, I installed the latest version of git with Homebrew.</p><pre># Install Homebrew<br>/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;<br><br># Install Git<br>brew install git</pre><h3>Installing Terraform</h3><p>I have to use multiple versions of Terraform for different projects, so I use <a href="https://tfswitch.warrensbox.com/">TFSwitch</a> to switch the version of Terraform installed based on the directory I am in and the version constraint in my Terraform configuration.</p><pre>brew install warrensbox/tap/tfswitch</pre><p>I have configured tfswitch to link the correct version of Terraform to~/bin/terraform as the ~/bin directory is in my $PATH. This is done by creating a~/.tfswitch.toml file with the following contents:</p><pre>bin=&quot;~/bin/terraform&quot;</pre><p>I use the following zsh function to load tfswitch in a directory with Terraform configuration files.</p><pre>load-tfswitch() {<br>  local tfswitchrc_path=&quot;${HOME}/.tfswitch.toml&quot;<br><br>  # if [[ -f &quot;$tfswitchrc_path&quot; ]] &amp;&amp; [[ -f &quot;terraform.tf&quot; ]]; then<br>  if [[ -f &quot;$tfswitchrc_path&quot; ]]; then<br>    if [[ $(ls -l *.tf | wc -l ) -gt 0 ]] 2&gt; /dev/null; then<br>      tfswitch<br>    fi<br>  fi<br>}<br><br>add-zsh-hook chpwd load-tfswitch<br>load-tfswitch</pre><p>Now, if you don&#39;t need to support multiple versions of Terraform, you can install it with Homebrew from HashiCorp&#39;s tap.</p><pre>  brew install hashicorp/tap/terraform</pre><p>Otherwise, you can download it directly from the <a href="https://developer.hashicorp.com/terraform/install">Terraform Install page</a>.</p><h3>Git Tools</h3><p>Storing your Terraform configuration in a version control system is a must. I don&#39;t do development daily, so I forget to format my code and check for secrets or other vulnerabilities. One of the worst is that I forgot to update the README.md with any variable or output changes. The most annoying thing is that I forgot to create a new branch for my changes, as most of the git repos I work in have the default branch protected, so I can&#39;t push directly to it.</p><p>To help me with all of these problems, I use <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git Hooks</a>, specifically the pre-commit hook to check for all of these things before I commit my changes. If you are unfamiliar with Git Hooks, they are scripts that run automatically when specific actions occur in a Git repository. You can use these scripts to enforce coding standards, check for vulnerabilities, or even run tests before you commit your changes.</p><p>Traditionally, you would have to write these scripts yourself, but there is a tool called <a href="https://pre-commit.com/">pre-commit</a> that makes it easy to install and manage these hooks.</p><pre>brew install pre-commit</pre><p>Once you have pre-commit installed, you can create a .pre-commit-config.yaml file in the root of your repository with the following contents:</p><pre># yaml-language-server: $schema=https://json.schemastore.org/pre-commit-config.json<br><br>repos:<br>  - repo: local<br>    hooks:<br>      - id: trufflehog<br>        name: TruffleHog<br>        description: Detect secrets in your data.<br>        entry: trufflehog git file://. --since-commit HEAD --fail<br>        language: golang<br>        pass_filenames: false<br>        stages: [&quot;pre-commit&quot;, &quot;pre-push&quot;]<br>  - repo: https://github.com/pre-commit/pre-commit-hooks<br>    rev: v5.0.0<br>    hooks:<br>      - id: check-merge-conflict<br>      - id: end-of-file-fixer<br>      - id: no-commit-to-branch<br>  - repo: https://github.com/antonbabenko/pre-commit-terraform<br>    rev: v1.97.0<br>    hooks:<br>      - id: terraform_fmt<br>      - id: terraform_validate<br>        args:<br>          - --hook-config=--retry-once-with-cleanup=true<br>      - id: terraform_docs<br>        args:<br>          - --args=--lockfile=false<br>      - id: terraform_tflint<br>        args:<br>          - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl<br>      - id: terraform_checkov<br>      - id: infracost_breakdown<br>        args:<br>          - --args=--path=.<br>          - --args=--terraform-var-file=&quot;terraform.tfvars&quot;<br>        # verbose: true</pre><p>Every time you commit your changes, the pre-commit tool runs and checks each hook you have defined. If any hook fails, the commit will abort, and you must fix the issues before committing your changes.</p><p>We haven&#39;t covered all of the hooks in the .pre-commit-config.yaml file, but I&#39;ll list the ones I use here:</p><ul><li>trufflehog: Scans your git repo for committed secrets 😱.</li><li>check-merge-conflict: Checks for files that contain merge conflict strings.</li><li>end-of-file-fixer: Ensures that files end with a newline.</li><li>no-commit-to-branch: Prevents commits directly to a branch (default branch in our case).</li><li>terraform_fmt: Formats your Terraform code.</li><li>terraform_validate: Validates your Terraform code.</li><li>terraform_docs: Dynamically updates your README.md with information on your module&#39;s inputs, outputs, and requirements.</li><li>terraform_tflint: A Terraform linter that checks for best practices and errors in your Terraform code.</li><li>terraform_checkov: A tool that checks your Terraform code for security vulnerabilities.</li><li>infracost_breakdown: Gives you a cost estimate for the cloud resources your module would deploy.</li></ul><h3>Tools needed for the Pre-commit hooks that I use</h3><p>You will need to install a few of these tools to use all of the pre-commit hooks that I have listed above.</p><p><a href="https://github.com/trufflesecurity/trufflehog">Trufflehog</a> scans your git repo for secrets 😱. Doing this as a pre-commit hook lets you catch secrets before committing them. This way, they don&#39;t end up in your git history, keeping your security team happy.</p><pre>brew install trufflesecurity/trufflehog/trufflehog</pre><p><a href="https://github.com/terraform-docs/terraform-docs">Terraform-docs</a> dynamically updates your README.md with information on your module&#39;s inputs, outputs, and requirements.</p><pre>brew install terraform-docs</pre><p>In your README.md file, you can add the following comments to have terraform-docs update the file.</p><pre>&lt;!-- BEGIN_TF_DOCS --&gt;<br>&lt;!-- END_TF_DOCS --&gt;</pre><p><a href="https://github.com/infracost/infracost">Infracost</a> provides a cost estimate for the cloud resources on which your configuration will be deployed.</p><pre>brew install infracost</pre><p><a href="https://github.com/stedolan/jq">Jq</a> is a lightweight and flexible command-line JSON processor. required for terraform_validate with--retry-once-with-cleanup flag, and for infracost_breakdown hook.</p><pre>brew install jq</pre><p><a href="https://github.com/terraform-linters/tflint">TFLint</a> is a Terraform linter that checks for best practices and errors in your Terraform code.</p><pre>brew install tflint</pre><p><a href="https://github.com/bridgecrewio/checkov">Checkov</a> is a static code analysis tool for infrastructure as code (IaC).</p><pre>brew install checkov</pre><h3>Convenience Tools</h3><p>With Terraform installed via tfswitch and the pre-commit hooks setup, I have a solid foundation for my Terraform development workflow. But I use a few more tools to make my life easier.</p><p>First, I use <a href="https://1password.com/">1Password</a> to store my secrets, API keys, and passwords. I also use the <a href="https://1password.com/downloads/command-line/">1Password CLI</a> to access my secrets via environment variables and shell scripts.</p><pre>brew install 1password 1password-cli</pre><p>With 1Password and the 1Password CLI installed, I can access my secrets via the op command.</p><pre>export TFE_TOKEN=$(op read --cache &quot;op://Vault/Item/Key&quot;)</pre><p>This allows me to easily set environment variables for my secrets in my shell. However, I need to remember to unset these variables when I&#39;m done with them. To help with this, I use <a href="https://direnv.net/">Direnv</a> to set and unset variables based on my directory.</p><pre>brew install direnv</pre><p>I use the following .envrc file in the root of my Terraform configuration to set my secrets.</p><pre># Exports<br>export TFE_TOKEN=$(op read --cache &quot;op://Vault/Item/Key&quot;)<br><br># Terraform Variable exports<br>export TF_VAR_okta_token=$(op read --cache &quot;op://Vault/Item/Section/Key&quot;)<br>export TF_VAR_okta_client_id=$(op read --cache &quot;op://Vault/Item/Section/Key&quot;)<br>export TF_VAR_okta_client_secret=$(op read --cache &quot;op://Vault/Item/Section/Key&quot;)</pre><p>The above example exports the TFE_TOKEN and a few Terraform variables. When I change into or out of the directory with the.envrc file, direnv will set and unset these variables for me.</p><p>With so many tools in use, remembering all the commands to run can be a pain. Yes, I could create aliases for them, but I like to keep my shell clean. So, I use <a href="https://taskfile.dev/">Task</a> to create a Taskfile.yml with all the commands I use.</p><pre># yaml-language-server: $schema=https://taskfile.dev/schema.json<br># https://taskfile.dev<br><br>version: &quot;3&quot;<br><br>dotenv: [&quot;.envrc&quot;]<br><br>vars:<br>  CURRENT_DATE:<br>    sh: date +&quot;%Y-%m-%dT%H:%M:%S%Z&quot;<br><br>tasks:<br>  default:<br>    cmds:<br>      - task: pre<br><br>  hog:<br>    cmds:<br>      - trufflehog git file://. --since-commit HEAD --only-verified --fail<br><br>  pre:<br>    cmds:<br>      - pre-commit autoupdate<br>      - pre-commit run -a<br><br>  push:<br>    cmds:<br>      - git add .<br>      - git commit -m &quot;{{.CURRENT_DATE}}&quot;<br>      - git push<br>    silent: true<br><br>  tag:<br>    cmds:<br>      - git push<br>      - git tag -s {{.CLI_ARGS}} -m &quot;{{.CLI_ARGS}}&quot;<br>      - git push --tags</pre><p>Now I can run task hog to scan my git repo for secrets, task pre to run all of my pre-commit hooks, task push to commit my changes and push them to the remote, and task tag -- v1.0.0 to tag my release.</p><h3>Conclusion</h3><p>This is how I set up my workstation for Terraform development. I hope you found it helpful. If you have any suggestions or tools that you use, please let me know. I am always looking for ways to improve my workflow.</p><p><em>Originally published at </em><a href="https://www.404-code-not-found.com/posts/workstation-setup-for-terraform/"><em>https://www.404-code-not-found.com</em></a><em> on January 31, 2025.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fffcd15180ae" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[VS Code Setup for Terraform]]></title>
            <link>https://medium.com/@michael_ethridge/vs-code-setup-for-terraform-17c3abee704c?source=rss-65eed4f9f723------2</link>
            <guid isPermaLink="false">https://medium.com/p/17c3abee704c</guid>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[vscode]]></category>
            <category><![CDATA[vscode-extension]]></category>
            <category><![CDATA[iac]]></category>
            <category><![CDATA[hashicorp]]></category>
            <dc:creator><![CDATA[Michael Ethridge]]></dc:creator>
            <pubDate>Thu, 10 Oct 2024 16:30:53 GMT</pubDate>
            <atom:updated>2025-01-31T18:35:34.441Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RnoY-DAjUGUIjR-8Hfu_mA.png" /></figure><p>Available on my blog <a href="https://www.404-code-not-found.com/posts/vscode-setup-for-terraform/">here</a></p><h3>Introduction</h3><p>One of the first questions I get when teaching Terraform is, “What editor should I use?” My answer is always, “Use what you are comfortable with. If you don’t have a preference, I recommend Visual Studio Code.” Over the years, I have used everything from vi to Notepad++ to Sublime Text to Atom to Visual Studio Code. I have found that Visual Studio Code is easy to pick up for new users and easy to customize to your liking.</p><p>I am not a developer by trade; I’m more of a hack-it-together kind of person. I copy a lot of code from the internet or use GenAI and modify it to fit my needs. So, I don’t work in an IDE every day. I need something easy to use and easy to customize.</p><h3>TL;DR</h3><p>For those of you like me who want the quick and dirty, here are the steps to set up your VS Code for Terraform development:</p><ol><li>Install Visual Studio Code — <a href="https://code.visualstudio.com/">Download</a></li><li>Create a profile for Terraform — See next section</li><li>Clone my Terraform template repository — <a href="https://github.com/404-code-not-found-com/terraform-module-template">Terraform Template</a></li><li>Open the repository in VS Code — This will prompt you to install the recommended extensions and import the settings.</li><li>Go forth and Terraform!</li></ol><h3>Visual Studio Code Profiles</h3><p>One of the reasons I like Visual Studio Code is that it supports profiles. I can create a profile for each type of work I do. I have profiles for Terraform, Python, Ansible, and Markdown. Each profile has its settings, extensions, and keybindings. By using profiles, I can tailor the editor to the work I am doing.</p><h3>Creating a Profile</h3><p>To create a profile, click on the gear icon in the lower-left corner of the VS Code window. This will open the Settings window. In the Settings window, click on the Profiles option.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/331/0*7xt9bFk9pikFd2nE.png" /></figure><p>The Profiles tab allows you to view your current profiles and create new ones. To create a new profile, click the `New Profile` button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/363/0*wHWu1tdzYo54cXb6.png" /></figure><p>This will create a new Untitled profile.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/693/0*iXHSD0PQ9N8ZUSHm.png" /></figure><p>You can now name your profile by editing the name field. You also have the option to copy the settings from an existing profile. This is useful for creating a new profile similar to an existing one or importing your default settings.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/490/0*IUqMUtpGXvYIrasL.png" /></figure><h3>VS Code Extensions for Terraform</h3><p>I will admit that I am a bit of an extension junkie. I have a lot of extensions, but some don’t play well together, so I use profiles to help limit the number of extensions I have enabled at any one time.</p><p>I use four extensions for Terraform development in Visual Studio Code:</p><p>- <a href="https://marketplace.visualstudio.com/items?itemName=hashicorp.terraform">HashiCorp Terraform</a></p><p>- <a href="https://marketplace.visualstudio.com/items?itemName=hashicorp.hcl">HashiCorp HCL</a></p><p>- <a href="https://marketplace.visualstudio.com/items?itemName=hashicorp.sentinel">HashiCorp Sentinel</a></p><p>- <a href="https://marketplace.visualstudio.com/items?itemName=cardinal90.multi-cursor-case-preserve">Multiple cursor case preserver</a></p><p>You’re probably looking at the list and wondering, “Why do you need a case preserver?” How many times have you decided to change the name of a variable and then had to go back and change the case of the variable in multiple places? This extension will preserve the case of the variable when you change it. It’s a small thing, but it saves me a lot of time.</p><h3>VS Code Settings for Terraform</h3><p>I like to use a few settings when working with Terraform. Most of these can be set in the VS Code UI, but they can also be shared in a JSON file within the.vscode. directory of your working directory.</p><p>Here are my Terraform specific VS Code settings:</p><pre>{<br>  &quot;[sentinel]&quot;: {<br>    &quot;editor.defaultFormatter&quot;: &quot;hashicorp.terraform&quot;<br>  },<br>  &quot;[terraform]&quot;: {<br>    &quot;editor.defaultFormatter&quot;: &quot;hashicorp.terraform&quot;<br>  },<br>  &quot;[tfvars]&quot;: {<br>    &quot;editor.defaultFormatter&quot;: &quot;hashicorp.terraform&quot;<br>  },<br>  &quot;editor.bracketPairColorization.enabled&quot;: true,<br>  &quot;editor.formatOnPaste&quot;: true,<br>  &quot;editor.formatOnSave&quot;: true,<br>  &quot;editor.formatOnType&quot;: true,<br>  &quot;editor.guides.bracketPairs&quot;: &quot;active&quot;,<br>  &quot;editor.inlineSuggest.enabled&quot;: true,<br>  &quot;editor.linkedEditing&quot;: true,<br>  &quot;editor.multiCursorModifier&quot;: &quot;alt&quot;,<br>  &quot;editor.renderControlCharacters&quot;: true,<br>  &quot;editor.renderWhitespace&quot;: &quot;all&quot;,<br>  &quot;editor.rulers&quot;: [<br>    {<br>      &quot;color&quot;: &quot;#A5FF90&quot;,<br>      &quot;column&quot;: 80<br>    },<br>    {<br>      &quot;color&quot;: &quot;#FF628C&quot;,<br>      &quot;column&quot;: 100<br>    }<br>  ],<br>  &quot;editor.stickyScroll.enabled&quot;: true,<br>  &quot;editor.suggestSelection&quot;: &quot;first&quot;,<br>  &quot;editor.tabCompletion&quot;: &quot;on&quot;,<br>  &quot;editor.tabSize&quot;: 2,<br>  &quot;files.associations&quot;: {<br>    &quot;*.policy&quot;: &quot;sentinel&quot;,<br>    &quot;*.sh.tmpl&quot;: &quot;shellscript&quot;<br>  },<br>  &quot;files.trimTrailingWhitespace&quot;: true,<br>  &quot;terraform.languageServer.enable&quot;: true<br>}</pre><p>Not all of these settings are needed, but they are the ones I like to use. You can adjust them to fit your needs.</p><ul><li>editor.defaultFormatter - This sets the default formatter for the editor. I like to use the HashiCorp Terraform formatter for all of my Terraform,tfvars, and Sentinel policy files.</li><li>editor.bracketPairColorization.enabled - This will colorize the brackets in your code to make it easier to see where they start and end.</li><li>editor.formatOnPaste, editor.formatOnSave, editor.formatOnType - These settings will format your code when you paste it, save the file, or type in the editor.</li><li>editor.guides.bracketPairs - This will highlight the bracket pair that you are currently working within.</li><li>editor.inlineSuggest.enabled - This will enable inline suggestions in the editor.</li><li>editor.linkedEditing - This will link the editing of a variable name to all instances of that variable in the file.</li><li>editor.multiCursorModifier - This sets the keybinding for creating multiple cursors.</li><li>editor.renderControlCharacters - This will render control characters in the editor.</li><li>editor.renderWhitespace - This will render whitespace in the editor.</li><li>editor.rulers - This will add rulers to the editor at the specified columns. I don&#39;t like to have lines that are too long, so I set my rulers at 80 and 100 columns.</li><li>editor.stickyScroll.enabled - This will enable sticky scrolling so that the block header stays at the top of the editor as you scroll through the code section.</li><li>editor.suggestSelection - This sets the default suggestion selection to the first suggestion.</li><li>editor.tabCompletion - This will enable tab completion in the editor.</li><li>editor.tabSize - This sets the tab size to 2 spaces.</li><li>files.associations - This will associate the specified file extensions with the specified language. I also work with many shell script templates that Terraform processes, so I associate the.sh.tmpl files with the shellscript language.</li><li>files.trimTrailingWhitespace - This will trim trailing whitespace from the end of lines.</li><li>terraform.indexing - This will set the indexing options for the Terraform configs we are working with.</li><li>terraform.languageServer.enable - This will enable the Terraform language server.</li></ul><h3>Conclusion</h3><p>This helps you get started with Terraform development in Visual Studio.</p><h3>Updates</h3><h4>2025–01–25</h4><p>I removed settings for an older Terraform extension I used before the official HashiCorp extension was released. Also, I removed file associations that used the Terraform extension to format Sentinel policy files and generic HCL files. I now use the HashiCorp Sentinel extension for Sentinel policy files and the HashiCorp HCL extension for generic HCL files.</p><p><em>Originally published at </em><a href="https://www.404-code-not-found.com/posts/vscode-setup-for-terraform/"><em>https://www.404-code-not-found.com</em></a><em> on October 10, 2024.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=17c3abee704c" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>