<?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: Andrii Bilorus</title>
    <description>The latest articles on DEV Community by Andrii Bilorus (@ewsct).</description>
    <link>https://dev.to/ewsct</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%2F897525%2Fd87498f8-71bc-4b3e-b0aa-01b54174e250.png</url>
      <title>DEV Community: Andrii Bilorus</title>
      <link>https://dev.to/ewsct</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ewsct"/>
    <language>en</language>
    <item>
      <title>Breaking down Terraform monolith into multiple environments</title>
      <dc:creator>Andrii Bilorus</dc:creator>
      <pubDate>Tue, 26 Jul 2022 13:45:00 +0000</pubDate>
      <link>https://dev.to/ewsct/breaking-down-terraform-monolith-into-multiple-environments-fcg</link>
      <guid>https://dev.to/ewsct/breaking-down-terraform-monolith-into-multiple-environments-fcg</guid>
      <description>&lt;p&gt;Imagine that you started a small project in terraform some time ago. And now your applications have hundreds of customers, infrastructure with a few environments, but still the same terraform monolith responsible for all your cloud infra. Sounds familiar? Then keep reading and you will know how to break down terraform monolith into multiple environments without resource recreation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Audience
&lt;/h2&gt;

&lt;p&gt;I assume you already have experience with terraform. You manage some of the cloud infrastructure with terraform, you are familiar with state files and backend configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Theory
&lt;/h2&gt;

&lt;p&gt;Let's quickly recap what we know about terraform state. Terraform keeps the information about its resources in a state file. Terraform has multiple options to store the state file, the most popular one is probably in the cloud object storage. So we'll be using S3 for this as an example.&lt;/p&gt;

&lt;p&gt;If we want to manage environments separately, we need to have a state per environment. So that we can apply configuration independently. This is not a big deal to create one more state. The challenging task is to move information about cloud resources from one state to another. And to do it without downtime or without the need to recreate resources.&lt;/p&gt;

&lt;p&gt;In the beginning, we have one directory with terraform code for both environments. Our goal is to split up the code, then move resource records from one state to another.&lt;/p&gt;

&lt;p&gt;Now let's agree on the file system structure. For the target state we'll be using two separate directories. This pattern is very well explained &lt;a href="https://www.terraform-best-practices.com/examples/terraform/medium-size-infrastructure" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to know more about terraform best practices, I would strongly suggest taking a look at &lt;a href="https://www.terraform-best-practices.com" rel="noopener noreferrer"&gt;Terraform best practices guide&lt;/a&gt; by Anton Babenko&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Migration in detail
&lt;/h2&gt;

&lt;p&gt;As soon as you allocate a new directory for your new environment, you can start moving the related code. Simply cut the code from one &lt;code&gt;main.tf&lt;/code&gt; to another. Do not apply any changes at this stage.&lt;/p&gt;

&lt;p&gt;Next, you'll need to manipulate a bit with terraform tool. Here is what needs to be done&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Init a new state for the new environment&lt;/li&gt;
&lt;li&gt;Pull the state file from the remote backend to your local directory&lt;/li&gt;
&lt;li&gt;Move respective resources from one state to another&lt;/li&gt;
&lt;li&gt;Finally, push the updated state for the remote backend&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ahh14jt4esa9isb9r0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ahh14jt4esa9isb9r0x.png" alt="splitting up the monolith"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Short example
&lt;/h2&gt;

&lt;p&gt;If you are power user of terraform, then this short example is for you&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;cd &lt;/span&gt;dev

&lt;span class="c"&gt;# now set up remote backend for this new env&lt;/span&gt;

terraform init
terraform state pull &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; new.tfstate

&lt;span class="c"&gt;# now open your favourite text editor&lt;/span&gt;
&lt;span class="c"&gt;# and move resource definitions from prod tf files to dev tf files&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; ../prod
terraform state list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; resource_list.txt

&lt;span class="c"&gt;# now open resource_list.txt&lt;/span&gt;
&lt;span class="c"&gt;# and leave there only the resource that you want to move&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;resource_list.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;terraform state &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="nt"&gt;-state-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;../dev/new.tfstate &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../dev
terraform state pull &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; new.tfstate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Detailed example
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Existing infrastructure
&lt;/h3&gt;

&lt;p&gt;We will consider a simple example with two VPC networks.&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;# prod/main.tf&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/24"&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;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.1.1.0/24"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The remote backend config 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="c1"&gt;# prod/provider.tf&lt;/span&gt;
&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0.0"&lt;/span&gt;

  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"myterraformstatemonolith"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&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;
  
  
  Initialising a new environment
&lt;/h3&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;&lt;span class="nb"&gt;mkdir &lt;/span&gt;dev
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting up the remote backend. Pay attention that the key changed, you can also set up a new bucket if you want. But right now it is enough for our needs, we have a separate state 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;# dev/provider.tf&lt;/span&gt;
&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0.0"&lt;/span&gt;

  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"myterraformstatemonolith"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&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;Now go ahead and run &lt;code&gt;terraform init&lt;/code&gt;, this will create a file in the S3 bucket.&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;terraform init

Initializing the backend...

Successfully configured the backend &lt;span class="s2"&gt;"s3"&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Terraform will automatically
use this backend unless the backend configuration changes.

&amp;lt;output omitted&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Move resource definitions
&lt;/h3&gt;

&lt;p&gt;Now you need to move the content of the terraform files related to the new environment. In my case my new &lt;code&gt;dev/main.tf&lt;/code&gt; will 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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.1.1.0/24"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Move resources from the state file to a new one
&lt;/h3&gt;

&lt;p&gt;This is a bit more complex than the previous steps. Firstly let's pull the new remote state&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;&lt;span class="nb"&gt;cd &lt;/span&gt;dev
&lt;span class="nv"&gt;$ &lt;/span&gt;terraform state pull &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; new.tfstate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then get back to &lt;code&gt;prod&lt;/code&gt; directory and move the resources&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;&lt;span class="nb"&gt;cd &lt;/span&gt;prod
&lt;span class="nv"&gt;$ &lt;/span&gt;terraform state list
aws_vpc.dev
aws_vpc.prod

&lt;span class="nv"&gt;$ &lt;/span&gt;terraform state &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="nt"&gt;-state-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;../dev/new.tfstate aws_vpc.dev aws_vpc.dev
Move &lt;span class="s2"&gt;"aws_vpc.dev"&lt;/span&gt; to &lt;span class="s2"&gt;"aws_vpc.dev"&lt;/span&gt;
Successfully moved 1 object&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Push the updated state file back to S3 bucket
&lt;/h3&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;terraform state push new.tfstate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, now make sure everything is up to date&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;terraform plan
aws_vpc.dev: Refreshing state... &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-070562866e6399b45a]

No changes. Your infrastructure matches the configuration.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>terraform</category>
      <category>devops</category>
      <category>sre</category>
    </item>
  </channel>
</rss>
