<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mary Mutua</title>
    <description>The latest articles on DEV Community by Mary Mutua (@mary_mutua_9d55b3c269f343).</description>
    <link>https://dev.to/mary_mutua_9d55b3c269f343</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%2F3835891%2F74c43ea9-1b3f-4c6f-b41c-a036e89143a0.png</url>
      <title>DEV Community: Mary Mutua</title>
      <link>https://dev.to/mary_mutua_9d55b3c269f343</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mary_mutua_9d55b3c269f343"/>
    <language>en</language>
    <item>
      <title>Ready for Terraform Certification: My Final Exam Prep and 30-Day Reflection</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Sat, 02 May 2026 02:14:18 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/ready-for-terraform-certification-my-final-exam-prep-and-30-day-reflection-4723</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/ready-for-terraform-certification-my-final-exam-prep-and-30-day-reflection-4723</guid>
      <description>&lt;p&gt;Day 30 of my 30-Day Terraform Challenge is complete.&lt;/p&gt;

&lt;p&gt;This was the final day of the challenge, and the focus was clear: consolidate, assess readiness, and reflect on the full journey.&lt;/p&gt;

&lt;p&gt;There was no new infrastructure to deploy today. No new AWS service to debug. No new Terraform module to refactor.&lt;/p&gt;

&lt;p&gt;Today was about proving readiness.&lt;/p&gt;

&lt;p&gt;After 29 days of building, testing, documenting, troubleshooting, and preparing, I used Day 30 to complete one final simulated exam, test command recall with fill-in-the-blank questions, and assess whether I am ready for the Terraform Associate certification.&lt;/p&gt;

&lt;p&gt;GitHub reference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_30" rel="noopener noreferrer"&gt;https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_30&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Practice Exam
&lt;/h2&gt;

&lt;p&gt;I completed Practice Exam 5 as my final simulated exam.&lt;/p&gt;

&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;57 / 57
100%
Time taken: 45 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was the strongest possible way to end the practice exam phase.&lt;/p&gt;

&lt;p&gt;More importantly, it confirmed that the consistency I saw on Days 28 and 29 was real. My earlier practice exams had already shown strong scores, but this final exam gave me confidence that the core concepts were sticking under timed conditions.&lt;/p&gt;

&lt;p&gt;Across the final five practice exams, my trend looked like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exam&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Practice Exam 1&lt;/td&gt;
&lt;td&gt;54 / 57&lt;/td&gt;
&lt;td&gt;94.7%&lt;/td&gt;
&lt;td&gt;55 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practice Exam 2&lt;/td&gt;
&lt;td&gt;56 / 57&lt;/td&gt;
&lt;td&gt;98.2%&lt;/td&gt;
&lt;td&gt;50 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practice Exam 3&lt;/td&gt;
&lt;td&gt;56 / 57&lt;/td&gt;
&lt;td&gt;98.2%&lt;/td&gt;
&lt;td&gt;40 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practice Exam 4&lt;/td&gt;
&lt;td&gt;56 / 57&lt;/td&gt;
&lt;td&gt;98.2%&lt;/td&gt;
&lt;td&gt;45 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practice Exam 5&lt;/td&gt;
&lt;td&gt;57 / 57&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;45 min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The trend matters more than any single score.&lt;/p&gt;

&lt;p&gt;It showed steady performance, comfortable timing, and fewer weak spots by the final day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fill-in-the-Blank Review
&lt;/h2&gt;

&lt;p&gt;After the final practice exam, I completed a 20-question fill-in-the-blank review.&lt;/p&gt;

&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;19 / 20
95%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was useful because fill-in-the-blank questions test recall differently from multiple choice.&lt;/p&gt;

&lt;p&gt;With multiple choice, you can sometimes recognize the correct answer. With fill-in-the-blank questions, you have to retrieve it from memory.&lt;/p&gt;

&lt;p&gt;The one question I missed was about the S3 backend encryption argument.&lt;/p&gt;

&lt;p&gt;I answered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server_side_encryption_configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correct backend argument is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;encrypt&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was a good final reminder: Terraform exam prep is often about precision. &lt;code&gt;server_side_encryption_configuration&lt;/code&gt; is related to configuring encryption on an actual S3 bucket resource. The S3 backend uses &lt;code&gt;encrypt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Small distinction. Important distinction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Readiness Assessment
&lt;/h2&gt;

&lt;p&gt;My final readiness rating is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ready
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The evidence is clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Practice Exam 5: 100%&lt;/li&gt;
&lt;li&gt;Fill-in-the-blank review: 95%&lt;/li&gt;
&lt;li&gt;Strong score trend across five practice exams&lt;/li&gt;
&lt;li&gt;Comfortable timing within the exam window&lt;/li&gt;
&lt;li&gt;Remaining weak areas are narrow and specific&lt;/li&gt;
&lt;li&gt;Core Terraform workflow, modules, state, providers, lifecycle rules, and HCP Terraform concepts are solid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only final topics I want to lightly review before the real exam are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 backend encryption: &lt;code&gt;encrypt = true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;saved plan staleness&lt;/li&gt;
&lt;li&gt;variable precedence&lt;/li&gt;
&lt;li&gt;state command distinctions&lt;/li&gt;
&lt;li&gt;provider alias usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, I do not need to learn brand-new material.&lt;/p&gt;

&lt;p&gt;I need to stay calm, review lightly, and trust the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed Over These 30 Days
&lt;/h2&gt;

&lt;p&gt;This challenge changed how I think about infrastructure.&lt;/p&gt;

&lt;p&gt;At the beginning, Terraform was mostly a tool for provisioning cloud resources.&lt;/p&gt;

&lt;p&gt;Now I see it differently.&lt;/p&gt;

&lt;p&gt;Terraform is not just about creating infrastructure. It is about managing change safely.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;writing infrastructure as code&lt;/li&gt;
&lt;li&gt;storing and protecting state&lt;/li&gt;
&lt;li&gt;designing reusable modules&lt;/li&gt;
&lt;li&gt;isolating environments&lt;/li&gt;
&lt;li&gt;reviewing plans before applying&lt;/li&gt;
&lt;li&gt;testing infrastructure code&lt;/li&gt;
&lt;li&gt;thinking about blast radius&lt;/li&gt;
&lt;li&gt;documenting decisions&lt;/li&gt;
&lt;li&gt;cleaning up resources responsibly&lt;/li&gt;
&lt;li&gt;understanding how teams collaborate around infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest shift was moving from “Can I make this work?” to “Can I make this safe, repeatable, and understandable?”&lt;/p&gt;

&lt;p&gt;That is a very different mindset.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built During the Challenge
&lt;/h2&gt;

&lt;p&gt;Across the 30 days, I worked through a wide range of Terraform and AWS concepts.&lt;/p&gt;

&lt;p&gt;Some of the major areas included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 instances and security groups&lt;/li&gt;
&lt;li&gt;Application Load Balancers&lt;/li&gt;
&lt;li&gt;Auto Scaling Groups&lt;/li&gt;
&lt;li&gt;launch templates&lt;/li&gt;
&lt;li&gt;remote state with S3 and DynamoDB&lt;/li&gt;
&lt;li&gt;Terraform workspaces and environment isolation&lt;/li&gt;
&lt;li&gt;reusable modules&lt;/li&gt;
&lt;li&gt;loops and conditionals&lt;/li&gt;
&lt;li&gt;zero-downtime deployment patterns&lt;/li&gt;
&lt;li&gt;secrets management concepts&lt;/li&gt;
&lt;li&gt;multiple providers and provider aliases&lt;/li&gt;
&lt;li&gt;Docker and Kubernetes provider usage&lt;/li&gt;
&lt;li&gt;production-grade module design&lt;/li&gt;
&lt;li&gt;manual and automated testing&lt;/li&gt;
&lt;li&gt;Terratest and native &lt;code&gt;terraform test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CI/CD workflow concepts&lt;/li&gt;
&lt;li&gt;Sentinel and policy-as-code concepts&lt;/li&gt;
&lt;li&gt;static website hosting with S3&lt;/li&gt;
&lt;li&gt;scalable web application architecture&lt;/li&gt;
&lt;li&gt;multi-region high availability architecture&lt;/li&gt;
&lt;li&gt;Terraform Associate exam preparation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That list is long, but the real value was not the number of topics.&lt;/p&gt;

&lt;p&gt;The value was seeing how the topics connect.&lt;/p&gt;

&lt;p&gt;State affects collaboration. Modules affect maintainability. Testing affects confidence. Provider aliases affect multi-region design. Plans affect safety. Documentation affects whether someone else can understand what was built.&lt;/p&gt;

&lt;p&gt;That is what made the challenge feel real.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Am Most Proud Of
&lt;/h2&gt;

&lt;p&gt;The part I am most proud of is pushing through the harder practical labs.&lt;/p&gt;

&lt;p&gt;Remote state, reusable modules, automated testing, and the multi-region high-availability architecture were not simple checklist items. They required debugging, rethinking, and patience.&lt;/p&gt;

&lt;p&gt;The multi-region architecture especially stood out because it brought many earlier lessons together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provider aliases&lt;/li&gt;
&lt;li&gt;region-specific module wiring&lt;/li&gt;
&lt;li&gt;networking&lt;/li&gt;
&lt;li&gt;load balancing&lt;/li&gt;
&lt;li&gt;Auto Scaling&lt;/li&gt;
&lt;li&gt;RDS&lt;/li&gt;
&lt;li&gt;Route 53 concepts&lt;/li&gt;
&lt;li&gt;cleanup and cost awareness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It felt like the kind of work that moves Terraform from tutorial knowledge into real engineering practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Challenge Taught Me
&lt;/h2&gt;

&lt;p&gt;The biggest lesson is that Terraform is not just syntax.&lt;/p&gt;

&lt;p&gt;Syntax matters, but the deeper skill is judgment.&lt;/p&gt;

&lt;p&gt;Terraform asks you to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what should be managed&lt;/li&gt;
&lt;li&gt;where state should live&lt;/li&gt;
&lt;li&gt;how teams should make changes&lt;/li&gt;
&lt;li&gt;how to reduce risk&lt;/li&gt;
&lt;li&gt;how to recover from mistakes&lt;/li&gt;
&lt;li&gt;when to modularize&lt;/li&gt;
&lt;li&gt;when not to overcomplicate&lt;/li&gt;
&lt;li&gt;how to prove infrastructure works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why hands-on practice matters so much.&lt;/p&gt;

&lt;p&gt;Reading about Terraform is useful, but building with it every day exposes the real lessons: backend bootstrapping, state locking, provider behavior, naming limits, account restrictions, cleanup order, and the difference between a clean plan and a safe deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;Next, I will take the Terraform Associate certification exam.&lt;/p&gt;

&lt;p&gt;After that, the goal is to keep applying these skills in real projects.&lt;/p&gt;

&lt;p&gt;I want to continue building infrastructure that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reusable&lt;/li&gt;
&lt;li&gt;documented&lt;/li&gt;
&lt;li&gt;tested&lt;/li&gt;
&lt;li&gt;secure&lt;/li&gt;
&lt;li&gt;cost-aware&lt;/li&gt;
&lt;li&gt;team-friendly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This challenge gave me a strong foundation, but the real value comes from continuing to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Day 30 confirmed that I am ready for the Terraform Associate exam.&lt;/p&gt;

&lt;p&gt;But more than that, it marked the end of a challenge that changed how I think about infrastructure.&lt;/p&gt;

&lt;p&gt;I started by learning Terraform.&lt;/p&gt;

&lt;p&gt;I finished by understanding infrastructure delivery more deeply: how to build, review, test, document, and operate infrastructure in a professional way.&lt;/p&gt;

&lt;p&gt;The certification is the next milestone.&lt;/p&gt;

&lt;p&gt;The real achievement is the confidence and discipline built over 30 days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 30 of my 30-Day Terraform Challenge.&lt;/p&gt;

&lt;p&gt;The challenge is complete.&lt;/p&gt;

&lt;p&gt;Now it is time to go pass the certification.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>learning</category>
    </item>
    <item>
      <title>Fine-tuning My Terraform Exam Prep with Practice Exams</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Sat, 02 May 2026 00:44:27 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/fine-tuning-my-terraform-exam-prep-with-practice-exams-j49</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/fine-tuning-my-terraform-exam-prep-with-practice-exams-j49</guid>
      <description>&lt;p&gt;Day 29 of my 30-Day Terraform Challenge was focused on exam readiness.&lt;/p&gt;

&lt;p&gt;There was no new infrastructure to deploy today. Instead, I continued preparing for the Terraform Associate exam by taking two more full practice exams and reviewing the results alongside my Day 28 scores.&lt;/p&gt;

&lt;p&gt;The goal was simple: use four practice exams as data.&lt;/p&gt;

&lt;p&gt;I wanted to know whether my preparation was consistent, which domains were already strong, and which areas still needed one final review before Day 30.&lt;/p&gt;

&lt;p&gt;GitHub reference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_29" rel="noopener noreferrer"&gt;https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_29&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Day 29 folder contains my notes and question sets for Practice Exams 3 and 4.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Did Today
&lt;/h2&gt;

&lt;p&gt;Today I completed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Practice Exam 3&lt;/li&gt;
&lt;li&gt;Practice Exam 4&lt;/li&gt;
&lt;li&gt;score tracking across all four practice exams&lt;/li&gt;
&lt;li&gt;wrong-answer review for today’s exams&lt;/li&gt;
&lt;li&gt;domain review using both Day 28 and Day 29 results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I treated both exams as timed simulations and reviewed the missed questions immediately afterward.&lt;/p&gt;

&lt;p&gt;This was less about learning new material and more about checking consistency, speed, and exam readiness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 29 Scores
&lt;/h2&gt;

&lt;p&gt;For Practice Exam 3, I scored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;56 / 57
98.2%
Time taken: 40 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Practice Exam 4, I scored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;56 / 57
98.2%
Time taken: 45 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both scores were strong and consistent with my Day 28 results.&lt;/p&gt;

&lt;p&gt;The main thing I wanted from today was not just another pass. I wanted to see whether my performance stayed steady across multiple attempts.&lt;/p&gt;

&lt;p&gt;It did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four-Exam Score Trend
&lt;/h2&gt;

&lt;p&gt;After today, I now have four practice exam attempts across Day 28 and Day 29.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exam&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Practice Exam 1&lt;/td&gt;
&lt;td&gt;54 / 57&lt;/td&gt;
&lt;td&gt;94.7%&lt;/td&gt;
&lt;td&gt;55 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practice Exam 2&lt;/td&gt;
&lt;td&gt;56 / 57&lt;/td&gt;
&lt;td&gt;98.2%&lt;/td&gt;
&lt;td&gt;50 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practice Exam 3&lt;/td&gt;
&lt;td&gt;56 / 57&lt;/td&gt;
&lt;td&gt;98.2%&lt;/td&gt;
&lt;td&gt;40 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practice Exam 4&lt;/td&gt;
&lt;td&gt;56 / 57&lt;/td&gt;
&lt;td&gt;98.2%&lt;/td&gt;
&lt;td&gt;45 min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Across all four exams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total questions: 228
Correct answers: 222
Overall accuracy: 97.4%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trend is encouraging.&lt;/p&gt;

