<?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: Stephanie Makori</title>
    <description>The latest articles on DEV Community by Stephanie Makori (@stephanie_makori_845bb2c0).</description>
    <link>https://dev.to/stephanie_makori_845bb2c0</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%2F3829770%2F6b12f232-db11-4209-80a0-654ac696718d.JPG</url>
      <title>DEV Community: Stephanie Makori</title>
      <link>https://dev.to/stephanie_makori_845bb2c0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stephanie_makori_845bb2c0"/>
    <language>en</language>
    <item>
      <title>Automating Terraform Testing: From Unit Tests to End-to-End Validation</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Tue, 07 Apr 2026 11:03:15 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/automating-terraform-testing-from-unit-tests-to-end-to-end-validation-51mk</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/automating-terraform-testing-from-unit-tests-to-end-to-end-validation-51mk</guid>
      <description>&lt;p&gt;Infrastructure as code (IaC) is powerful, but deploying untested changes can be risky. On Day 18 of my 30-Day Terraform Challenge, I focused on automating testing for Terraform code, covering unit tests, integration tests, and end-to-end tests, all tied together in a CI/CD pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Unit Tests
&lt;/h2&gt;

&lt;p&gt;Unit tests are fast, cheap, and safe because they test your module plan only—no real resources are created. Each unit test ensures your resources are configured correctly, such as validating cluster names, instance types, and open ports. These tests catch configuration errors and bad variables before anything reaches production.&lt;/p&gt;

&lt;p&gt;Unit tests run on pull requests, giving developers &lt;strong&gt;fast feedback&lt;/strong&gt; and confidence that changes won’t break the module.&lt;/p&gt;




&lt;h2&gt;
  
  
  Integration Tests
&lt;/h2&gt;

&lt;p&gt;Integration tests deploy real infrastructure, assert behavior, then destroy everything. They check how modules interact with actual cloud resources, like verifying that the application load balancer responds correctly and that EC2 instances are running as expected.&lt;/p&gt;

&lt;p&gt;Integration tests run only on pushes to the main branch, because they are slower and use real AWS resources. Using &lt;code&gt;defer destroy&lt;/code&gt; ensures all resources are cleaned up after the test, preventing cost leaks.&lt;/p&gt;




&lt;h2&gt;
  
  
  End-to-End Tests
&lt;/h2&gt;

&lt;p&gt;End-to-end (E2E) tests validate the entire stack—from networking and databases to applications. They ensure that the full system works as a whole. E2E tests are &lt;strong&gt;slow and costlier&lt;/strong&gt;, so they are run less frequently.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI/CD Test Strategy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests run on pull requests (fast, free)
&lt;/li&gt;
&lt;li&gt;Integration tests run only on push to main (slower, real AWS)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test Type&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Deploys Real Infra&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;What It Catches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unit&lt;/td&gt;
&lt;td&gt;terraform test&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Seconds&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Config errors, bad variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration&lt;/td&gt;
&lt;td&gt;Terratest&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Resource behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;End-to-End&lt;/td&gt;
&lt;td&gt;Terratest&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;15–30 min&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Full system issues&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Integration vs End-to-End: Integration tests focus on a module in isolation, while E2E tests validate the full stack.
&lt;/li&gt;
&lt;li&gt;Unit tests on PRs → fast feedback
&lt;/li&gt;
&lt;li&gt;E2E tests less frequent → expensive &amp;amp; slower&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Challenges &amp;amp; Fixes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Missing required variables → added dummy values for unit tests
&lt;/li&gt;
&lt;li&gt;Go module errors → used &lt;code&gt;go mod tidy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Terraform syntax mistakes → corrected &lt;code&gt;.tftest.hcl&lt;/code&gt; content
&lt;/li&gt;
&lt;li&gt;Application Load Balancer slow startup → added retry logic
&lt;/li&gt;
&lt;li&gt;AWS credentials setup → properly configured GitHub secrets
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Apply Screenshot
&lt;/h2&gt;

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




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Automated testing with Terraform ensures infrastructure deploys reliably and safely. Combining unit, integration, and E2E tests gives full confidence while minimizing cost and risk. With CI/CD, every commit is validated, enabling rapid and safe iteration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Terraform Test Documentation
&lt;/li&gt;
&lt;li&gt;Terratest Documentation
&lt;/li&gt;
&lt;li&gt;GitHub Actions Terraform Setup
&lt;/li&gt;
&lt;li&gt;Go Testing Package
&lt;/li&gt;
&lt;li&gt;AWS Documentation&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>terraform</category>
      <category>testing</category>
    </item>
    <item>
      <title>The Importance of Manual Testing in Terraform</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Mon, 06 Apr 2026 07:20:34 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/the-importance-of-manual-testing-in-terraform-pn6</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/the-importance-of-manual-testing-in-terraform-pn6</guid>
      <description>&lt;p&gt;Manual testing is often overlooked in infrastructure as code workflows, especially with powerful tools like Terraform. However, before introducing automated tests, manual testing is essential to fully understand how your infrastructure behaves in real-world conditions.&lt;/p&gt;

&lt;p&gt;On Day 17 of my 30-Day Terraform Challenge, I focused on building a structured manual testing process for my webserver cluster (Application Load Balancer + Auto Scaling Group + EC2 instances). This experience reinforced one key idea: &lt;strong&gt;you cannot automate what you do not understand.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Manual Testing Matters
&lt;/h2&gt;

&lt;p&gt;Manual testing helps answer critical questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the infrastructure deploy correctly?&lt;/li&gt;
&lt;li&gt;Does it behave as expected under real conditions?&lt;/li&gt;
&lt;li&gt;Are there hidden misconfigurations that validation tools miss?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While &lt;code&gt;terraform validate&lt;/code&gt; and &lt;code&gt;terraform plan&lt;/code&gt; ensure correctness at a configuration level, they do not guarantee real-world functionality. Manual testing bridges that gap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Structured Test Checklist
&lt;/h2&gt;

&lt;p&gt;Instead of randomly clicking around the AWS Console, I created a structured checklist to guide my testing process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provisioning Verification
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;terraform init&lt;/code&gt; and confirm initialization completes successfully
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;terraform validate&lt;/code&gt; and ensure configuration is valid
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;terraform plan&lt;/code&gt; and verify expected resources
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;terraform apply&lt;/code&gt; and confirm successful provisioning
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resource Correctness
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Verify resources exist in AWS Console (EC2, ALB, Target Groups)
&lt;/li&gt;
&lt;li&gt;Confirm names, tags, and region match configuration
&lt;/li&gt;
&lt;li&gt;Ensure security group rules are correctly applied
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Functional Verification
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Retrieve ALB DNS using &lt;code&gt;terraform output&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;curl http://&amp;lt;alb-dns&amp;gt;&lt;/code&gt; and verify response
&lt;/li&gt;
&lt;li&gt;Confirm all instances pass health checks
&lt;/li&gt;
&lt;li&gt;Terminate one instance and verify ASG replaces it
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  State Consistency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;terraform plan&lt;/code&gt; after apply → expect “No changes”
&lt;/li&gt;
&lt;li&gt;Confirm Terraform state matches actual infrastructure
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Regression Check
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Make a small configuration change (e.g., add a tag)
&lt;/li&gt;
&lt;li&gt;Ensure only intended changes appear in &lt;code&gt;terraform plan&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Apply and verify the plan is clean afterward
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Test Execution: What Worked and What Didn’t
&lt;/h2&gt;

&lt;p&gt;Running the checklist revealed both successes and failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Successful Tests
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Terraform Initialization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;terraform init&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Terraform Apply&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;terraform apply -auto-approve&lt;/p&gt;

&lt;p&gt;Result: PASS&lt;br&gt;&lt;br&gt;
All resources were successfully created (see screenshots).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ALB Functional Test&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;curl &lt;a href="http://my-app-alb-123456.us-east-1.elb.amazonaws.com" rel="noopener noreferrer"&gt;http://my-app-alb-123456.us-east-1.elb.amazonaws.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Result: PASS&lt;br&gt;&lt;br&gt;
Returned: &lt;code&gt;"Hello World v1"&lt;/code&gt; (confirmed via browser)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto Scaling Self-Healing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;aws ec2 terminate-instances --instance-ids i-xxxx&lt;/p&gt;

&lt;p&gt;Result: PASS&lt;br&gt;&lt;br&gt;
A replacement instance was automatically launched.&lt;/p&gt;




&lt;h3&gt;
  
  
  Failure and Fix
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Test: Terraform Plan Consistency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;terraform plan&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expected: No changes
&lt;/li&gt;
&lt;li&gt;Actual: 1 resource change detected
&lt;/li&gt;
&lt;li&gt;Result: FAIL
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A missing tag in the security group configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Added the missing tag in the Terraform code and re-applied:&lt;/p&gt;

&lt;p&gt;terraform apply&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retest Result:&lt;/strong&gt; PASS  &lt;/p&gt;

&lt;p&gt;This failure highlighted how manual testing uncovers real issues that static validation cannot.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing Across Environments
&lt;/h2&gt;

&lt;p&gt;I ran the same tests in both development and production environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Differences:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Dev used &lt;code&gt;t2.micro&lt;/code&gt;, production used &lt;code&gt;t3.medium&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Production had stricter security group rules
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unexpected Issue:
&lt;/h3&gt;

&lt;p&gt;The application initially failed in production because HTTP (port 80) was blocked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Updated the security group to allow inbound HTTP traffic.&lt;/p&gt;

&lt;p&gt;This demonstrated a common real-world problem: &lt;strong&gt;something works in dev but fails in production due to configuration differences.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Importance of Cleanup
&lt;/h2&gt;

&lt;p&gt;After testing, I destroyed all resources to avoid unnecessary costs.&lt;/p&gt;

