<?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>Forem: Matheus Cunha</title>
    <description>The latest articles on Forem by Matheus Cunha (@macunha).</description>
    <link>https://forem.com/macunha</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%2F641618%2Fb1a97d08-7f05-4f6a-b711-7b06790859ff.png</url>
      <title>Forem: Matheus Cunha</title>
      <link>https://forem.com/macunha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/macunha"/>
    <language>en</language>
    <item>
      <title>Terraform Modules: Atomic Design</title>
      <dc:creator>Matheus Cunha</dc:creator>
      <pubDate>Sat, 28 Aug 2021 10:10:48 +0000</pubDate>
      <link>https://forem.com/macunha/terraform-modules-atomic-design-3i7m</link>
      <guid>https://forem.com/macunha/terraform-modules-atomic-design-3i7m</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Following &lt;a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/"&gt;The Pragmatic Programmer&lt;/a&gt; mantra, I do my best to ...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Learn at least one new language every year.&lt;/strong&gt; Different languages solve the same problems in different ways. By learning several different approaches, you can help broaden your thinking and avoid getting stuck in a rut.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not necessarily to show it off or to be capable of talking about random technologies, but to expand and train my problem-solving skills, to get new perspectives when approaching a challenge.&lt;/p&gt;

&lt;p&gt;We might not notice it but when we learn (or have learned) to code we aren't just learning to type some characters that a compiler/interpreter can understand, it is a new way of thinking, a new way of breaking down solutions (into sequential steps).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It doesn't matter whether you ever use any of these technologies on a project, or even whether you put them on your resume. The process of learning will expand your thinking, opening you to new possibilities and new ways of doing things.&lt;br&gt;
The cross-pollination of ideas is important;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As someone who works intensively with infrastructure components (servers, databases, Kubernetes, CI/CD, etc) I aimed for something completely different this year. Something that stands on &lt;em&gt;a whole different spectrum&lt;/em&gt; of the system, this year I decided to learn &lt;a href="https://flutter.dev/"&gt;Flutter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In-a-nutshell, Flutter is a better React Native. A framework that enables implementation of GUI applications for multiple platforms with a single code base.&lt;/p&gt;

&lt;p&gt;Then it reminded me a discussion I had with a friend in the past about React components and the &lt;a href="https://bradfrost.com/blog/post/atomic-web-design/"&gt;Atomic Design&lt;/a&gt; methodology, which helps to structure web components into modules.&lt;/p&gt;

&lt;p&gt;In the Atomic Design methodology, the granularity of modules is distinguished by using chemistry inspired names: atoms, molecules and organisms.&lt;/p&gt;

&lt;p&gt;Then the connection of the ideas from&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pragmatic Programmer's cross-pollination to&lt;/li&gt;
&lt;li&gt;Atomic Design (on Flutter components) to&lt;/li&gt;
&lt;li&gt;Terraform modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;came almost like a thunderbolt, striking me with this insight when I was working with a huge legacy Terraform code base refactoring with lots of code duplication (read: copy+paste, "we fix it later", then the author quits the company and&lt;br&gt;
never fix anything).&lt;/p&gt;

&lt;p&gt;Although initially proposed as a Web UI methodology, Infrastructure as Code tools such as Terraform that makes heavy usage of modules can benefit from Atomic Design to improve its code reusability and massively reduce duplication.&lt;/p&gt;
&lt;h2&gt;
  
  
  Details
&lt;/h2&gt;

&lt;p&gt;The Atomic Design methodology proposes five distinct levels, listed from the finest to the thickest:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Atom;&lt;/li&gt;
&lt;li&gt; Molecules;&lt;/li&gt;
&lt;li&gt; Organisms;&lt;/li&gt;
&lt;li&gt; Templates;&lt;/li&gt;
&lt;li&gt; Pages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, to extract the gist, we'll only be focusing on Atoms, Molecules, and Organisms (from 1. to 3.). Templates and Pages are too domain-specific focused on Web UI development.&lt;/p&gt;
&lt;h3&gt;
  
  
  Atoms
&lt;/h3&gt;

&lt;p&gt;Atoms represent the finest grain in terms of granularity in the design. When referring specifically to its implementation in Terraform a &lt;code&gt;resource&lt;/code&gt; and a small scoped single-purpose &lt;code&gt;module&lt;/code&gt; could be used interchangeably.&lt;/p&gt;

&lt;p&gt;Sometimes the idea of turning a simple resource into a module makes sense to ease parameterization and reusability, especially when it is necessary to parse inputs. Although, due to its extreme limited scope it might not look attractive&lt;br&gt;
to convert the &lt;code&gt;resource&lt;/code&gt; into a &lt;code&gt;module&lt;/code&gt; at first sight, on the long run it pays off to do so in order to achieve scalability and reproducibility.&lt;/p&gt;