&lt;p&gt;The first score was already above the passing target, and the next three exams stayed at 98.2%. My timing also improved compared with the first attempt.&lt;/p&gt;

&lt;p&gt;That tells me my preparation is stable. I am not relying on one good practice score.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain Review Across All Four Exams
&lt;/h2&gt;

&lt;p&gt;I used the Day 28 and Day 29 results to assess my readiness by topic area.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic Area&lt;/th&gt;
&lt;th&gt;Missed Questions&lt;/th&gt;
&lt;th&gt;Assessment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terraform CLI and workflow&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modules&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HCP Terraform / Terraform Cloud&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle rules&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State management and refactoring&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Light review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Providers and aliases&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Improved after review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Variables and configuration&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Main Day 29 review area&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IaC concepts and Terraform purpose&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Light conceptual review&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern was clear.&lt;/p&gt;

&lt;p&gt;My strongest areas are the Terraform workflow, modules, lifecycle behavior, and HCP Terraform concepts. The areas that need light review are not broad weaknesses. They are specific precision topics.&lt;/p&gt;

&lt;p&gt;For Day 29, the main review area was &lt;strong&gt;variables and configuration&lt;/strong&gt;, especially:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;custom variable validation&lt;/li&gt;
&lt;li&gt;variable value precedence&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Today’s Review Showed Me
&lt;/h2&gt;

&lt;p&gt;The two questions I missed today were both in the variables and configuration area.&lt;/p&gt;

&lt;p&gt;That gave me a clear final review target.&lt;/p&gt;

&lt;p&gt;The key reminders were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;variable validation checks input values&lt;/li&gt;
&lt;li&gt;variable defaults are fallback values&lt;/li&gt;
&lt;li&gt;CLI-provided values have higher precedence than defaults and automatically loaded variable files&lt;/li&gt;
&lt;li&gt;Terraform language behavior and provider behavior are not the same thing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are small details, but they are exactly the kind of details that matter in a certification exam.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Assessment
&lt;/h2&gt;

&lt;p&gt;After four practice exams, I feel much more confident about my readiness.&lt;/p&gt;

&lt;p&gt;The scores are consistently above the passing threshold, the timing is comfortable, and the missed questions are concentrated in a few narrow areas.&lt;/p&gt;

&lt;p&gt;That is a good sign.&lt;/p&gt;

&lt;p&gt;It means I do not need to restart from broad revision. I need one final focused review before Day 30.&lt;/p&gt;

&lt;p&gt;My final review areas are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;variable precedence&lt;/li&gt;
&lt;li&gt;variable validation&lt;/li&gt;
&lt;li&gt;state command distinctions&lt;/li&gt;
&lt;li&gt;provider alias usage&lt;/li&gt;
&lt;li&gt;Terraform purpose compared with cloud-specific tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Day 29 helped me move from general preparation to exam readiness assessment.&lt;/p&gt;

&lt;p&gt;The biggest win was not only the score. It was the consistency across four exams.&lt;/p&gt;

&lt;p&gt;At this stage, my focus is precision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;knowing the exact behavior of Terraform commands&lt;/li&gt;
&lt;li&gt;separating similar concepts clearly&lt;/li&gt;
&lt;li&gt;reviewing the few domains where mistakes still appeared&lt;/li&gt;
&lt;li&gt;staying calm under timed conditions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The four-exam trend shows that the foundation is strong.&lt;/p&gt;

&lt;p&gt;Now the work is to sharpen the final details before Day 30.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 29 of my 30-Day Terraform Challenge.&lt;/p&gt;

&lt;p&gt;See you on Day 30.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>learning</category>
    </item>
    <item>
      <title>How I Prepared for the Terraform Associate Exam with Practice Questions</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Thu, 30 Apr 2026 20:58:38 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/how-i-prepared-for-the-terraform-associate-exam-with-practice-questions-1ipp</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/how-i-prepared-for-the-terraform-associate-exam-with-practice-questions-1ipp</guid>
      <description>&lt;p&gt;Day 28 of my 30-Day Terraform Challenge was different from the previous days.&lt;/p&gt;

&lt;p&gt;There was no new infrastructure to deploy, no AWS resource to troubleshoot, and no module to refactor. The focus shifted from building to proving. After weeks of hands-on Terraform work, I wanted to know whether the concepts were actually sticking under pressure.&lt;/p&gt;

&lt;p&gt;So I treated the day like an exam simulation.&lt;/p&gt;

&lt;p&gt;The plan was simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;take two full practice exams&lt;/li&gt;
&lt;li&gt;answer 57 questions in each exam&lt;/li&gt;
&lt;li&gt;work under timed conditions&lt;/li&gt;
&lt;li&gt;avoid looking anything up mid-exam&lt;/li&gt;
&lt;li&gt;review every wrong answer properly afterward&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was not casual revision. It was a deliberate test of recall, reasoning, and speed.&lt;/p&gt;

&lt;p&gt;GitHub reference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_28" rel="noopener noreferrer"&gt;https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_28&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Scores
&lt;/h2&gt;

&lt;p&gt;I completed two full practice exams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practice Exam 1
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Score: &lt;strong&gt;54/57&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Accuracy: &lt;strong&gt;94.7%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Practice Exam 2
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Score: &lt;strong&gt;56/57&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Accuracy: &lt;strong&gt;98.2%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I found interesting was not just that both scores were strong, but that the second one was slightly better.&lt;/p&gt;

&lt;p&gt;That told me something useful about how I perform under repetition. Instead of fading, I seemed to settle into the exam rhythm. The warm-up effect was real.&lt;/p&gt;

&lt;p&gt;Still, the scores were only part of the story.&lt;/p&gt;

&lt;p&gt;The real value of the day came from reviewing the misses.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Most Valuable Part Was the Wrong-Answer Review
&lt;/h2&gt;

&lt;p&gt;After each exam, I did not want to stop at “correct” or “incorrect.” I wanted to understand the reasoning behind every miss.&lt;/p&gt;

&lt;p&gt;For each wrong answer, I reviewed it using this structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;question topic&lt;/li&gt;
&lt;li&gt;what I answered&lt;/li&gt;
&lt;li&gt;correct answer&lt;/li&gt;
&lt;li&gt;why I was wrong&lt;/li&gt;
&lt;li&gt;what I needed to reinforce&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last part mattered a lot.&lt;/p&gt;

&lt;p&gt;It is easy to read the correct answer and move on. It is much harder, and much more useful, to identify the real gap in your thinking.&lt;/p&gt;

&lt;p&gt;Sometimes the gap is a missing concept.&lt;br&gt;
Sometimes it is misreading the wording.&lt;br&gt;
Sometimes it is mixing up two Terraform features that sound similar but behave differently.&lt;/p&gt;

&lt;p&gt;That kind of review is what makes practice exams useful instead of just repetitive.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I Got Wrong
&lt;/h2&gt;

&lt;p&gt;Across both exams, I only missed a few questions, but they were interesting misses because they were not about broad fundamentals. They were about precision.&lt;/p&gt;
&lt;h3&gt;
  
  
  Exam 1
&lt;/h3&gt;

&lt;p&gt;I missed questions related to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;immutable infrastructure&lt;/li&gt;
&lt;li&gt;CloudFormation vs Terraform provider scope&lt;/li&gt;
&lt;li&gt;provider alias syntax&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Exam 2
&lt;/h3&gt;

&lt;p&gt;I missed one question related to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;terraform state mv&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That pattern actually gave me confidence.&lt;/p&gt;

&lt;p&gt;I was not struggling with the main Terraform workflow. I was not missing modules, state concepts, or core CLI usage at a high level. What I needed to sharpen were the finer distinctions that often appear in certification exams.&lt;/p&gt;

&lt;p&gt;Those are the kinds of questions that can catch you off guard if you rely only on familiarity instead of understanding.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Surprised Me
&lt;/h2&gt;

&lt;p&gt;The biggest surprise was not my score. It was which topics felt easiest and which ones still created small mistakes.&lt;/p&gt;

&lt;p&gt;My stronger areas were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform CLI&lt;/li&gt;
&lt;li&gt;core workflow&lt;/li&gt;
&lt;li&gt;state basics&lt;/li&gt;
&lt;li&gt;modules&lt;/li&gt;
&lt;li&gt;HCP Terraform / Terraform Cloud&lt;/li&gt;
&lt;li&gt;general configuration structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The weaker spots were much narrower:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;immutable vs mutable infrastructure thinking&lt;/li&gt;
&lt;li&gt;cloud-specific tooling vs provider-agnostic tooling&lt;/li&gt;
&lt;li&gt;exact provider alias usage&lt;/li&gt;
&lt;li&gt;state subcommands during refactoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was a good reminder that exam readiness is not only about knowing the “big ideas.”&lt;/p&gt;

&lt;p&gt;Sometimes the harder questions are the ones that test whether you can clearly separate two concepts that look similar on the surface.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Hands-On Reinforcement That Helped
&lt;/h2&gt;

&lt;p&gt;I did not want my review to be passive.&lt;/p&gt;

&lt;p&gt;Instead of just rereading the answers, I used small practical reinforcements to close the gaps.&lt;/p&gt;
&lt;h3&gt;
  
  
  Provider alias syntax
&lt;/h3&gt;

&lt;p&gt;One of my misses came from confusing alias declaration with alias usage.&lt;/p&gt;

&lt;p&gt;To reinforce it, I went back to the exact pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"west"&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;"us-west-2"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"west_bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;west&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;"example-west-bucket-12345"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That helped lock in the difference between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;declaring &lt;code&gt;alias = "west"&lt;/code&gt; in the provider block&lt;/li&gt;
&lt;li&gt;using &lt;code&gt;provider = aws.west&lt;/code&gt; inside a resource&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a small distinction, but it is exactly the kind of thing that can cost points if your memory is fuzzy.&lt;/p&gt;

&lt;h3&gt;
  
  
  State command review
&lt;/h3&gt;

&lt;p&gt;Another useful review point was around &lt;code&gt;terraform state mv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That made me revisit the difference between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;terraform import&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform state mv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform state rm&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The distinction became much clearer once I framed them by purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;terraform import&lt;/code&gt; brings existing infrastructure into Terraform state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform state mv&lt;/code&gt; changes the address of an object Terraform already tracks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform state rm&lt;/code&gt; removes an object from state without deleting the real resource&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is not just exam trivia. It is practical Terraform knowledge that matters when refactoring real infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Taught Me About Exam Prep
&lt;/h2&gt;

&lt;p&gt;The biggest lesson from Day 28 was this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A practice exam is only as valuable as the review that follows it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A high score can be encouraging, but it is not enough on its own.&lt;/p&gt;

&lt;p&gt;What really improves exam readiness is being able to say:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;this is what I misunderstood&lt;/li&gt;
&lt;li&gt;this is why I misunderstood it&lt;/li&gt;
&lt;li&gt;this is how I will stop making the same mistake&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That turns a wrong answer into a useful learning asset.&lt;/p&gt;

&lt;p&gt;Without that step, practice questions can become little more than scorekeeping.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Helped Me Most
&lt;/h2&gt;

&lt;p&gt;A few things made this study session much more effective:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;answering under timed conditions&lt;/li&gt;
&lt;li&gt;not interrupting the exam to look things up&lt;/li&gt;
&lt;li&gt;taking two separate full-length practice runs&lt;/li&gt;
&lt;li&gt;reviewing the misses immediately afterward&lt;/li&gt;
&lt;li&gt;paying attention to whether my second score improved or dropped&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point was especially helpful.&lt;/p&gt;

&lt;p&gt;If the second score had been significantly worse, it would have suggested fatigue. Since it improved, it suggested that a short warm-up before the real exam might actually help me perform better.&lt;/p&gt;

&lt;p&gt;That is the kind of insight I would not get from doing random untimed questions here and there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Day 28 was one of the most useful preparation days in the whole challenge because it forced me to move from learning Terraform to testing my command of it.&lt;/p&gt;

&lt;p&gt;It reminded me that exam readiness is not just about memorizing commands.&lt;/p&gt;

&lt;p&gt;It is about being able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reason quickly&lt;/li&gt;
&lt;li&gt;separate similar concepts accurately&lt;/li&gt;
&lt;li&gt;stay calm under time pressure&lt;/li&gt;
&lt;li&gt;review mistakes honestly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scores were strong, but the biggest win was getting clarity on the few areas that still need sharper recall.&lt;/p&gt;

&lt;p&gt;That is what makes practice exams worth doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 28 of my 30-Day Terraform Challenge.&lt;/p&gt;

&lt;p&gt;See you on Day 29.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a 3-Tier Multi-Region High Availability Architecture with Terraform</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Tue, 28 Apr 2026 16:22:42 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/building-a-3-tier-multi-region-high-availability-architecture-with-terraform-5bio</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/building-a-3-tier-multi-region-high-availability-architecture-with-terraform-5bio</guid>
      <description>&lt;p&gt;Day 27 of my Terraform journey moved from a single-region scalable app to a &lt;strong&gt;multi-region high-availability design&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Yesterday, I built a scalable web application in one AWS region. Today, I expanded that pattern into a 3-tier architecture spread across &lt;strong&gt;two regions&lt;/strong&gt; using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a VPC per region&lt;/li&gt;
&lt;li&gt;an ALB per region&lt;/li&gt;
&lt;li&gt;an Auto Scaling Group per region&lt;/li&gt;
&lt;li&gt;a primary Multi-AZ RDS instance&lt;/li&gt;
&lt;li&gt;a cross-region read replica&lt;/li&gt;
&lt;li&gt;optional Route53 failover DNS&lt;/li&gt;
&lt;li&gt;reusable Terraform modules&lt;/li&gt;
&lt;li&gt;remote state with S3 and DynamoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub reference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_27" rel="noopener noreferrer"&gt;https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_27&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;For Day 27, I separated the infrastructure into five focused modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;day27-multi-region-ha/
├── modules/
│   ├── vpc/
│   ├── alb/
│   ├── asg/
│   ├── rds/
│   └── route53/
├── envs/
│   └── prod/
├── bootstrap/
├── backend.tf
└── provider.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal was not just to make the stack work.&lt;/p&gt;

&lt;p&gt;The goal was to make the design reusable, understandable, and safe to change across regions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Five Modules Instead of One?
&lt;/h2&gt;

