<?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: Ekin Öcalan</title>
    <description>The latest articles on Forem by Ekin Öcalan (@gzg).</description>
    <link>https://forem.com/gzg</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%2F195304%2F35c862b4-4993-4a43-84e9-83a72d083d57.jpeg</url>
      <title>Forem: Ekin Öcalan</title>
      <link>https://forem.com/gzg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gzg"/>
    <language>en</language>
    <item>
      <title>WILw Reading Software Architecture: Cohesion</title>
      <dc:creator>Ekin Öcalan</dc:creator>
      <pubDate>Sun, 31 Jan 2021 21:32:49 +0000</pubDate>
      <link>https://forem.com/gzg/wilw-reading-software-architecture-cohesion-50f2</link>
      <guid>https://forem.com/gzg/wilw-reading-software-architecture-cohesion-50f2</guid>
      <description>&lt;p&gt;In a software module, cohesion defines how well the individual parts are related to each other. When those parts are highly connected, then we can say that module is highly cohesive. If they're not operating or depending on each other, then the module has low cohesion. It is also significantly linked to coupling, as can be seen from a quote by &lt;em&gt;Larry Constantine&lt;/em&gt; who developed those concepts:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Attempting to divide a cohesive module would only result in increased coupling and decreased readability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cohesion types from worst to best
&lt;/h2&gt;

&lt;p&gt;When we're talking about cohesion, we generally prefer high cohesion (and low coupling). However, it's also true that not all types of cohesion are benefiting the architecture and the codebase. It's a good practice to investigate how to achieve better cohesion and high cohesion — although it depends on a case-by-case basis.&lt;/p&gt;

&lt;p&gt;To understand cohesion, we can benefit from Object-Oriented Programming and borrow the Class concept as our module example.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coincidental cohesion — the worst
&lt;/h3&gt;

&lt;p&gt;Functions in a class are not related at all. They've just been grouped arbitrarily like in a Utility class.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logical cohesion
&lt;/h3&gt;

&lt;p&gt;Class functions are logically related, although operationally different. Think of a class for sending a notification, for example. One function handles push notifications, the next one sends emails, and the third sends text messages. They're logically doing the same thing (sending notifications), but what they do is entirely different.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporal cohesion
&lt;/h3&gt;

&lt;p&gt;Functions in a class are related based on when they're run. Whenever an exception occurs in one of the functions, another function is called to do the cleanup. They're doing two different things, so they're only temporally cohesive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Procedural cohesion
&lt;/h3&gt;

&lt;p&gt;If you can properly run a specific function only after a different function is run, then those two functions are procedurally cohesive. Think of a function that is checking user permissions. Only if the user is authorized, the target function is run. Then these two functions have procedural cohesion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Communicational cohesion
&lt;/h3&gt;

&lt;p&gt;When two functions are operating on the same set of data, they're communicationally cohesive. Let's say we have a class that is handling the payments. One of the functions writes the successful payment information to the database. Then the other function reads that data to start the shipping process. In this case, these two functions have communicational cohesion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sequential cohesion
&lt;/h3&gt;

&lt;p&gt;If one function's output is another one's input, then those two functions are sequentially cohesive. Think of a function that is creating a user profile. It accepts certain user information as well as a username. Instead of asking the user for a username, another function creates a slug from the user's first and last names. In this case, these two functions have sequential cohesion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Functional cohesion
&lt;/h3&gt;

&lt;p&gt;When functions of a class all contribute to a single task, then they are functionally cohesive. Let's say we have a class implementing the builder pattern. The class is named &lt;code&gt;CarBuilder&lt;/code&gt;, and all of its functions are used to build a car. The class has functional cohesion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Atomic cohesion — the best
&lt;/h3&gt;

&lt;p&gt;Although functional cohesion can also be defined as the perfect cohesion, atomic cohesion moves the needle one more step. When the class achieves functional cohesion and doesn't have anything besides the bare essentials, it has atomic cohesion.&lt;/p&gt;




&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@bobajaglicic?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Boba Jaglicic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[1] WILw: What I Learned while&lt;br&gt;
[2] &lt;a href="https://en.wikipedia.org/wiki/Cohesion_(computer_science)#Types_of_cohesion"&gt;Types of Cohesion&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
    </item>
    <item>
      <title>What I've Learned Learning Terraform: Part 8</title>
      <dc:creator>Ekin Öcalan</dc:creator>
      <pubDate>Mon, 30 Nov 2020 13:18:13 +0000</pubDate>
      <link>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-8-1328</link>
      <guid>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-8-1328</guid>
      <description>&lt;h3&gt;
  
  
  Terraform Series
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-1-4en4"&gt;Part 1: Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2: Creating the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32"&gt;Part 3: Provisioning the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4"&gt;Part 4: Managing Terraform State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5: Cleaner Code with Terraform Modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0"&gt;Part 6: Loops with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7"&gt;Part 7: Conditionals with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 8: Testing Terraform Code&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;We conclude the series by putting a cherry on top: Testing Terraform code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manual Testing vs. Automated Testing
&lt;/h2&gt;

&lt;p&gt;Testing is a crucial part of CI/CD cycles. Since the DevOps movement is fundamentally for delivering software, what could be more important than testing infrastructure-as-code?&lt;/p&gt;

&lt;p&gt;Here we are going to mention two strategies. Manual testing will help us test our infrastructure code by running it on the cloud infrastructure. Automated testing will allow us to automate these manual tests by using a Go-based library: Terratest.&lt;/p&gt;

&lt;p&gt;One thing to keep in mind is that there is no local environment for testing Terraform code since it's mostly dependent on third party services like AWS, GCP, DigitalOcean, etc. That's why there is always be resource creation &amp;amp; deletion in the cloud.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual Testing
&lt;/h3&gt;

&lt;p&gt;The following statement might seem like a no-brainer, but we've already done many manual testing throughout this series. Running &lt;code&gt;terraform plan&lt;/code&gt; and &lt;code&gt;terraform apply&lt;/code&gt; consecutively were merely manual tests. We write the code, and then we apply it to the cloud. If everything goes well and the changes are made, our manual testing was a success.&lt;/p&gt;

&lt;p&gt;There is a rule of thumb to make our codes more testable, though. Take a look at this resource creation example from &lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2&lt;/a&gt; below. Its purpose is to declare a droplet (server):&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-20-04-x64"&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;"terraform-sandbox"&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;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"s-1vcpu-1gb"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The declarative code we see above is very much in a frozen state. No matter how you want to test it, it's going to create a single CPU machine with 1 GB of RAM running Ubuntu 20.04 in Amsterdam's AMS3 region. However, it would be better if we could test this piece of code with different inputs.&lt;/p&gt;

&lt;p&gt;For that, we could apply our knowledge from &lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5&lt;/a&gt; and make them variables like below. Thus, we'll be able to test our code with different inputs manually.&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&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;droplet_web_image&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;droplet_web_name&lt;/span&gt;
  &lt;span class="nx"&gt;region&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;droplet_web_region&lt;/span&gt;
  &lt;span class="nx"&gt;size&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;droplet_web_size&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"var.droplet_web_image"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Web droplet's image"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"var.droplet_web_name"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Web droplet's name"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"var.droplet_web_region"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Web droplet's region"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"var.droplet_web_size"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Web droplet's size"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that it's a better idea to encapsulate your values in a different file, as we talked about in &lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5&lt;/a&gt;. The example above is only a simplified version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated Testing
&lt;/h3&gt;

&lt;p&gt;Manual testing is a useful and important part of testing infrastructure-as-code (IaC). However, a test should be run as frequently as possible. That's why an automated test is much more beneficial in the long run.&lt;/p&gt;

&lt;p&gt;Automated testing of the Terraform code is nothing but a collection of examples of manual testing. There is an excellent open-source tool called &lt;a href="https://github.com/gruntwork-io/terratest"&gt;Terratest&lt;/a&gt; written in Go, which helps you do exactly that. You write examples for your Terraform code, and then you use Terratest to initialize and apply your IaC. You can learn more about how to start using the tool Terratest via their documentation, but here, I'd like to show you how an example test looks like.&lt;/p&gt;

&lt;p&gt;The example below is not directly related to DigitalOcean, because &lt;a href="https://github.com/gruntwork-io/terratest/issues/143"&gt;Terratest support for DigitalOcean&lt;/a&gt; is still yet to come at the time of this writing. However, you can always use Terratest extensively for generic Terraform code as well as other parts by making use of outputs.&lt;/p&gt;