&lt;p&gt;terraform destroy -auto-approve&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification
&lt;/h3&gt;

&lt;p&gt;aws ec2 describe-instances --filters "Name=tag:ManagedBy,Values=terraform"&lt;/p&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;p&gt;[]&lt;/p&gt;

&lt;p&gt;aws elbv2 describe-load-balancers&lt;/p&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;p&gt;[]&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Cleanup Matters
&lt;/h3&gt;

&lt;p&gt;Cleaning up sounds simple, but it is often where engineers fail. Terraform may partially destroy resources, leaving orphaned infrastructure behind.&lt;/p&gt;

&lt;p&gt;If cleanup is ignored:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS costs can accumulate quickly
&lt;/li&gt;
&lt;li&gt;Old resources can interfere with future tests
&lt;/li&gt;
&lt;li&gt;Infrastructure drift becomes harder to manage
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Lessons from Terraform Import
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;terraform import&lt;/code&gt; lab introduced a critical concept: bringing existing infrastructure under Terraform management.&lt;/p&gt;

&lt;h3&gt;
  
  
  What It Solves
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Allows Terraform to manage manually created resources
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What It Does NOT Solve
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It does not generate Terraform configuration
&lt;/li&gt;
&lt;li&gt;You must manually write &lt;code&gt;.tf&lt;/code&gt; files
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reinforces that Terraform is not just a tool — it requires discipline and understanding.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Challenges and Fixes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Root Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Missing tag&lt;/td&gt;
&lt;td&gt;Not defined in config&lt;/td&gt;
&lt;td&gt;Added tag block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALB not accessible&lt;/td&gt;
&lt;td&gt;Port 80 blocked&lt;/td&gt;
&lt;td&gt;Updated security group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan inconsistency&lt;/td&gt;
&lt;td&gt;Config drift&lt;/td&gt;
&lt;td&gt;Re-applied configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Manual testing is not optional — it is the foundation of reliable infrastructure.&lt;/p&gt;

&lt;p&gt;It helps you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand your system deeply
&lt;/li&gt;
&lt;li&gt;Catch real-world failures early
&lt;/li&gt;
&lt;li&gt;Build confidence before automation
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every failure discovered during manual testing becomes a future automated test case.&lt;/p&gt;

&lt;p&gt;As I move forward in this challenge, the next step is clear: &lt;strong&gt;turn these manual checks into automated tests.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Day 17 Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Built a structured manual testing checklist
&lt;/li&gt;
&lt;li&gt;Tested both dev and production environments
&lt;/li&gt;
&lt;li&gt;Identified and fixed real infrastructure issues
&lt;/li&gt;
&lt;li&gt;Practiced strict cleanup discipline
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manual testing isn’t just a step — it’s a mindset.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>terraform</category>
      <category>testing</category>
    </item>
    <item>
      <title>Refactoring Terraform Toward Production-Grade Standards</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Tue, 31 Mar 2026 13:00:58 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/refactoring-terraform-toward-production-grade-standards-il</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/refactoring-terraform-toward-production-grade-standards-il</guid>
      <description>&lt;p&gt;Day 16 of my &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; was all about improving infrastructure quality rather than simply adding more resources.&lt;/p&gt;

&lt;p&gt;Today I took an existing Terraform setup and refactored it to make it more &lt;strong&gt;production-ready&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;I focused on several key areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reusable module structure&lt;/li&gt;
&lt;li&gt;consistent tagging&lt;/li&gt;
&lt;li&gt;lifecycle protection&lt;/li&gt;
&lt;li&gt;input validation&lt;/li&gt;
&lt;li&gt;CloudWatch monitoring&lt;/li&gt;
&lt;li&gt;basic automated testing with Terratest&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Biggest Refactors
&lt;/h2&gt;

&lt;p&gt;One of the most useful improvements was introducing a shared &lt;code&gt;common_tags&lt;/code&gt; block so I could apply consistent metadata across resources without repeating the same tag definitions everywhere.&lt;/p&gt;

&lt;p&gt;I also added lifecycle rules like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create_before_destroy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prevent_destroy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are small changes in code, but they make a huge difference in real environments where accidental deletion or downtime can be expensive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring and Validation
&lt;/h2&gt;

&lt;p&gt;I added a &lt;strong&gt;CloudWatch CPU alarm&lt;/strong&gt; and input validation rules to make the infrastructure safer and easier to operate.&lt;/p&gt;

&lt;p&gt;That helped shift my thinking from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Will this deploy?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Will this still be safe, maintainable, and observable later?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Real Challenge I Hit
&lt;/h2&gt;

&lt;p&gt;The most realistic issue today was with &lt;strong&gt;ALB access logging&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Terraform failed because the Application Load Balancer didn’t have permission to write logs to my S3 bucket. I had to fix that by adding the correct bucket policy.&lt;/p&gt;

&lt;p&gt;That was a great reminder that “working Terraform” and “production-grade Terraform” are not the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;Today showed me that strong infrastructure is not just about provisioning resources - it is about designing for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;safety&lt;/li&gt;
&lt;li&gt;maintainability&lt;/li&gt;
&lt;li&gt;observability&lt;/li&gt;
&lt;li&gt;operational reliability&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Terraform #IaC #AWS #DevOps #CloudComputing #30DayTerraformChallenge #TerraformChallenge
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Working with Multiple Providers in Terraform</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Tue, 31 Mar 2026 11:00:24 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/working-with-multiple-providers-in-terraform-1f3o</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/working-with-multiple-providers-in-terraform-1f3o</guid>
      <description>&lt;p&gt;Day 15 of my &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; was all about understanding how Terraform works across &lt;strong&gt;different provider ecosystems&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Today I explored three areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS multi-provider modules&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes on EKS&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Multi-Provider Modules
&lt;/h2&gt;

&lt;p&gt;One of the biggest lessons from today was learning how reusable Terraform modules handle &lt;strong&gt;provider aliases&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of defining providers inside the module, the root configuration passes them in. This makes modules much more reusable across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;regions&lt;/li&gt;
&lt;li&gt;accounts&lt;/li&gt;
&lt;li&gt;environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used this pattern to deploy AWS resources across multiple regions and better understand how Terraform maps resources to the correct provider instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker with Terraform
&lt;/h2&gt;

&lt;p&gt;I also worked with the &lt;strong&gt;Docker provider&lt;/strong&gt; to define a local nginx container using Terraform.&lt;/p&gt;

&lt;p&gt;This was a nice reminder that Terraform is not limited to cloud infrastructure — it can also manage local containerized workloads and other platforms through providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  EKS and Kubernetes
&lt;/h2&gt;

&lt;p&gt;The most advanced part of today was provisioning an &lt;strong&gt;EKS cluster&lt;/strong&gt; and configuring the &lt;strong&gt;Kubernetes provider&lt;/strong&gt; to deploy workloads into it.&lt;/p&gt;

&lt;p&gt;I successfully created the EKS infrastructure, but I also ran into a very realistic issue:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the Kubernetes provider timed out when trying to reach the cluster API endpoint.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That challenge helped me understand an important real-world distinction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provisioning infrastructure is one step&lt;/li&gt;
&lt;li&gt;connecting workloads to it is another&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Today helped me understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why &lt;code&gt;configuration_aliases&lt;/code&gt; matters&lt;/li&gt;
&lt;li&gt;how providers are passed into modules&lt;/li&gt;
&lt;li&gt;how Terraform can work with AWS, Docker, and Kubernetes in one workflow&lt;/li&gt;
&lt;li&gt;why EKS troubleshooting often goes beyond just “Terraform code”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though Kubernetes deployment was not fully completed, this was one of the most practical learning days so far.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terraform #IaC #AWS #Docker #Kubernetes #EKS #DevOps #30DayTerraformChallenge #TerraformChallenge
&lt;/h1&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>terraform</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with Multiple Providers in Terraform</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Mon, 30 Mar 2026 13:03:16 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/getting-started-with-multiple-providers-in-terraform-294n</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/getting-started-with-multiple-providers-in-terraform-294n</guid>
      <description>&lt;p&gt;Day 14 of my &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; focused on understanding how &lt;strong&gt;Terraform providers&lt;/strong&gt; work and why they are so important in real-world infrastructure.&lt;/p&gt;

&lt;p&gt;A provider is the plugin Terraform uses to communicate with a platform like AWS. When you run &lt;code&gt;terraform init&lt;/code&gt;, Terraform reads the &lt;code&gt;required_providers&lt;/code&gt; block, downloads the correct provider versions from the Terraform Registry, and records the exact versions in &lt;code&gt;.terraform.lock.hcl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That lock file turned out to be one of the most important things I learned about today. It keeps provider versions consistent across machines and CI/CD pipelines, which helps avoid “it works on my machine” problems. It should always be committed to version control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Provider Versioning Matters
&lt;/h2&gt;

&lt;p&gt;I used version constraints such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~&amp;gt; 6.0&lt;/code&gt; for AWS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~&amp;gt; 3.6&lt;/code&gt; for the random provider&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means Terraform can use safe updates within a version range while avoiding unexpected breaking changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Region Deployment with Provider Aliases
&lt;/h2&gt;

&lt;p&gt;The most practical part of today was using &lt;strong&gt;provider aliases&lt;/strong&gt; to deploy resources into &lt;strong&gt;multiple AWS regions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I configured:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;default AWS provider&lt;/strong&gt; for &lt;code&gt;us-east-1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;aliased AWS provider&lt;/strong&gt; for &lt;code&gt;us-west-2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I used them to create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;primary S3 bucket&lt;/strong&gt; in one region&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;replica S3 bucket&lt;/strong&gt; in another region&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made it much easier to understand how Terraform decides which AWS API endpoint to call for each resource.&lt;/p&gt;

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