&lt;p&gt;I split the project into five modules because each part has a different responsibility.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;vpc&lt;/code&gt; module owns networking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC&lt;/li&gt;
&lt;li&gt;public subnets&lt;/li&gt;
&lt;li&gt;private subnets&lt;/li&gt;
&lt;li&gt;internet gateway&lt;/li&gt;
&lt;li&gt;NAT gateways&lt;/li&gt;
&lt;li&gt;route tables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;alb&lt;/code&gt; module owns traffic entry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application Load Balancer&lt;/li&gt;
&lt;li&gt;target group&lt;/li&gt;
&lt;li&gt;listener&lt;/li&gt;
&lt;li&gt;ALB security group&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;asg&lt;/code&gt; module owns compute and scaling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;launch template&lt;/li&gt;
&lt;li&gt;instance security group&lt;/li&gt;
&lt;li&gt;Auto Scaling Group&lt;/li&gt;
&lt;li&gt;scaling policies&lt;/li&gt;
&lt;li&gt;CloudWatch CPU alarms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;rds&lt;/code&gt; module owns the database layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DB subnet group&lt;/li&gt;
&lt;li&gt;RDS security group&lt;/li&gt;
&lt;li&gt;primary RDS instance&lt;/li&gt;
&lt;li&gt;cross-region replica logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;route53&lt;/code&gt; module owns failover DNS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;health checks&lt;/li&gt;
&lt;li&gt;primary failover record&lt;/li&gt;
&lt;li&gt;secondary failover record&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything lived in one large file, it would still work, but it would be harder to reuse and harder to reason about.&lt;/p&gt;

&lt;p&gt;Modules make the boundaries clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Modules Connect
&lt;/h2&gt;

&lt;p&gt;The most important part of today was understanding the data flow between modules.&lt;/p&gt;

&lt;p&gt;The VPC modules create the base networking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.vpc_primary.vpc_id
module.vpc_primary.public_subnet_ids
module.vpc_primary.private_subnet_ids

module.vpc_secondary.vpc_id
module.vpc_secondary.public_subnet_ids
module.vpc_secondary.private_subnet_ids
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those outputs feed the rest of the stack.&lt;/p&gt;

&lt;p&gt;The ALB module in the primary region creates a target group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.alb_primary.target_group_arn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That output flows into the primary ASG module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;target_group_arns&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alb_primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target_group_arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells the Auto Scaling Group where its EC2 instances should register.&lt;/p&gt;

&lt;p&gt;Then the RDS primary module creates the main database and outputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.rds_primary.db_instance_arn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That output flows into the replica module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;replicate_source_db&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_primary&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db_instance_arn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells AWS to create the secondary database as a &lt;strong&gt;cross-region read replica&lt;/strong&gt; of the primary database.&lt;/p&gt;

&lt;p&gt;This closes the loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VPC → ALB → Target Group → ASG → Primary RDS → Cross-Region Replica
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is what made the architecture feel like one connected system instead of separate AWS resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment Output
&lt;/h2&gt;

&lt;p&gt;After applying the Terraform plan, Terraform returned regional ALB DNS names.&lt;/p&gt;

&lt;p&gt;I verified the primary ALB in the browser and the app responded with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Region: us-east-1 | AZ: us-east-1b | Environment: prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That confirmed the ALB, target group, Auto Scaling Group, and EC2 user data were all working together correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route53 Failover Design
&lt;/h2&gt;

&lt;p&gt;The Route53 module was included in the project design to support DNS failover between the two regions.&lt;/p&gt;

&lt;p&gt;In my actual lab run, I left Route53 disabled because I did not have a hosted zone and domain ready in the account. So I verified the stack through the ALB DNS names directly instead.&lt;/p&gt;

&lt;p&gt;Still, the failover behavior is important to understand.&lt;/p&gt;

&lt;p&gt;If the primary region fails, the sequence looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the primary Route53 health check fails&lt;/li&gt;
&lt;li&gt;Route53 stops returning the primary record&lt;/li&gt;
&lt;li&gt;clients continue using cached DNS until TTL expires&lt;/li&gt;
&lt;li&gt;after TTL expiry, new DNS lookups resolve to the secondary record&lt;/li&gt;
&lt;li&gt;traffic shifts to the secondary ALB&lt;/li&gt;
&lt;li&gt;the secondary ALB continues serving the application tier in the backup region&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That means failover is not instant in the same way an internal service failover might be. It depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;health check detection&lt;/li&gt;
&lt;li&gt;Route53 failover policy&lt;/li&gt;
&lt;li&gt;DNS cache expiry&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Multi-AZ vs Cross-Region Read Replicas
&lt;/h2&gt;

&lt;p&gt;One of the most useful lessons today was understanding that these solve different problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-AZ&lt;/strong&gt; protects against &lt;strong&gt;Availability Zone failure within one region&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If one AZ in &lt;code&gt;us-east-1&lt;/code&gt; fails, AWS can fail the database over to another AZ in that same region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-region read replicas&lt;/strong&gt; protect against a &lt;strong&gt;full regional outage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the primary region has a larger failure, the replica already exists in the secondary region and can become part of the recovery plan.&lt;/p&gt;

&lt;p&gt;So the difference is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-AZ&lt;/strong&gt; = resilience within a region&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cross-region replica&lt;/strong&gt; = resilience across regions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You need both ideas to think clearly about high availability.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Useful Debugging Lesson
&lt;/h2&gt;

&lt;p&gt;Day 27 had a few real-world AWS constraints that made the project more realistic.&lt;/p&gt;

&lt;p&gt;A few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ALB naming had to be shortened to stay within AWS limits&lt;/li&gt;
&lt;li&gt;the RDS module needed the application security group ID, not the ASG name&lt;/li&gt;
&lt;li&gt;the cross-region replica needed proper encryption handling to be created from an encrypted source&lt;/li&gt;
&lt;li&gt;the backend had to be bootstrapped first before the main environment could use remote state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was a good reminder that Terraform can describe the architecture, but AWS service rules still matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote State and Bootstrap
&lt;/h2&gt;

&lt;p&gt;Like previous days, I used a remote backend with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 for Terraform state&lt;/li&gt;
&lt;li&gt;DynamoDB for state locking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this project, I also used a separate &lt;code&gt;bootstrap&lt;/code&gt; stack to create the backend resources first.&lt;/p&gt;

&lt;p&gt;That mattered because Terraform cannot use a backend bucket and lock table until they already exist.&lt;/p&gt;

&lt;p&gt;Remote state keeps the stack safer and more realistic, especially once infrastructure starts growing beyond one simple environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;After verifying the app worked, I destroyed both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Day 27 production stack&lt;/li&gt;
&lt;li&gt;the bootstrap backend resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because NAT Gateways, ALBs, EC2 instances, and RDS resources can keep generating cost even after the learning task is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Day 27 helped me connect several Terraform lessons into one practical multi-region system.&lt;/p&gt;

&lt;p&gt;A high-availability architecture is not just “more resources.” It is the relationship between networking, load balancing, scaling, database topology, failover strategy, and state management.&lt;/p&gt;

&lt;p&gt;The biggest lesson:&lt;/p&gt;

&lt;p&gt;Terraform modules are not just for organizing files. They help define the boundaries of responsibility in infrastructure.&lt;/p&gt;

&lt;p&gt;That is what makes the system easier to understand, reuse, and safely change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 27 of my 30-Day Terraform Challenge.&lt;/p&gt;

&lt;p&gt;See you on Day 28.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>aws</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Building a Scalable Web Application on AWS with EC2, ALB, and Auto Scaling using Terraform</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Wed, 22 Apr 2026 22:27:12 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/building-a-scalable-web-application-on-aws-with-ec2-alb-and-auto-scaling-using-terraform-5162</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/building-a-scalable-web-application-on-aws-with-ec2-alb-and-auto-scaling-using-terraform-5162</guid>
      <description>&lt;p&gt;Day 26 of my Terraform journey moved from static hosting to dynamic compute.&lt;/p&gt;

&lt;p&gt;Yesterday, I deployed a static website on S3. Today, I built a scalable web application stack on AWS using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 Launch Template&lt;/li&gt;
&lt;li&gt;Application Load Balancer&lt;/li&gt;
&lt;li&gt;Auto Scaling Group&lt;/li&gt;
&lt;li&gt;CloudWatch alarms&lt;/li&gt;
&lt;li&gt;scaling policies&lt;/li&gt;
&lt;li&gt;reusable Terraform modules&lt;/li&gt;
&lt;li&gt;remote state with S3 and DynamoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub reference:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_26" rel="noopener noreferrer"&gt;https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_26&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;For Day 26, I separated the infrastructure into three focused modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;day26-scalable-web-app/
├── modules/
│   ├── ec2/
│   ├── alb/
│   └── asg/
├── envs/
│   └── dev/
├── bootstrap/
├── backend.tf
└── provider.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal was not just to make the app work.&lt;br&gt;&lt;br&gt;
The goal was to make the design reusable, understandable, and safe to change.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Three Modules Instead of One?
&lt;/h2&gt;

&lt;p&gt;I split the project into three modules because each part has a different responsibility.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ec2&lt;/code&gt; module owns the compute template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Launch Template&lt;/li&gt;
&lt;li&gt;instance security group&lt;/li&gt;
&lt;li&gt;user data script&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;alb&lt;/code&gt; module owns traffic entry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application Load Balancer&lt;/li&gt;
&lt;li&gt;target group&lt;/li&gt;
&lt;li&gt;listener&lt;/li&gt;
&lt;li&gt;ALB security group&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;asg&lt;/code&gt; module owns scaling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto Scaling Group&lt;/li&gt;
&lt;li&gt;scaling policies&lt;/li&gt;
&lt;li&gt;CloudWatch CPU alarms&lt;/li&gt;
&lt;li&gt;dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything lived in one large file, it would still work, but it would be harder to reuse and harder to reason about.&lt;/p&gt;

&lt;p&gt;Modules make the boundaries clear.&lt;/p&gt;
&lt;h2&gt;
  
  
  How the Modules Connect
&lt;/h2&gt;

&lt;p&gt;The most important part of today was understanding the data flow between modules.&lt;/p&gt;

&lt;p&gt;The EC2 module creates the launch template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch_template_id&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch_template_version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those outputs flow into the ASG module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;launch_template_id&lt;/span&gt;      &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch_template_id&lt;/span&gt;
&lt;span class="nx"&gt;launch_template_version&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch_template_version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells the Auto Scaling Group what kind of EC2 instances to launch.&lt;/p&gt;

&lt;p&gt;Then the ALB module creates a target group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alb&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target_group_arn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That output flows into the ASG module too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;target_group_arns&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target_group_arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This closes the loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EC2 Launch Template → ASG → ALB Target Group → ALB DNS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ASG creates instances from the launch template, then registers those instances behind the load balancer target group.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment Output
&lt;/h2&gt;

&lt;p&gt;After applying the Terraform plan, Terraform returned the ALB DNS name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;day26-web-alb-dev-400577037.us-east-1.elb.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I opened it in the browser, the app responded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deployed with Terraform - Day 26
Environment: dev
Served by an Auto Scaling Group behind an Application Load Balancer.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ASG healthy instances screenshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Paste AWS Console screenshot here showing the Auto Scaling Group with healthy instances]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why health_check_type = "ELB" Matters
&lt;/h2&gt;

&lt;p&gt;One important setting today was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;health_check_type&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ELB"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells the Auto Scaling Group to use load balancer health checks, not only EC2 instance status checks.&lt;/p&gt;

&lt;p&gt;That matters because an EC2 instance can be “running” but still not serving the application correctly.&lt;/p&gt;

&lt;p&gt;With ELB health checks enabled, the ASG checks whether the instance is healthy from the load balancer’s point of view. If the app fails behind the ALB, the ASG can replace the instance.&lt;/p&gt;

&lt;p&gt;This is much closer to real production behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens When CPU Exceeds 70%
&lt;/h2&gt;

&lt;p&gt;The ASG module includes CloudWatch alarms and scaling policies.&lt;/p&gt;

&lt;p&gt;When average CPU goes above 70%, this happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CloudWatch alarm enters &lt;code&gt;ALARM&lt;/code&gt; state.&lt;/li&gt;
&lt;li&gt;The alarm triggers the scale-out policy.&lt;/li&gt;
&lt;li&gt;The scale-out policy increases ASG capacity by 1.&lt;/li&gt;
&lt;li&gt;The ASG launches a new EC2 instance using the Launch Template.&lt;/li&gt;
&lt;li&gt;The new instance registers with the ALB target group.&lt;/li&gt;
&lt;li&gt;The ALB starts sending traffic to the new healthy instance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the feedback loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CPU spike → CloudWatch alarm → scaling policy → new EC2 instance → ALB target group
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also a scale-in policy for low CPU, so the system can reduce capacity when traffic drops.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote State
&lt;/h2&gt;

&lt;p&gt;Like previous days, I used a remote backend with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 for Terraform state&lt;/li&gt;
&lt;li&gt;DynamoDB for state locking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents local-only state problems and protects against two people applying changes at the same time.&lt;/p&gt;

&lt;p&gt;Remote state is one of those things that feels small at first, but it becomes critical as soon as infrastructure work becomes collaborative.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Useful Debugging Lesson
&lt;/h2&gt;

&lt;p&gt;I hit a Terraform planning issue around &lt;code&gt;for_each&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The lesson was simple but important:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;for_each&lt;/code&gt; keys must be known during planning.&lt;/p&gt;

&lt;p&gt;If Terraform cannot know the keys until apply time, it cannot build the dependency graph safely. The fix was to use stable keys and put dynamic values inside the map values instead.&lt;/p&gt;

&lt;p&gt;That was a good reminder that Terraform is very strict about what must be known at plan time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;After verifying the app worked, I destroyed both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the dev application stack&lt;/li&gt;
&lt;li&gt;the bootstrap backend resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because ALBs and EC2 instances can keep generating cost even after the learning task is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Day 26 helped me connect several Terraform lessons into one practical system.&lt;/p&gt;

&lt;p&gt;A scalable web application is not just EC2. It is the relationship between compute, networking, health checks, monitoring, scaling policies, and state management.&lt;/p&gt;

&lt;p&gt;The biggest lesson:&lt;/p&gt;

&lt;p&gt;Terraform modules are not just for organizing files. They help define the boundaries of responsibility in infrastructure.&lt;/p&gt;

&lt;p&gt;That is what makes the system easier to understand, reuse, and safely change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 26 of my 30-Day Terraform Challenge.&lt;br&gt;&lt;br&gt;
See you on Day 27.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>terraform</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Deploying a Static Website on AWS S3 with Terraform: A Beginner's Guide</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Tue, 21 Apr 2026 17:36:12 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/deploying-a-static-website-on-aws-s3-with-terraform-a-beginners-guide-ki</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/deploying-a-static-website-on-aws-s3-with-terraform-a-beginners-guide-ki</guid>
      <description>&lt;p&gt;Day 25 of my 30-Day Terraform Challenge was a practical build: deploy a static website on AWS using Terraform.&lt;/p&gt;