&lt;p&gt;e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_route53_zone"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;zone_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route53_record"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;zone_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_route53_zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;

  &lt;span class="nx"&gt;ttl&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ttl&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record_type&lt;/span&gt;

  &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;

  &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"alias"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
      &lt;span class="nx"&gt;zone_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_route53_zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="nx"&gt;evaluate_target_health&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"evaluate_target_health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&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;In this case, even though &lt;code&gt;aws_route53_record&lt;/code&gt; is a simple resource that might feel too narrow in scope to write a module, the implementation of the module allows to bundle the AWS Route53 Zone data source together, which helps to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; provide a simpler contract by allowing the usage of &lt;code&gt;zone_name&lt;/code&gt; alone;&lt;/li&gt;
&lt;li&gt; validate the &lt;code&gt;zone_name&lt;/code&gt; input, ensuring that a given &lt;code&gt;zone_name&lt;/code&gt; corresponds to an actual &lt;strong&gt;existing and valid&lt;/strong&gt; AWS resource;&lt;/li&gt;
&lt;li&gt; same goes to &lt;code&gt;zone_id&lt;/code&gt;, which will feel (and oftentimes, be) redundant, &lt;em&gt;when&lt;/em&gt; specified as an input Terraform will read the data from AWS API ensuring consistency.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"awesome_dns_fqdn"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"path/to/modules/atoms/aws_route53_record"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"record.example.com"&lt;/span&gt;
  &lt;span class="nx"&gt;zone_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example.com."&lt;/span&gt;

  &lt;span class="nx"&gt;record_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"A"&lt;/span&gt;
  &lt;span class="nx"&gt;records&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"1.2.3.4"&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;Hence, resources and modules are sometimes interchangeable as they deliver the same outcome for the finest resources' granularity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Molecules
&lt;/h3&gt;

&lt;p&gt;When groups of atoms are bounded together, they create a molecule which is the smallest fundamental unit of a compound.&lt;/p&gt;

&lt;p&gt;Contrary to the original Atomic Design for Web UI, in Terraform, Atoms are useful on their own. However, the usage of atoms comes with a high price on scalability: code duplication. Actually, duplication is an understatement, it is more like code exponentiation (more on this later).&lt;/p&gt;

&lt;h4&gt;
  
  
  Implementation example
&lt;/h4&gt;

&lt;p&gt;Suppose we are creating a public facing API Gateway that needs a DNS record.&lt;/p&gt;

&lt;p&gt;Let's compose it with the previous example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_route53_zone"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"awesome_api_gateway_certificate"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/acm/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; v3.0"&lt;/span&gt;

  &lt;span class="nx"&gt;domain_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain_name&lt;/span&gt;
  &lt;span class="nx"&gt;zone_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_route53_zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;

  &lt;span class="nx"&gt;wait_for_validation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"awesome_api_gateway"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/apigateway-v2/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_gateway_name&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_gateway_description&lt;/span&gt;
  &lt;span class="nx"&gt;protocol_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP"&lt;/span&gt;

  &lt;span class="nx"&gt;cors_configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;allow_headers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"content-type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"x-amz-date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"authorization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"x-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"x-amz-security-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"x-amz-user-agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;allow_methods&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;allow_origins&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Custom domain&lt;/span&gt;
  &lt;span class="nx"&gt;domain_name&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain_name&lt;/span&gt;
  &lt;span class="nx"&gt;domain_name_certificate_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;awesome_api_gateway_certificate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acm_certificate_arn&lt;/span&gt;

  &lt;span class="c1"&gt;# Routes and integrations&lt;/span&gt;
  &lt;span class="nx"&gt;integrations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_gateway_integrations&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"awesome_dns_fqdn"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"path/to/modules/atoms/aws_route53_record"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain_name&lt;/span&gt;
  &lt;span class="nx"&gt;zone_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_route53_zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;

  &lt;span class="nx"&gt;record_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CNAME"&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;awesome_api_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apigatewayv2_domain_name_configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;target_domain_name&lt;/span&gt;
    &lt;span class="nx"&gt;zone_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;awesome_api_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apigatewayv2_domain_name_configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;hosted_zone_id&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;This helps illustrating an example in which the &lt;code&gt;aws_route53_record&lt;/code&gt; atom could be easily replaced with its equivalent resource and it would still provide the &lt;strong&gt;same&lt;/strong&gt; outcome.&lt;/p&gt;

&lt;p&gt;Commonly it is possible to use &lt;code&gt;module&lt;/code&gt; and &lt;code&gt;resource&lt;/code&gt; interchangeably as Atoms, the decision of whether or not to implement a &lt;code&gt;module&lt;/code&gt; is ultimately defined by the need of parsing and/or validating the inputs (variables).&lt;/p&gt;