&lt;p&gt;What stood out to me most today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Providers are what connect Terraform to cloud APIs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform init&lt;/code&gt; is where provider installation and setup happens&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.terraform.lock.hcl&lt;/code&gt; is essential for consistency&lt;/li&gt;
&lt;li&gt;Provider aliases make &lt;strong&gt;multi-region deployments clean and manageable&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Day 14 gave me a much deeper understanding of how Terraform works under the hood, and it made multi-region infrastructure feel much more approachable.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terraform #IaC #AWS #DevOps #MultiRegion #CloudComputing #30DayTerraformChallenge #TerraformChallenge
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Protecting Secrets in Terraform: What I Learned About Sensitive Data Management</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Mon, 30 Mar 2026 10:04:10 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/protecting-secrets-in-terraform-what-i-learned-about-sensitive-data-management-18nh</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/protecting-secrets-in-terraform-what-i-learned-about-sensitive-data-management-18nh</guid>
      <description>&lt;p&gt;Day 13 of my &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; was focused on one of the most important production topics in Infrastructure as Code: &lt;strong&gt;handling secrets securely&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Today’s work was less about deploying flashy infrastructure and more about learning how easily sensitive data can leak if Terraform is not configured carefully.&lt;/p&gt;

&lt;p&gt;It was a very practical lesson because secrets are everywhere in cloud environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;database passwords&lt;/li&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;tokens&lt;/li&gt;
&lt;li&gt;access credentials&lt;/li&gt;
&lt;li&gt;connection strings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if they are not handled properly, they can end up exposed in places you might not immediately think about.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Secret Management Matters in Terraform
&lt;/h2&gt;

&lt;p&gt;Terraform makes it very easy to define infrastructure in code, but that also creates risk.&lt;/p&gt;

&lt;p&gt;The moment you start working with values like passwords or credentials, you have to think about &lt;strong&gt;where those values live&lt;/strong&gt; and &lt;strong&gt;who can see them&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A secret does not have to be directly printed in your code to be leaked.&lt;/p&gt;

&lt;p&gt;It can show up in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform files&lt;/li&gt;
&lt;li&gt;terminal output&lt;/li&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;state files&lt;/li&gt;
&lt;li&gt;version control&lt;/li&gt;
&lt;li&gt;backend storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means secret management is not just one problem — it is a set of security risks across the Terraform workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Three Main Secret Leak Paths
&lt;/h2&gt;

&lt;p&gt;One of the biggest takeaways from today was learning that there are &lt;strong&gt;three major places secrets can leak&lt;/strong&gt; when using Terraform.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Secrets hardcoded in &lt;code&gt;.tf&lt;/code&gt; files
&lt;/h3&gt;

&lt;p&gt;This is the most obvious mistake.&lt;/p&gt;

&lt;p&gt;If you directly write a password or API key in a Terraform file, then anyone who has access to the codebase can see it.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;teammates&lt;/li&gt;
&lt;li&gt;collaborators&lt;/li&gt;
&lt;li&gt;Git history&lt;/li&gt;
&lt;li&gt;backups&lt;/li&gt;
&lt;li&gt;accidental commits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the easiest kind of leak to avoid, but it is also one of the most common mistakes beginners make.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Secrets exposed in plan and apply output
&lt;/h3&gt;

&lt;p&gt;Even if a secret is not hardcoded in a file, Terraform can still print it to the terminal if you are not careful.&lt;/p&gt;

&lt;p&gt;That means secrets can accidentally appear in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;screenshots&lt;/li&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;CI/CD pipelines&lt;/li&gt;
&lt;li&gt;recorded demos&lt;/li&gt;
&lt;li&gt;shell history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially dangerous because people often assume that if a secret is not in the &lt;code&gt;.tf&lt;/code&gt; file, then it is automatically safe.&lt;/p&gt;

&lt;p&gt;That is not true.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Secrets stored in Terraform state
&lt;/h3&gt;

&lt;p&gt;This was the most important lesson of the day.&lt;/p&gt;

&lt;p&gt;Even if a secret is hidden in terminal output and not stored directly in code, Terraform may still keep it in the &lt;strong&gt;state file&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means the state file becomes one of the most sensitive assets in your Terraform workflow.&lt;/p&gt;

&lt;p&gt;If someone can access your state file, they may still be able to retrieve sensitive information.&lt;/p&gt;

&lt;p&gt;That is why protecting state is such a critical part of Terraform security.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using AWS Secrets Manager Instead of Hardcoding Values
&lt;/h2&gt;

&lt;p&gt;To avoid placing secrets directly in my Terraform files, I used &lt;strong&gt;AWS Secrets Manager&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This was a much better pattern because the secret value lived in AWS instead of inside my code.&lt;/p&gt;

&lt;p&gt;Terraform only referenced the secret when it needed to use it.&lt;/p&gt;

&lt;p&gt;That gave me a much safer workflow because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the actual password did not appear in my &lt;code&gt;.tf&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;the secret was centrally stored&lt;/li&gt;
&lt;li&gt;the secret could be managed outside the Terraform codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a much more realistic pattern for production infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why &lt;code&gt;sensitive = true&lt;/code&gt; Matters
&lt;/h2&gt;

&lt;p&gt;Another important thing I practiced today was using &lt;strong&gt;&lt;code&gt;sensitive = true&lt;/code&gt;&lt;/strong&gt; on variables and outputs.&lt;/p&gt;

&lt;p&gt;This tells Terraform to &lt;strong&gt;hide the value in CLI output&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means instead of printing the actual value, Terraform shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(sensitive value)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful because it helps reduce accidental leaks in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;terminal output&lt;/li&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;screenshots&lt;/li&gt;
&lt;li&gt;demos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it worked exactly as expected during my apply output.&lt;/p&gt;

&lt;p&gt;That said, today also taught me something very important:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;sensitive = true&lt;/code&gt; is not enough on its own
&lt;/h2&gt;

&lt;p&gt;It only protects &lt;strong&gt;display output&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; mean Terraform stops storing the value in state.&lt;/p&gt;

&lt;p&gt;That distinction is extremely important.&lt;/p&gt;




&lt;h2&gt;
  
  
  Securing Terraform State Properly
&lt;/h2&gt;

&lt;p&gt;Since Terraform state may still contain sensitive values, securing the backend is essential.&lt;/p&gt;

&lt;p&gt;For this challenge, I used an &lt;strong&gt;S3 backend&lt;/strong&gt; with the right protections in mind.&lt;/p&gt;

&lt;p&gt;The security measures I focused on included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;encryption enabled&lt;/li&gt;
&lt;li&gt;versioning enabled&lt;/li&gt;
&lt;li&gt;public access blocked&lt;/li&gt;
&lt;li&gt;access restricted through IAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made me realize that in real-world Terraform usage, protecting state is just as important as protecting code.&lt;/p&gt;

&lt;p&gt;A lot of teams focus on hiding secrets in variables but forget that state is often where the real exposure risk still exists.&lt;/p&gt;

&lt;p&gt;That was one of the strongest lessons from today.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why &lt;code&gt;.gitignore&lt;/code&gt; Matters More Than It Seems
&lt;/h2&gt;

&lt;p&gt;Another small but very important part of today’s work was making sure Terraform files that should never be committed are excluded from Git.&lt;/p&gt;

&lt;p&gt;That includes things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.tfstate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.tfvars&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.terraform/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;backup state files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because many secret leaks happen not from Terraform itself, but from &lt;strong&gt;accidental commits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A strong &lt;code&gt;.gitignore&lt;/code&gt; is a simple but important layer of protection.&lt;/p&gt;

&lt;p&gt;It is one of those small things that can prevent very big mistakes.&lt;/p&gt;




&lt;h2&gt;
  
  
  HashiCorp Vault vs AWS Secrets Manager
&lt;/h2&gt;

&lt;p&gt;Today also pushed me to think more broadly about &lt;strong&gt;where secrets should live&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I learned that:&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Secrets Manager is great when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;your infrastructure is mostly in AWS&lt;/li&gt;
&lt;li&gt;you want native AWS integration&lt;/li&gt;
&lt;li&gt;you need a simpler managed secrets workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  HashiCorp Vault is better when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;you are working across multiple clouds&lt;/li&gt;
&lt;li&gt;you need dynamic secrets&lt;/li&gt;
&lt;li&gt;you want more advanced centralized secrets management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That comparison was useful because it showed that secure secret handling is not only about Terraform syntax — it is also about choosing the right tool for your environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Challenges I Faced
&lt;/h2&gt;

&lt;p&gt;One issue I ran into was that Terraform could not read the secret until it actually existed in AWS Secrets Manager.&lt;/p&gt;

&lt;p&gt;That meant I had to create the secret first before Terraform could successfully reference it.&lt;/p&gt;

&lt;p&gt;I also had to make sure the JSON structure inside the secret matched exactly with what Terraform expected.&lt;/p&gt;

&lt;p&gt;That is a small detail, but if the keys do not match, Terraform will fail when trying to decode and reference the values.&lt;/p&gt;

&lt;p&gt;Finally, I had to be very clear in my understanding of what &lt;code&gt;sensitive = true&lt;/code&gt; actually does.&lt;/p&gt;

&lt;p&gt;At first glance, it feels like full protection, but in reality it is just &lt;strong&gt;output masking&lt;/strong&gt;, not full secret protection.&lt;/p&gt;

&lt;p&gt;That was probably the biggest conceptual lesson from today.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned from Day 13
&lt;/h2&gt;

&lt;p&gt;Day 13 taught me that secret management in Terraform is not just about “hiding passwords.”&lt;/p&gt;