&lt;p&gt;Very briefly from official Terratest docs, let's say we have this output resource in the &lt;code&gt;main.tf&lt;/code&gt; file under the &lt;code&gt;examples&lt;/code&gt; directory:&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;output&lt;/span&gt; &lt;span class="s2"&gt;"hello_world"&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;"Hello World!"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, a simple automated test written with Terratest looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"testing"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/gruntwork-io/terratest/modules/terraform"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/stretchr/testify/assert"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestTerraformHelloWorldExample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// retryable errors in terraform testing.&lt;/span&gt;
    &lt;span class="n"&gt;terraformOptions&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithDefaultRetryableErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TerraformDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"../examples"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;terraformOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InitAndApply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;terraformOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;terraformOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hello_world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&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;Terratest follows the same testing guidelines as Go. Hence, it's a good idea to save your test file as &lt;code&gt;*_test.go&lt;/code&gt; and name your test functions with &lt;code&gt;Test&lt;/code&gt; prefixes like in our example above: &lt;code&gt;TestTerraformHelloWorldExample&lt;/code&gt;. Then you can run &lt;code&gt;go test&lt;/code&gt; to start your test suite.&lt;/p&gt;

&lt;p&gt;Let's go through the test code now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;terraformOptions&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithDefaultRetryableErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TerraformDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"../examples"&lt;/span&gt;&lt;span class="p"&gt;,})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since Terraform is all about dealing with third-party providers, we should also consider network errors. If a test fails on one occasion due to a network error, it doesn't mean it will fail the second time. That's why retrying tests automatically helps a lot. Terratest makes it easy to do that with &lt;code&gt;WithDefaultRetryableErrors&lt;/code&gt;. We enable the retrying and also define the directory for our Terraform code above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;terraformOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While testing the Terraform code, we create real resources on the cloud. When the test is done, we should destroy them to avoid unnecessary costs and future test dependencies. Go's &lt;code&gt;defer&lt;/code&gt; keyword here helps us to run this destroy keyword at the end. By starting our code with the destroy directive, we make sure Go will destroy any resources created; because even if we face errors during the test, Go will tear it down since it already has the deferred directive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InitAndApply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;terraformOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, this directive initializes the resources defined in the Terraform code on a temporary directory and then applies it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;terraformOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hello_world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last part of our test code is the assertion of the expected and actual values. Our expected output resource is "Hello World!" while we get the actual value directly from the Terraform output by using &lt;code&gt;terraform.Output&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/s/photos/test?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Chris Liverani&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7"&gt;Part 7&lt;/a&gt;...................................................................................................................&lt;/p&gt;

</description>
      <category>testing</category>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What I've Learned Learning Terraform: Part 7</title>
      <dc:creator>Ekin Öcalan</dc:creator>
      <pubDate>Tue, 08 Sep 2020 14:28:46 +0000</pubDate>
      <link>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7</link>
      <guid>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7</guid>
      <description>&lt;h3&gt;
  
  
  Terraform Series
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-1-4en4"&gt;Part 1: Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2: Creating the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32"&gt;Part 3: Provisioning the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4"&gt;Part 4: Managing Terraform State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5: Cleaner Code with Terraform Modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0"&gt;Part 6: Loops with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 7: Conditionals with Terraform&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328"&gt;Part 8: Testing Terraform Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;We continue to learn more about the basic Terraform features.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conditionals
&lt;/h1&gt;

&lt;p&gt;We've already used some sort of conditionals in the previous parts of this series. However, it makes sense to categorize the usage of conditionals in Terraform. Just like with the loops, we use a different approach to use conditionals since Terraform is a declarative language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional expressions
&lt;/h2&gt;

&lt;p&gt;Although we don't have a procedural way of implementing if-else statements, Terraform supports the ternary syntax. Let's say you have two environments: Production and staging. You'd like to have a production environment with more computing power, but you also want to cut costs on staging and have less configuration there. How do you declare your condition?&lt;/p&gt;

&lt;p&gt;Let's say you create a variable to determine whether the environment is production or not:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"is_production"&lt;/span&gt; &lt;span class="p"&gt;{&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;"If the environment is production or not"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&gt;
  &lt;span class="nx"&gt;default&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="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"is_staging"&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="err"&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;is_production&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above is only a demonstration of a pair of helpers. One of them is a variable, and the other one is an output. To keep the example simple, we are not going to populate the variable programmatically, but make it &lt;code&gt;is_production=false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's say you are going to create one server for each environment. The production server will have a more powerful configuration.&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"server"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-20-04-x64"&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;"example-droplet"&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;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;size&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;is_production&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"s-8vcpu-32gb"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"s-1vcpu-1gb"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how we used the ternary syntax to declare the size of the droplet. Now if you run &lt;code&gt;terraform plan&lt;/code&gt;, you'll see that Terraform plans to create a droplet with the size of &lt;code&gt;s-1vcpu-1gb&lt;/code&gt; because, by default, it's not the production environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terraform will perform the following actions:

  # digitalocean_droplet.server will be created
  + resource "digitalocean_droplet" "server" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "example-droplet"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "ams3"
      + resize_disk          = true
      + size                 = "s-1vcpu-1gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating resources conditionally
&lt;/h2&gt;

&lt;p&gt;Resources are the backbone of IaC. We declare resources, and Terraform finds a way to create the defined resources. It's easy to make a resource when you know that it's necessary. But what if you don't know if you're going to need that resource?&lt;/p&gt;

&lt;p&gt;Moving on from the previous example of production and staging environments, let's move one step up and create a resource conditionally. Add this resource declaration to the file:&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;"digitalocean_domain"&lt;/span&gt; &lt;span class="s2"&gt;"domain"&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;is_production&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;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;"example-domain.com"&lt;/span&gt;
  &lt;span class="nx"&gt;ip_address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;digitalocean_droplet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ipv4_address&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Domain declaration requires name and the IP address attributes. &lt;code&gt;count&lt;/code&gt; parameter is the one that allows us to create this resource conditionally. I should mention that this is a hack to simulate a conditional statement on a resource-level. If the environment is production, Terraform will create one domain with this configuration. If it's staging, then it's not going to create at all.&lt;/p&gt;

&lt;p&gt;If you run &lt;code&gt;terraform plan&lt;/code&gt;, you'll see that nothing changes in the plan because our environment is staging by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terraform will perform the following actions:

  # digitalocean_droplet.server will be created
  + resource "digitalocean_droplet" "server" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "example-droplet"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "ams3"
      + resize_disk          = true
      + size                 = "s-1vcpu-1gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's go ahead and change the default value for &lt;code&gt;is_production&lt;/code&gt; variable to &lt;code&gt;true&lt;/code&gt;:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"is_production"&lt;/span&gt; &lt;span class="p"&gt;{&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;"If the environment is production or not"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&gt;
  &lt;span class="nx"&gt;default&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run the &lt;code&gt;terraform plan&lt;/code&gt; command now, you'll see two additional changes on the plan. One for the variable change, and the other for the domain resource creation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terraform will perform the following actions:

  # digitalocean_domain.domain[0] will be created
  + resource "digitalocean_domain" "domain" {
      + id         = (known after apply)
      + ip_address = (known after apply)
      + name       = "example-domain.com"
      + urn        = (known after apply)
    }

  # digitalocean_droplet.server will be created
  + resource "digitalocean_droplet" "server" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "example-droplet"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "ams3"
      + resize_disk          = true
      + size                 = "s-8vcpu-32gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  ~ is_staging = true -&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a drawback with this hack, though. As you can see from the plan, the domain resource will be created as part of a list: &lt;code&gt;digitalocean_domain.domain[0]&lt;/code&gt;. That's because we use the &lt;code&gt;count&lt;/code&gt; parameter within the resource.&lt;/p&gt;

&lt;p&gt;By the way, starting from Terraform 0.13, you can do this hack with modules as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting values conditionally (Interpolation)
&lt;/h2&gt;

&lt;p&gt;There is one more feature for conditionals on Terraform, which is the interpolation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In computer programming, string interpolation (or variable interpolation, variable substitution, or variable expansion) is the process of evaluating a string literal containing one or more placeholders, yielding a result in which the placeholders are replaced with their corresponding values. &lt;a href="https://en.wikipedia.org/wiki/String_interpolation"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Interpolation in Terraform is perhaps the closest thing to a conditional in other programming languages due to its direct if-else statement usage.&lt;/p&gt;

&lt;p&gt;Let's demonstrate this by setting the droplet name based on the environment:&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"server"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-20-04-x64"&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;"%{if var.is_production}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="s2"&gt;"production-droplet"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%{else}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="s2"&gt;"staging-droplet"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%{endif}"&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;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;size&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;is_production&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"s-8vcpu-32gb"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"s-1vcpu-1gb"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how we used if-else statements inside the name attribute. You can even use variables here to define your interpolation with placeholders. Running the &lt;code&gt;terraform plan&lt;/code&gt; command will give us the plan below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # digitalocean_droplet.server will be created
  + resource "digitalocean_droplet" "server" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "production-droplet"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "ams3"
      + resize_disk          = true
      + size                 = "s-8vcpu-32gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see that the name is &lt;code&gt;production-droplet&lt;/code&gt; since we set our environment to production in the last step. Now you have a conditional value setting.&lt;/p&gt;