&lt;p&gt;The goal was to apply the habits from the previous days in one small project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reusable modules&lt;/li&gt;
&lt;li&gt;environment separation&lt;/li&gt;
&lt;li&gt;remote state&lt;/li&gt;
&lt;li&gt;clean variables&lt;/li&gt;
&lt;li&gt;consistent tagging&lt;/li&gt;
&lt;li&gt;reviewed plans&lt;/li&gt;
&lt;li&gt;safe cleanup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub reference:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_25" rel="noopener noreferrer"&gt;Day 25 code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built a static website stack with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an S3 bucket&lt;/li&gt;
&lt;li&gt;S3 static website hosting&lt;/li&gt;
&lt;li&gt;uploaded &lt;code&gt;index.html&lt;/code&gt; and &lt;code&gt;error.html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;bucket policy for public website reads&lt;/li&gt;
&lt;li&gt;reusable Terraform module&lt;/li&gt;
&lt;li&gt;dev environment configuration&lt;/li&gt;
&lt;li&gt;remote backend with S3 and DynamoDB&lt;/li&gt;
&lt;li&gt;optional CloudFront support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The website was verified through the S3 website endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://mary-mutua-day25-static-website-dev-718417034043.s3-website-us-east-1.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: I destroyed the resources after verification to avoid AWS charges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;I used this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;day_25/day25-static-website/
├── bootstrap/
│   ├── main.tf
│   ├── outputs.tf
│   └── variables.tf
├── envs/
│   └── dev/
│       ├── backend.tf
│       ├── main.tf
│       ├── outputs.tf
│       ├── provider.tf
│       ├── terraform.tfvars
│       └── variables.tf
└── modules/
    └── s3-static-website/
        ├── main.tf
        ├── outputs.tf
        └── variables.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module lives in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;modules/s3-static-website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dev environment calls that module from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;envs/dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation matters because the module should be reusable, while the environment folder should contain environment-specific values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Used a Module
&lt;/h2&gt;

&lt;p&gt;I could have put everything in one &lt;code&gt;main.tf&lt;/code&gt;, but that would not scale well.&lt;/p&gt;

&lt;p&gt;A module lets me define the static website once and reuse it later for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dev&lt;/li&gt;
&lt;li&gt;staging&lt;/li&gt;
&lt;li&gt;production&lt;/li&gt;
&lt;li&gt;another website&lt;/li&gt;
&lt;li&gt;another AWS account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The module accepts inputs like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;
&lt;span class="nx"&gt;index_document&lt;/span&gt;
&lt;span class="nx"&gt;error_document&lt;/span&gt;
&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="nx"&gt;enable_cloudfront&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the dev environment passes values into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"static_website"&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/s3-static-website"&lt;/span&gt;

  &lt;span class="nx"&gt;bucket_name&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt;
  &lt;span class="nx"&gt;environment&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
  &lt;span class="nx"&gt;index_document&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index_document&lt;/span&gt;
  &lt;span class="nx"&gt;error_document&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error_document&lt;/span&gt;
  &lt;span class="nx"&gt;enable_cloudfront&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_cloudfront&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Owner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-challenge"&lt;/span&gt;
    &lt;span class="nx"&gt;Day&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"25"&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;That is the DRY principle in practice: define the infrastructure pattern once, then reuse it with different inputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The S3 Website Module
&lt;/h2&gt;

&lt;p&gt;The module creates an S3 bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"website"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt;
  &lt;span class="nx"&gt;force_destroy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;force_destroy&lt;/code&gt; setting is useful for dev because it allows Terraform to delete the bucket even when it contains uploaded objects.&lt;/p&gt;

&lt;p&gt;But I would not want that behavior in production, so the condition protects production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;force_destroy&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module also enables static website hosting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket_website_configuration"&lt;/span&gt; &lt;span class="s2"&gt;"website"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;website&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;index_document&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;suffix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index_document&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;error_document&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error_document&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;Then Terraform uploads the HTML files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_object"&lt;/span&gt; &lt;span class="s2"&gt;"index"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;website&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;"index.html"&lt;/span&gt;
  &lt;span class="nx"&gt;content_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"text/html"&lt;/span&gt;

  &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="sh"&gt;
    &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Terraform Static Website&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;h1&amp;gt;Deployed with Terraform&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;Environment: ${var.environment}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Bucket: ${var.bucket_name}&amp;lt;/p&amp;gt;
    &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;  HTML
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Remote State
&lt;/h2&gt;

&lt;p&gt;Before deploying the website, I created a remote backend using a bootstrap folder.&lt;/p&gt;

&lt;p&gt;That created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an S3 bucket for Terraform state&lt;/li&gt;
&lt;li&gt;a DynamoDB table for state locking&lt;/li&gt;
&lt;li&gt;S3 encryption&lt;/li&gt;
&lt;li&gt;S3 versioning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remote state matters because Terraform state is how Terraform tracks real infrastructure. Keeping it locally is risky when working with teams or across machines.&lt;/p&gt;

&lt;p&gt;The backend protects the workflow by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;storing state remotely&lt;/li&gt;
&lt;li&gt;preventing concurrent changes with locking&lt;/li&gt;
&lt;li&gt;keeping state versions for recovery&lt;/li&gt;
&lt;li&gt;avoiding local-only state files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One important cleanup lesson: if your state bucket has versioning enabled, deleting the bucket later requires deleting all object versions and delete markers first.&lt;/p&gt;

&lt;p&gt;That was a useful real-world reminder.&lt;/p&gt;

&lt;h2&gt;
  
  
  CloudFront Configuration
&lt;/h2&gt;

&lt;p&gt;The module includes optional CloudFront support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudfront_distribution"&lt;/span&gt; &lt;span class="s2"&gt;"website"&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="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_cloudfront&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;enabled&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;default_root_object&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index_document&lt;/span&gt;
  &lt;span class="nx"&gt;price_class&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PriceClass_100"&lt;/span&gt;

  &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;domain_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket_website_configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;website&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;website_endpoint&lt;/span&gt;
    &lt;span class="nx"&gt;origin_id&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"s3-website"&lt;/span&gt;

    &lt;span class="nx"&gt;custom_origin_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;http_port&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
      &lt;span class="nx"&gt;https_port&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
      &lt;span class="nx"&gt;origin_protocol_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http-only"&lt;/span&gt;
      &lt;span class="nx"&gt;origin_ssl_protocols&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"TLSv1.2"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this lab, CloudFront creation was blocked by AWS account verification.&lt;/p&gt;

&lt;p&gt;Terraform and the AWS Console both returned the same issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your account must be verified before you can add new CloudFront resources.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I disabled CloudFront in dev:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;enable_cloudfront&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module is still CloudFront-ready, but the working deployment used the S3 website endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment Commands
&lt;/h2&gt;

&lt;p&gt;From the bootstrap folder, I created the remote backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform validate
terraform plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bootstrap.tfplan
terraform apply bootstrap.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I initialized the dev environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_25/day25-static-website/envs/dev init &lt;span class="nt"&gt;-reconfigure&lt;/span&gt;
terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_25/day25-static-website/envs/dev validate
terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_25/day25-static-website/envs/dev plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day25.tfplan
terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_25/day25-static-website/envs/dev apply day25.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plan showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan: 7 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After apply, I checked the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_25/day25-static-website/envs/dev output &lt;span class="nt"&gt;-raw&lt;/span&gt; website_endpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I opened the S3 website endpoint in the browser and confirmed the site loaded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;Because this was a lab, cleanup mattered.&lt;/p&gt;

&lt;p&gt;I destroyed the website stack first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_25/day25-static-website/envs/dev plan &lt;span class="nt"&gt;-destroy&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day25-destroy.tfplan
terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_25/day25-static-website/envs/dev apply day25-destroy.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I cleaned up the bootstrap backend after deleting versioned objects from the state bucket.&lt;/p&gt;

&lt;p&gt;That final cleanup was a good reminder: production-grade safety features like S3 versioning are excellent, but they also change how cleanup works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Day 25 was not just about S3.&lt;/p&gt;

&lt;p&gt;The bigger lessons were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reusable modules make infrastructure easier to scale&lt;/li&gt;
&lt;li&gt;environment folders keep dev/staging/prod cleanly separated&lt;/li&gt;
&lt;li&gt;remote state protects collaboration and recovery&lt;/li&gt;
&lt;li&gt;saved plans make changes reviewable&lt;/li&gt;
&lt;li&gt;cleanup is part of the workflow&lt;/li&gt;
&lt;li&gt;real cloud provider account limits can block otherwise-correct Terraform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was a small project, but it pulled together many best practices from the challenge so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;A static website may look simple, but deploying it properly with Terraform teaches important infrastructure habits.&lt;/p&gt;

&lt;p&gt;The goal is not just to make a page load.&lt;/p&gt;

&lt;p&gt;The goal is to make the deployment repeatable, reviewable, reusable, and safe to clean up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Code
&lt;/h2&gt;

&lt;p&gt;GitHub reference:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_25" rel="noopener noreferrer"&gt;GitHub Link&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 25 of my 30-Day Terraform Challenge.&lt;br&gt;&lt;br&gt;
See you on Day 26.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>beginners</category>
      <category>terraform</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Final Preparation for the Terraform Associate Exam</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Tue, 21 Apr 2026 14:32:05 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/my-final-preparation-for-the-terraform-associate-exam-1mmd</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/my-final-preparation-for-the-terraform-associate-exam-1mmd</guid>
      <description>&lt;p&gt;Day 24 of my 30-Day Terraform Challenge was pure exam preparation.&lt;/p&gt;

&lt;p&gt;No new AWS resources.&lt;br&gt;&lt;br&gt;
No new modules.&lt;br&gt;&lt;br&gt;
No new deployments.&lt;/p&gt;

&lt;p&gt;Just one goal:&lt;/p&gt;

&lt;p&gt;to close the gap between “I understand Terraform” and “I can answer Terraform questions accurately under time pressure.”&lt;/p&gt;

&lt;p&gt;After spending the last few weeks building real infrastructure with Terraform, today was about sharpening the details that matter for the Terraform Associate exam.&lt;/p&gt;

&lt;p&gt;GitHub reference:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_24" rel="noopener noreferrer"&gt;Day 24 GitHub Link&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Day 24 Was Different
&lt;/h2&gt;

&lt;p&gt;Most of the challenge so far has been hands-on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2&lt;/li&gt;
&lt;li&gt;ALBs&lt;/li&gt;
&lt;li&gt;Auto Scaling Groups&lt;/li&gt;
&lt;li&gt;remote state&lt;/li&gt;
&lt;li&gt;modules&lt;/li&gt;
&lt;li&gt;testing&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;Terraform Cloud concepts&lt;/li&gt;
&lt;li&gt;Sentinel policies&lt;/li&gt;
&lt;li&gt;deployment workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That hands-on work helped a lot.&lt;/p&gt;

&lt;p&gt;But certification exams test precision.&lt;/p&gt;

&lt;p&gt;It is not enough to know that Terraform uses state.&lt;br&gt;&lt;br&gt;
You need to know exactly what happens when you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform state &lt;span class="nb"&gt;rm
&lt;/span&gt;terraform import
terraform init &lt;span class="nt"&gt;-upgrade&lt;/span&gt;
terraform apply &lt;span class="nt"&gt;-refresh-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was the focus for today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Resources I Used
&lt;/h2&gt;

&lt;p&gt;For Day 24, I focused on the official HashiCorp exam preparation resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Terraform Associate Study Guide&lt;br&gt;&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-study-004" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-study-004&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Official Sample Questions&lt;br&gt;&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-questions-004" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-questions-004&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Terraform Associate Review Tutorial&lt;br&gt;&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-review-004" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-review-004&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The official sample questions were especially useful for understanding the style of questions, but they are not a full mock exam. They are more like a format check.&lt;/p&gt;

&lt;p&gt;The review tutorial was useful as a checklist of what to study, not as a question bank.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Exam Simulation Result
&lt;/h2&gt;

&lt;p&gt;I did a 57-question timed simulation based on the official domains and my weak areas from Day 23.&lt;/p&gt;

&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total questions: 57
Correct answers: 44
Wrong answers: 13
Score: 77.2%
Passing target: 70%
Result: Pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was encouraging.&lt;/p&gt;

&lt;p&gt;But more importantly, it showed me exactly where my weak spots were.&lt;/p&gt;

&lt;h2&gt;
  
  
  Topics I Missed
&lt;/h2&gt;

&lt;p&gt;The questions I missed were not random. They clustered around common exam traps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.terraform.lock.hcl&lt;/code&gt; vs &lt;code&gt;terraform.tfstate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform init -upgrade&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;provider alias syntax&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform apply -refresh-only&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Terraform Cloud vs Terraform Enterprise&lt;/li&gt;
&lt;li&gt;backend migration behavior&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;random_password&lt;/code&gt; and secrets in state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform state rm&lt;/code&gt; vs real infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform import&lt;/code&gt; behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was actually good news.&lt;/p&gt;

&lt;p&gt;It means the gap is not “I do not understand Terraform.”&lt;br&gt;&lt;br&gt;
The gap is “I need to memorize exact command behavior.”&lt;/p&gt;

&lt;p&gt;That is much easier to fix.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Biggest Exam Traps I Reviewed
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. State vs lock file
&lt;/h3&gt;

&lt;p&gt;This is one of the easiest things to mix up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform.tfstate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tracks real infrastructure and maps it to Terraform resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.terraform.lock.hcl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tracks provider versions and checksums.&lt;/p&gt;

&lt;p&gt;The lock file is about dependency consistency.&lt;br&gt;&lt;br&gt;
The state file is about infrastructure reality.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. &lt;code&gt;terraform state rm&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This command does not destroy infrastructure.&lt;/p&gt;

&lt;p&gt;It only removes the resource from Terraform state.&lt;/p&gt;

&lt;p&gt;So if you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform state &lt;span class="nb"&gt;rm &lt;/span&gt;aws_instance.web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the EC2 instance still exists in AWS. Terraform just stops managing it.&lt;/p&gt;

&lt;p&gt;That is a very exam-friendly trap.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;terraform import&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;terraform import&lt;/code&gt; brings an existing real-world object into Terraform state.&lt;/p&gt;

&lt;p&gt;But it does not write the &lt;code&gt;.tf&lt;/code&gt; resource block for you.&lt;/p&gt;

&lt;p&gt;You still need to write the matching configuration yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;terraform apply -refresh-only&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This updates Terraform state to match real infrastructure.&lt;/p&gt;

&lt;p&gt;It is not the same as a normal apply.&lt;/p&gt;

&lt;p&gt;It is useful when you want Terraform to refresh its understanding of reality without making normal create/update/destroy changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Sensitive values
&lt;/h3&gt;

&lt;p&gt;This one matters a lot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;masks values in CLI output.&lt;/p&gt;