&lt;h4&gt;
  
  
  Usage example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"awesome_lambda"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"path/to/modules/molecules/aws_lambda_function"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awesome"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"An Awesome lambda function for the Awesome API Gateway"&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"index.lambda_handler"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.8"&lt;/span&gt;

  &lt;span class="c1"&gt;# Incomplete implementation, don't use this on production&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"another_awesome_lambda"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"path/to/modules/molecules/aws_lambda_function"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awesome"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"An Awesome lambda function for the Awesome API Gateway"&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"index.lambda_handler"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.8"&lt;/span&gt;

  &lt;span class="c1"&gt;# Incomplete implementation, don't use this on production&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"awesome_api_gateway"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"path/to/modules/molecules/aws_api_gateway"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;domain_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"record.example.com"&lt;/span&gt;
  &lt;span class="nx"&gt;zone_name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example.com."&lt;/span&gt;

  &lt;span class="nx"&gt;api_gateway_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awesome-api-gateway"&lt;/span&gt;
  &lt;span class="nx"&gt;api_gateway_description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"An Awesome API Gateway"&lt;/span&gt;

  &lt;span class="nx"&gt;api_gateway_integrations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"POST /"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;lambda_arn&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;awesome_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_arn&lt;/span&gt;
      &lt;span class="nx"&gt;payload_format_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;default"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;lambda_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;another_awesome_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_arn&lt;/span&gt;
    &lt;span class="p"&gt;}&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;As you probably have already realized, when the level of abstraction goes up (e.g. from atom to molecule) the module implementation is in itself a good implementation example (i.e. as in &lt;a href="https://github.com/terraform-aws-modules/terraform-aws-lambda/blob/master/main.tf"&gt;community modules examples&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;They help to self-document the usage and implementation of a given module and through generic implementations it allows us to have multiple molecules implementing multiple distinct use-cases. e.g.:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Public API Gateway with DNS record + TLS certificate;&lt;/li&gt;
&lt;li&gt; Public API Gateway v1, no DNS record;&lt;/li&gt;
&lt;li&gt; Private API Gateway.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why would we chose to implement multiple times the Atom modules in order to create multiple distinct use-cases? We are getting closer to the &lt;em&gt;code exponentiation&lt;/em&gt; problem and solution proposal. Can you feel it?&lt;/p&gt;

&lt;h3&gt;
  
  
  Organisms
&lt;/h3&gt;

&lt;p&gt;Going further, the example of composition for molecules can have its hard-coded values turned into variables in order to compose an Organism, which can facilitate the implementation of the same definition across different environments. Thus, achieving reproducibility as well as the &lt;a href="https://12factor.net/dev-prod-parity"&gt;Factor X.&lt;/a&gt; of the Twelve Factor App.&lt;/p&gt;

&lt;p&gt;However, it is important to note that the level of abstraction between Organisms and Molecules can be easily confused or misunderstood. Generally speaking, as a&lt;br&gt;
rule of thumb an Organism is the composition of Molecules that allow parameterization for business or domain-specific logic (e.g. the actual &lt;code&gt;awesome_api&lt;/code&gt; configuration).&lt;br&gt;
Therefore, in comparison with the previous, Organisms (usually) have a lower level of generalization since they are business-specialized modules.&lt;/p&gt;

&lt;p&gt;Iterating over our implementation example, the Organism would implement the &lt;code&gt;awesome_api&lt;/code&gt;, creating the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Lambda function;&lt;/li&gt;
&lt;li&gt;AWS API Gateway;&lt;/li&gt;
&lt;li&gt;TLS Certificate on AWS ACM;&lt;/li&gt;
&lt;li&gt;DNS record on AWS Route53.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By implementing the previous examples as organisms we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; reduce the amount of boilerplate code;&lt;/li&gt;
&lt;li&gt; foster reusability of modules;&lt;/li&gt;
&lt;li&gt; provide a simple interface for non-operators to manage TF code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you sum it all up, you will notice that it is &lt;strong&gt;all about autonomy&lt;/strong&gt; and "DevOps" through encouragement of self-service Ops. One wouldn't need to know a lot about Terraform to grab a module and pass some parameters to it, followed by&lt;br&gt;
a code review process Operators and Software Developers can manage the Infrastructure in harmony, &lt;strong&gt;together&lt;/strong&gt;. (:&lt;/p&gt;
&lt;h3&gt;
  
  
  Code Exponentiation? What?
&lt;/h3&gt;

&lt;p&gt;Read that as a dramatization of the &lt;a href="https://en.wikipedia.org/wiki/Duplicate%5Fcode"&gt;"code duplication"&lt;/a&gt; term.&lt;/p&gt;

&lt;p&gt;When it comes to Infrastructure as Code, there is no easy way around the jungle of resources that grows over time. Fast pacing tech companies are "moving fast and breaking things", oftentimes the Operators are worried about a massive&lt;br&gt;
amount of challenges at once: keep the servers up and running, with a consistent response time, low error rate, and all that &lt;a href="https://sre.google/sre-book/table-of-contents/"&gt;playbook from Google's SRE wisdom&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All things considered, a good Infrastructure as Code design is generally a first-world problem. However, as the time passes it evolves into a real issue that slows down the implementation of resources as code. Either that or there&lt;br&gt;
will be a &lt;strong&gt;huge ton&lt;/strong&gt; of copy+paste to keep up with the pace, followed by a routine of find+replace when changes are applied, &lt;em&gt;then&lt;/em&gt; harder to track pull requests and slower code reviews.&lt;/p&gt;

&lt;p&gt;Lets take our &lt;code&gt;awesome_api&lt;/code&gt; example and scale it up to multiple environments followed by a second &lt;code&gt;awesome_api&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── development
│   ├── an-awesome-api
│   │   └── main.tf
│   └── another-awesome-api
│       └── main.tf
├── staging
│   ├── an-awesome-api
│   │   └── main.tf
│   └── another-awesome-api
│       └── main.tf
└── production
    ├── an-awesome-api
    │   └── main.tf
    └── another-awesome-api
        └── main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to replicate the configuration and ensure consistency, the following is way simpler to implement (and review) than copy+paste huge chunks of Terraform definitions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"awesome_api"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"path/to/modules/organisms/aws_lambda_with_api_gateway"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;domain_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"record.example.com"&lt;/span&gt;
  &lt;span class="nx"&gt;zone_name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example.com."&lt;/span&gt;

  &lt;span class="nx"&gt;lambda_functions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# Index 0 -- An Awesome Lambda Function, used for POST&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"an-awesome"&lt;/span&gt;
      &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"An Awesome lambda function for the Awesome API Gateway"&lt;/span&gt;
      &lt;span class="nx"&gt;handler&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"an_awesome.lambda_handler"&lt;/span&gt;
      &lt;span class="nx"&gt;runtime&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.8"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;# Index 1 -- Another Awesome Lambda Function, used as $default&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"another-awesome"&lt;/span&gt;
      &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Another Awesome lambda function for the Awesome API Gateway"&lt;/span&gt;
      &lt;span class="nx"&gt;handler&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"another_awesome.lambda_handler"&lt;/span&gt;
      &lt;span class="nx"&gt;runtime&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.8"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;api_gateway_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awesome-api-gateway"&lt;/span&gt;
  &lt;span class="nx"&gt;api_gateway_description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"An Awesome API Gateway"&lt;/span&gt;

  &lt;span class="nx"&gt;api_gateway_integrations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"POST /"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;lambda_function_index&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="nx"&gt;payload_format_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;default"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;lambda_function_index&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;}&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;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;At the end of the day we get an ugly Terraform state containing many&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.something.module.something_else.module.yet_another_thing...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the productivity boost gained by merging modules based on context is a worth investment. Especially for huge Terraform repositories with multiple teams collaborating and managing a lot of resources.&lt;/p&gt;

&lt;p&gt;Cross-team collaboration is fostered by applying the Atomic Design methodology for Terraform modules, code reusability becomes an important factor over copy+paste and the repository gravitates towards the &lt;a href="https://en.wikipedia.org/wiki/Don%27t%5Frepeat%5Fyourself"&gt;DRY principle&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same post, different places
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.reddit.com/r/Terraform/comments/pd708z/terraform_modules_atomic_design/"&gt;Reddit r/Terraform post: Terraform Modules: Atomic Design&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>infrastructureascode</category>
      <category>design</category>
    </item>
    <item>
      <title>Real-life Terraform Refactoring Guide</title>
      <dc:creator>Matheus Cunha</dc:creator>
      <pubDate>Mon, 16 Aug 2021 18:06:35 +0000</pubDate>
      <link>https://forem.com/macunha/real-life-terraform-refactoring-guide-2jo9</link>
      <guid>https://forem.com/macunha/real-life-terraform-refactoring-guide-2jo9</guid>
      <description>&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt; Intro
&lt;/li&gt;
&lt;li&gt; How to break a big ball of mud? STRANGLE IT
&lt;/li&gt;
&lt;li&gt; The mono-repository (monorepo) approach to Legacy

&lt;ol&gt;
&lt;li&gt; Splitting the &lt;code&gt;modules&lt;/code&gt; sub-path to its own repository
&lt;/li&gt;
&lt;li&gt; Let’s start strangling the repository

&lt;ol&gt;
&lt;li&gt; Import state? Remove state and code from what? Where?
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="orga4c7a97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;As reality hits, the unavoidable fact of dealing with a hard-to-manage Terraform &lt;a href="https://en.wikipedia.org/wiki/Big_ball_of_mud"&gt;Big ball of mud&lt;/a&gt; code base comes in. There is no way around natural growth and evolution of code bases and the design flaws that come with it. Our Agile mindset is to &lt;a href="https://www.brainyquote.com/quotes/mark_zuckerberg_453439"&gt;“move fast and break things”&lt;/a&gt;, implement something as simple as possible and let the design decisions for the next iterations (if any).&lt;/p&gt;

&lt;p&gt;Refactoring Terraform code is actually as natural as developing it, time and time again you will be face situation where a better structure or organization can be achieved, maybe you want to upgrade from a home-made module to an open-source/community alternative, maybe you just want to segregate your resources into different states to speed-up development. Regardless of the goal, once you get into it, you will realize that Terraform code refactoring is actually a basic missing step on the development process that no one told you before.&lt;/p&gt;

&lt;p&gt;As the &lt;a href="http://nathanmarz.com/blog/suffering-oriented-programming.html"&gt;Suffering-Oriented Programming&lt;/a&gt; mantra dictates:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“First make it possible. Then make it beautiful. Then make it fast.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, time to make the Terraform code beautiful!&lt;/p&gt;

&lt;p&gt;&lt;a id="org3702753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  How to break a big ball of mud? STRANGLE IT
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;joke&amp;gt;&lt;/code&gt;Martin Fowler has already written everything there is to write about (early 2000s) DevOps, Agile, and Software Development. Therefore, we could reference Martin Fowler for virtually anything Software related&lt;code&gt;&amp;lt;/joke&amp;gt;&lt;/code&gt;, but really, the &lt;a href="https://martinfowler.com/books/refactoring.html"&gt;Refactoring book&lt;/a&gt; is &lt;strong&gt;THE&lt;/strong&gt; reference on this subject.&lt;/p&gt;

&lt;p&gt;Martin Fowler shared the &lt;a href="https://martinfowler.com/bliki/StranglerFigApplication.html"&gt;Stangler (Fig) Pattern&lt;/a&gt;, which describes a strategy to refactor a legacy code base by re-implementing the same features (sometimes even the bugs) on another application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[…] the huge strangler figs. They seed in the upper branches of a tree and gradually work their way down the tree until they root in the soil. Over many years they grow into fantastic and beautiful shapes, meanwhile strangling and killing the tree that was their host.&lt;/p&gt;

&lt;p&gt;This metaphor struck me as a way of describing a way of doing a rewrite of an important system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this document we are going to follow the same idea:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; implement the same feature on a different &lt;a href="https://www.terraform-best-practices.com/key-concepts#composition"&gt;Terraform composition&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt; migrate the Terraform state;&lt;/li&gt;
&lt;li&gt; delete (kill) the previous implementation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="orgeaee6de"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The mono-repository (monorepo) approach to Legacy
&lt;/h1&gt;

&lt;p&gt;Let’s suppose that your Terraform code base is versioned in a single repository (a.k.a. monorepo), following the random structure displayed below (just to help illustrate)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── modules/    # Definition of TF modules used by underlying compositions
├── global/     # Resources that aren't restricted to one environment
│   ├── aws/
├── production/ # Production environment resources
│   └── aws/
└── staging/    # Staging environment resources
    └── aws/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, each directory corresponds to a Terraform state. In order to apply changes, you have to walk to a path and execute &lt;code&gt;terraform&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The structure on this example repository was created a few hypothetical years ago when the number of existing microservices and resources (DB, message queues, etc) was significantly smaller. At the time, it was feasible to keep Terraform definitions together because it was easier to maintain, Cloud resources were managed with one-shot!&lt;/p&gt;

&lt;p&gt;As time went by, the number of Products and the team grew, and engineers started facing concurrency issues: Terraform lock executions on shared storage when someone else is running &lt;code&gt;terraform apply&lt;/code&gt; as well as a general slowness on &lt;strong&gt;every execution&lt;/strong&gt; since the number of data sources to sync is frightening.&lt;/p&gt;

&lt;p&gt;A mono-repository approach is not necessarily bad, versioning is actually simpler when performed in one single repository. Ideally, there won’t be many changes on the scale of GiB meaning that it is safe to proceed on this one &lt;em&gt;as long as the Terraform remote states are divided&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="orgdd4979a"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Splitting the &lt;code&gt;modules&lt;/code&gt; sub-path to its own repository
&lt;/h2&gt;

&lt;p&gt;One thing to mention though is the &lt;code&gt;modules&lt;/code&gt; sub-path, this one could be stored in a different git repository to leverage its own versioning. Since Terraform modules and their implementations don’t always evolve at the same pace, keeping two distinct version trees is beneficial. Additionally, a separated repository for Terraform modules allows the specification of “pinned versions”, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"aws_main_vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git?ref=2ca733d"&lt;/span&gt;
    &lt;span class="c1"&gt;# Note the ref=${GIT_REVISION_DIGEST}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That reference for a module’s version should always be specified, regardless if it comes from an internal/private repository or public. When you specify the version, you are ensuring reproducibility.&lt;/p&gt;

&lt;p&gt;Therefore, let’s move the &lt;code&gt;modules&lt;/code&gt; sub-path to another git repository, following instructions from &lt;a href="https://stackoverflow.com/questions/359424/detach-move-subdirectory-into-separate-git-repository/17864475#17864475"&gt;this StackOverflow answer&lt;/a&gt; so that the git commit history is preserved:&lt;/p&gt;

&lt;h3&gt;
  
  
  0.
&lt;/h3&gt;

&lt;p&gt;Walk to the monorepo path and create a branch from the commits at &lt;code&gt;monorepo/modules&lt;/code&gt; path&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;MAIN_BIGGER_REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/the/monorepo
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MAIN_BIGGER_REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
git subtree &lt;span class="nb"&gt;split&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; modules &lt;span class="nt"&gt;-b&lt;/span&gt; refact-modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.
&lt;/h3&gt;

&lt;p&gt;Create the new repository&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;mkdir&lt;/span&gt; /path/to/the/terraform-modules &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;
git init
git pull &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MAIN_BIGGER_REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; refact-modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.
&lt;/h3&gt;

&lt;p&gt;Link the new repository to your remote Git (server) and push the commits&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin &amp;lt;git@git.com:user/terraform-modules.git&amp;gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.
&lt;/h3&gt;

&lt;p&gt;Cleanup the history related to &lt;code&gt;modules&lt;/code&gt; from &lt;code&gt;$MAIN_BIGGER_REPO&lt;/code&gt; [OPTIONAL]&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; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MAIN_BIGGER_REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
git &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; modules
git filter-branch &lt;span class="nt"&gt;--prune-empty&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--tree-filter&lt;/span&gt; &lt;span class="s2"&gt;"rm -rf modules"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="org5026de0"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s start strangling the repository
&lt;/h2&gt;

&lt;p&gt;Now that a substantial piece of code was moved somewhere else, it is time to put the &lt;a href="https://martinfowler.com/bliki/StranglerFigApplication.html"&gt;Stangler (Fig) Pattern&lt;/a&gt; in practice.&lt;/p&gt;

&lt;p&gt;Move all the existing content as-is to the &lt;code&gt;legacy&lt;/code&gt; sub-path, keeping the same repository and change history (commits). It also allows applying the &lt;code&gt;legacy&lt;/code&gt; code as it used to be from one of those paths.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└── legacy
    ├── global
    │   └── aws
    ├── production
    │   └── aws
    └── staging
        └── aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the content is moved to legacy, the idea is to follow the &lt;a href="https://www.oreilly.com/library/view/97-things-every/9780596809515/ch08.html"&gt;Boy Scout rule&lt;/a&gt; in order to strangle the &lt;code&gt;legacy&lt;/code&gt; content little by little (unless you are&lt;br&gt;
really committed to migrating it all at once, which is going to be exhaustive).&lt;/p&gt;

&lt;p&gt;The Boy Scout rule goes like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; every time a task that involves deprecated code appears, we implement it on &lt;a href="https://dev.to/macunha/terraform-design-best-practices-1i0h"&gt;the new structure&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt; import the Terraform state to keep the Cloud resources that a given code represents/describes;&lt;/li&gt;
&lt;li&gt; remove the state and the code from &lt;code&gt;legacy&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Until there is nothing left inside &lt;code&gt;legacy&lt;/code&gt; (or there are only unused resources/left-behinds that could be destroyed/garbage collected either way).&lt;/p&gt;

&lt;p&gt;&lt;a id="org5230407"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Import state? Remove state and code from what? Where?
&lt;/h3&gt;

&lt;p&gt;That will depend on the kind of resource we are migrating from the remote state, on the bottom of each &lt;code&gt;resource&lt;/code&gt; on Terraform’s provider documentation you can find a reference command to import existing resources into your Terraform code specification. e.g.: &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#import"&gt;AWS RDS DB instance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Suppose we want to replace the code of the AWS RDS Aurora defined in &lt;code&gt;production/aws&lt;/code&gt; and then re-implement the same using &lt;a href="https://github.com/terraform-aws-modules/terraform-aws-rds-aurora"&gt;the community module&lt;/a&gt;. After creating the corresponding sub-path to the monorepo according to your preference, provisioning the bucket and initializing the Terraform &lt;code&gt;backend&lt;/code&gt;:&lt;/p&gt;
&lt;h3&gt;
  
  
  1.
&lt;/h3&gt;

&lt;p&gt;Implement the definition of the community module &lt;a href="https://github.com/terraform-aws-modules/terraform-aws-rds-aurora"&gt;github.com/terraform-aws-modules/terraform-aws-rds-aurora&lt;/a&gt; with the closest parameters from the existing one; e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"aws_aurora_main_cluster"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/rds-aurora/aws"&lt;/span&gt;
 &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.2"&lt;/span&gt;

 &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.
&lt;/h3&gt;

&lt;p&gt;Import the Terraform states from the previous (existing) cluster&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform import &lt;span class="s1"&gt;'aws_aurora_main_cluster.aws_rds_cluster.this[0]'&lt;/span&gt; main-database-name
terraform import &lt;span class="s1"&gt;'aws_aurora_main_cluster.aws_rds_cluster_instance.this[0]'&lt;/span&gt; main-database-instance-name-01
terraform import &lt;span class="s1"&gt;'aws_aurora_main_cluster.aws_rds_cluster_instance.this[1]'&lt;/span&gt; main-database-instance-name-02

&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then if you haven’t yet and would like to “match reality” between the existing and the specified resource, run &lt;code&gt;terraform plan&lt;/code&gt; a few times and adjust the parameters until Terraform reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No changes. Your infrastructure matches the configuration.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.
&lt;/h3&gt;

&lt;p&gt;Last but not least, remove the corresponding resources from the &lt;code&gt;legacy&lt;/code&gt; Terraform state so that it doesn’t try to keep track of the changes and also don’t try to destroy once the resource definition is no longer in that code base:&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;# Hypothetical name of the resource inside production/aws/main.tf&lt;/span&gt;
terraform state &lt;span class="nb"&gt;rm &lt;/span&gt;aws_rds_cluster.default &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s1"&gt;'aws_rds_cluster_instance.default[0]'&lt;/span&gt; &lt;span class="s1"&gt;'aws_rds_cluster_instance.default[1]'&lt;/span&gt;

&lt;span class="c"&gt;# ...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;once that is performed, feel free to remove the corresponding resource’s definition from the &lt;code&gt;legacy&lt;/code&gt; code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_rds_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_rds_cluster_instance"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number_of_database_instances&lt;/span&gt;

    &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>terraform</category>
      <category>devops</category>
      <category>infrastructureascode</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Terraform Design Best Practices</title>
      <dc:creator>Matheus Cunha</dc:creator>
      <pubDate>Sat, 07 Aug 2021 10:10:55 +0000</pubDate>
      <link>https://forem.com/macunha/terraform-design-best-practices-1i0h</link>
      <guid>https://forem.com/macunha/terraform-design-best-practices-1i0h</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Intro

&lt;ol&gt;
&lt;li&gt; “Bad Idea” capitalized!
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt; Design

&lt;ol&gt;
&lt;li&gt; Shallow “tree” of shared resources
&lt;/li&gt;
&lt;li&gt; Product areas (a.k.a. Business capabilities) structure and ownership
&lt;/li&gt;
&lt;li&gt; Shared resources, organized around technologies
&lt;/li&gt;
&lt;li&gt; Files inside the composition?
&lt;/li&gt;
&lt;li&gt; What about Terraform modules?
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="org8d40ccc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;As someone who believes in empowering people and distributing power in order to achieve higher outcomes I always felt that the best existing best-practices proposals don’t touch some key aspects (IMHO) on code evolution and business structures.&lt;/p&gt;

&lt;p&gt;Therefore, this design document shall compose on the previous including some self-service Ops and micro-services spice to the mix.&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://www.terraform-best-practices.com"&gt;Terraform best practices&lt;/a&gt; great insights on how to write code inside a module is provided, e.g. &lt;a href="https://www.terraform-best-practices.com/naming"&gt;naming conventions&lt;/a&gt;, &lt;a href="https://www.terraform-best-practices.com/code-structure#getting-started-with-structuring-of-terraform-configurations"&gt;Terraform file naming&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can’t leave Terragrunt epic blog post unmentioned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://blog.gruntwork.io/5-lessons-learned-from-writing-over-300-000-lines-of-infrastructure-code-36ba7fadeac1"&gt;5 Lessons Learned From Writing Over 300,000 Lines of Infrastructure Code&lt;/a&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As well as the &lt;a href="https://terragrunt.gruntwork.io/docs/getting-started/quick-start/#promote-immutable-versioned-terraform-modules-across-environments"&gt;Terragrunt documentation pointing&lt;/a&gt; “one of the most important lessons” is that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;large modules should be considered harmful. That is, it is a Bad Idea to define all of your environments (dev, stage, prod, etc), or even a large amount of infrastructure (servers, databases, load balancers, DNS, etc), in a single Terraform module. Large modules are slow, insecure, hard to update, hard to code review, hard to test, and brittle (i.e., you have all your eggs in one basket).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a id="orgecbf663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  “Bad Idea” capitalized!
&lt;/h3&gt;

&lt;p&gt;Which is totally true, as this “Bad Idea” usually coming from a lack of care towards Terraform code design tend to be harmful in the long run, with a tendency towards making the implementation a &lt;a href="https://en.wikipedia.org/wiki/Big_ball_of_mud"&gt;big ball of mud&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Big Ball of Mud is a haphazardly structured, sprawling, sloppy, duct-tape-and-baling-wire, spaghetti-code jungle. These systems show unmistakable signs of unregulated growth, and repeated, expedient repair. Information is shared promiscuously among distant elements of the system, often to the point where nearly all the important information becomes global or duplicated.&lt;/p&gt;

&lt;p&gt;The overall structure of the system may never have been well defined.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oftentimes, Terraform code implementation fluctuate towards mono-repositories (a.k.a. monorepos) containing all the specification in a single place. In order to tame the chaos, the Terraform state needs to be at least sub-divided into logical sections.&lt;/p&gt;

&lt;p&gt;&lt;a id="org4623263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;p&gt;&lt;a id="orged346e3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Shallow “tree” of shared resources
&lt;/h3&gt;

&lt;p&gt;Following the &lt;a href="https://www.terraform-best-practices.com/code-structure#common-recommendations-for-structuring-code"&gt;recommendations for structuring code&lt;/a&gt; one of the proposals is to keep a shallow “tree” of resources and modules. This tree produces a small and clear distribution of Terraform code.&lt;/p&gt;

&lt;p&gt;Why a shallow “tree” of resources? It helps achieving a short amount of resources and modules that result in a small &lt;a href="https://www.terraform.io/docs/language/state/remote.html"&gt;remote state&lt;/a&gt; file. With a small remote state we speed-up the development process and reduce waste (&lt;em&gt;Muda&lt;/em&gt; in the Toyota 3M model), as the shallow tree enables faster executions of Terraform (less data to sync and compare).&lt;/p&gt;

&lt;p&gt;The granularity level will be defined for each specific case (no silver bullet) balancing the smallest and most feasible composition possible.&lt;/p&gt;

&lt;p&gt;&lt;a id="org8255d73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Product areas (a.k.a. Business capabilities) structure and ownership
&lt;/h3&gt;

&lt;p&gt;Ideally, the &lt;a href="https://www.terraform-best-practices.com/key-concepts#composition"&gt;composition level&lt;/a&gt; would be organized around Product Areas (either squads/crews or guilds) with a fallback to shared technologies (e.g. vpc, databases). Therefore, Terraform compositions are designed around what Martin Fowler &lt;a href="https://youtu.be/wgdBVIX9ifA?t=388"&gt;calls “Business capabilities”&lt;/a&gt; in micro-services terminology, ideally the Terraform composition will follow the organizational structure so that each team “owns” (in both senses: ownership and freedom) its own state.&lt;/p&gt;

&lt;p&gt;The main goal here is to structure the Terraform code as a reflection of the organization so that is fosters self-service Ops. If the Infrastructure as Code is mature enough to the point of having well-described Terraform modules, everyone should be empowered to define these modules by setting the parameters according to their needs, without centralizing power on a Operations team.&lt;/p&gt;

&lt;p&gt;The resource composition must gravitate towards the following (ordered by priority from higher to lower):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Product Areas (ownership) directory structure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; squad/crew OR guild;&lt;/li&gt;
&lt;li&gt; product.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shared resources, around technologies.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Looking on the structure from bottom-up it starts from the product and then attributes the product to a crew through the directory tree.&lt;/p&gt;

&lt;p&gt;e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Squad or Crew
red-team
└── payment     # Product (i.e. micro-service) name
    └── main.tf # Any resource used by the payment product

# Guild (organized around technology)
back-end
└─ monolith    # Shared application in terms of ownership
   └── main.tf # Cloud resources used by the monolith
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the example above, we can’t ignore that &lt;code&gt;monolith&lt;/code&gt; is a product with shared ownership among back-end developers and therefore it is organized to follow the business structure.&lt;/p&gt;

&lt;p&gt;The structure is inspired &lt;a href="https://terragrunt.gruntwork.io/docs/getting-started/quick-start/#promote-immutable-versioned-terraform-modules-across-environments"&gt;on Terragrunt’s best-practices&lt;/a&gt; to some extend. However, it distinct from Terragrunt proposal in the way resources are divided, rather than organizing resources exclusively around technologies.&lt;/p&gt;

&lt;p&gt;&lt;a id="org3b25b57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Shared resources, organized around technologies
&lt;/h3&gt;

&lt;p&gt;Oftentimes in organizations we will face shared resources among products, there is no way around reality. e.g. a shared VPC or SQL database.&lt;/p&gt;

&lt;p&gt;However, these situations should be the exception and not the norm. Dealt similar to the organization of Terraform compositions around guilds/technologies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;platform # as in Platform Engineering
└── vpc
    └── main.tf

back-end
└── database
    └── main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="org94eac51"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Files inside the composition?
&lt;/h3&gt;

&lt;p&gt;Ideally the files in the sub-directory (which specify the composition) are going to partially &lt;a href="https://www.terraform-best-practices.com/code-structure#getting-started-with-structuring-of-terraform-configurations"&gt;follow this spec&lt;/a&gt; and include &lt;code&gt;data.tf&lt;/code&gt;, &lt;code&gt;terraform.tf&lt;/code&gt; and &lt;code&gt;providers.tf&lt;/code&gt; on top of that.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;main.tf:&lt;/strong&gt; contains locals, module and resource definitions;&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;variables.tf:&lt;/strong&gt; contains declarations of variables (i.e. inputs/parameters)
used in main.tf;&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;data.tf:&lt;/strong&gt; contains data-resources for input data used in main.tf;&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;outputs.tf:&lt;/strong&gt; contains outputs from the resources created in main.tf;&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;providers.tf:&lt;/strong&gt; contains provider and provider’s versions definitions;&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;terraform.tf:&lt;/strong&gt; contains the terraform back-end (e.g. remote state)
definition;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="org8b76cd9"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What about Terraform modules?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.terraform.io/docs/language/modules/index.html"&gt;Terraform modules&lt;/a&gt; are containers for multiple resources that are used together to achieve a shared goal. Modules can be used to create lightweight abstractions, facilitating reusability and distribution of Terraform code.&lt;/p&gt;

&lt;p&gt;Therefore, we assume that the following are anti-patterns that make Terraform modules’ reusability difficult:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Configuration of Terraform Providers inside a module;&lt;/li&gt;
&lt;li&gt;  Implementation of Business logic and/or hard-coded parameters in a module;&lt;/li&gt;
&lt;li&gt;  Default values are specified in optional variables instead of hard-coding;&lt;/li&gt;
&lt;li&gt;  Modules should be self-contained and provide a clear contract. Dependencies (pre-existing resources) must be specified through required variables.&lt;/li&gt;
&lt;li&gt;  Modules must serve to a singular purpose. Multiple purpose must be achieved through composability of modules and not by “monolithic” modules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modules are abstractions that should be used to reduce the amount of code duplication, implementing the &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY (don’t repeat yourself) principle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On top of that, modules are an important factor to reduce the parity among environments, which helps to better address the &lt;a href="https://12factor.net/"&gt;Twelve-Factor App model&lt;/a&gt; in regards to &lt;a href="https://12factor.net/dev-prod-parity"&gt;Factor X (ten)&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>infrastructureascode</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