&lt;p&gt;It is about understanding the &lt;strong&gt;entire lifecycle of sensitive data&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;where it is defined&lt;/li&gt;
&lt;li&gt;where it is displayed&lt;/li&gt;
&lt;li&gt;where it is stored&lt;/li&gt;
&lt;li&gt;where it might accidentally leak&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest lessons for me were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;never hardcode secrets in Terraform files&lt;/li&gt;
&lt;li&gt;use a secret manager whenever possible&lt;/li&gt;
&lt;li&gt;mark sensitive values properly&lt;/li&gt;
&lt;li&gt;protect Terraform state seriously&lt;/li&gt;
&lt;li&gt;never assume a hidden CLI value means the secret is fully safe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was one of the most practical and security-focused challenge days so far.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Day 13 reminded me that secure infrastructure is not just about provisioning resources — it is also about protecting the information that makes those resources work.&lt;/p&gt;

&lt;p&gt;Terraform is powerful, but with that power comes responsibility.&lt;/p&gt;

&lt;p&gt;Learning how to handle secrets properly is one of the most important habits to build early.&lt;/p&gt;

&lt;p&gt;And I’m glad this challenge included it, because it made me think more like someone building &lt;strong&gt;real production infrastructure&lt;/strong&gt;, not just completing labs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connect With Me
&lt;/h2&gt;

&lt;p&gt;I’m documenting my journey through the &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; as I continue learning more about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;AWS&lt;/li&gt;
&lt;li&gt;Infrastructure as Code&lt;/li&gt;
&lt;li&gt;DevOps best practices&lt;/li&gt;
&lt;li&gt;Cloud security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are also learning cloud or IaC, feel free to connect.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terraform #IaC #AWS #DevOps #CloudSecurity #SecretsManagement #InfrastructureAsCode #30DayTerraformChallenge #TerraformChallenge
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Mastering Zero-Downtime Deployments with Terraform</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Mon, 30 Mar 2026 08:52:08 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/mastering-zero-downtime-deployments-with-terraform-480m</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/mastering-zero-downtime-deployments-with-terraform-480m</guid>
      <description>&lt;p&gt;Day 12 of my &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; was all about one of the most practical and important DevOps concepts: &lt;strong&gt;deploying updates without taking an application offline&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Today I learned how Terraform handles infrastructure replacement, why its default behavior can cause downtime, and how to solve that using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create_before_destroy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Auto Scaling Groups&lt;/li&gt;
&lt;li&gt;Application Load Balancers&lt;/li&gt;
&lt;li&gt;a full &lt;strong&gt;blue/green deployment strategy&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was one of the most exciting challenge days so far because it felt much closer to how production systems are actually managed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Default Terraform Can Cause Downtime
&lt;/h2&gt;

&lt;p&gt;Terraform is great at managing infrastructure, but not every resource can be updated in place.&lt;/p&gt;

&lt;p&gt;Some resources — especially those tied to EC2 instance configuration, such as Launch Templates or Launch Configurations — often require replacement instead of modification.&lt;/p&gt;

&lt;p&gt;By default, Terraform follows this sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Destroy the old resource&lt;/li&gt;
&lt;li&gt;Create the new resource&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is a problem for live infrastructure.&lt;/p&gt;

&lt;p&gt;If an Auto Scaling Group is destroyed before the replacement is ready, then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the old EC2 instances are terminated&lt;/li&gt;
&lt;li&gt;the application stops serving traffic&lt;/li&gt;
&lt;li&gt;users experience downtime&lt;/li&gt;
&lt;li&gt;only after that does the new infrastructure come online&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gap is the outage window.&lt;/p&gt;

&lt;p&gt;In real systems, even a short outage like that can be unacceptable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;create_before_destroy&lt;/code&gt; Solution
&lt;/h2&gt;

&lt;p&gt;Terraform provides a lifecycle rule called &lt;strong&gt;&lt;code&gt;create_before_destroy&lt;/code&gt;&lt;/strong&gt; to fix this.&lt;/p&gt;

&lt;p&gt;Instead of deleting the old resource first, Terraform reverses the order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the new resource&lt;/li&gt;
&lt;li&gt;Wait for it to become healthy&lt;/li&gt;
&lt;li&gt;Destroy the old resource&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That simple change makes a huge difference.&lt;/p&gt;

&lt;p&gt;It allows new infrastructure to come online before the old infrastructure disappears, which is exactly what you want during updates.&lt;/p&gt;

&lt;p&gt;This was the key pattern behind today’s zero-downtime deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Auto Scaling Group Naming Problem
&lt;/h2&gt;

&lt;p&gt;There is an important catch with &lt;code&gt;create_before_destroy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When Terraform creates the replacement resource before destroying the original one, &lt;strong&gt;both resources must exist at the same time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That becomes a problem with Auto Scaling Groups because AWS does &lt;strong&gt;not&lt;/strong&gt; allow two ASGs with the same name to exist simultaneously.&lt;/p&gt;

&lt;p&gt;So if your ASG name is fixed, the deployment fails.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix
&lt;/h3&gt;

&lt;p&gt;The correct solution is to avoid hardcoding the name and instead use something like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name_prefix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;or a generated unique suffix&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used &lt;strong&gt;&lt;code&gt;name_prefix&lt;/code&gt;&lt;/strong&gt; so AWS could generate unique names for replacement Auto Scaling Groups during deployment.&lt;/p&gt;

&lt;p&gt;That solved the conflict and made zero-downtime replacement possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deploying Version 1
&lt;/h2&gt;

&lt;p&gt;For the first deployment, I launched my infrastructure with a simple application response showing &lt;strong&gt;version 1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once the Auto Scaling Group instances passed health checks and the Load Balancer became healthy, I verified the application in the browser.&lt;/p&gt;

&lt;p&gt;This gave me my initial “live” environment.&lt;/p&gt;

&lt;p&gt;At that point, traffic was being served successfully by the original deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verifying Zero Downtime with a Traffic Loop
&lt;/h2&gt;

&lt;p&gt;To actually prove that my deployment was zero-downtime, I opened a second terminal and ran a continuous traffic check against the ALB.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;keep sending requests during deployment&lt;/li&gt;
&lt;li&gt;observe whether traffic ever failed&lt;/li&gt;
&lt;li&gt;confirm that the application stayed available the whole time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a very practical way to test infrastructure changes because it simulates a real user continuously accessing the app while updates are happening.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deploying Version 2
&lt;/h2&gt;

&lt;p&gt;Next, I updated the application response from &lt;strong&gt;v1&lt;/strong&gt; to &lt;strong&gt;v2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This forced Terraform to replace the underlying instance configuration.&lt;/p&gt;

&lt;p&gt;Normally, this kind of change could cause downtime.&lt;/p&gt;

&lt;p&gt;But because I had configured Terraform with &lt;code&gt;create_before_destroy&lt;/code&gt;, the update behaved differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the replacement infrastructure was created first&lt;/li&gt;
&lt;li&gt;the new instances came online&lt;/li&gt;
&lt;li&gt;health checks passed&lt;/li&gt;
&lt;li&gt;traffic remained available&lt;/li&gt;
&lt;li&gt;then the old resources were removed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What I Observed
&lt;/h3&gt;

&lt;p&gt;The traffic loop kept returning responses during the entire deployment.&lt;/p&gt;

&lt;p&gt;At one point, the output changed from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;v1&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;v2&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is that there were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;no connection errors&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;no timeouts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;no interruption in service&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was my clearest proof that the deployment was truly zero-downtime.&lt;/p&gt;

&lt;p&gt;This was honestly one of the most satisfying Terraform experiments I’ve done so far.&lt;/p&gt;




&lt;h2&gt;
  
  
  Taking It Further with Blue/Green Deployment
&lt;/h2&gt;

&lt;p&gt;After proving a rolling zero-downtime update, I moved on to a more advanced deployment strategy:&lt;/p&gt;

&lt;h2&gt;
  
  
  Blue/Green Deployment
&lt;/h2&gt;

&lt;p&gt;Blue/green deployment keeps &lt;strong&gt;two complete environments&lt;/strong&gt; running at the same time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blue&lt;/strong&gt; = current live environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Green&lt;/strong&gt; = new environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of replacing the live environment directly, you deploy the new version separately and then shift traffic when it is ready.&lt;/p&gt;

&lt;p&gt;This pattern is extremely powerful because it makes rollbacks much easier and reduces deployment risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the Traffic Switch Works
&lt;/h2&gt;

&lt;p&gt;I implemented blue/green using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;two target groups&lt;/li&gt;
&lt;li&gt;two Auto Scaling Groups&lt;/li&gt;
&lt;li&gt;one ALB listener rule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A variable called &lt;code&gt;active_environment&lt;/code&gt; controlled which target group received traffic.&lt;/p&gt;

&lt;p&gt;That meant I could switch between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;blue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;green&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;just by changing one Terraform variable and applying again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Is So Effective
&lt;/h3&gt;

&lt;p&gt;The traffic switch happens at the &lt;strong&gt;load balancer level&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means Terraform does not need to tear down one environment before enabling the other.&lt;/p&gt;

&lt;p&gt;Instead, the ALB updates where traffic is routed.&lt;/p&gt;

&lt;p&gt;This makes the cutover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fast&lt;/li&gt;
&lt;li&gt;clean&lt;/li&gt;
&lt;li&gt;effectively instantaneous&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is what makes blue/green deployment such a strong production deployment pattern.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing the Blue/Green Switch
&lt;/h2&gt;

&lt;p&gt;After both environments were deployed, I tested switching traffic from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;blue → green&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;then &lt;strong&gt;green → blue&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each time, the Terraform apply completed successfully and traffic moved to the selected environment.&lt;/p&gt;

&lt;p&gt;There was no observable interruption while switching.&lt;/p&gt;

