<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>DevOps on /home/andrzejgor.ski</title>
    <link>https://andrzejgor.ski/categories/devops/</link>
    <description>Recent content in DevOps on /home/andrzejgor.ski</description>
    <image>
      <title>/home/andrzejgor.ski</title>
      <url>https://andrzejgor.ski/metaimage.png</url>
      <link>https://andrzejgor.ski/metaimage.png</link>
    </image>
    <generator>Hugo -- 0.145.0</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 21 Feb 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://andrzejgor.ski/categories/devops/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Backblaze B2 as a Terraform remote state storage</title>
      <link>https://andrzejgor.ski/posts/backblaze_b2_tf_state/</link>
      <pubDate>Fri, 21 Feb 2025 00:00:00 +0000</pubDate>
      <guid>https://andrzejgor.ski/posts/backblaze_b2_tf_state/</guid>
      <description>It seems that Backblaze B2 is enough S3-compatible to be used as a remote state storage for Terraform!</description>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Recently I&rsquo;ve started The Big Migration™ from Ansible to Terraform in my homelab. But as soon as I started to write my first Terraform manifests, I&rsquo;ve started to think about remote state storage too.</p>
<p>First, I thought about storing the state file on selfhosted Minio instance. But the problem is, that Minio instance will be managed by the very same Terraform manifests.</p>
<p>Later on, I started researching what else can I manage with Terraform. I&rsquo;ve found out that Backblaze B2, where I keep backups anyway, is manageable with Terraform! That was the moment when it clicked in my head - all in all, B2 is S3 compatible, so I can use it as a remote state storage for Terraform!</p>
<h2 id="prerequisites">Prerequisites</h2>
<ol>
<li>Backblaze B2 account, with:
<ol>
<li>Bucket in which you want to store the state file</li>
<li>Application key with permissions to manage this bucket</li>
</ol>
</li>
<li>Terraform installed on your machine</li>
</ol>
<h2 id="terraform-configuration">Terraform configuration</h2>
<p>The basic config for storing the state file in S3 would look like this:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">terraform</span> { 
</span></span><span class="line"><span class="cl">  <span class="k">backend</span> <span class="s2">&#34;s3&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">    bucket</span>         <span class="o">=</span> <span class="s2">&#34;my-terraform-state-bucket&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    key</span>            <span class="o">=</span> <span class="s2">&#34;terraform.tfstate&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    region</span>         <span class="o">=</span> <span class="s2">&#34;us-east-1&#34;</span>
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>One might think that it&rsquo;s enough to change the <code>region</code> to B2 substitute, add Backblaze <code>endpoint</code> and we&rsquo;re good to go. But it&rsquo;s not that simple - B2 is <strong>almost</strong> S3 compatible, so we have to do some extra steps.</p>
<p>What we need to do is to skip some checks and validations that Backblaze B2 doesn&rsquo;t support. And there&rsquo;s actually quite a lot of them.</p>
<p>A fully working example of Terraform configuration for storing the state file in Backblaze B2 would look like this:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">terraform</span> {
</span></span><span class="line"><span class="cl">  <span class="k">backend</span> <span class="s2">&#34;s3&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">    bucket</span>    <span class="o">=</span> <span class="s2">&#34;my-terraform-state-bucket&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    key</span>       <span class="o">=</span> <span class="s2">&#34;terraform.tfstate&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    region</span>    <span class="o">=</span> <span class="s2">&#34;us-west-004&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    endpoints</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">      s3</span> <span class="o">=</span> <span class="s2">&#34;https://s3.us-west-004.backblazeb2.com&#34;</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">    skip_credentials_validation</span> <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl"><span class="n">    skip_region_validation</span>      <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl"><span class="n">    skip_metadata_api_check</span>     <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl"><span class="n">    skip_requesting_account_id</span>  <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl"><span class="n">    skip_s3_checksum</span>            <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>Of course, you&rsquo;ll have to replace <code>bucket</code>, <code>region</code>, and <code>endpoints.s3</code> values with proper ones according to your Backblaze B2 config.</p>
<p>As you can see, there&rsquo;s no <code>access_key</code> and <code>secret_key</code> provided. That&rsquo;s because I provide them through environment variables (and you should too!). B2&rsquo;s application key goes to <code>AWS_SECRET_ACCESS_KEY</code> and key ID goes to <code>AWS_ACCESS_KEY_ID</code> env var.</p>
<h2 id="some-security-considerations">Some security considerations</h2>
<h3 id="state-bucket">State bucket</h3>
<p>Keep it private. It&rsquo;s not a good idea to make your state file publicly available, as it might contain secrets.</p>
<p>You might also want to enable versioning on this bucket. With versioning you can easily revert to the previous state if something goes wrong. I&rsquo;ve seen Terraform go bananas a few times, so it&rsquo;s a good idea to have it enabled.</p>
<h3 id="application-key">Application key</h3>
<p>Don&rsquo;t use your master key for this. Create a separate application key with permissions to manage only this bucket. It&rsquo;s a good practice to have separate keys for different tasks.</p>
<p>Don&rsquo;t put your credentials in the Terraform code (or any code, really). Especially if you&rsquo;re gonna ever put that code publicly eg. on GitHub. One &ldquo;oops&rdquo; too far and your keys leaked. Use environment variables to provide them to Terraform. I personally load them into env vars from 1password with <code>op</code> cli tool.</p>
<h2 id="summary">Summary</h2>
<p>It seems that Backblaze B2 is enough S3-compatible to be used as a remote state storage for Terraform. It&rsquo;s good to keep your state file in some remote storage, as it&rsquo;s a good practice to have it versioned and not stored on your local machine. And if you already use B2 for backups, why not use it for Terraform state file as well?</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