&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/s/photos/yin-yang?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Mario Dobelmann&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0"&gt;Part 6&lt;/a&gt;.........................................................................................................&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328"&gt;Part 8&lt;/a&gt;&lt;/p&gt;

</description>
      <category>iac</category>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What I've Learned Learning Terraform: Part 6</title>
      <dc:creator>Ekin Öcalan</dc:creator>
      <pubDate>Thu, 27 Aug 2020 10:36:44 +0000</pubDate>
      <link>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-6-id0</link>
      <guid>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-6-id0</guid>
      <description>&lt;h3&gt;
  
  
  Terraform Series
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-1-4en4"&gt;Part 1: Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2: Creating the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32"&gt;Part 3: Provisioning the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4"&gt;Part 4: Managing Terraform State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5: Cleaner Code with Terraform Modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 6: Loops with Terraform&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7"&gt;Part 7: Conditionals with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328"&gt;Part 8: Testing Terraform Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;We're going to continue on the same path as we left off. I'm going to do a deep dive into Terraform with the help of some DigitalOcean resources. Instead of focusing on resources, though, I'll explain what we can do with those resources using some of the basic Terraform features.&lt;/p&gt;

&lt;h1&gt;
  
  
  Loops
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;The Terraform language is declarative, describing an intended goal rather than the steps to reach that goal. &lt;a href="https://www.terraform.io/docs/configuration/index.html"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since we need to declare the end goal in Terraform, it shouldn't come as a surprise when I say that Terraform does not have loops. But there are three different features that we can emulate loops.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;count&lt;/code&gt; parameter to create multiple resources
&lt;/h2&gt;

&lt;p&gt;Let's imagine you want to create multiple instances from the same resource type. How do you achieve that? By copying the resource declaration. What if you need a lot of them? It's inefficient to repeat the same logic on a single resource over and over and over.&lt;/p&gt;

&lt;p&gt;Terraform's &lt;code&gt;count&lt;/code&gt; parameter is the oldest way of encapsulating repeating the resources. It's got supercharged by Terraform's 0.13 version and is now also available in module blocks as well.&lt;/p&gt;

&lt;p&gt;Let's imagine you want to create tags on DigitalOcean, later to be used on droplets. We'd like to tag droplets by the amount of RAM it's provided with.&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;"digitalocean_tag"&lt;/span&gt; &lt;span class="s2"&gt;"ram_8"&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;"ram_8"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can use this resource for tagging droplets with 8 GB of RAM. But RAM amounts are variable. So we need to create a variety of tags starting from 1 GB and going up until maybe 64 GB. Let's use the &lt;code&gt;count&lt;/code&gt; parameter to make all of them.&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;"digitalocean_tag"&lt;/span&gt; &lt;span class="s2"&gt;"ram"&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="mi"&gt;64&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;"ram_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run &lt;code&gt;terraform plan&lt;/code&gt;, you'll see a list of tags that Terraform will create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terraform will perform the following actions:

  # digitalocean_tag.ram[0] will be created
  + resource "digitalocean_tag" "ram" {
      + databases_count        = (known after apply)
      + droplets_count         = (known after apply)
      + id                     = (known after apply)
      + images_count           = (known after apply)
      + name                   = "ram_1"
      + total_resource_count   = (known after apply)
      + volume_snapshots_count = (known after apply)
      + volumes_count          = (known after apply)
    }

  # digitalocean_tag.ram[1] will be created
  + resource "digitalocean_tag" "ram" {
      + databases_count        = (known after apply)
      + droplets_count         = (known after apply)
      + id                     = (known after apply)
      + images_count           = (known after apply)
      + name                   = "ram_2"
      + total_resource_count   = (known after apply)
      + volume_snapshots_count = (known after apply)
      + volumes_count          = (known after apply)
    }

  # digitalocean_tag.ram[2] will be created
  + resource "digitalocean_tag" "ram" {
      + databases_count        = (known after apply)
      + droplets_count         = (known after apply)
      + id                     = (known after apply)
      + images_count           = (known after apply)
      + name                   = "ram_3"
      + total_resource_count   = (known after apply)
      + volume_snapshots_count = (known after apply)
      + volumes_count          = (known after apply)
    }

...
...
...

  # digitalocean_tag.ram[63] will be created
  + resource "digitalocean_tag" "ram" {
      + databases_count        = (known after apply)
      + droplets_count         = (known after apply)
      + id                     = (known after apply)
      + images_count           = (known after apply)
      + name                   = "ram_64"
      + total_resource_count   = (known after apply)
      + volume_snapshots_count = (known after apply)
      + volumes_count          = (known after apply)
    }

Plan: 64 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;*_count&lt;/code&gt; parameters inside the tag resources are not related to our own &lt;code&gt;count&lt;/code&gt; parameter to let us create multiple resources. And as you can see from our Terraform code above, we'll be able to access the index of the &lt;code&gt;count&lt;/code&gt; parameter and convert it to a 1-based index before naming our tag.&lt;/p&gt;

&lt;p&gt;One other thing to note here is the internal name of the resource. When we did not use the &lt;code&gt;count&lt;/code&gt; parameter, our tag was named &lt;code&gt;ram_8&lt;/code&gt;, so we could access it with &lt;code&gt;digitalocean_tag.ram_8&lt;/code&gt;. However, now that we have a list of &lt;code&gt;digitalocean_tag&lt;/code&gt; resources, we'll access our resources like they're inside an array: &lt;code&gt;digitalocean_tag.ram[0]&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;for_each&lt;/code&gt; parameter to iterate over a data structure
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;count&lt;/code&gt; parameter was helpful to create numeric resources. But what if you need to iterate over a data structure like a list, a set, or a map? Let's continue from our earlier example, but this time create tags for operating systems.&lt;/p&gt;

&lt;p&gt;Let's first create a variable to list our available operating systems:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"operating_systems"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Operating systems available on droplets"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&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="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"centos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"debian"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"fedora"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"freebsd"&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;Notice how we defined our type as the list of strings. We'll come back to type usages at a later point. Now let's create our tags by using the &lt;code&gt;for_each&lt;/code&gt; parameter:&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;"digitalocean_tag"&lt;/span&gt; &lt;span class="s2"&gt;"os"&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="nx"&gt;toset&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;operating_systems&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason we are using &lt;code&gt;toset&lt;/code&gt; on the &lt;code&gt;operating_systems&lt;/code&gt; variable is that &lt;code&gt;for_each&lt;/code&gt; supports a set or a map of strings. Otherwise, it would complain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The given "for_each" argument value is unsuitable: the "for_each" argument must be a map or set of strings, and you have provided a value of type list of string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, note how we used &lt;code&gt;each.value&lt;/code&gt; to get the value from each iteration. Now when we run &lt;code&gt;terraform plan&lt;/code&gt;, we'll see our tags in the execution plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terraform will perform the following actions:

  # digitalocean_tag.os["centos"] will be created
  + resource "digitalocean_tag" "os" {
      + databases_count        = (known after apply)
      + droplets_count         = (known after apply)
      + id                     = (known after apply)
      + images_count           = (known after apply)
      + name                   = "centos"
      + total_resource_count   = (known after apply)
      + volume_snapshots_count = (known after apply)
      + volumes_count          = (known after apply)
    }

  # digitalocean_tag.os["debian"] will be created
  + resource "digitalocean_tag" "os" {
      + databases_count        = (known after apply)
      + droplets_count         = (known after apply)
      + id                     = (known after apply)
      + images_count           = (known after apply)
      + name                   = "debian"
      + total_resource_count   = (known after apply)
      + volume_snapshots_count = (known after apply)
      + volumes_count          = (known after apply)
    }

  # digitalocean_tag.os["fedora"] will be created
  + resource "digitalocean_tag" "os" {
      + databases_count        = (known after apply)
      + droplets_count         = (known after apply)
      + id                     = (known after apply)
      + images_count           = (known after apply)
      + name                   = "fedora"
      + total_resource_count   = (known after apply)
      + volume_snapshots_count = (known after apply)
      + volumes_count          = (known after apply)
    }

  # digitalocean_tag.os["freebsd"] will be created
  + resource "digitalocean_tag" "os" {
      + databases_count        = (known after apply)
      + droplets_count         = (known after apply)
      + id                     = (known after apply)
      + images_count           = (known after apply)
      + name                   = "freebsd"
      + total_resource_count   = (known after apply)
      + volume_snapshots_count = (known after apply)
      + volumes_count          = (known after apply)
    }

  # digitalocean_tag.os["ubuntu"] will be created
  + resource "digitalocean_tag" "os" {
      + databases_count        = (known after apply)
      + droplets_count         = (known after apply)
      + id                     = (known after apply)
      + images_count           = (known after apply)
      + name                   = "ubuntu"
      + total_resource_count   = (known after apply)
      + volume_snapshots_count = (known after apply)
      + volumes_count          = (known after apply)
    }