&lt;p&gt;But it does not guarantee the value is absent from state.&lt;/p&gt;

&lt;p&gt;That means state security is still important.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Provider aliases
&lt;/h3&gt;

&lt;p&gt;The correct syntax is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"west"&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;"us-west-2"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;west&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;"example-west-bucket"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key part is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;west&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not &lt;code&gt;alias = aws.west&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Exam-Day Strategy
&lt;/h2&gt;

&lt;p&gt;The Terraform Associate exam is time-sensitive.&lt;/p&gt;

&lt;p&gt;The strategy I am taking is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Move quickly through easy questions.&lt;/li&gt;
&lt;li&gt;Flag uncertain questions.&lt;/li&gt;
&lt;li&gt;Do not spend too long on one hard question.&lt;/li&gt;
&lt;li&gt;Return to flagged questions at the end.&lt;/li&gt;
&lt;li&gt;Watch carefully for wording like “state,” “real infrastructure,” “configuration,” and “select TWO.”&lt;/li&gt;
&lt;li&gt;Eliminate obviously wrong answers first.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The biggest reminder for myself:&lt;/p&gt;

&lt;p&gt;hard questions and easy questions are worth the same.&lt;/p&gt;

&lt;p&gt;So it is better to secure all the easy points first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Topics I Will Review Again
&lt;/h2&gt;

&lt;p&gt;Before the exam, I want to revisit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform state commands&lt;/li&gt;
&lt;li&gt;backend migration&lt;/li&gt;
&lt;li&gt;provider aliases&lt;/li&gt;
&lt;li&gt;HCP Terraform vs Terraform Enterprise&lt;/li&gt;
&lt;li&gt;sensitive values in state&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform init -upgrade&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform import&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform apply -refresh-only&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are now my final review targets.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Helped Most
&lt;/h2&gt;

&lt;p&gt;The most useful preparation was not just reading.&lt;/p&gt;

&lt;p&gt;It was combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;real hands-on Terraform work&lt;/li&gt;
&lt;li&gt;official documentation&lt;/li&gt;
&lt;li&gt;practice questions&lt;/li&gt;
&lt;li&gt;timed simulation&lt;/li&gt;
&lt;li&gt;reviewing every wrong answer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The timed simulation was especially helpful because it showed how easy it is to make small mistakes under pressure.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Main Takeaway
&lt;/h2&gt;

&lt;p&gt;The Terraform Associate exam rewards precise understanding.&lt;/p&gt;

&lt;p&gt;It tests whether you know what Terraform does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to configuration&lt;/li&gt;
&lt;li&gt;to state&lt;/li&gt;
&lt;li&gt;to real infrastructure&lt;/li&gt;
&lt;li&gt;during each command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That precision only comes from practice.&lt;/p&gt;

&lt;p&gt;Day 24 gave me a clear picture of where I stand:&lt;/p&gt;

&lt;p&gt;I am in passing range, but I still have a few sharp edges to clean up.&lt;/p&gt;

&lt;p&gt;And honestly, that feels like a good place to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 24 of my 30-Day Terraform Challenge.&lt;/p&gt;

&lt;p&gt;The book is complete.&lt;br&gt;&lt;br&gt;
The labs are done.&lt;br&gt;&lt;br&gt;
Now the focus is certification readiness.&lt;/p&gt;

&lt;p&gt;See you on Day 25.&lt;/p&gt;

</description>
      <category>career</category>
      <category>devops</category>
      <category>learning</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Preparing for the Terraform Associate Exam - Key Resources and Tips</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Mon, 20 Apr 2026 12:37:08 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/preparing-for-the-terraform-associate-exam-key-resources-and-tips-1lk</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/preparing-for-the-terraform-associate-exam-key-resources-and-tips-1lk</guid>
      <description>&lt;p&gt;Day 23 of my Terraform journey marked a clear shift.&lt;/p&gt;

&lt;p&gt;The book is done.&lt;br&gt;&lt;br&gt;
The labs are running.&lt;br&gt;&lt;br&gt;
The workflows are built.&lt;/p&gt;

&lt;p&gt;Now the focus is exam preparation.&lt;/p&gt;

&lt;p&gt;For the past three weeks, I have been building real Terraform infrastructure: EC2 instances, load balancers, Auto Scaling Groups, reusable modules, remote state examples, manual tests, Terratest, GitHub Actions, Sentinel policies, and full deployment workflows.&lt;/p&gt;

&lt;p&gt;But certification preparation requires a different mindset.&lt;/p&gt;

&lt;p&gt;It is not enough to say:&lt;/p&gt;

&lt;p&gt;“I have used Terraform.”&lt;/p&gt;

&lt;p&gt;The better question is:&lt;/p&gt;

&lt;p&gt;“Can I explain what Terraform is doing, why it is doing it, and what happens in edge cases?”&lt;/p&gt;

&lt;p&gt;That was the focus of Day 23.&lt;/p&gt;

&lt;p&gt;Official study guide:&lt;br&gt;&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-study-004" rel="noopener noreferrer"&gt;Terraform Associate 004 Study Guide&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Starting With the Official Objectives
&lt;/h2&gt;

&lt;p&gt;The first thing I did was go back to the official HashiCorp Terraform Associate study material.&lt;/p&gt;

&lt;p&gt;That matters because the exam is not based on what I personally used most during the challenge.&lt;/p&gt;

&lt;p&gt;It is based on the published exam objectives.&lt;/p&gt;

&lt;p&gt;Some topics came up almost every day in my hands-on work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;terraform init&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform plan&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform apply&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;variables&lt;/li&gt;
&lt;li&gt;outputs&lt;/li&gt;
&lt;li&gt;providers&lt;/li&gt;
&lt;li&gt;modules&lt;/li&gt;
&lt;li&gt;state&lt;/li&gt;
&lt;li&gt;remote state&lt;/li&gt;
&lt;li&gt;workspaces&lt;/li&gt;
&lt;li&gt;lifecycle rules&lt;/li&gt;
&lt;li&gt;testing workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other topics appeared less often but are still important for the exam:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;terraform state mv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform state rm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform import&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;provider aliases&lt;/li&gt;
&lt;li&gt;non-cloud providers&lt;/li&gt;
&lt;li&gt;HCP Terraform workspaces&lt;/li&gt;
&lt;li&gt;Terraform Cloud variable handling&lt;/li&gt;
&lt;li&gt;private registry&lt;/li&gt;
&lt;li&gt;cost estimation&lt;/li&gt;
&lt;li&gt;Sentinel policy concepts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why Day 23 was not about deploying another big stack.&lt;/p&gt;

&lt;p&gt;It was about auditing what I know honestly.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Self-Audit Approach
&lt;/h2&gt;

&lt;p&gt;I used a simple Green / Yellow / Red rating system.&lt;/p&gt;
&lt;h3&gt;
  
  
  Green
&lt;/h3&gt;

&lt;p&gt;I can explain it clearly and I have done it hands-on.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;core Terraform workflow&lt;/li&gt;
&lt;li&gt;modules&lt;/li&gt;
&lt;li&gt;variables and outputs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform plan&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform apply&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;remote state concepts&lt;/li&gt;
&lt;li&gt;GitHub Actions workflow basics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Yellow
&lt;/h3&gt;

&lt;p&gt;I understand it conceptually, but I need more practice.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;state subcommands&lt;/li&gt;
&lt;li&gt;provider alias syntax&lt;/li&gt;
&lt;li&gt;complex Terraform functions&lt;/li&gt;
&lt;li&gt;non-cloud providers&lt;/li&gt;
&lt;li&gt;import edge cases&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Red
&lt;/h3&gt;

&lt;p&gt;I am not confident enough yet and need focused study.&lt;/p&gt;

&lt;p&gt;For me, the closest Red/Yellow area is HCP Terraform detail.&lt;/p&gt;

&lt;p&gt;I understand the purpose, but I still need to review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remote runs vs local runs&lt;/li&gt;
&lt;li&gt;HCP Terraform workspaces vs CLI workspaces&lt;/li&gt;
&lt;li&gt;variable sets&lt;/li&gt;
&lt;li&gt;Sentinel policy timing&lt;/li&gt;
&lt;li&gt;private registry behavior&lt;/li&gt;
&lt;li&gt;cost estimation limitations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest benefit of this audit was clarity.&lt;/p&gt;

&lt;p&gt;Instead of saying “I need to study Terraform,” I now know exactly what to study.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Areas I Found Most Challenging
&lt;/h2&gt;

&lt;p&gt;The hardest areas are not the big visible Terraform commands.&lt;/p&gt;

&lt;p&gt;They are the small operational details.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. State Commands
&lt;/h3&gt;

&lt;p&gt;This is one section I think many people underestimate.&lt;/p&gt;

&lt;p&gt;Commands like these matter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform state list
terraform state show
terraform state &lt;span class="nb"&gt;mv
&lt;/span&gt;terraform state &lt;span class="nb"&gt;rm
&lt;/span&gt;terraform import
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exam can ask scenario-based questions such as:&lt;/p&gt;

&lt;p&gt;“What happens when you run &lt;code&gt;terraform state rm&lt;/code&gt;?”&lt;/p&gt;

&lt;p&gt;The answer is important:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;terraform state rm&lt;/code&gt; removes the resource from Terraform state only.&lt;br&gt;&lt;br&gt;
It does not destroy the real infrastructure.&lt;/p&gt;

&lt;p&gt;That distinction is exactly the kind of thing the exam tests.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Provider Aliases
&lt;/h3&gt;

&lt;p&gt;Provider aliases are easy to understand, but the syntax needs to be familiar.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"west"&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;"us-west-2"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"west_bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;west&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;"example-west-bucket"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key idea:&lt;/p&gt;

&lt;p&gt;Terraform uses the default provider unless you explicitly tell a resource or module to use an aliased provider.&lt;/p&gt;

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

&lt;p&gt;HCP Terraform is another area I want to review carefully.&lt;/p&gt;

&lt;p&gt;The exam expects you to understand concepts like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remote execution&lt;/li&gt;
&lt;li&gt;workspace variables&lt;/li&gt;
&lt;li&gt;sensitive variables&lt;/li&gt;
&lt;li&gt;environment variables&lt;/li&gt;
&lt;li&gt;private registry&lt;/li&gt;
&lt;li&gt;policy enforcement&lt;/li&gt;
&lt;li&gt;cost estimation&lt;/li&gt;
&lt;li&gt;team workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if you mostly use local Terraform or an S3 backend, these concepts are still exam material.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Non-Cloud Providers
&lt;/h3&gt;

&lt;p&gt;Terraform is not only for AWS, Azure, or Google Cloud.&lt;/p&gt;

&lt;p&gt;It also has providers like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"random_id"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;byte_length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"local_file"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Hello from Terraform"&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/output.txt"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a good exam reminder:&lt;/p&gt;

&lt;p&gt;A Terraform provider is a plugin that exposes resource types.&lt;br&gt;&lt;br&gt;
It does not have to manage a cloud platform.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Study Plan Structure
&lt;/h2&gt;

&lt;p&gt;Instead of creating a vague study plan like:&lt;/p&gt;

&lt;p&gt;“Review Terraform state”&lt;/p&gt;

&lt;p&gt;I made the plan action-based.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Confidence&lt;/th&gt;
&lt;th&gt;Study Method&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State commands&lt;/td&gt;
&lt;td&gt;Yellow&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;state list&lt;/code&gt;, &lt;code&gt;state show&lt;/code&gt;, &lt;code&gt;state mv&lt;/code&gt;, &lt;code&gt;state rm&lt;/code&gt;, and &lt;code&gt;import&lt;/code&gt; on test resources&lt;/td&gt;
&lt;td&gt;60 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HCP Terraform&lt;/td&gt;
&lt;td&gt;Yellow/Red&lt;/td&gt;
&lt;td&gt;Review workspaces, variable sets, remote runs, policies, and registry&lt;/td&gt;
&lt;td&gt;75 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider aliases&lt;/td&gt;
&lt;td&gt;Yellow&lt;/td&gt;
&lt;td&gt;Write one AWS alias example and one module provider mapping example&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-cloud providers&lt;/td&gt;
&lt;td&gt;Yellow&lt;/td&gt;
&lt;td&gt;Create a tiny &lt;code&gt;random&lt;/code&gt; + &lt;code&gt;local_file&lt;/code&gt; config&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex types/functions&lt;/td&gt;
&lt;td&gt;Yellow&lt;/td&gt;
&lt;td&gt;Practice maps, objects, &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;merge&lt;/code&gt;, &lt;code&gt;lookup&lt;/code&gt;, &lt;code&gt;toset&lt;/code&gt;, and splats&lt;/td&gt;
&lt;td&gt;45 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Official sample questions&lt;/td&gt;
&lt;td&gt;Yellow&lt;/td&gt;
&lt;td&gt;Complete questions and explain wrong answers&lt;/td&gt;
&lt;td&gt;45 min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That kind of plan is much easier to follow because every study block has an output.&lt;/p&gt;
&lt;h2&gt;
  
  
  CLI Commands People Underestimate
&lt;/h2&gt;

&lt;p&gt;If there is one section I would tell people not to ignore, it is the Terraform CLI.&lt;/p&gt;

&lt;p&gt;Most people remember:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform plan
terraform apply
terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the exam also expects you to understand commands like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nb"&gt;fmt
&lt;/span&gt;terraform validate
terraform output
terraform state list
terraform state show
terraform state &lt;span class="nb"&gt;mv
&lt;/span&gt;terraform state &lt;span class="nb"&gt;rm
&lt;/span&gt;terraform import
terraform workspace
terraform providers
terraform login
terraform graph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing is not just memorizing command names.&lt;/p&gt;

&lt;p&gt;You need to know what they affect.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;terraform fmt&lt;/code&gt; changes formatting only&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform validate&lt;/code&gt; checks configuration, not live infrastructure health&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform state rm&lt;/code&gt; removes something from state, not from AWS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform import&lt;/code&gt; adds an existing object to state but does not write a full configuration for you&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform graph&lt;/code&gt; outputs a dependency graph&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform login&lt;/code&gt; authenticates the CLI to HCP Terraform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those details matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice Questions Help More Than Passive Reading
&lt;/h2&gt;

&lt;p&gt;I also worked through the official sample questions and wrote a few of my own.&lt;/p&gt;

&lt;p&gt;Writing your own questions is surprisingly useful because it forces you to understand the material well enough to create plausible wrong answers.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A saved Terraform plan becomes stale. What should you do?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Regenerate the plan, review it again, then apply the new saved plan.&lt;/p&gt;

&lt;p&gt;That question came directly from something I experienced during the challenge.&lt;/p&gt;

&lt;p&gt;That is the best kind of study material: real mistakes turned into exam memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Biggest Exam Prep Takeaway
&lt;/h2&gt;