&lt;p&gt;This was a really useful demonstration because it showed how infrastructure can support &lt;strong&gt;safe application rollouts and fast rollback paths&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is exactly the kind of deployment flexibility teams want in production systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations of &lt;code&gt;create_before_destroy&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Even though &lt;code&gt;create_before_destroy&lt;/code&gt; is powerful, it has tradeoffs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some limitations include:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You temporarily run duplicate infrastructure, which increases cost&lt;/li&gt;
&lt;li&gt;Some AWS resources still have naming or uniqueness constraints&lt;/li&gt;
&lt;li&gt;It does not provide fine-grained traffic control&lt;/li&gt;
&lt;li&gt;It is still tied to replacement behavior rather than explicit traffic switching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where blue/green becomes more powerful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tradeoffs of Blue/Green Deployment
&lt;/h2&gt;

&lt;p&gt;Blue/green solves several of the limitations of &lt;code&gt;create_before_destroy&lt;/code&gt;, but it introduces its own tradeoffs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;safer rollouts&lt;/li&gt;
&lt;li&gt;fast rollback&lt;/li&gt;
&lt;li&gt;cleaner traffic switching&lt;/li&gt;
&lt;li&gt;better deployment isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tradeoffs:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;higher cost because two environments run at once&lt;/li&gt;
&lt;li&gt;more infrastructure complexity&lt;/li&gt;
&lt;li&gt;more moving parts to manage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So while blue/green is more powerful, it also requires more operational discipline.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned from Day 12
&lt;/h2&gt;

&lt;p&gt;Today taught me that infrastructure updates are not just about changing code — they are about &lt;strong&gt;changing systems safely&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The biggest lessons for me were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why Terraform’s default replacement behavior can be dangerous&lt;/li&gt;
&lt;li&gt;how &lt;code&gt;create_before_destroy&lt;/code&gt; avoids downtime&lt;/li&gt;
&lt;li&gt;why naming strategy matters in AWS&lt;/li&gt;
&lt;li&gt;how blue/green deployment gives even more control&lt;/li&gt;
&lt;li&gt;how to verify deployment safety using real traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was one of the most production-relevant challenge days so far.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Day 12 made Terraform feel much closer to real-world DevOps work.&lt;/p&gt;

&lt;p&gt;It moved beyond simply provisioning infrastructure and into something more valuable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;deploying live updates safely and predictably&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is one of the most important skills in cloud and infrastructure engineering.&lt;/p&gt;

&lt;p&gt;And honestly, seeing traffic switch from &lt;strong&gt;v1 → v2&lt;/strong&gt; and &lt;strong&gt;blue → green&lt;/strong&gt; without downtime was very satisfying.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connect With Me
&lt;/h2&gt;

&lt;p&gt;I’m documenting my journey through the &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; as I continue learning more about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;AWS&lt;/li&gt;
&lt;li&gt;Infrastructure as Code&lt;/li&gt;
&lt;li&gt;DevOps best practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are also learning cloud or IaC, feel free to connect.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terraform #IaC #AWS #DevOps #ZeroDowntime #CloudComputing #30DayTerraformChallenge #TerraformChallenge #InfrastructureAsCode
&lt;/h1&gt;

</description>
      <category>aws</category>
      <category>devchallenge</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>How Conditionals Make Terraform Infrastructure Dynamic and Efficient</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Mon, 30 Mar 2026 06:29:28 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/how-conditionals-make-terraform-infrastructure-dynamic-and-efficient-2nbb</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/how-conditionals-make-terraform-infrastructure-dynamic-and-efficient-2nbb</guid>
      <description>&lt;p&gt;Day 11 of my &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; was all about going deeper into &lt;strong&gt;conditionals in Terraform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Yesterday I used conditionals briefly alongside loops, but today the focus was entirely on how conditionals make Terraform configurations more flexible, reusable, and environment-aware.&lt;/p&gt;

&lt;p&gt;This was one of the most practical Terraform days so far because it showed how a single codebase can behave differently in development and production without duplication.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Conditionals Matter in Terraform
&lt;/h2&gt;

&lt;p&gt;Without conditionals, it is very easy to end up maintaining multiple copies of similar infrastructure code.&lt;/p&gt;

&lt;p&gt;For example, you might create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one version for development&lt;/li&gt;
&lt;li&gt;another for staging&lt;/li&gt;
&lt;li&gt;another for production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That quickly becomes hard to maintain.&lt;/p&gt;

&lt;p&gt;Conditionals solve this by allowing Terraform to make decisions at plan time and apply time based on input variables and local values.&lt;/p&gt;

&lt;p&gt;That means one configuration can support multiple use cases while staying clean and reusable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Ternary Expression
&lt;/h2&gt;

&lt;p&gt;The core Terraform conditional is the &lt;strong&gt;ternary expression&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It follows this pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if condition is true → use one value&lt;/li&gt;
&lt;li&gt;otherwise → use another value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used this pattern to make my infrastructure behave differently depending on the environment.&lt;/p&gt;

&lt;p&gt;Instead of scattering conditional logic directly inside resource arguments, I moved all the decisions into a &lt;code&gt;locals&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;That made the module much easier to read and much easier to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Centralizing Logic in &lt;code&gt;locals&lt;/code&gt; Matters
&lt;/h2&gt;

&lt;p&gt;One of the most useful lessons from today was learning that &lt;strong&gt;locals are the right place for conditional decisions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I used locals to control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instance type&lt;/li&gt;
&lt;li&gt;minimum cluster size&lt;/li&gt;
&lt;li&gt;maximum cluster size&lt;/li&gt;
&lt;li&gt;whether monitoring is enabled&lt;/li&gt;
&lt;li&gt;environment-specific behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made my module cleaner because the resources themselves stayed focused on infrastructure, while the &lt;code&gt;locals&lt;/code&gt; block handled the logic.&lt;/p&gt;

&lt;p&gt;That separation is important when your Terraform grows beyond small examples.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conditional Resource Creation with &lt;code&gt;count&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One of the most powerful Terraform patterns I practiced today was:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;count = condition ? 1 : 0&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is how you make an entire resource optional.&lt;/p&gt;

&lt;p&gt;I used this for resources such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloudWatch alarms&lt;/li&gt;
&lt;li&gt;Route53 records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern is very useful because some resources should only exist in specific environments or use cases.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;production might need monitoring&lt;/li&gt;
&lt;li&gt;development might not&lt;/li&gt;
&lt;li&gt;DNS records might only be created when explicitly requested&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern lets Terraform create or skip resources based on simple boolean toggles.&lt;/p&gt;




&lt;h2&gt;
  
  
  Referencing Conditional Resources Safely
&lt;/h2&gt;

&lt;p&gt;A very important lesson today was that &lt;strong&gt;conditionally created resources cannot be referenced like normal resources&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If a resource uses &lt;code&gt;count&lt;/code&gt;, then Terraform treats it like a list.&lt;/p&gt;

&lt;p&gt;That means if the count is zero, there is no resource at index &lt;code&gt;[0]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So if you reference it directly without checking whether it exists, Terraform throws an error.&lt;/p&gt;

&lt;p&gt;The correct pattern is to guard the reference with another conditional so Terraform only tries to read the resource when it was actually created.&lt;/p&gt;

&lt;p&gt;This is one of those details that seems small until it breaks your plan.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building an Environment-Aware Module
&lt;/h2&gt;

&lt;p&gt;The most practical part of today’s work was making my &lt;strong&gt;webserver cluster module&lt;/strong&gt; fully environment-aware.&lt;/p&gt;

&lt;p&gt;I used a single input variable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;environment&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That one variable then controlled multiple infrastructure decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  In development:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;smaller instance type&lt;/li&gt;
&lt;li&gt;smaller cluster size&lt;/li&gt;
&lt;li&gt;monitoring disabled&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  In production:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;larger instance type&lt;/li&gt;
&lt;li&gt;larger cluster size&lt;/li&gt;
&lt;li&gt;monitoring enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was a really good example of how one Terraform module can behave like multiple infrastructure configurations without duplicating code.&lt;/p&gt;

&lt;p&gt;That is exactly the kind of pattern that becomes valuable in real teams.&lt;/p&gt;




&lt;h2&gt;
  
  
  Input Validation: Catching Errors Early
&lt;/h2&gt;

&lt;p&gt;Another feature I added today was an &lt;strong&gt;input validation block&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This made sure that only valid environment names could be used, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dev&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;staging&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;production&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If someone passed an invalid value, Terraform returned a clear error before any infrastructure was deployed.&lt;/p&gt;

&lt;p&gt;This is such a useful pattern because it helps catch mistakes early and prevents confusing downstream issues.&lt;/p&gt;

&lt;p&gt;It is a small addition, but it makes a module much safer to share and reuse.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conditional Data Sources: Existing vs New Infrastructure
&lt;/h2&gt;

&lt;p&gt;One of the most interesting patterns from today was using conditionals with &lt;strong&gt;data sources&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I used this to support two deployment styles:&lt;/p&gt;

&lt;h3&gt;
  
  
  Brownfield deployment
&lt;/h3&gt;

&lt;p&gt;Use an &lt;strong&gt;existing VPC&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Greenfield deployment
&lt;/h3&gt;

&lt;p&gt;Create a &lt;strong&gt;new VPC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was controlled with a simple boolean toggle.&lt;/p&gt;

&lt;p&gt;That pattern makes a module much more flexible because it can work in environments where infrastructure already exists, as well as in environments where everything needs to be created from scratch.&lt;/p&gt;

&lt;p&gt;That is a very realistic real-world use case.&lt;/p&gt;




&lt;h2&gt;
  
  
  Challenges I Faced
&lt;/h2&gt;

&lt;p&gt;One of the key things I had to be careful with today was making sure conditionally created resources were referenced safely.&lt;/p&gt;