Plan: 5 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;for&lt;/code&gt; expression to map &amp;amp; filter
&lt;/h2&gt;

&lt;p&gt;Another good loop-like behavior Terraform provides us with is the &lt;code&gt;for&lt;/code&gt; expression. It resembles Python's comprehensions.&lt;/p&gt;

&lt;p&gt;Let's define a variable for RAM requirements of operating systems:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"os_requirements"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Which operating system requires how much RAM"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ubuntu&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="nx"&gt;centos&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;debian&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="nx"&gt;fedora&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;freebsd&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numbers are entirely arbitrary, of course. :-) But note how we defined the type and assigned the RAM amounts to different operating systems here. Each of the OS described in the variable is the key in the map. RAM amounts are the values in the map.&lt;/p&gt;

&lt;p&gt;Now we are going to filter &amp;amp; map within the same for expression.&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;output&lt;/span&gt; &lt;span class="s2"&gt;"usable_operating_systems"&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ram&lt;/span&gt; &lt;span class="nx"&gt;in&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;os_requirements&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;ram&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&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;I always liked this type of one-liner comprehension. Let's see what we have achieved with this one-liner:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we extract both keys (&lt;code&gt;os&lt;/code&gt;) and values (&lt;code&gt;ram&lt;/code&gt;) from the map (&lt;code&gt;var.os_requirements&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Then, we filter all map elements by their RAM amount. It should be smaller than or equal to 2.&lt;/li&gt;
&lt;li&gt;For every filtered operating system, we map its name to uppercase by &lt;code&gt;upper(os)&lt;/code&gt;/&lt;/li&gt;
&lt;li&gt;Finally, we map the whole result into a list with the surrounding brackets &lt;code&gt;[]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's one type of filtering and two types of mapping in a one-liner.&lt;/p&gt;

&lt;p&gt;Now we need to use &lt;code&gt;terraform apply&lt;/code&gt; instead of &lt;code&gt;terraform plan&lt;/code&gt; here because our Terraform code will not create any resources. To see the output, let's run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:

Terraform will perform the following actions:

Plan: 0 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes


Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

usable_operating_systems = [
  "CENTOS",
  "FEDORA",
  "FREEBSD",
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect, now we know which operating systems we can use with the amount of RAM we have. :-)&lt;/p&gt;

&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/s/photos/loop?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Lysander Yuen&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5&lt;/a&gt;..........................................................................................................&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7"&gt;Part 7&lt;/a&gt;&lt;/p&gt;

</description>
      <category>iac</category>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What I've Learned Learning Terraform: Part 5</title>
      <dc:creator>Ekin Öcalan</dc:creator>
      <pubDate>Mon, 24 Aug 2020 11:20:52 +0000</pubDate>
      <link>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-5-j4b</link>
      <guid>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-5-j4b</guid>
      <description>&lt;h3&gt;
  
  
  Terraform Series
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-1-4en4"&gt;Part 1: Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2: Creating the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32"&gt;Part 3: Provisioning the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4"&gt;Part 4: Managing Terraform State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 5: Cleaner Code with Terraform Modules&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0"&gt;Part 6: Loops with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7"&gt;Part 7: Conditionals with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328"&gt;Part 8: Testing Terraform Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Up until now, we focused on the functionality Terraform provided. Creating servers, adjusting network settings, configuring domain names, running bash scripts remotely, creating storage units, and moving Terraform state to shared space.&lt;/p&gt;

&lt;p&gt;In this post, we're going to take a break and see how we can write cleaner, &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;DRY&lt;/a&gt;er, and reusable Terraform code. The main keyword here is "reusable" because this post is mostly about Terraform modules.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terraform Modules
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Modules are the key ingredient to writing reusable, maintainable, and testable Terraform code. [1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Having a module in Terraform easy, because any Terraform code stored under a directory is considered within the same module. For the same reasons, we can also say that modules in Terraform are implicit, and that's why creating a module is a bit of magic.&lt;/p&gt;

&lt;p&gt;Let's take a look at our Terraform files we have created so far:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;provider.tf&lt;/code&gt;: Defines the SSH variables Terraform uses to connect to our droplet. By the way, &lt;code&gt;provider.tf&lt;/code&gt; is a pretty lousy name for this file. :-) We'll refactor it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;domain.tf&lt;/code&gt;: Points our domain to our droplet and defines its CNAME record.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;space.tf&lt;/code&gt;: Declares our storage unit. It's a bucket in AWS and a space in DigitalOcean lingo.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main.tf&lt;/code&gt;: Configures pretty much what's left, including the provider version, remote backend, and our beloved droplet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We created all of these files under the same directory. That's why they all belong to the same Terraform module. Now let's break it down so that we can reuse our code to create multiple droplets that be accessed by different domain names. This way, we can have an infrastructure supporting both production and staging environments. We are going to leave the buckets and remote state files out of this article to make things simple.&lt;/p&gt;

&lt;p&gt;What we are aiming for is to create an infrastructure with both production and staging environments. We are going to create them with reusable Terraform modules. After we convert our codebase, our directory structure will look like as follows:&lt;/p&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%2Fi%2Fjxkw2b981q2dvscb0xkt.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%2Fi%2Fjxkw2b981q2dvscb0xkt.png" alt="Production &amp;amp; Staging infrastructure with Terraform Modules"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me get one thing out of the way first: &lt;code&gt;versions.tf&lt;/code&gt;. I'm using Terraform v0.13 and you need to define your &lt;a href="https://www.terraform.io/docs/configuration/provider-requirements.html" rel="noopener noreferrer"&gt;provider requirements&lt;/a&gt; starting with this version. So all my &lt;code&gt;versions.tf&lt;/code&gt; files are the same:&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;digitalocean&lt;/span&gt; &lt;span class="p"&gt;=&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-providers/digitalocean"&lt;/span&gt;
    &lt;span class="p"&gt;}&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;= 0.13"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Directory structure with modules
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier, each directory behaves as a separate module. I decided to divide my infrastructure into two modules: &lt;code&gt;domain&lt;/code&gt; and &lt;code&gt;server&lt;/code&gt;. Each of my environments will have a different server and a different domain pointing to that server. That's why I created a directory for each module under &lt;code&gt;modules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's start with the &lt;code&gt;domain&lt;/code&gt; module. I copied the code from &lt;code&gt;domain.tf&lt;/code&gt; into &lt;code&gt;modules/domain/main.tf&lt;/code&gt;:&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;"digitalocean_domain"&lt;/span&gt; &lt;span class="s2"&gt;"domain"&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;domain_name&lt;/span&gt;
  &lt;span class="nx"&gt;ip_address&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;server_ipv4&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;"digitalocean_record"&lt;/span&gt; &lt;span class="s2"&gt;"cname_www"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;digitalocean_domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&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;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;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"www"&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;"@"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should notice a change inside the &lt;code&gt;digitalocean_domain&lt;/code&gt; resource: The values of &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;ip_address&lt;/code&gt; are not hard coded anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Module inputs
&lt;/h2&gt;

&lt;p&gt;So I created a &lt;code&gt;vars.tf&lt;/code&gt; file to define my module variables:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"domain_name"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Domain name like yourdomain.com"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"server_ipv4"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Server's IP address where the domain should point to"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I'm able to use these variables as inputs in my &lt;code&gt;domain&lt;/code&gt; module. Whenever we use the &lt;code&gt;domain&lt;/code&gt; module, we will have to provide both variables to create this resource. That's how we'll be able to define a domain with different domain names and separate IP addresses pointing to different servers.&lt;/p&gt;