&lt;p&gt;The Terraform Associate exam is not only about knowing how to write &lt;code&gt;.tf&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;It tests whether you understand the Terraform workflow.&lt;/p&gt;

&lt;p&gt;That means knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what Terraform reads&lt;/li&gt;
&lt;li&gt;what Terraform writes&lt;/li&gt;
&lt;li&gt;when state changes&lt;/li&gt;
&lt;li&gt;when real infrastructure changes&lt;/li&gt;
&lt;li&gt;how providers are selected&lt;/li&gt;
&lt;li&gt;how modules are sourced&lt;/li&gt;
&lt;li&gt;how Terraform Cloud changes the workflow&lt;/li&gt;
&lt;li&gt;what each CLI command actually does&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why hands-on practice matters so much.&lt;/p&gt;

&lt;p&gt;Reading alone is not enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Tips
&lt;/h2&gt;

&lt;p&gt;If you are preparing for the Terraform Associate exam, my advice is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with the official study guide.&lt;/li&gt;
&lt;li&gt;Audit yourself honestly.&lt;/li&gt;
&lt;li&gt;Do not skip state commands.&lt;/li&gt;
&lt;li&gt;Practice provider aliases.&lt;/li&gt;
&lt;li&gt;Review HCP Terraform even if you do not use it daily.&lt;/li&gt;
&lt;li&gt;Learn what each CLI command changes.&lt;/li&gt;
&lt;li&gt;Turn your own mistakes into practice questions.&lt;/li&gt;
&lt;li&gt;Keep your study plan specific and time-boxed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Official Resource
&lt;/h2&gt;

&lt;p&gt;Terraform Associate 004 Study Guide:&lt;br&gt;&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-study-004" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-study-004&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Official Sample Questions:&lt;br&gt;&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-questions-004" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/tutorials/certification-004/associate-questions-004&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Notes
&lt;/h2&gt;

&lt;p&gt;GitHub Day 23:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_23" rel="noopener noreferrer"&gt;https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_23&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 23 of my 30-Day Terraform Challenge.&lt;br&gt;&lt;br&gt;
See you on Day 24.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>learning</category>
      <category>resources</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Putting It All Together: Application and Infrastructure Workflows with Terraform</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Fri, 17 Apr 2026 13:36:09 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/putting-it-all-together-application-and-infrastructure-workflows-with-terraform-59f2</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/putting-it-all-together-application-and-infrastructure-workflows-with-terraform-59f2</guid>
      <description>&lt;p&gt;Day 22 of my Terraform journey felt like a checkpoint.&lt;/p&gt;

&lt;p&gt;After three weeks of building infrastructure, testing modules, writing workflows, using GitHub Actions, working with Terraform plans, and learning about Terraform Cloud, this day brought everything together.&lt;/p&gt;

&lt;p&gt;The focus came from Chapter 10 of &lt;em&gt;Terraform: Up &amp;amp; Running&lt;/em&gt; by Yevgeniy Brikman: how application delivery and infrastructure delivery can follow the same disciplined workflow.&lt;/p&gt;

&lt;p&gt;The big lesson was this:&lt;/p&gt;

&lt;p&gt;Infrastructure should not be deployed casually.&lt;/p&gt;

&lt;p&gt;It should move through the same kind of process we already expect from application code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;version control&lt;/li&gt;
&lt;li&gt;pull request review&lt;/li&gt;
&lt;li&gt;automated checks&lt;/li&gt;
&lt;li&gt;immutable artifacts&lt;/li&gt;
&lt;li&gt;policy enforcement&lt;/li&gt;
&lt;li&gt;controlled deployment&lt;/li&gt;
&lt;li&gt;verification&lt;/li&gt;
&lt;li&gt;rollback planning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Day 22, I built a standalone staging webserver cluster and wired it into an integrated Terraform workflow.&lt;/p&gt;

&lt;p&gt;GitHub reference:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_22" rel="noopener noreferrer"&gt;Day 22 Code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Integrated Pipeline
&lt;/h2&gt;

&lt;p&gt;The goal was to combine the application workflow and infrastructure workflow into one complete delivery process.&lt;/p&gt;

&lt;p&gt;For application code, a team might produce a Docker image or binary as the build artifact.&lt;/p&gt;

&lt;p&gt;For infrastructure code, the equivalent artifact is a saved Terraform plan file.&lt;/p&gt;

&lt;p&gt;That was the main idea I practiced today.&lt;/p&gt;

&lt;p&gt;My Day 22 workflow looked like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write Terraform code in Git.&lt;/li&gt;
&lt;li&gt;Open a pull request.&lt;/li&gt;
&lt;li&gt;Run automated checks.&lt;/li&gt;
&lt;li&gt;Generate a saved Terraform plan.&lt;/li&gt;
&lt;li&gt;Upload the plan as a CI artifact.&lt;/li&gt;
&lt;li&gt;Review the plan, blast radius, and rollback notes.&lt;/li&gt;
&lt;li&gt;Merge the PR.&lt;/li&gt;
&lt;li&gt;Apply the reviewed plan.&lt;/li&gt;
&lt;li&gt;Verify the infrastructure.&lt;/li&gt;
&lt;li&gt;Destroy the lab resources after testing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The GitHub Actions workflow had two main jobs.&lt;/p&gt;

&lt;p&gt;The first job handled validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;terraform fmt -check -recursive&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;terraform init -backend=false&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;terraform validate&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;terraform test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second job handled planning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;terraform plan -out=ci.tfplan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the workflow uploaded the plan file as an artifact.&lt;/p&gt;

&lt;p&gt;That is important because the plan becomes the thing reviewers inspect before apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Deployed
&lt;/h2&gt;

&lt;p&gt;For Day 22, I created a standalone staging stack under:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;day_22/live/staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stack used reusable modules under:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;day_22/modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plan showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan: 16 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resources included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application Load Balancer&lt;/li&gt;
&lt;li&gt;ALB listener and listener rule&lt;/li&gt;
&lt;li&gt;Target group&lt;/li&gt;
&lt;li&gt;Auto Scaling Group&lt;/li&gt;
&lt;li&gt;Launch template&lt;/li&gt;
&lt;li&gt;Security groups and rules&lt;/li&gt;
&lt;li&gt;SNS topic&lt;/li&gt;
&lt;li&gt;CloudWatch CPU alarm&lt;/li&gt;
&lt;li&gt;CloudWatch ASG capacity alarm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After applying the reviewed plan, the application returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;Hello from Day 22 integrated workflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That confirmed the workflow moved from PR review to real AWS infrastructure successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Immutable Artifact Promotion
&lt;/h2&gt;

&lt;p&gt;This was the most important concept for me today.&lt;/p&gt;

&lt;p&gt;In application delivery, teams often build one artifact and promote it through environments.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker image v1.5.0 -&amp;gt; staging -&amp;gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The artifact does not change between environments. That makes releases predictable.&lt;/p&gt;

&lt;p&gt;Terraform has a similar idea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform plan file -&amp;gt; review -&amp;gt; apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The saved plan file represents the exact infrastructure changes Terraform intends to make.&lt;/p&gt;

&lt;p&gt;Instead of running a fresh, unreviewed apply, we apply the saved plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day22-reviewed.tfplan
terraform apply day22-reviewed.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means what was reviewed is what gets applied.&lt;/p&gt;

&lt;p&gt;One important lesson: saved plans can become stale.&lt;/p&gt;

&lt;p&gt;If Terraform says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Saved plan is stale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;that is not a failure. It is Terraform protecting you.&lt;/p&gt;

&lt;p&gt;It means the state changed after the plan was created, so Terraform can no longer guarantee that the plan still matches reality. The right fix is to regenerate the plan, review it again, and apply the new saved plan.&lt;/p&gt;

&lt;p&gt;That safety check is exactly why plan files matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pull Request Review for Infrastructure
&lt;/h2&gt;

&lt;p&gt;For application code, reviewers usually inspect a code diff.&lt;/p&gt;

&lt;p&gt;For infrastructure code, the code diff is not enough.&lt;/p&gt;

&lt;p&gt;A Terraform PR should also include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;plan summary&lt;/li&gt;
&lt;li&gt;resources created, changed, and destroyed&lt;/li&gt;
&lt;li&gt;blast radius&lt;/li&gt;
&lt;li&gt;rollback plan&lt;/li&gt;
&lt;li&gt;test results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Day 22, the PR stated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Created: 16
Modified: 0
Destroyed: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The blast radius was low because the stack was standalone and used Day 22-specific names, tags, and state.&lt;/p&gt;

&lt;p&gt;The rollback plan was simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_22/live/staging destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That may sound basic, but writing it down matters.&lt;/p&gt;

&lt;p&gt;Infrastructure reviews should answer one question clearly:&lt;/p&gt;

&lt;p&gt;“If this goes wrong, what is affected and how do we recover?”&lt;/p&gt;

&lt;h2&gt;
  
  
  Sentinel Policies
&lt;/h2&gt;

&lt;p&gt;Sentinel is Terraform Cloud’s policy-as-code framework.&lt;/p&gt;

&lt;p&gt;Terraform validation checks whether the code is syntactically and structurally valid.&lt;/p&gt;

&lt;p&gt;Sentinel checks whether the plan is allowed according to organizational rules.&lt;/p&gt;

&lt;p&gt;That distinction clicked for me today.&lt;/p&gt;

&lt;p&gt;A Terraform configuration can be valid but still unsafe.&lt;/p&gt;

&lt;p&gt;For Day 22, I added Sentinel policy examples for three controls.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Allowed instance types
&lt;/h3&gt;

&lt;p&gt;This policy prevents teams from using instance types outside an approved list.&lt;/p&gt;

&lt;p&gt;Example intent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Allow: t2.micro, t2.small, t2.medium, t3.micro, t3.small
Block: larger or unapproved instance types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps control cost and standardize infrastructure.&lt;/p&gt;

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

&lt;p&gt;This policy enforces tagging, especially:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ManagedBy = terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That matters because tags help with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ownership&lt;/li&gt;
&lt;li&gt;cost tracking&lt;/li&gt;
&lt;li&gt;cleanup&lt;/li&gt;
&lt;li&gt;audits&lt;/li&gt;
&lt;li&gt;identifying Terraform-managed resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Cost estimation gate
&lt;/h3&gt;

&lt;p&gt;I also added a cost policy example that blocks applies if the monthly increase is above a threshold.&lt;/p&gt;

&lt;p&gt;Example threshold:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;maximum_monthly_increase = 50.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns cost awareness into a deployment control.&lt;/p&gt;

&lt;p&gt;It is not just “we hope this does not cost too much.”&lt;/p&gt;

&lt;p&gt;It becomes:&lt;/p&gt;

&lt;p&gt;“This apply cannot proceed if it exceeds the approved cost increase.”&lt;/p&gt;

&lt;p&gt;That is a powerful guardrail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Application and Infrastructure Workflows Align
&lt;/h2&gt;

&lt;p&gt;By Day 22, the similarities were clear.&lt;/p&gt;

&lt;p&gt;Both workflows need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git as the source of truth&lt;/li&gt;
&lt;li&gt;pull request review&lt;/li&gt;
&lt;li&gt;automated testing&lt;/li&gt;
&lt;li&gt;versioned releases&lt;/li&gt;
&lt;li&gt;promotion through environments&lt;/li&gt;
&lt;li&gt;deployment verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes Terraform feel less like a separate special process and more like mature software delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where They Differ
&lt;/h2&gt;

&lt;p&gt;The differences are where infrastructure gets serious.&lt;/p&gt;

&lt;p&gt;Application code usually produces a running service or artifact.&lt;/p&gt;

&lt;p&gt;Terraform changes real cloud resources.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;state must be protected&lt;/li&gt;
&lt;li&gt;plans must be reviewed&lt;/li&gt;
&lt;li&gt;applies need controlled execution&lt;/li&gt;
&lt;li&gt;destructive changes need extra approval&lt;/li&gt;
&lt;li&gt;rollback may not be instant&lt;/li&gt;
&lt;li&gt;tests may create real resources and cost money&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A bad application deploy may return a 500 error.&lt;/p&gt;

&lt;p&gt;A bad infrastructure deploy can delete a database, expose a service publicly, or break networking.&lt;/p&gt;

&lt;p&gt;That is why infrastructure delivery needs more discipline, not less.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Clicked for Me
&lt;/h2&gt;

&lt;p&gt;The biggest mental shift was this:&lt;/p&gt;

&lt;p&gt;Terraform is not just about creating infrastructure.&lt;/p&gt;

&lt;p&gt;Terraform is about creating a safe system for changing infrastructure.&lt;/p&gt;

&lt;p&gt;Before this challenge, it was easy to think of Terraform as a provisioning tool.&lt;/p&gt;

&lt;p&gt;Now I see it more as a workflow tool.&lt;/p&gt;

&lt;p&gt;The real value is not only in writing &lt;code&gt;.tf&lt;/code&gt; files. It is in the process around them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;plan before apply&lt;/li&gt;
&lt;li&gt;review the plan&lt;/li&gt;
&lt;li&gt;document blast radius&lt;/li&gt;
&lt;li&gt;test what can be tested&lt;/li&gt;
&lt;li&gt;protect state&lt;/li&gt;
&lt;li&gt;apply from trusted environments&lt;/li&gt;
&lt;li&gt;verify cleanup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is what makes infrastructure reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Broke
&lt;/h2&gt;

&lt;p&gt;A few things broke along the way.&lt;/p&gt;

&lt;p&gt;Provider setup sometimes failed because plugins had to initialize correctly.&lt;/p&gt;

&lt;p&gt;Saved plans became stale when state changed after the plan was created.&lt;/p&gt;

&lt;p&gt;GitHub PR formatting also reminded me that documentation matters. If plan output or commands are hard to read, reviewers cannot review properly.&lt;/p&gt;

&lt;p&gt;Even code block formatting in a PR matters when the goal is making infrastructure changes understandable.&lt;/p&gt;

&lt;p&gt;The fix was always the same pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;slow down&lt;/li&gt;
&lt;li&gt;inspect the actual error&lt;/li&gt;
&lt;li&gt;regenerate or re-run safely&lt;/li&gt;
&lt;li&gt;document what happened&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Surprised Me
&lt;/h2&gt;

&lt;p&gt;The biggest surprise was how much of Terraform maturity is not Terraform syntax.&lt;/p&gt;

&lt;p&gt;It is engineering discipline.&lt;/p&gt;

&lt;p&gt;The harder parts were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deciding what belongs in a module&lt;/li&gt;
&lt;li&gt;thinking about state boundaries&lt;/li&gt;
&lt;li&gt;writing useful PR descriptions&lt;/li&gt;
&lt;li&gt;knowing when a test should be manual, unit, integration, or end-to-end&lt;/li&gt;
&lt;li&gt;cleaning up every test run properly&lt;/li&gt;
&lt;li&gt;explaining blast radius clearly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are the skills that turn Terraform from “scripts that create AWS resources” into real Infrastructure as Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflection on the Journey So Far
&lt;/h2&gt;