&lt;p&gt;Without the proper guards, Terraform would throw index errors when trying to access a resource that did not exist.&lt;/p&gt;

&lt;p&gt;I also had to make sure my validation logic only accepted the intended environment values and behaved correctly when testing invalid input.&lt;/p&gt;

&lt;p&gt;These were not huge blockers, but they were useful reminders that Terraform logic has to be designed carefully, especially when conditions affect whether resources exist at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned from Day 11
&lt;/h2&gt;

&lt;p&gt;Today helped me understand the difference between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;conditional expressions&lt;/strong&gt; → choosing between values&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;conditional resource creation&lt;/strong&gt; → deciding whether a resource exists at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That distinction is very important.&lt;/p&gt;

&lt;p&gt;I also learned that conditionals become much more powerful when combined with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;locals&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;li&gt;optional resources&lt;/li&gt;
&lt;li&gt;data sources&lt;/li&gt;
&lt;li&gt;environment-aware design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made Terraform feel much more like writing reusable infrastructure logic rather than just declaring resources one by one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Day 11 was one of the most practical and useful Terraform challenge days so far.&lt;/p&gt;

&lt;p&gt;It showed me how to write Terraform that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cleaner&lt;/li&gt;
&lt;li&gt;more flexible&lt;/li&gt;
&lt;li&gt;easier to reuse&lt;/li&gt;
&lt;li&gt;safer across environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, it showed how one Terraform configuration can support multiple environments and deployment patterns without needing multiple codebases.&lt;/p&gt;

&lt;p&gt;That is a huge step toward writing production-ready Infrastructure as Code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connect With Me
&lt;/h2&gt;

&lt;p&gt;I’m documenting my journey through the &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; as I continue learning more about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;AWS&lt;/li&gt;
&lt;li&gt;Infrastructure as Code&lt;/li&gt;
&lt;li&gt;DevOps best practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are also learning cloud or IaC, feel free to connect.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terraform #IaC #AWS #DevOps #CloudComputing #30DayTerraformChallenge #TerraformChallenge #InfrastructureAsCode
&lt;/h1&gt;

</description>
      <category>automation</category>
      <category>devchallenge</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Mastering Loops and Conditionals in Terraform</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Sun, 29 Mar 2026 12:12:23 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/mastering-loops-and-conditionals-in-terraform-4dii</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/mastering-loops-and-conditionals-in-terraform-4dii</guid>
      <description>&lt;p&gt;Day 10 of my &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; was all about making Terraform configurations more dynamic, scalable, and less repetitive.&lt;/p&gt;

&lt;p&gt;Until now, most of my Terraform resources were declared one by one. That works for small labs, but it quickly becomes inefficient when you need multiple similar resources. Today’s focus was on the features that solve that problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;count&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;for_each&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;for&lt;/code&gt; expressions&lt;/li&gt;
&lt;li&gt;conditional logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the Terraform features that make Infrastructure as Code feel much closer to programming.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;When working with infrastructure, repetition becomes a problem very quickly.&lt;/p&gt;

&lt;p&gt;If you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple IAM users&lt;/li&gt;
&lt;li&gt;repeated security group resources&lt;/li&gt;
&lt;li&gt;optional scaling policies&lt;/li&gt;
&lt;li&gt;different environment-specific configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…copy-pasting resource blocks is not a good long-term solution.&lt;/p&gt;

&lt;p&gt;Terraform’s looping and conditional tools help make configurations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;more reusable&lt;/li&gt;
&lt;li&gt;easier to maintain&lt;/li&gt;
&lt;li&gt;safer to update&lt;/li&gt;
&lt;li&gt;more scalable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is exactly what I worked on today.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using &lt;code&gt;count&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The simplest loop in Terraform is &lt;code&gt;count&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is useful when you want to create &lt;strong&gt;N copies of the same resource&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For my lab, I used &lt;code&gt;count&lt;/code&gt; to create multiple IAM users from a list.&lt;/p&gt;

&lt;p&gt;This worked well for creating a fixed number of users and helped me understand how &lt;code&gt;count.index&lt;/code&gt; works.&lt;/p&gt;

&lt;h3&gt;
  
  
  What &lt;code&gt;count.index&lt;/code&gt; Does
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;count.index&lt;/code&gt; gives the numeric position of the resource currently being created.&lt;/p&gt;

&lt;p&gt;So if I have three usernames in a list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;index &lt;code&gt;0&lt;/code&gt; → first user&lt;/li&gt;
&lt;li&gt;index &lt;code&gt;1&lt;/code&gt; → second user&lt;/li&gt;
&lt;li&gt;index &lt;code&gt;2&lt;/code&gt; → third user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes &lt;code&gt;count&lt;/code&gt; very straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with &lt;code&gt;count&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The downside is that Terraform tracks these resources by &lt;strong&gt;position&lt;/strong&gt;, not by identity.&lt;/p&gt;

&lt;p&gt;That means if I remove an item from the middle of the list, Terraform shifts the indexes of everything after it.&lt;/p&gt;

&lt;p&gt;As a result, resources can be unexpectedly recreated even if their names did not logically change.&lt;/p&gt;

&lt;p&gt;This is one of those Terraform behaviors that can easily surprise you if you do not know how resource indexing works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using &lt;code&gt;for_each&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;for_each&lt;/code&gt; solves the biggest weakness of &lt;code&gt;count&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of tracking resources by numeric position, Terraform tracks them by a &lt;strong&gt;key or value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means resources are more stable when the input collection changes.&lt;/p&gt;

&lt;p&gt;For today’s challenge, I used &lt;code&gt;for_each&lt;/code&gt; in two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;with a &lt;strong&gt;set&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;with a &lt;strong&gt;map&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;for_each&lt;/code&gt; with a Set
&lt;/h3&gt;

&lt;p&gt;This is useful when you just need unique values, such as usernames.&lt;/p&gt;

&lt;p&gt;Terraform creates one resource per value.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;for_each&lt;/code&gt; with a Map
&lt;/h3&gt;

&lt;p&gt;This is more powerful because it lets each resource carry its own extra configuration.&lt;/p&gt;

&lt;p&gt;For example, each IAM user could have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a department&lt;/li&gt;
&lt;li&gt;an admin status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made the configuration more descriptive and closer to a real-world use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;for_each&lt;/code&gt; Is Safer
&lt;/h3&gt;

&lt;p&gt;If I remove one user from a &lt;code&gt;for_each&lt;/code&gt; collection, Terraform only removes that specific resource.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; renumber the rest of them.&lt;/p&gt;

&lt;p&gt;That makes it much safer than &lt;code&gt;count&lt;/code&gt; for collections that may change over time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using &lt;code&gt;for&lt;/code&gt; Expressions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;for&lt;/code&gt; expressions are different from &lt;code&gt;count&lt;/code&gt; and &lt;code&gt;for_each&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;They do &lt;strong&gt;not&lt;/strong&gt; create resources.&lt;/p&gt;

&lt;p&gt;Instead, they are used to &lt;strong&gt;transform data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For this challenge, I used a &lt;code&gt;for&lt;/code&gt; expression in an output to create a clean map of IAM user ARNs keyed by username.&lt;/p&gt;

&lt;p&gt;This was useful because it turned raw Terraform resource data into a much more readable and reusable structure.&lt;/p&gt;

&lt;p&gt;I liked this part because it made outputs feel more intentional and structured rather than just dumping raw values.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using Conditional Logic
&lt;/h2&gt;

&lt;p&gt;Terraform conditionals use a ternary-style pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if condition is true → use one value&lt;/li&gt;
&lt;li&gt;otherwise → use another value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used conditionals in two practical ways today.&lt;/p&gt;

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

&lt;p&gt;I added an &lt;code&gt;enable_autoscaling&lt;/code&gt; variable to my webserver cluster module.&lt;/p&gt;

&lt;p&gt;When it is set to &lt;code&gt;true&lt;/code&gt;, Terraform creates the autoscaling policies.&lt;/p&gt;

&lt;p&gt;When it is set to &lt;code&gt;false&lt;/code&gt;, Terraform skips them.&lt;/p&gt;

&lt;p&gt;This is a great example of how conditionals can make infrastructure optional without duplicating code.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Environment-Based Instance Sizing
&lt;/h3&gt;

&lt;p&gt;I also used conditional logic to vary instance size by environment.&lt;/p&gt;

&lt;p&gt;This meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;dev&lt;/strong&gt; could use a smaller instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;production&lt;/strong&gt; could use a larger instance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a realistic pattern and something I would absolutely expect to see in a real deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Refactoring My Existing Infrastructure
&lt;/h2&gt;

&lt;p&gt;The most useful part of today’s challenge was applying these ideas to infrastructure I had already built.&lt;/p&gt;

&lt;p&gt;I refactored my &lt;strong&gt;webserver-cluster&lt;/strong&gt; module from previous challenge days and improved it using what I learned.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Changed
&lt;/h3&gt;

&lt;p&gt;I updated my module to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;replace repeated resource patterns with &lt;code&gt;for_each&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;count&lt;/code&gt; for optional autoscaling policies&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;locals&lt;/code&gt; to centralize environment-based logic&lt;/li&gt;
&lt;li&gt;use a &lt;code&gt;for&lt;/code&gt; expression in outputs to create cleaner maps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made the module much more reusable and much easier to maintain.&lt;/p&gt;

&lt;p&gt;That is one of the biggest things I am starting to appreciate with Terraform: a lot of the improvement is not about adding more resources, but about &lt;strong&gt;writing infrastructure more intelligently&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Challenge I Ran Into
&lt;/h2&gt;

&lt;p&gt;One issue I hit during the &lt;code&gt;for_each&lt;/code&gt; lab was accidentally trying to create duplicate IAM usernames in two different resource blocks.&lt;/p&gt;