&lt;p&gt;Now let's move on to the &lt;code&gt;server&lt;/code&gt; module. Again, I copied my code from my old &lt;code&gt;main.tf&lt;/code&gt; file to here:&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"server"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-20-04-x64"&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;server_name&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;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;size&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;server_size&lt;/span&gt;
  &lt;span class="nx"&gt;ssh_keys&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;ssh_fingerprint&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;host&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ipv4_address&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"root"&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
    &lt;span class="nx"&gt;private_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&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;ssh_private_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2m"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"remote-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;inline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"export PATH=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;PATH:/usr/bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# install nginx&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo apt-get update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo apt-get -y install nginx"&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;I created a &lt;code&gt;vars.tf&lt;/code&gt; file for my &lt;code&gt;server&lt;/code&gt; module as well:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"server_name"&lt;/span&gt; &lt;span class="p"&gt;{&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;"The name of the server"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"server_size"&lt;/span&gt; &lt;span class="p"&gt;{&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;"The size of the server"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ssh_fingerprint"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Fingerprint of the SSH key that is allowed to connect to the server"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ssh_private_key"&lt;/span&gt; &lt;span class="p"&gt;{&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;"Private key of the SSH key that is allowed to connect to the server"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we pass the SSH variables, you will see that we are now able to configure the server name and the server size in this module. That's how we'll be able to create a production server with a bigger size while keeping the staging server at a smaller size.&lt;/p&gt;

&lt;p&gt;We now have both the &lt;code&gt;domain&lt;/code&gt; and &lt;code&gt;server&lt;/code&gt; modules to create ourselves in an environment. Let's start with &lt;code&gt;production&lt;/code&gt;. Here is what it looks like to create an environment with our modules at &lt;code&gt;production/main.tf&lt;/code&gt;:&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;"server"&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;"../modules/server"&lt;/span&gt;

  &lt;span class="nx"&gt;server_name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-sandbox"&lt;/span&gt;
  &lt;span class="nx"&gt;server_size&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"s-1vcpu-1gb"&lt;/span&gt;
  &lt;span class="nx"&gt;ssh_fingerprint&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;ssh_fingerprint&lt;/span&gt;
  &lt;span class="nx"&gt;ssh_private_key&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;ssh_private_key&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;"domain"&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;"../modules/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="s2"&gt;"productiondomain.com"&lt;/span&gt;
  &lt;span class="nx"&gt;server_ipv4&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;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server_ipv4&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm going to pass the SSH variables here as we're going to provide them as command-line arguments. As you can see, we declare the name and size for the &lt;code&gt;server&lt;/code&gt; module. Similarly, we give the name for our &lt;code&gt;domain&lt;/code&gt; module. All hardcoded now. However, &lt;code&gt;server_ipv4&lt;/code&gt; argument for the &lt;code&gt;domain&lt;/code&gt; module looks a bit strange, isn't it? :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Module outputs
&lt;/h2&gt;

&lt;p&gt;What you see as &lt;code&gt;module.server.server_ipv4&lt;/code&gt; is the usage of module outputs. We have isolated our &lt;code&gt;domain&lt;/code&gt; and &lt;code&gt;server&lt;/code&gt; modules, but the &lt;code&gt;domain&lt;/code&gt; configuration requires the &lt;code&gt;server&lt;/code&gt;'s IP address. We can access a module's values by defining an output in that module. Here is the content of &lt;code&gt;modules/server/outs.tf&lt;/code&gt;:&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;output&lt;/span&gt; &lt;span class="s2"&gt;"server_ipv4"&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;digitalocean_droplet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ipv4_address&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By defining the &lt;code&gt;server_ipv4&lt;/code&gt; output, we grant the module user access to the &lt;code&gt;digitalocean_droplet&lt;/code&gt; resource's &lt;code&gt;ipv4_address&lt;/code&gt; argument within the &lt;code&gt;server&lt;/code&gt; instance. Similarly, we access the output by following this structure:&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="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MODULE_NAME&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OUTPUT_NAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our case, this becomes:&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="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server_ipv4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, Terraform will create the server first, and then use its IP address to configure our domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Module locals
&lt;/h2&gt;

&lt;p&gt;Apart from inputs and outputs, Terraform provides another data structure to make our codebase DRY. Instead of inputs and outputs, we don't use locals across modules. Their usage is limited to encapsulate local values, much like the constants in programming languages, but only within the same module.&lt;/p&gt;

&lt;p&gt;For example, instead of hardcoding our server image and region, let's encapsulate them within &lt;code&gt;modules/server/vars.tf&lt;/code&gt;:&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="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;server_image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-20-04-x64"&lt;/span&gt;
  &lt;span class="nx"&gt;server_region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ams3"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can go ahead and use them in our &lt;code&gt;modules/server/main.tf&lt;/code&gt; file:&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"server"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server_image&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;server_name&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server_region&lt;/span&gt;
  &lt;span class="nx"&gt;size&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;server_size&lt;/span&gt;
  &lt;span class="nx"&gt;ssh_keys&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;ssh_fingerprint&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  A note on inputs, outputs, and locals
&lt;/h1&gt;

&lt;p&gt;I just wanted to take a small note here and say that all three structures are not unique to modules. As you can imagine, modules are implicit structures. In a theoretical sense, it would be true if we say that we can also use inputs, outputs, and locals outside of the context of the module. Although each of the usages will practically fall under the module usage since each directory in Terraform is a module.&lt;/p&gt;

&lt;p&gt;[1]: Terraform Up &amp;amp; Running: Writing Infrastructure as Code by Yevgeniy Brikman (2nd edition)&lt;/p&gt;

&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/s/photos/seedling?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Andrej Lišakov&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4"&gt;Part 4&lt;/a&gt;.........................................................................................................&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0"&gt;Part 6&lt;/a&gt;&lt;/p&gt;

</description>
      <category>iac</category>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What I've Learned Learning Terraform: Part 4</title>
      <dc:creator>Ekin Öcalan</dc:creator>
      <pubDate>Thu, 20 Aug 2020 10:56:14 +0000</pubDate>
      <link>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-4-np4</link>
      <guid>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-4-np4</guid>
      <description>&lt;h3&gt;
  
  
  Terraform Series
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-1-4en4"&gt;Part 1: Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2: Creating the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32"&gt;Part 3: Provisioning the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 4: Managing Terraform State&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5: Cleaner Code with Terraform Modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0"&gt;Part 6: Loops with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7"&gt;Part 7: Conditionals with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328"&gt;Part 8: Testing Terraform Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;Every time you run Terraform, it records information about what infrastructure it created in a Terraform state file. [1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Managing Terraform State
&lt;/h1&gt;

&lt;p&gt;Terraform state files are the backbone of your infrastructure. Terraform can only operate on the infrastructure it knows, and state files are the only way it can know about them.&lt;/p&gt;

&lt;p&gt;Limiting Terraform by state files is very good in the context of isolation. You may have other resources you created manually in the same provider account. Terraform doesn't know them. Thus, you cannot accidentally change or destroy those resources.&lt;/p&gt;

&lt;p&gt;On the other hand, it means that state files are the source of truth, and thus critical to your infrastructure-as-code. If you lose or corrupt your state file, you'll no longer have the ability to maintain your resources. That's why you need some sort of backup of your state against data loss. Additionally, you need versioning against data corruption.&lt;/p&gt;

&lt;p&gt;A version control system might be the first possible solution that comes to mind. It indeed can be a remedy to both loss and corruption. You should use version control for your Terraform code; however, you should not put your state files to your version control for two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If multiple people are working on the same codebase, your state can get out-of-date because getting the latest changes requires manual pulls from the version control.&lt;/li&gt;
&lt;li&gt;State files contain secrets as plain-text. It's a bad idea to store them unencrypted.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Using remote backend instead of local
&lt;/h1&gt;

&lt;p&gt;Fortunately, Terraform offers two ways to store state files. The first one is storing locally, and that's the default way. The second one is &lt;a href="https://www.terraform.io/docs/backends/types/index.html" rel="noopener noreferrer"&gt;storing state files remotely&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are several advantages to store state files remotely. If you use version control for your Terraform codebase and remote backend for your state files, you no longer depend on your local environment. Even if you lose your machine, you can still work your infrastructure with Terraform.&lt;/p&gt;

&lt;p&gt;It also allows collaboration as well. With some of the remote backends, you can set up a lock mechanism to prevent race conditions.&lt;/p&gt;

&lt;p&gt;Most remote backends support encryption by default. Thus, your secrets are stored encrypted. You can also set access rules, so only your codebase is allowed to access the state programmatically, and thus restricting any 3rd party access.&lt;/p&gt;

&lt;h1&gt;
  
  
  Moving state files to DigitalOcean Spaces
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.digitalocean.com/products/spaces/" rel="noopener noreferrer"&gt;DigitalOcean Spaces&lt;/a&gt; is an object storage product similar to AWS S3. In fact, it's S3-compatible [2]. That's why, even though Terraform's standard remote backends do not list Spaces as a possible backend type, we can use it as an &lt;a href="https://www.terraform.io/docs/backends/types/s3.html" rel="noopener noreferrer"&gt;S3 type&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Space
&lt;/h2&gt;

&lt;p&gt;While it's alright to create a Space manually to store state files and use its configuration, let's create it with Terraform for this article's sake. Create a &lt;code&gt;space.tf&lt;/code&gt; file and populate it with the code below:&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;"digitalocean_spaces_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"tf_state"&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;"terraform-sandbox"&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;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;acl&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt;
  &lt;span class="nx"&gt;versioning&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we create a &lt;a href="https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/spaces_bucket" rel="noopener noreferrer"&gt;&lt;code&gt;digitalocean_spaces_bucket&lt;/code&gt;&lt;/a&gt; resource. In AWS lingo, a bucket is a standalone storage unit. "tf_state" is the internal name of this resource within the codebase. Let's also take a look at the configuration parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: The bucket/space name. It should be unique within the region.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;region&lt;/code&gt;: One of the datacenters listed at &lt;a href="https://www.digitalocean.com/docs/platform/availability-matrix/" rel="noopener noreferrer"&gt;Regional Availability Matrix&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;acl&lt;/code&gt;: Access control list. &lt;code&gt;public&lt;/code&gt; for public file listing, &lt;code&gt;private&lt;/code&gt; for restriction. Since we are going to use this space to store state files, we use &lt;code&gt;private&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;versioning&lt;/code&gt;: Enabling the versioning will allow us to restore the state file(s) to an older but working version in case of data corruption or human error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run &lt;code&gt;terraform init&lt;/code&gt; and &lt;code&gt;terraform apply&lt;/code&gt;. You'll see an execution plan like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # digitalocean_spaces_bucket.tf_state will be created
  + resource "digitalocean_spaces_bucket" "tf_state" {
      + acl                = "private"
      + bucket_domain_name = (known after apply)
      + force_destroy      = false
      + id                 = (known after apply)
      + name               = "terraform-sandbox"
      + region             = "ams3"
      + urn                = (known after apply)

      + versioning {
          + enabled = true
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To apply this plan, you are going to need an access key and a secret key for Spaces. Go to &lt;a href="https://cloud.digitalocean.com/account/api/tokens" rel="noopener noreferrer"&gt;API&lt;/a&gt; on DigitalOcean to create a pair. Then, on command line, you can export these environment variables for Terraform to pick up the credentials for Spaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ export SPACES_ACCESS_KEY_ID=11111111111111111111
$ export SPACES_SECRET_ACCESS_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring Terraform to use remote state on the Space
&lt;/h2&gt;

&lt;p&gt;Now that we have a Space on DigitalOcean to store our state files, let's configure Terraform to use the state file there. While configuring, Terraform will also ask us if we want to transfer our local state to the remote or start from scratch. Since we already have some resources created, we are going to pick the transfer option to avoid multiple resource creation.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;main.tf&lt;/code&gt; and find the &lt;code&gt;terraform&lt;/code&gt; block:&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;digitalocean&lt;/span&gt; &lt;span class="p"&gt;=&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;"digitalocean/digitalocean"&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;"1.22.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;p&gt;What we're doing now is directly related to how Terraform will behave. That's why we are defining our remote backend in this block:&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;digitalocean&lt;/span&gt; &lt;span class="p"&gt;=&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;"digitalocean/digitalocean"&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;"1.22.1"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&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;endpoint&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ams3.digitaloceanspaces.com"&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;"terraform-sandbox"&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;"terraform.tfstate"&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;"ams3"&lt;/span&gt;
    &lt;span class="nx"&gt;access_key&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"11111111111111111111"&lt;/span&gt;
    &lt;span class="nx"&gt;secret_key&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"&lt;/span&gt;
    &lt;span class="nx"&gt;skip_credentials_validation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;skip_metadata_api_check&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;skip_region_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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how we defined our backend as &lt;code&gt;s3&lt;/code&gt;. Our Space on DigitalOcean is S3-compatible, and that's why we are defining our remote backend as it was S3 storage. Let's go over the configuration parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;endpoint&lt;/code&gt;: You can find this at your &lt;a href="https://cloud.digitalocean.com/spaces" rel="noopener noreferrer"&gt;Spaces&lt;/a&gt; page. At the time of this writing, the structure is &lt;code&gt;region.digitaloceanspaces.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bucket&lt;/code&gt;: The name of your Space.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;key&lt;/code&gt;: The full path to the state file on remote backend. This will be your state file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;region&lt;/code&gt;: Just put your Space region. We'll ask Terraform to not use this value as it's going to look at AWS regions here, and not DigitalOcean ones.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;access_key&lt;/code&gt;: Your Spaces access key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secret_key&lt;/code&gt;: Your Spaces secret key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;skip_credentials_validation&lt;/code&gt;: We are not using AWS credentials, that's why we skip.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;skip_metadata_api_check&lt;/code&gt;: We are not using AWS EC2, that's why we skip.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;skip_region_validation&lt;/code&gt;: We are not using AWS, that's why we skip the region validation. Since &lt;code&gt;region&lt;/code&gt; is a required parameter, Terraform will complain about our DigitalOcean region if we don't skip this validation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now when you run &lt;code&gt;terraform init&lt;/code&gt;, you'll see a question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initializing the backend...
Do you want to copy existing state to the new backend? Pre-existing state was found while migrating the previous "local" backend to the newly configured "s3" backend. No existing state was found in the newly configured "s3" backend. Do you want to copy this state to the new "s3" backend? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I mentioned before, we'll say &lt;code&gt;yes&lt;/code&gt;. Then, Terraform will transfer our local state file to Spaces, and configure itself to use the remote state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From now on, Terraform will read state values from remote, and write updated values to the remote.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on the Terraform configuration values
&lt;/h2&gt;

&lt;p&gt;If you noticed, we used plain-text sensitive values (access key and secret key) when configuring our remote backend. That is because Terraform doesn't allow variables inside the Terraform configuration. For the same reason, we could not populate &lt;code&gt;bucket&lt;/code&gt;, &lt;code&gt;key&lt;/code&gt;, and &lt;code&gt;region&lt;/code&gt; parameters from our &lt;code&gt;digitalocean_spaces_bucket.tf_state&lt;/code&gt; resource definition.&lt;/p&gt;

&lt;p&gt;One way of improving this process is to use &lt;a href="https://www.terraform.io/docs/backends/config.html#partial-configuration" rel="noopener noreferrer"&gt;partial configuration&lt;/a&gt;. You can remove &lt;code&gt;access_key&lt;/code&gt; and &lt;code&gt;secret_key&lt;/code&gt; from the configuration, and then supply them as backend configs on the initialization process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform init -backend-config="access_key= 11111111111111111111" -backend-config="secret_key= AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fi%2Fz0wu3sj01m0yvi1coc18.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%2Fi%2Fz0wu3sj01m0yvi1coc18.png" alt="Terraform State on DigitalOcean Spaces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[1]: Terraform Up &amp;amp; Running: Writing Infrastructure as Code by Yevgeniy Brikman (2nd edition)&lt;br&gt;
[2]: S3-Compatibility on &lt;a href="https://www.digitalocean.com/docs/spaces/#features" rel="noopener noreferrer"&gt;Spaces Features&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@lyvjaan?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Lÿv Jaan&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32"&gt;Part 3&lt;/a&gt;.........................................................................................................&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>digitalocean</category>
    </item>
    <item>
      <title>What I've Learned Learning Terraform: Part 3</title>
      <dc:creator>Ekin Öcalan</dc:creator>
      <pubDate>Mon, 17 Aug 2020 10:27:03 +0000</pubDate>
      <link>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-3-2e32</link>
      <guid>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-3-2e32</guid>
      <description>&lt;h3&gt;
  
  
  Terraform Series
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-1-4en4"&gt;Part 1: Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2: Creating the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3: Provisioning the Server&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4"&gt;Part 4: Managing Terraform State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5: Cleaner Code with Terraform Modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0"&gt;Part 6: Loops with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7"&gt;Part 7: Conditionals with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328"&gt;Part 8: Testing Terraform Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Now that we could create a server, it's time to configure it a bit. It's no use to us if it sits right there. So let's install nginx and then have one of our domains point to the server to see if it's working or not.&lt;/p&gt;

&lt;h1&gt;
  
  
  Provisioning the server
&lt;/h1&gt;

&lt;p&gt;It's only to install nginx for now, but it's still a server provision. We are going to use &lt;a href="https://www.terraform.io/docs/provisioners/remote-exec.html"&gt;remote-exec provisioner&lt;/a&gt; to achieve this simple install.&lt;/p&gt;

&lt;p&gt;Within &lt;code&gt;digitalocean_droplet&lt;/code&gt; resource, add the provisioner below:&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-20-04-x64"&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;"terraform-sandbox"&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;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"s-1vcpu-1gb"&lt;/span&gt;

  &lt;span class="k"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"remote-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;inline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"export PATH=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;PATH:/usr/bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# install nginx&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo apt-get update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo apt-get -y install nginx"&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;There are better ways to provision a server, but we'll get to that later. For our simple nginx install, this would do the trick.&lt;/p&gt;

&lt;p&gt;Still, there is one thing missing here. How is Terraform going to connect to our server to run the bash script above?&lt;/p&gt;

&lt;h1&gt;
  
  
  Granting Terraform the SSH access
&lt;/h1&gt;

&lt;p&gt;You first need to go to your &lt;a href="https://cloud.digitalocean.com/account/security"&gt;Account/Security&lt;/a&gt; page on DigitalOcean and add your computer's SSH key there. If you don't know how to do it, DigitalOcean helps you to create and use an SSH key on that page. You're going to need the SSH key fingerprint shown on that page.&lt;/p&gt;

&lt;p&gt;Now we are going to create a new Terraform file and call it &lt;code&gt;variables.tf&lt;/code&gt;.&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"public_key"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"private_key"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ssh_fingerprint"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see how we create variables in Terraform. Typically, you can give them default values as well. However, since they contain sensitive data, we deliberately left them empty. This way, Terraform will ask us to fill them while planning or applying.&lt;/p&gt;

&lt;p&gt;We head back to our &lt;code&gt;main.tf&lt;/code&gt; now, and add the &lt;code&gt;ssh_keys&lt;/code&gt; parameter inside our droplet resource.&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-20-04-x64"&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;"terraform-sandbox"&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;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"s-1vcpu-1gb"&lt;/span&gt;
  &lt;span class="nx"&gt;ssh_keys&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;ssh_fingerprint&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"remote-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;inline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"export PATH=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;PATH:/usr/bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# install nginx&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo apt-get update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo apt-get -y install nginx"&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;This addition will grant the SSH key holder to access our droplet. We provided a list of &lt;code&gt;ssh_keys&lt;/code&gt;. It means that you can add more than one. Additionally, it also shows how we can access variables defined within the Terraform files. Hint: &lt;code&gt;var.ssh_fingerprint&lt;/code&gt;, &lt;code&gt;var.public_key&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Lastly, we are going to define a &lt;code&gt;connection&lt;/code&gt; within the droplet resource.&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-20-04-x64"&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;"terraform-sandbox"&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;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"s-1vcpu-1gb"&lt;/span&gt;
  &lt;span class="nx"&gt;ssh_keys&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;ssh_fingerprint&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;host&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ipv4_address&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"root"&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
    &lt;span class="nx"&gt;private_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&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;private_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2m"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"remote-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;inline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"export PATH=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;PATH:/usr/bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# install nginx&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo apt-get update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"sudo apt-get -y install nginx"&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;This connection declaration defines an SSH connection. The host is the IP address of our droplet. Notice how we used the &lt;code&gt;self&lt;/code&gt; object there. It's going to connect with the &lt;code&gt;root&lt;/code&gt; user, using the private key of our computer.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;main.tf&lt;/code&gt; is ready to create a droplet, and then make a connection to it to install nginx. Now, instead of a plain &lt;code&gt;terraform apply&lt;/code&gt;, we need to do it with populating the variables as well:&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 apply &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s2"&gt;"public_key=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.ssh/id_rsa.pub"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s2"&gt;"private_key=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.ssh/id_rsa"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s2"&gt;"ssh_fingerprint=YOUR_FINGERPRINT"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to use your SSH key fingerprint instead of YOUR_FINGERPRINT. And please also note that the &lt;code&gt;public_key&lt;/code&gt; and &lt;code&gt;private_key&lt;/code&gt; values may be different based on how you created them.&lt;/p&gt;

&lt;p&gt;When you run the command, it's going to show you the plan and ask for your approval. You won't see the provisioner in the plan. But when you apply itthe plan, you'll see how Terraform makes the connection to the droplet, and you'll see the logs of the provisioner script.&lt;/p&gt;

&lt;h1&gt;
  
  
  Pointing a domain to the server
&lt;/h1&gt;

&lt;p&gt;We have a server, and we have nginx running on it. You can visit the IP address of your server to see it with your eyes. However, let's move one step ahead and use a domain instead of the IP address.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;domain.tf&lt;/code&gt; file and populate it with:&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;"digitalocean_domain"&lt;/span&gt; &lt;span class="s2"&gt;"yourdomain"&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;"yourdomain.com"&lt;/span&gt;
   &lt;span class="nx"&gt;ip_address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;digitalocean_droplet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ipv4_address&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;"digitalocean_record"&lt;/span&gt; &lt;span class="s2"&gt;"cname_www"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;digitalocean_domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yourdomain&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;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;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"www"&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;"@"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we create a &lt;code&gt;digitalocean_domain&lt;/code&gt; resource. You need to update the name with your domain address. Notice how we used the &lt;code&gt;ipv4_address&lt;/code&gt; attribute to point our domain to a specific IP address. You can see the &lt;a href="https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/droplet#attributes-reference"&gt;full list of attributes reference&lt;/a&gt; on Terraform Registry. The same way we access the IP address here, you can access any attribute from &lt;code&gt;digitalocean_droplet.web&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Second, we create a &lt;code&gt;digitalocean_record&lt;/code&gt; resource to point all &lt;code&gt;www&lt;/code&gt; subdomain requests to the main one. Thus, both yourdomain.com and &lt;a href="http://www.yourdomain.com"&gt;www.yourdomain.com&lt;/a&gt; will lead to our droplet. See how we set the &lt;code&gt;domain&lt;/code&gt; parameter inside to &lt;code&gt;digitalocean_domain.yourdomain.name&lt;/code&gt;. That's how Terraform will understand under which domain it should create this record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resource order is not important
&lt;/h2&gt;

&lt;p&gt;One interesting fact with Terraform we can mention here is that Terraform doesn't care about the order of the resources. Even if you declare &lt;code&gt;digitalocean_record&lt;/code&gt; resource before the &lt;code&gt;digitalocean_domain&lt;/code&gt; one, it would still work. That's because Terraform is declarative, and it creates its dependency graph based on what you declared.&lt;/p&gt;

&lt;p&gt;Back to the domain creation... Now if you apply your plan again:&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 apply &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s2"&gt;"public_key=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.ssh/id_rsa.pub"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s2"&gt;"private_key=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.ssh/id_rsa"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s2"&gt;"ssh_fingerprint=YOUR_FINGERPRINT"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see a plan just like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terraform will perform the following actions:

  # digitalocean_domain.yourdomain will be created
  + resource "digitalocean_domain" "yourdomain" {
      + id         = (known after apply)
      + ip_address = "111.111.111.111"
      + name       = "yourdomain.com"
      + urn        = (known after apply)
    }

  # digitalocean_record.cname_www will be created
  + resource "digitalocean_record" "cname_www" {
      + domain = "yourdomain.com"
      + fqdn   = (known after apply)
      + id     = (known after apply)
      + name   = "www"
      + ttl    = (known after apply)
      + type   = "CNAME"
      + value  = "@"
    }

Plan: 2 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the plan, and that's all. Now if you visit your domain, you'll see your that nginx's default site configuration will catch your request and show you its default page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8ON1Tf5a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ymohqbp7tje9yd7t6p5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8ON1Tf5a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ymohqbp7tje9yd7t6p5s.png" alt="nginx welcomes you"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PS: Cover photo by &lt;a href="https://unsplash.com/@realaxer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;tian kuan&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2&lt;/a&gt;.........................................................................................................&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4"&gt;Part 4&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>nginx</category>
    </item>
    <item>
      <title>What I've Learned Learning Terraform: Part 2</title>
      <dc:creator>Ekin Öcalan</dc:creator>
      <pubDate>Thu, 13 Aug 2020 19:08:56 +0000</pubDate>
      <link>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-2-jon</link>
      <guid>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-2-jon</guid>
      <description>&lt;h3&gt;
  
  
  Terraform Series
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-1-4en4"&gt;Part 1: Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2: Creating the Server&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32"&gt;Part 3: Provisioning the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4"&gt;Part 4: Managing Terraform State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5: Cleaner Code with Terraform Modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0"&gt;Part 6: Loops with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7"&gt;Part 7: Conditionals with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328"&gt;Part 8: Testing Terraform Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Choosing a Provider
&lt;/h1&gt;

&lt;p&gt;Terraform is using HashiCorp Configuration Language (HCL). Although HCL is unifying how Terraform should be coded, different providers allow you to achieve different goals with different scopes. That's why it's not the same to create a server with AWS provider and GCP provider (although similar).&lt;/p&gt;

&lt;p&gt;I have a few resources I use to learn and practice Terraform. One of them is the book Terraform: Up &amp;amp; Running: Writing Infrastructure as Code by Yevgeniy Brikman. It's a beautiful book in terms of how simple it goes through the basics. All examples are for AWS. My day-to-day job, on the other hand, requires me to work with GCP. That's why I chose to practice with something different which is also my favorite cloud provider: &lt;a href="https://m.do.co/c/4916561afc69"&gt;DigitalOcean&lt;/a&gt;. :-)&lt;/p&gt;

&lt;p&gt;You can use my referral link to get $100 DigitalOcean credits: &lt;a href="https://m.do.co/c/4916561afc69"&gt;https://m.do.co/c/4916561afc69&lt;/a&gt;. It is more than enough to run through all practices in this series several times.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F10fTOsP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jm9p5fzmfmpu9mfij5u7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F10fTOsP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jm9p5fzmfmpu9mfij5u7.jpg" alt="DigitalOcean is a budget cloud platform for start-ups https://medium.com/brainstation23/reduce-cost-in-digital-ocean-cloud-for-startups-e6855e895c6e"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why DigitalOcean? Because it's not as big and as complex as AWS or GCP. I love how DigitalOcean focuses on a limited set of assets like servers, managed DBs, and spaces. If you're not building for an enterprise, DigitalOcean provides more than enough with excellent UX.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating a Server aka Droplet
&lt;/h1&gt;

&lt;p&gt;We are going to create a droplet, and then configure it step by step. First, create a &lt;code&gt;main.tf&lt;/code&gt; file and put the content below:&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;digitalocean&lt;/span&gt; &lt;span class="p"&gt;=&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;"digitalocean/digitalocean"&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;"1.22.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;p&gt;1.22.1 is the latest version of the DigitalOcean provider at the time of this writing. You can check the &lt;a href="https://registry.terraform.io/providers/digitalocean/digitalocean"&gt;registry&lt;/a&gt; to find the newest version.&lt;/p&gt;

&lt;p&gt;Next, we are going to create a resource to declare the droplet. In the same &lt;code&gt;main.tf&lt;/code&gt; file, add this to the bottom:&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;"digitalocean_droplet"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-20-04-x64"&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;"terraform-sandbox"&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;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"s-1vcpu-1gb"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the name &lt;code&gt;digitalocean_droplet&lt;/code&gt; identifies the resource as defined by the provider. &lt;code&gt;web&lt;/code&gt;, on the other hand, is a name given by us to use this resource within Terraform later. Let me also list the configuration parameters below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;image&lt;/code&gt;: The OS image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: The server's name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;region&lt;/code&gt;: The datacenter's region. You can check &lt;a href="https://www.digitalocean.com/docs/platform/availability-matrix/"&gt;Regional Availability Matrix&lt;/a&gt; to find yourself a suitable region.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size&lt;/code&gt;: The server type DigitalOcean provides. You can check &lt;a href="https://developers.digitalocean.com/documentation/changelog/api-v2/new-size-slugs-for-droplet-plan-changes/"&gt;the list of size slugs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Image and size options are available via DO's API, but we'll talk about them later on. The example values given above are enough to continue our demo.&lt;/p&gt;

&lt;p&gt;The code inside the &lt;code&gt;main.tf&lt;/code&gt; file is sufficient to create a droplet. Now let's head to the terminal and run the commands below from the directory the &lt;code&gt;main.tf&lt;/code&gt; file resides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export your DigitalOcean access token as env
&lt;/h2&gt;

&lt;p&gt;There are &lt;a href="https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs#DIGITALOCEAN_ACCESS_TOKEN"&gt;multiple ways to provide your access token to Terraform&lt;/a&gt; so that it can communicate with the provider using its API. For the sake of simplicity, we are going to export it on the command line, and Terraform will pick it up as long as we keep that command-line session open.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export DIGITALOCEAN_ACCESS_TOKEN=cbb3b23bf9281232bd7cc60d0b281f1483d82b45cd067e4ca8a401cbbf44df3e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The access token above is fake, of course. You need to visit &lt;a href="https://cloud.digitalocean.com/account/api"&gt;the API page&lt;/a&gt; on your DigitalOcean account to create one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize Terraform
&lt;/h2&gt;

&lt;p&gt;Terraform has hundreds of providers, and those providers don't come out of the package. By initializing, Terraform prepares your working directory and installs the required provider plugins. You need to rerun this command whenever you set or change modules or backend configuration for Terraform.&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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will do what I described above and return a long success message which eventually comes down to this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Terraform has been successfully initialized!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Plan the changes
&lt;/h2&gt;

&lt;p&gt;Terraform has a very nice feature where you can audit the changes it will make before applying anything.&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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will generate an execution plan and show it to you. In our case, it's going to look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terraform will perform the following actions:

  # digitalocean_droplet.web will be created
  + resource "digitalocean_droplet" "web" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "terraform-sandbox"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "ams3"
      + resize_disk          = true
      + size                 = "s-1vcpu-1gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can already see the configuration values we've provided in the plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply the execution plan
&lt;/h2&gt;

&lt;p&gt;Our final step to create the server is to apply the plan. You can ignore the &lt;code&gt;terraform plan&lt;/code&gt; command after some time because Terraform is going to require your approval one more time at this step.&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 apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will show you the execution plan we've been shown above and ask you to approve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you enter &lt;em&gt;yes&lt;/em&gt;, it's going to go ahead and create your droplet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Enter a value: yes

digitalocean_droplet.web: Creating...
digitalocean_droplet.web: Still creating... [10s elapsed]
digitalocean_droplet.web: Still creating... [20s elapsed]
digitalocean_droplet.web: Still creating... [30s elapsed]
digitalocean_droplet.web: Creation complete after 34s [id=111111111]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can go ahead and check your newly created droplet on your account:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--moebll8U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ltm33vnt2rf380dapbax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--moebll8U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ltm33vnt2rf380dapbax.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PS: Cover photo by &lt;a href="https://unsplash.com/@aronvisuals?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Aron Visuals&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="http://dev.to/gzg/what-i-ve-learned-learning-terraform-part-1-4en4"&gt;Part 1&lt;/a&gt;..........................................................................................................&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32"&gt;Part 3&lt;/a&gt;&lt;/p&gt;

</description>
      <category>iac</category>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What I've Learned Learning Terraform: Part 1</title>
      <dc:creator>Ekin Öcalan</dc:creator>
      <pubDate>Mon, 10 Aug 2020 19:05:47 +0000</pubDate>
      <link>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-1-4en4</link>
      <guid>https://forem.com/gzg/what-i-ve-learned-learning-terraform-part-1-4en4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Software isn't done until you deliver it to the user. [1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We've been discussing what &lt;em&gt;done&lt;/em&gt; means at my Scrum team lately. There were a lot of suggestions. One of us said that when the code is ready for review, at least the implementation part is done. Others said that we could not specify a task as done until we test it on production and see it working as expected. "Done" means that we don't have anything left to do with it. That's why the above quote explains what "Done" means in terms of software simply.&lt;/p&gt;

&lt;p&gt;You have a small feature to implement. You write the code. Someone from your team reviews it and gives feedback. You take your code back, polish it, maybe refactor it to meet your team's criteria. Then you move on to the QA. Oh, something just doesn't look right. You fix it and push it again. Cool! Now your work passes the QA. You merge your PR and deploy it to production.&lt;/p&gt;

&lt;p&gt;The scenario above touches two main points at the end:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Continuous Integration (CI) where you integrate your work as frequently and as soon as possible. Having small changes in your work helps, because this way, your code does not get too different from the main branch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuous Deployment (CD) [2] where you not only keep your integrated work deployable at all times, but you also deploy your main branch to production with each integration automatically.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CI and CD approach aims to deliver software faster. They pretty much succeed at this job. However, your software isn't the only thing that is delivered quickly and frequently. Your third party libraries get updated regularly. Your cloud provider issues updates to its infrastructure. Your data gets bigger by the day. Your network needs may change as you expand. So, basically, how do you scale?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Devops movement encourages software developers, operations staff, and everyone else involved in delivery to work together - avoiding hand-offs that add delays and brittleness. Infrastructure-As-Code takes advantage of our cloud age ability to rapidly deploy and provision new servers. [3]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Infrastructure-As-Code (IaC) lets you define your infrastructure with code so that you can configure your provisions and replicate it at a later point. With version control, you can reverse your infra to an older state if something goes wrong. It makes your operations reliable and scalable. You don't have to worry about upgrading or maintaining a single server you have.&lt;/p&gt;

&lt;p&gt;CI is up to me. I need to integrate my work as frequently as possible. CD is up to our deployment rules. I want to deploy my work as often as possible. Now with IaC, I also get control over where my code gets deployed. That is the main reason why I decided to learn more about Terraform [4]. And that is why I want to summarize what I've learned while practicing Terraform.&lt;/p&gt;




&lt;h2&gt;
  
  
  Terraform Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: Introduction&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2: Creating the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32"&gt;Part 3: Provisioning the Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4"&gt;Part 4: Managing Terraform State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b"&gt;Part 5: Cleaner Code with Terraform Modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0"&gt;Part 6: Loops with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7"&gt;Part 7: Conditionals with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328"&gt;Part 8: Testing Terraform Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;[1]: Terraform Up &amp;amp; Running: Writing Infrastructure as Code by Yevgeniy Brikman (2nd edition)&lt;br&gt;
[2]: Not to be confused with Continuous Delivery where you keep your work deployable at all times but make the deployments manually from time to time.&lt;br&gt;
[3]: &lt;a href="https://martinfowler.com/delivery.html"&gt;Software Delivery Guide&lt;/a&gt; by Martin Fowler&lt;br&gt;
[4]: Terraform is one of the most popular IaC tools. Popularity and adoption are key to keep the IaC tool up-to-date with various providers.&lt;/p&gt;

&lt;p&gt;Cover picture by &lt;a href="https://unsplash.com/@unthunk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Lucas Myers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PS: Also checkout &lt;a href="https://en.wikipedia.org/wiki/Terraforming"&gt;Terraforming on Wikipedia&lt;/a&gt;!&lt;/p&gt;




&lt;p&gt;...................................................................................................................&lt;a href="https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon"&gt;Part 2&lt;/a&gt;&lt;/p&gt;

</description>
      <category>iac</category>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