&lt;p&gt;In 22 days, I moved through a lot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2&lt;/li&gt;
&lt;li&gt;security groups&lt;/li&gt;
&lt;li&gt;load balancers&lt;/li&gt;
&lt;li&gt;Auto Scaling Groups&lt;/li&gt;
&lt;li&gt;remote state&lt;/li&gt;
&lt;li&gt;workspaces&lt;/li&gt;
&lt;li&gt;reusable modules&lt;/li&gt;
&lt;li&gt;provider aliases&lt;/li&gt;
&lt;li&gt;multiple environments&lt;/li&gt;
&lt;li&gt;secrets handling&lt;/li&gt;
&lt;li&gt;production-readiness checks&lt;/li&gt;
&lt;li&gt;manual testing&lt;/li&gt;
&lt;li&gt;Terratest&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;deployment workflows&lt;/li&gt;
&lt;li&gt;Sentinel policies&lt;/li&gt;
&lt;li&gt;cost gates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the real progress was not the number of resources created.&lt;/p&gt;

&lt;p&gt;The real progress was learning how to think.&lt;/p&gt;

&lt;p&gt;I now think more carefully about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what changes&lt;/li&gt;
&lt;li&gt;who reviews it&lt;/li&gt;
&lt;li&gt;where state lives&lt;/li&gt;
&lt;li&gt;what happens if apply fails&lt;/li&gt;
&lt;li&gt;what gets destroyed&lt;/li&gt;
&lt;li&gt;how cleanup is verified&lt;/li&gt;
&lt;li&gt;whether a future engineer can understand the workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the point of Infrastructure as Code.&lt;/p&gt;

&lt;p&gt;Not just automation.&lt;/p&gt;

&lt;p&gt;Repeatable, reviewable, explainable infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Day 22 tied the whole book together for me.&lt;/p&gt;

&lt;p&gt;Application code and infrastructure code can follow the same delivery philosophy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reviewed change -&amp;gt; tested artifact -&amp;gt; controlled deployment -&amp;gt; verified result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But infrastructure needs additional safeguards because the blast radius is bigger.&lt;/p&gt;

&lt;p&gt;The winning pattern is not “move fast and apply.”&lt;/p&gt;

&lt;p&gt;It is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plan carefully
review clearly
enforce policy
apply intentionally
verify everything
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the kind of Terraform workflow I want to keep building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Code
&lt;/h2&gt;

&lt;p&gt;GitHub reference:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_22" rel="noopener noreferrer"&gt;Day 22 Code&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 22 of my 30-Day Terraform Challenge.&lt;br&gt;&lt;br&gt;
See you on Day 23.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>A Workflow for Deploying Infrastructure Code with Terraform</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Thu, 16 Apr 2026 00:27:27 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/a-workflow-for-deploying-infrastructure-code-with-terraform-1m24</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/a-workflow-for-deploying-infrastructure-code-with-terraform-1m24</guid>
      <description>&lt;p&gt;Day 21 of my Terraform journey focused on a workflow that looks familiar at first, but carries a much bigger blast radius than normal application code deployment.&lt;/p&gt;

&lt;p&gt;In Chapter 10 of &lt;em&gt;Terraform: Up &amp;amp; Running&lt;/em&gt;, Yevgeniy Brikman explains that infrastructure code can follow the same release shape as application code:&lt;/p&gt;

&lt;p&gt;branch, review, test, merge, release, deploy.&lt;/p&gt;

&lt;p&gt;But infrastructure code has extra risks.&lt;/p&gt;

&lt;p&gt;A bad application deploy might return a &lt;code&gt;500&lt;/code&gt; error.&lt;br&gt;&lt;br&gt;
A bad infrastructure deploy can break networking, remove a load balancer, corrupt state, or destroy critical resources.&lt;/p&gt;

&lt;p&gt;That is why Terraform needs more than good code.&lt;br&gt;&lt;br&gt;
It needs a safe deployment workflow around the code.&lt;/p&gt;

&lt;p&gt;For Day 21, I deployed a real infrastructure change end-to-end: adding a CloudWatch alarm to a webserver Auto Scaling Group.&lt;/p&gt;

&lt;p&gt;GitHub reference:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_21" rel="noopener noreferrer"&gt;Day 21 Code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Infrastructure Change
&lt;/h2&gt;

&lt;p&gt;I created a standalone Day 21 environment and added a CloudWatch alarm that watches the number of healthy in-service instances in my Auto Scaling Group.&lt;/p&gt;

&lt;p&gt;The alarm monitors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS/AutoScaling
GroupInServiceInstances
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It fires when the ASG has fewer in-service instances than expected.&lt;/p&gt;

&lt;p&gt;This was a good infrastructure workflow change because it was useful, but controlled. It improved observability without changing routing, instance capacity, databases, or production resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Version Control
&lt;/h2&gt;

&lt;p&gt;I started with a feature branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; add-cloudwatch-alarms-day21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps infrastructure changes reviewable before they touch real cloud resources.&lt;/p&gt;

&lt;p&gt;One key lesson from today: branches are good for review, but shared environments should be applied only after merge. Applying stale feature branches to real environments can accidentally undo someone else’s infrastructure change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Run Locally With Terraform Plan
&lt;/h2&gt;

&lt;p&gt;For application code, “run locally” might mean starting the app.&lt;/p&gt;

&lt;p&gt;For Terraform, the equivalent is running a plan.&lt;/p&gt;

&lt;p&gt;You are not running the infrastructure locally. You are asking Terraform:&lt;/p&gt;

&lt;p&gt;“What would you change in the real cloud environment?”&lt;/p&gt;

&lt;p&gt;I ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_21/live/dev plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day21.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plan showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan: 16 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That summary matters.&lt;/p&gt;

&lt;p&gt;It told me Terraform would only create new Day 21 dev resources. No existing resources would be changed or destroyed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Make the Infrastructure Change
&lt;/h2&gt;

&lt;p&gt;The main change was this CloudWatch alarm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_metric_alarm"&lt;/span&gt; &lt;span class="s2"&gt;"asg_capacity_low"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alarm_name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.cluster_name}-${random_id.suffix.hex}-asg-capacity-low"&lt;/span&gt;
  &lt;span class="nx"&gt;alarm_description&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Alerts when the ASG has fewer in-service instances than expected."&lt;/span&gt;
  &lt;span class="nx"&gt;namespace&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS/AutoScaling"&lt;/span&gt;
  &lt;span class="nx"&gt;metric_name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GroupInServiceInstances"&lt;/span&gt;
  &lt;span class="nx"&gt;statistic&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Average"&lt;/span&gt;
  &lt;span class="nx"&gt;period&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
  &lt;span class="nx"&gt;evaluation_periods&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nx"&gt;threshold&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired_capacity&lt;/span&gt;
  &lt;span class="nx"&gt;comparison_operator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"LessThanThreshold"&lt;/span&gt;
  &lt;span class="nx"&gt;treat_missing_data&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"breaching"&lt;/span&gt;
  &lt;span class="nx"&gt;alarm_actions&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webserver_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alerts_topic_arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;dimensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;AutoScalingGroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webserver_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asg_name&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.cluster_name}-asg-capacity-low"&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 detects when the ASG is not maintaining the expected number of healthy instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Submit for Review
&lt;/h2&gt;

&lt;p&gt;This is where infrastructure code differs from application code.&lt;/p&gt;

&lt;p&gt;For app code, reviewers often focus on the code diff.&lt;/p&gt;

&lt;p&gt;For Terraform, reviewers need the code diff &lt;strong&gt;and&lt;/strong&gt; the plan output.&lt;/p&gt;

&lt;p&gt;The plan is the real infrastructure preview. It shows what Terraform will actually create, change, or destroy.&lt;/p&gt;

&lt;p&gt;In my PR, I included the plan summary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan: 16 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also documented the two sections that are easy to skip but very important: blast radius and rollback plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blast Radius
&lt;/h2&gt;

&lt;p&gt;Blast radius explains what could be affected if the apply goes wrong.&lt;/p&gt;

&lt;p&gt;For this change, the blast radius was low:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This creates a standalone Day 21 dev webserver stack and adds a CloudWatch alarm for ASG capacity. It does not modify older day folders, production resources, existing routing, existing security groups, existing databases, or existing capacity.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the apply failed halfway, the likely impact would be partial Day 21 dev resources such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ALB&lt;/li&gt;
&lt;li&gt;ASG&lt;/li&gt;
&lt;li&gt;security groups&lt;/li&gt;
&lt;li&gt;CloudWatch alarms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Existing infrastructure should not be affected because the lab used separate Day 21 names, tags, and Terraform workspace state.&lt;/p&gt;

&lt;p&gt;This is one of the biggest Day 21 lessons for me: an infrastructure PR should not only say what changed. It should also say what could break.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rollback Plan
&lt;/h2&gt;

&lt;p&gt;The rollback plan was documented before deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If the apply causes issues, revert the PR and apply the reviewed rollback plan.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because this was a standalone dev lab, the fastest cleanup path was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_21/live/dev destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rollback planning matters because infrastructure does not always roll back cleanly like application code. Sometimes rollback means reverting code. Sometimes it means restoring state. Sometimes it means manual cleanup.&lt;/p&gt;

&lt;p&gt;The worst time to decide that is during an incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Run Automated Tests
&lt;/h2&gt;

&lt;p&gt;The PR ran GitHub Actions checks.&lt;/p&gt;

&lt;p&gt;For Day 21, the workflow ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nb"&gt;fmt&lt;/span&gt; &lt;span class="nt"&gt;-check&lt;/span&gt; &lt;span class="nt"&gt;-recursive&lt;/span&gt; day_21
terraform validate
terraform &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The native Terraform tests passed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4 passed, 0 failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These tests were intentionally fast. Day 21 was not about re-running expensive end-to-end tests. It was about building a safe infrastructure deployment workflow.&lt;/p&gt;

&lt;p&gt;The real AWS verification happened after the reviewed apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Merge and Release
&lt;/h2&gt;

&lt;p&gt;After the PR checks passed, I merged the PR and tagged the release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"v1.4.0"&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add ASG capacity alarm for Day 21 infrastructure workflow"&lt;/span&gt;
git push origin v1.4.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For application code, the release artifact might be a Docker image.&lt;/p&gt;

&lt;p&gt;For Terraform, the Git commit or module tag becomes the release artifact. That makes infrastructure changes traceable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Deploy
&lt;/h2&gt;

&lt;p&gt;The safest approach is to apply a saved plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply day21.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I hit an important real-world Terraform lesson:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Saved plan is stale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the plan was created against an older state snapshot. Terraform could no longer guarantee that applying it would match the current state.&lt;/p&gt;

&lt;p&gt;That is a safety feature, not a failure.&lt;/p&gt;

&lt;p&gt;The fix was to create a new saved plan from the current state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_21/live/dev plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day21-reviewed.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I confirmed the new plan still matched the reviewed intent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan: 16 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I applied the regenerated saved plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_21/live/dev apply day21-reviewed.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That kept the workflow disciplined: even after the stale-plan issue, I still applied a reviewed saved plan instead of running a plain &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;After apply, I verified the application through the ALB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://&lt;span class="si"&gt;$(&lt;/span&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_21/live/dev output &lt;span class="nt"&gt;-raw&lt;/span&gt; alb_dns_name&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response confirmed the app was live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Day 21 infrastructure workflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I verified the CloudWatch alarm existed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudwatch describe-alarms &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--alarm-names&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_21/live/dev output &lt;span class="nt"&gt;-raw&lt;/span&gt; asg_capacity_alarm_name&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"MetricAlarms[*].[AlarmName,Namespace,MetricName,Threshold,ComparisonOperator,StateValue]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The alarm was created against:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS/AutoScaling
GroupInServiceInstances
LessThanThreshold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I ran another plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_21/live/dev plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform returned:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;That confirmed Terraform state and AWS matched after apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;Because this deployed real AWS resources, cleanup was part of the workflow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_21/live/dev destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After destroy, I verified that no active Day 21 resources remained.&lt;/p&gt;

&lt;p&gt;Cleanup is not an afterthought. It is part of testing infrastructure safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure Safeguards That Application Code Usually Does Not Need
&lt;/h2&gt;

&lt;p&gt;Day 21 made the infrastructure-specific safeguards very clear.&lt;/p&gt;

&lt;h3&gt;
  
  
  Saved plans
&lt;/h3&gt;

&lt;p&gt;A saved plan helps ensure that what gets applied is what was reviewed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;reviewed.tfplan
terraform apply reviewed.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  State protection
&lt;/h3&gt;

&lt;p&gt;Terraform state represents real infrastructure. If state is wrong, Terraform’s view of the world is wrong.&lt;/p&gt;

&lt;p&gt;That is why remote state, locking, and state backups matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Destructive-change approval
&lt;/h3&gt;

&lt;p&gt;Any plan with destroys deserves extra attention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan: 0 to add, 2 to change, 1 to destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should trigger a deeper review before apply.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blast radius documentation
&lt;/h3&gt;

&lt;p&gt;Every infrastructure PR should explain what could be affected if the change fails.&lt;/p&gt;

&lt;p&gt;This is especially important for shared resources like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPCs&lt;/li&gt;
&lt;li&gt;security groups&lt;/li&gt;
&lt;li&gt;IAM roles&lt;/li&gt;
&lt;li&gt;databases&lt;/li&gt;
&lt;li&gt;load balancers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rollback plan
&lt;/h3&gt;

&lt;p&gt;A rollback plan should exist before apply.&lt;/p&gt;

&lt;p&gt;If something breaks, the team should already know whether the rollback is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;revert the PR&lt;/li&gt;
&lt;li&gt;apply a previous plan&lt;/li&gt;
&lt;li&gt;restore state&lt;/li&gt;
&lt;li&gt;restore from backup&lt;/li&gt;
&lt;li&gt;manually clean up partial resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sentinel as the Enforcement Layer
&lt;/h2&gt;

&lt;p&gt;Sentinel is Terraform Cloud’s policy-as-code framework.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;terraform validate&lt;/code&gt; checks whether Terraform code is valid.&lt;/p&gt;

&lt;p&gt;Sentinel checks whether a valid plan is allowed by policy.&lt;/p&gt;

&lt;p&gt;For Day 21, I added a Sentinel policy that only allows approved EC2 instance types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;import&lt;/span&gt; &lt;span class="s2"&gt;"tfplan/v2"&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;tfplan&lt;/span&gt;

&lt;span class="nx"&gt;allowed_instance_types&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"t2.small"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"t2.medium"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"t3.small"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;all&lt;/span&gt; &lt;span class="nx"&gt;tfplan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resource_changes&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt;
    &lt;span class="nx"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;allowed_instance_types&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 means a plan could be syntactically correct, but still blocked because it violates infrastructure policy.&lt;/p&gt;