&lt;p&gt;Terraform returned an &lt;code&gt;EntityAlreadyExists&lt;/code&gt; error because IAM usernames must be unique.&lt;/p&gt;

&lt;p&gt;That problem was not really about Terraform syntax — it was about understanding how the configuration behaved as a whole.&lt;/p&gt;

&lt;p&gt;I fixed it by changing the usernames in one of the collections so there was no overlap.&lt;/p&gt;

&lt;p&gt;That was a useful reminder that infrastructure logic still needs careful thinking, even when the syntax is correct.&lt;/p&gt;

&lt;p&gt;I also hit a temporary provider registry connection issue during &lt;code&gt;terraform init&lt;/code&gt;, but retrying resolved it.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Verdict: &lt;code&gt;count&lt;/code&gt; vs &lt;code&gt;for_each&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;After today, my takeaway is this:&lt;/p&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;count&lt;/code&gt; when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;you need a fixed number of almost identical resources&lt;/li&gt;
&lt;li&gt;indexing is acceptable&lt;/li&gt;
&lt;li&gt;you want a simple optional toggle pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;for_each&lt;/code&gt; when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;resources are tied to meaningful names or keys&lt;/li&gt;
&lt;li&gt;the collection may change over time&lt;/li&gt;
&lt;li&gt;you want more stable infrastructure behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So while &lt;code&gt;count&lt;/code&gt; is still useful, I would generally choose &lt;code&gt;for_each&lt;/code&gt; for anything that is likely to evolve.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Lessons from Day 10
&lt;/h2&gt;

&lt;p&gt;Today taught me that Terraform becomes much more powerful when you stop thinking in terms of static resource blocks and start thinking in terms of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collections&lt;/li&gt;
&lt;li&gt;transformations&lt;/li&gt;
&lt;li&gt;reusable logic&lt;/li&gt;
&lt;li&gt;controlled optional behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That shift makes Terraform feel much more expressive and much more maintainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Day 10 was one of the most practical challenge days so far.&lt;/p&gt;

&lt;p&gt;It was not just about learning new syntax — it was about learning how to write &lt;strong&gt;better Terraform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I now have a much clearer understanding of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when to use &lt;code&gt;count&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;when to use &lt;code&gt;for_each&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;how to use &lt;code&gt;for&lt;/code&gt; expressions&lt;/li&gt;
&lt;li&gt;how to make infrastructure conditional and environment-aware&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the kinds of patterns that make Infrastructure as Code more maintainable in real projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connect With Me
&lt;/h2&gt;

&lt;p&gt;I’m documenting my journey through the &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; as I continue learning more about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;AWS&lt;/li&gt;
&lt;li&gt;Infrastructure as Code&lt;/li&gt;
&lt;li&gt;DevOps best practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are also learning cloud or IaC, feel free to connect.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terraform #IaC #AWS #DevOps #CloudComputing #30DayTerraformChallenge #TerraformChallenge #InfrastructureAsCode
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Advanced Terraform Module Usage: Versioning, Gotchas, and Reuse Across Environments</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Sun, 29 Mar 2026 08:36:34 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/advanced-terraform-module-usage-versioning-gotchas-and-reuse-across-environments-e4n</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/advanced-terraform-module-usage-versioning-gotchas-and-reuse-across-environments-e4n</guid>
      <description>&lt;p&gt;Day 9 of my &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; focused on moving beyond basic Terraform modules into more practical, real-world infrastructure patterns.&lt;/p&gt;

&lt;p&gt;Today’s learning was centered around three key areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Module gotchas&lt;/li&gt;
&lt;li&gt;Module versioning&lt;/li&gt;
&lt;li&gt;Reusing modules safely across multiple environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was one of the most useful Terraform days so far because it introduced concepts that are essential when working in real teams and production environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Terraform modules make infrastructure reusable and easier to manage, but they can also introduce subtle bugs and inconsistencies if not designed carefully.&lt;/p&gt;

&lt;p&gt;In real-world DevOps and cloud engineering work, infrastructure should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reusable&lt;/li&gt;
&lt;li&gt;Predictable&lt;/li&gt;
&lt;li&gt;Versioned&lt;/li&gt;
&lt;li&gt;Safe across environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is exactly what today’s work helped me understand.&lt;/p&gt;




&lt;h2&gt;
  
  
  Module Gotcha #1: File Paths Inside Modules
&lt;/h2&gt;

&lt;p&gt;One of the most common mistakes when working with Terraform modules is referencing files using relative paths without considering where Terraform is being executed from.&lt;/p&gt;

&lt;p&gt;This becomes a problem when a module depends on files such as startup scripts, templates, or configuration files. If the file path is not handled correctly, Terraform may fail to locate the file.&lt;/p&gt;

&lt;p&gt;The safer and more reliable approach is to always reference files relative to the module itself rather than where Terraform is being run. This makes the module portable and easier to reuse.&lt;/p&gt;

&lt;p&gt;This was a very useful reminder that modules should always be written with portability in mind.&lt;/p&gt;




&lt;h2&gt;
  
  
  Module Gotcha #2: Inline Blocks vs Separate Resources
&lt;/h2&gt;

&lt;p&gt;Another common issue comes from mixing inline resource configuration with separate resource definitions.&lt;/p&gt;

&lt;p&gt;A good example is security group rules. Some Terraform resources allow rules to be defined directly inside the resource, while also allowing them to be created separately.&lt;/p&gt;

&lt;p&gt;Mixing both approaches in the same module can lead to conflicts, confusing behavior, and harder debugging.&lt;/p&gt;

&lt;p&gt;The better practice is to choose one style and remain consistent. Using separate resources tends to be more flexible and makes it easier to extend modules later without editing their internal structure too much.&lt;/p&gt;

&lt;p&gt;This is a small design decision, but it has a big impact on maintainability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Module Gotcha #3: Module Output Dependencies
&lt;/h2&gt;

&lt;p&gt;One subtle but important gotcha is depending on an entire module when only one specific output or resource is needed.&lt;/p&gt;

&lt;p&gt;When Terraform treats a whole module as a dependency, it can cause more resources than necessary to be recreated or reevaluated. This makes plans noisier and can lead to avoidable infrastructure changes.&lt;/p&gt;

&lt;p&gt;The better design approach is to expose specific outputs and structure modules so dependencies remain as granular as possible.&lt;/p&gt;

&lt;p&gt;This is one of those issues that may not seem serious at first, but in larger projects it can create a lot of unnecessary complexity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Reusable Module
&lt;/h2&gt;

&lt;p&gt;For this challenge, I worked with a reusable Terraform module called &lt;strong&gt;webserver-cluster&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The purpose of this module is to provision a complete web server cluster setup in AWS, including the supporting infrastructure needed to serve traffic reliably.&lt;/p&gt;

&lt;p&gt;The module provisions resources such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A VPC lookup&lt;/li&gt;
&lt;li&gt;Subnets&lt;/li&gt;
&lt;li&gt;Security groups&lt;/li&gt;
&lt;li&gt;An Application Load Balancer&lt;/li&gt;
&lt;li&gt;A Launch Template&lt;/li&gt;
&lt;li&gt;An Auto Scaling Group&lt;/li&gt;
&lt;li&gt;EC2 instances configured to run a simple Python-based web server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was exciting to see how much infrastructure could be abstracted into a reusable module that can be called from multiple environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Versioning the Module
&lt;/h2&gt;

&lt;p&gt;One of the most important parts of today’s challenge was learning how to version Terraform modules.&lt;/p&gt;

&lt;p&gt;Instead of always referencing a module directly from a local folder or an unpinned GitHub repository, I learned how to use version tags to control exactly which module release an environment should use.&lt;/p&gt;

&lt;p&gt;This is a very important practice because infrastructure code changes over time. Without versioning, an environment could suddenly behave differently just because the source module was updated.&lt;/p&gt;

&lt;p&gt;To solve this, I created versioned releases for my module:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;v0.0.1&lt;/strong&gt; — the initial reusable module release&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v0.0.2&lt;/strong&gt; — an updated version with a new customizable input variable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allowed me to safely introduce improvements while keeping full control over which environments used which version.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changed Between Versions
&lt;/h2&gt;

&lt;p&gt;The key improvement I introduced in the second version of the module was the ability to customize the text displayed by the web server.&lt;/p&gt;

&lt;p&gt;This made the module more flexible and more useful across different environments.&lt;/p&gt;

&lt;p&gt;For example, development environments can now display a custom message that clearly identifies them as development deployments, while production can remain more stable and unchanged.&lt;/p&gt;

&lt;p&gt;This may seem like a small feature, but it demonstrates the real value of versioning: making controlled improvements without breaking existing environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-Environment Reuse
&lt;/h2&gt;

&lt;p&gt;This was the part of the challenge that tied everything together.&lt;/p&gt;

&lt;p&gt;I configured different environments to intentionally use different module versions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development&lt;/strong&gt; uses the newer module version&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production&lt;/strong&gt; stays pinned to the older stable version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a best practice because development is where changes should be tested first. Production should only move to a newer module version after that version has been validated.&lt;/p&gt;

&lt;p&gt;This approach helps teams reduce risk, avoid unexpected infrastructure changes, and maintain confidence in production deployments.&lt;/p&gt;

&lt;p&gt;It also reflects how real engineering teams manage infrastructure releases in a controlled way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Version Pinning Matters
&lt;/h2&gt;

&lt;p&gt;One of the biggest lessons from today is that &lt;strong&gt;not pinning module versions is risky&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If a shared module changes and environments are not pinned to a specific release, then different engineers may end up deploying different infrastructure without realizing it.&lt;/p&gt;

&lt;p&gt;That can lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inconsistent deployments&lt;/li&gt;
&lt;li&gt;Unexpected behavior&lt;/li&gt;
&lt;li&gt;Hard-to-debug infrastructure issues&lt;/li&gt;
&lt;li&gt;Production instability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Version pinning helps solve this by making infrastructure behavior consistent and predictable.&lt;/p&gt;

&lt;p&gt;It gives teams control over when and how changes are introduced.&lt;/p&gt;




&lt;h2&gt;
  
  
  Challenges I Faced
&lt;/h2&gt;

&lt;p&gt;One of the biggest practical challenges I encountered was accidentally committing Terraform-generated files into Git.&lt;/p&gt;

&lt;p&gt;These included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.terraform&lt;/code&gt; directories&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform.tfstate&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;provider binaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made my repository extremely large and caused push failures.&lt;/p&gt;

&lt;p&gt;I fixed this by cleaning up the repository and adding a proper &lt;code&gt;.gitignore&lt;/code&gt; file so that only the actual source files were tracked.&lt;/p&gt;

&lt;p&gt;This turned out to be a valuable lesson on its own.&lt;/p&gt;

&lt;p&gt;Infrastructure repositories should be clean, lightweight, and focused only on reusable source code—not generated artifacts.&lt;/p&gt;




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

&lt;p&gt;Today’s challenge taught me several practical lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modules should be written to be portable and reusable&lt;/li&gt;
&lt;li&gt;Small design choices inside modules can cause major issues later&lt;/li&gt;
&lt;li&gt;Versioning is essential for safe infrastructure reuse&lt;/li&gt;
&lt;li&gt;Development and production should not always move at the same speed&lt;/li&gt;
&lt;li&gt;Clean Git practices are just as important in Infrastructure as Code as they are in software development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the kinds of details that make the difference between writing Terraform that “works” and writing Terraform that is safe, maintainable, and team-ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Day 9 was one of the most practical and eye-opening parts of my Terraform challenge so far.&lt;/p&gt;

&lt;p&gt;I moved beyond simply creating modules and started thinking more like a real infrastructure engineer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do I make this reusable?&lt;/li&gt;
&lt;li&gt;How do I make this safe for teams?&lt;/li&gt;
&lt;li&gt;How do I control change across environments?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are the kinds of questions that matter in real production work.&lt;/p&gt;

&lt;p&gt;I’m really enjoying how each day of this challenge builds not just technical skill, but also better engineering habits.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connect With Me
&lt;/h2&gt;

&lt;p&gt;I’m documenting my journey through the &lt;strong&gt;30-Day Terraform Challenge&lt;/strong&gt; as I continue learning more about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;AWS&lt;/li&gt;
&lt;li&gt;Infrastructure as Code&lt;/li&gt;
&lt;li&gt;DevOps best practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're also learning cloud or IaC, feel free to connect.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terraform #IaC #AWS #DevOps #CloudComputing #30DayTerraformChallenge #TerraformChallenge #Inf
&lt;/h1&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>terraform</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building Reusable Infrastructure with Terraform Modules</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Fri, 27 Mar 2026 13:21:31 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/building-reusable-infrastructure-with-terraform-modules-3l6a</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/building-reusable-infrastructure-with-terraform-modules-3l6a</guid>
      <description>&lt;h1&gt;
  
  
  Building Reusable Infrastructure with Terraform Modules
&lt;/h1&gt;

&lt;p&gt;As I progress through my 30-Day Terraform Challenge, Day 8 introduced one of the most practical and transformative concepts in Infrastructure as Code: &lt;strong&gt;Terraform modules&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Up to this point, I had been writing Terraform configurations directly for each environment. While this approach works for simple setups, it quickly becomes inefficient and repetitive when the same infrastructure needs to be deployed multiple times. This is where modules come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Modules Matter
&lt;/h2&gt;

&lt;p&gt;Terraform modules allow you to package infrastructure into reusable components. Instead of rewriting the same resources—such as load balancers, auto scaling groups, and security groups—you define them once and reuse them wherever needed.&lt;/p&gt;

&lt;p&gt;For this challenge, I created a reusable module for a complete web server cluster. This module includes all the necessary components required to run a scalable application behind an Application Load Balancer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying the Module Across Environments
&lt;/h2&gt;

&lt;p&gt;Once the module was created, I used it in two different environments: &lt;strong&gt;development&lt;/strong&gt; and &lt;strong&gt;production&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The key difference between these environments was not the infrastructure itself, but the &lt;strong&gt;input values&lt;/strong&gt; passed into the module. For example, development used smaller instances and fewer resources, while production used larger instances and higher scaling limits.&lt;/p&gt;

&lt;p&gt;This demonstrated a key advantage of Terraform modules: the ability to reuse the same infrastructure while adapting it to different needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Inputs and Outputs
&lt;/h2&gt;

&lt;p&gt;A major part of working with modules is understanding how &lt;strong&gt;inputs&lt;/strong&gt; and &lt;strong&gt;outputs&lt;/strong&gt; function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inputs (variables)&lt;/strong&gt; allow you to customize how the module behaves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outputs&lt;/strong&gt; expose important information from the module, such as the load balancer DNS name used to access the deployed application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation makes modules flexible and easy to integrate into different environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Root Modules vs Child Modules
&lt;/h2&gt;

&lt;p&gt;Another important concept I learned is the distinction between &lt;strong&gt;root modules&lt;/strong&gt; and &lt;strong&gt;child modules&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;root module&lt;/strong&gt; is the main Terraform configuration you execute.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;child module&lt;/strong&gt; is a reusable component called by the root module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure encourages better organization and makes infrastructure easier to manage as projects grow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges and Lessons Learned
&lt;/h2&gt;

&lt;p&gt;One of the main challenges I faced was using the wrong relative path when referencing the module. This caused Terraform to fail until I corrected the path. It was a small mistake, but it reinforced how important structure and accuracy are when working with Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This challenge changed how I think about Terraform. It is not just a tool for provisioning resources—it is a way to build &lt;strong&gt;clean, reusable, and scalable infrastructure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By using modules, I was able to reduce duplication, improve organization, and create a setup that can easily be extended in the future. This is a critical skill for anyone working in DevOps or cloud engineering.&lt;/p&gt;

&lt;p&gt;Moving forward, I will approach Terraform projects with modular design in mind, knowing that it leads to more efficient and maintainable infrastructure.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Terraform Workspaces vs File Layout for Environment Isolation</title>
      <dc:creator>Stephanie Makori</dc:creator>
      <pubDate>Tue, 24 Mar 2026 09:13:52 +0000</pubDate>
      <link>https://dev.to/stephanie_makori_845bb2c0/terraform-workspaces-vs-file-layout-for-environment-isolation-370k</link>
      <guid>https://dev.to/stephanie_makori_845bb2c0/terraform-workspaces-vs-file-layout-for-environment-isolation-370k</guid>
      <description>&lt;p&gt;Day 7 focused on solving a real world problem in infrastructure management, handling multiple environments safely and efficiently.&lt;/p&gt;

&lt;p&gt;In most projects, we need separate environments such as development, staging, and production. Terraform provides different ways to isolate these environments, and today I explored two of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Terraform Workspaces
&lt;/h2&gt;

&lt;p&gt;Workspaces allow you to reuse the same configuration while maintaining separate state files for each environment.&lt;/p&gt;

&lt;p&gt;I created three workspaces:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;terraform workspace new dev&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;terraform workspace new staging&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;terraform workspace new production&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then I used the active workspace inside my configuration:&lt;/p&gt;

&lt;p&gt;instance_type = var.instance_type[terraform.workspace]&lt;/p&gt;

&lt;p&gt;This allowed me to dynamically change the instance type based on the environment.&lt;/p&gt;

&lt;p&gt;Each workspace created its own infrastructure with separate state, even though the code was identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using File Layout Isolation
&lt;/h2&gt;

&lt;p&gt;The second approach was using separate directories for each environment.&lt;/p&gt;

&lt;p&gt;environments/&lt;br&gt;
dev/&lt;br&gt;
staging/&lt;br&gt;
production/&lt;/p&gt;

&lt;p&gt;Each folder had its own Terraform configuration and backend pointing to a different state file in S3.&lt;/p&gt;

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

&lt;p&gt;backend "s3" {&lt;br&gt;
bucket = "stephanie-day6-terraform-state-2026-unique"&lt;br&gt;
key = "environments/dev/terraform.tfstate"&lt;br&gt;
region = "us-east-1"&lt;br&gt;
dynamodb_table = "terraform-state-locks"&lt;br&gt;
encrypt = true&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;This ensured that each environment was completely isolated at both the code and state level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing the Two Approaches
&lt;/h2&gt;

&lt;p&gt;Workspaces are simple and fast to use. They are ideal when you want to manage multiple environments using the same configuration with minimal setup.&lt;/p&gt;

&lt;p&gt;However, they rely heavily on selecting the correct workspace. A mistake here can lead to changes being applied to the wrong environment.&lt;/p&gt;

&lt;p&gt;File layout isolation is more structured. Each environment has its own directory and backend configuration, which reduces the risk of accidental changes. This approach is more suitable for production systems where safety is critical.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Workspaces provide quick and flexible environment separation
&lt;/li&gt;
&lt;li&gt;File layouts provide stronger isolation and clarity
&lt;/li&gt;
&lt;li&gt;Both approaches use separate state files to manage infrastructure
&lt;/li&gt;
&lt;li&gt;Choosing the right approach depends on project size and team needs
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This lab showed that managing infrastructure is not just about creating resources. It is about organizing environments in a way that is safe, scalable, and easy to understand.&lt;/p&gt;

&lt;p&gt;Understanding these patterns is essential for building reliable systems in real world DevOps workflows.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terraform #AWS #DevOps #InfrastructureAsCode #CloudComputing
&lt;/h1&gt;

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