&lt;p&gt;That is the difference between validation and governance.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Main Takeaway
&lt;/h2&gt;

&lt;p&gt;The infrastructure workflow looks similar to the application workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;branch&lt;/li&gt;
&lt;li&gt;plan&lt;/li&gt;
&lt;li&gt;review&lt;/li&gt;
&lt;li&gt;test&lt;/li&gt;
&lt;li&gt;merge&lt;/li&gt;
&lt;li&gt;release&lt;/li&gt;
&lt;li&gt;deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But infrastructure needs stronger safeguards because the consequences are different.&lt;/p&gt;

&lt;p&gt;The biggest lesson from Day 21 was this:&lt;/p&gt;

&lt;p&gt;the Terraform plan is not just a command output.&lt;br&gt;&lt;br&gt;
It is the review artifact, the safety checkpoint, and the bridge between code and real cloud changes.&lt;/p&gt;

&lt;p&gt;Good infrastructure deployment is not just &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is plan, review, blast-radius thinking, rollback planning, policy checks, verification, and cleanup.&lt;/p&gt;

&lt;p&gt;That is what turns Terraform code into safe infrastructure engineering.&lt;/p&gt;




&lt;p&gt;GitHub reference:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_21" rel="noopener noreferrer"&gt;Day 21 Code&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 21 of my 30-Day Terraform Challenge.&lt;br&gt;&lt;br&gt;
See you on Day 22.&lt;/p&gt;

</description>
      <category>terraform</category>
    </item>
    <item>
      <title>A Workflow for Deploying Application Code with Terraform</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Mon, 13 Apr 2026 21:10:05 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/a-workflow-for-deploying-application-code-with-terraform-2n43</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/a-workflow-for-deploying-application-code-with-terraform-2n43</guid>
      <description>&lt;p&gt;Day 20 of my Terraform journey focused on one practical question from Chapter 10 of &lt;em&gt;Terraform: Up &amp;amp; Running&lt;/em&gt; by Yevgeniy Brikman:&lt;/p&gt;

&lt;p&gt;How do we apply the same trusted release workflow used for application code to infrastructure code?&lt;/p&gt;

&lt;p&gt;I walked through the full 7-step workflow using my Day 20 webserver cluster update, from local change to PR to deploy and cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Most teams already trust an app-code workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;branch&lt;/li&gt;
&lt;li&gt;review&lt;/li&gt;
&lt;li&gt;tests&lt;/li&gt;
&lt;li&gt;merge&lt;/li&gt;
&lt;li&gt;release&lt;/li&gt;
&lt;li&gt;deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Terraform should follow the same discipline.&lt;br&gt;&lt;br&gt;
The big difference is that infrastructure changes can create, modify, or destroy real cloud resources, so review and execution controls matter even more.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1: Use version control
&lt;/h2&gt;

&lt;p&gt;I kept all Terraform code in Git and worked through a feature branch instead of changing &lt;code&gt;main&lt;/code&gt; directly.&lt;/p&gt;

&lt;p&gt;Example branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; update-app-version-day20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Goal: every infrastructure change is traceable, reviewable, and reversible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Run locally first
&lt;/h2&gt;

&lt;p&gt;I made a small, intentional app change in Terraform by updating the user-facing response text from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Hello from Day 20 v2&lt;/code&gt;
to&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Hello from Day 20 v3&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I generated a reviewed plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_20/live/dev validate
terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_20/live/dev plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day20.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validate passed&lt;/li&gt;
&lt;li&gt;plan file saved (&lt;code&gt;day20.tfplan&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;summary showed expected create actions only (&lt;code&gt;15 to add, 0 to change, 0 to destroy&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key habit: do not apply unreviewed changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Make and commit the change
&lt;/h2&gt;

&lt;p&gt;After confirming the local plan, I committed on the feature branch and pushed for review.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Day 20: update app response to v3 and simulate deployment workflow"&lt;/span&gt;
git push origin update-app-version-day20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Submit for review (PR)
&lt;/h2&gt;

&lt;p&gt;In the PR, I included the rendered plan output so reviewers could see exactly what Terraform would do before merge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_20/live/dev show &lt;span class="nt"&gt;-no-color&lt;/span&gt; day20.tfplan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the infrastructure equivalent of reviewing a code diff, but with cloud-impact context.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Run automated tests
&lt;/h2&gt;

&lt;p&gt;For this workflow, unit tests ran via GitHub Actions on PR, and passed.&lt;/p&gt;

&lt;p&gt;I also ran local module unit tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_20/modules/services/webserver-cluster &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;4 passed, 0 failed&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important note: native &lt;code&gt;terraform test&lt;/code&gt; can live next to the module in &lt;code&gt;.tftest.hcl&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
You do not need a separate &lt;code&gt;test/&lt;/code&gt; folder for this layer.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 6: Merge and release
&lt;/h2&gt;

&lt;p&gt;After checks passed, the PR merged into &lt;code&gt;main&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Release tagging is the same idea as app releases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"v1.3.0"&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Update app response to v3"&lt;/span&gt;
git push origin v1.3.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tags make rollout points explicit and easier to audit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Deploy and verify
&lt;/h2&gt;

&lt;p&gt;I applied the reviewed plan and verified the live result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_20/live/dev apply day20.tfplan
curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://&lt;span class="si"&gt;$(&lt;/span&gt;terraform &lt;span class="nt"&gt;-chdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;day_20/live/dev output &lt;span class="nt"&gt;-raw&lt;/span&gt; alb_dns_name&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Live response:&lt;br&gt;
&lt;code&gt;Hello from Day 20 v3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then I destroyed resources and verified cleanup to avoid cost leaks.&lt;/p&gt;


&lt;h2&gt;
  
  
  Where app and infra workflows align
&lt;/h2&gt;

&lt;p&gt;Both workflows use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git branches&lt;/li&gt;
&lt;li&gt;PR review&lt;/li&gt;
&lt;li&gt;automated checks&lt;/li&gt;
&lt;li&gt;merge + release tags&lt;/li&gt;
&lt;li&gt;controlled deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This alignment is what makes Terraform adoption easier in teams.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where they diverge
&lt;/h2&gt;

&lt;p&gt;Infrastructure adds constraints app workflows usually do not have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;State management&lt;br&gt;&lt;br&gt;
Terraform state is critical and must be protected (not committed to Git).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Plan-as-review artifact&lt;br&gt;&lt;br&gt;
Reviewing &lt;code&gt;.tf&lt;/code&gt; alone is not enough. Reviewers need plan output.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real cloud blast radius&lt;br&gt;&lt;br&gt;
Tests and applies can create paid resources and cause outages if unmanaged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trusted apply environment&lt;br&gt;&lt;br&gt;
&lt;code&gt;terraform apply&lt;/code&gt; should run from controlled environments with locking and auditability.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Terraform Cloud variable management: the hidden win
&lt;/h2&gt;

&lt;p&gt;One of the most useful lessons is variable security in Terraform Cloud.&lt;/p&gt;

&lt;p&gt;Instead of credentials on laptops, store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; as sensitive environment variables&lt;/li&gt;
&lt;li&gt;Terraform inputs (&lt;code&gt;cluster_name&lt;/code&gt;, &lt;code&gt;instance_type&lt;/code&gt;, &lt;code&gt;environment&lt;/code&gt;) as workspace variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;safer secret handling&lt;/li&gt;
&lt;li&gt;consistent runs&lt;/li&gt;
&lt;li&gt;better audit trails&lt;/li&gt;
&lt;li&gt;less “works on my machine” behavior&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Terraform Cloud private registry: another underrated feature
&lt;/h2&gt;

&lt;p&gt;Many teams miss this.&lt;/p&gt;

&lt;p&gt;By publishing internal modules (for example &lt;code&gt;terraform-aws-webserver-cluster&lt;/code&gt;) to Terraform Cloud private registry, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;versioned internal modules&lt;/li&gt;
&lt;li&gt;central documentation&lt;/li&gt;
&lt;li&gt;consistent reuse across teams&lt;/li&gt;
&lt;li&gt;cleaner source references&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example module source pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app.terraform.io/&amp;lt;org&amp;gt;/webserver-cluster/aws"&lt;/span&gt;
&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a big step from one-off copy-paste module usage to real platform engineering.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;Terraform works best when it follows the same release discipline as application code.&lt;br&gt;&lt;br&gt;
The 7-step workflow is not just process overhead. It is what makes infrastructure safer, reviewable, and team-friendly at scale.&lt;/p&gt;

&lt;p&gt;Day 20 made that practical for me: plan-first review, automated checks, controlled merge, verified deploy, and explicit cleanup.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code Reference
&lt;/h2&gt;

&lt;p&gt;GitHub (Day 20):&lt;br&gt;
👉 &lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_20" rel="noopener noreferrer"&gt;Github Link&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 20 of my 30-Day Terraform Challenge.&lt;br&gt;&lt;br&gt;
See you on Day 21 🚀&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>How to Convince Your Team to Adopt Infrastructure as Code</title>
      <dc:creator>Mary Mutua</dc:creator>
      <pubDate>Sat, 11 Apr 2026 21:28:34 +0000</pubDate>
      <link>https://dev.to/mary_mutua_9d55b3c269f343/how-to-convince-your-team-to-adopt-infrastructure-as-code-8bf</link>
      <guid>https://dev.to/mary_mutua_9d55b3c269f343/how-to-convince-your-team-to-adopt-infrastructure-as-code-8bf</guid>
      <description>&lt;p&gt;Convincing a team to adopt Infrastructure as Code is not a technical challenge - it is a people and process challenge.  &lt;/p&gt;

&lt;p&gt;Day 19 of my Terraform journey focused on this exact question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you actually convince a team (and leadership) to adopt IaC?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is what worked for me and what I’ve seen in real team workflows, grounded in Chapter 10 of &lt;em&gt;Terraform: Up &amp;amp; Running&lt;/em&gt; by Yevgeniy Brikman.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start With the Business Case, Not the Tool
&lt;/h2&gt;

&lt;p&gt;The biggest mistake is leading with Terraform features.&lt;/p&gt;

&lt;p&gt;What leadership really cares about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fewer incidents&lt;/li&gt;
&lt;li&gt;less downtime&lt;/li&gt;
&lt;li&gt;predictable releases&lt;/li&gt;
&lt;li&gt;auditability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the teams I’ve worked with, the real pain points are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manual changes after PR merges&lt;/li&gt;
&lt;li&gt;environment updates applied directly on servers&lt;/li&gt;
&lt;li&gt;outages caused by exhausted workers&lt;/li&gt;
&lt;li&gt;secrets stored on servers and readable by anyone with access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the argument is not “Terraform is cool.”&lt;br&gt;&lt;br&gt;
The argument is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“We can reduce production incidents caused by manual drift.”&lt;/li&gt;
&lt;li&gt;“We can standardize autoscaling and reduce downtime.”&lt;/li&gt;
&lt;li&gt;“We can build a full audit trail for infra changes.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This aligns with Brikman’s key point: &lt;strong&gt;lead with the problem you are solving, not the tooling.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Adopt Incrementally, Not All at Once
&lt;/h2&gt;

&lt;p&gt;Big‑bang migrations almost always fail.&lt;/p&gt;

&lt;p&gt;The safer strategy is incremental:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pick one small, real problem&lt;/li&gt;
&lt;li&gt;solve it with IaC&lt;/li&gt;
&lt;li&gt;show results quickly&lt;/li&gt;
&lt;li&gt;build trust and momentum&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple first win would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a new S3 bucket managed entirely in Terraform&lt;/li&gt;
&lt;li&gt;remote state stored in S3&lt;/li&gt;
&lt;li&gt;PR workflow for infra changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reflects Brikman’s lesson: &lt;strong&gt;incremental wins build momentum and reduce risk.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Give the Team Time and Safety to Learn
&lt;/h2&gt;

&lt;p&gt;IaC fails when only one person knows how it works.&lt;/p&gt;

&lt;p&gt;In a real incident, people will choose the fastest fix.&lt;br&gt;&lt;br&gt;
If Terraform feels slow or unfamiliar, they will go manual - and drift begins.&lt;/p&gt;

&lt;p&gt;To avoid that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;training time must be deliberate&lt;/li&gt;
&lt;li&gt;workflows must be documented&lt;/li&gt;
&lt;li&gt;the “right way” must be the easiest way&lt;/li&gt;
&lt;li&gt;PR reviews and &lt;code&gt;terraform plan&lt;/code&gt; should be standard&lt;/li&gt;
&lt;li&gt;no manual console changes for Terraform‑managed resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the cultural shift Brikman emphasizes: &lt;strong&gt;code‑first operations must be taught and supported, not assumed.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Common Failure Modes to Avoid
&lt;/h2&gt;

&lt;p&gt;These are the patterns that keep showing up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trying to migrate everything at once&lt;/li&gt;
&lt;li&gt;Starting without leadership buy‑in&lt;/li&gt;
&lt;li&gt;Underestimating the learning curve&lt;/li&gt;
&lt;li&gt;One person owns all IaC knowledge&lt;/li&gt;
&lt;li&gt;Outage happens and the team goes manual&lt;/li&gt;
&lt;li&gt;Drift accumulates until Terraform is abandoned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix is always the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;smaller steps&lt;/li&gt;
&lt;li&gt;clearer training&lt;/li&gt;
&lt;li&gt;consistent team practices&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A 4‑Phase Adoption Plan That Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Phase 1 - Start with something new (2–4 weeks)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Create a new S3 bucket (logs/backups) in Terraform.&lt;br&gt;&lt;br&gt;
Low risk, clear win.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2 - Import existing infrastructure&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use &lt;code&gt;terraform import&lt;/code&gt; for one high‑change resource (e.g., security group).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3 - Establish team practices&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Module versioning, PR reviews, &lt;code&gt;terraform plan&lt;/code&gt; in PRs, CI checks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 4 - Automate deployments&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
CI/CD runs &lt;code&gt;terraform apply&lt;/code&gt; on merge to main with approval gates.&lt;/p&gt;

&lt;p&gt;Each phase delivers value even if the next phase takes longer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;If you want your team to adopt IaC, don’t start with the tool.&lt;br&gt;&lt;br&gt;
Start with the biggest pain the team is already feeling.&lt;/p&gt;

&lt;p&gt;Solve that one problem with IaC.&lt;br&gt;&lt;br&gt;
Then repeat.&lt;/p&gt;

&lt;p&gt;That is how real adoption starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub (Day 19):
&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/mary20205090/30-day-Terraform-Challenge/tree/main/day_19" rel="noopener noreferrer"&gt;Github Link&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow My Journey
&lt;/h2&gt;

&lt;p&gt;This is Day 19 of my 30‑Day Terraform Challenge.&lt;br&gt;&lt;br&gt;
See you on Day 20.&lt;/p&gt;

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