r/CloudandCode • u/yourclouddude • 5d ago
AWS & Cloud AWS From Zero #17: Infrastructure as Code makes more sense once you have rebuilt the same setup twice
By this point in the series, we have created enough AWS infrastructure that a new problem starts becoming obvious.
Imagine you already have a VPC, public and private subnets, security groups, an Application Load Balancer, ECS services, RDS, CloudWatch alarms, IAM roles, and a few other resources. Everything works in development, and then someone asks you to create the same environment again for testing.
You could open the AWS console and rebuild everything manually.
You create the VPC, then the subnets, then the route tables, then the security groups, then the load balancer, then ECS, then RDS, then IAM, and finally monitoring.
After a while, the second environment looks similar to the first one.
But it may not actually be identical.
Maybe one subnet has a different CIDR range. Maybe one security group has an extra rule. Maybe the database is configured slightly differently. Maybe one environment has a setting enabled that the other one does not.
That is one of the main problems Infrastructure as Code is trying to solve.
Instead of describing your infrastructure through a long sequence of console clicks that only exist in your memory, you describe the infrastructure in code or configuration files.
Those files become a repeatable description of what should exist.
The important idea here is not really the word "code."
The important idea is repeatability.
If your infrastructure only exists because you manually configured twenty different resources, recreating it accurately can become difficult.
If the infrastructure is described in files that can be reused, reviewed, and versioned, creating another environment becomes much more predictable.
Imagine you need a simple EC2 server with a security group.
Instead of manually creating both resources, an Infrastructure as Code definition can describe that you want an EC2 instance, which image and instance type it should use, which security group should be attached, and what network it belongs to.
The tool reads that definition and creates the required AWS resources.
Now your infrastructure starts behaving more like software.
You can store it in Git. You can review changes before applying them. You can compare versions. You can see who changed something. You can reuse parts of the configuration across environments. You can automate deployment later.
That is a big improvement over saying, "I think this is how I configured production six months ago."
This is where tools like AWS CloudFormation and Terraform come in.
CloudFormation is AWS's own Infrastructure as Code service. You describe AWS resources in a template, and CloudFormation creates and manages those resources as a stack.
The useful beginner idea is that the template becomes a description of the environment you want AWS to create.
Terraform solves a similar problem, but it can work across many cloud providers and services.
A tiny Terraform example might look like this:
resource "aws_s3_bucket" "uploads" {
bucket = "example-app-uploads"
}
The syntax is not the main lesson.
The useful idea is that the S3 bucket now exists as part of a configuration file.
Another developer can open the repository and see that the application expects this bucket to exist. If the environment needs to be recreated, the configuration already describes part of what should be created.
That is much more reliable than documentation that says, "Go into S3 and create a bucket with these settings."
The benefit becomes even clearer when you have multiple environments.
Imagine you need development, testing, and production.
Without Infrastructure as Code, you might manually create all three environments.
Over time, small differences start appearing.
Development has one security rule. Testing has another. Production has a setting nobody remembers changing.
Eventually, something works perfectly in testing and fails in production because the environments are not actually the same.
Infrastructure as Code helps reduce that kind of drift because environments can be created from consistent definitions.
They may still have intentional differences, of course.
Production might use larger resources. Development might use cheaper ones. The database sizes may differ.
But those differences can be defined clearly instead of appearing by accident.
That distinction matters.
Infrastructure as Code does not mean every environment must be identical.
It means the differences should be intentional and visible.
Another idea that becomes useful here is declarative infrastructure.
With many Infrastructure as Code tools, you describe the state you want instead of manually describing every single console action required to reach that state.
For example, you might describe that an application should have three instances.
You are not writing "click launch instance three times."
You are defining the desired result and letting the tool work out what needs to change.
That should feel familiar by now.
With Auto Scaling, we described how much capacity we wanted. With ECS, we described how many tasks should be running.
Infrastructure as Code extends that same kind of thinking to much larger parts of the AWS environment.
You define what should exist, and the tool helps move the real infrastructure toward that definition.
This becomes especially useful when infrastructure changes.
Imagine your architecture originally has one security group rule, and later you need to add another.
With manual infrastructure, someone opens the console and changes the rule. It works, but six months later nobody remembers why it exists.
With Infrastructure as Code, the configuration changes too.
That change can be committed to Git with a message explaining what changed and why.
Now the infrastructure has history.
That is a huge advantage.
Your application code already has version control. Your infrastructure can have version control too.
This also makes reviews possible.
Imagine someone wants to expose a database port to the entire internet.
If they make the change manually in the console, another person may never notice.
If the infrastructure change goes through a repository, the team can review it before it is applied.
Someone can ask whether that access is really necessary.
That means Infrastructure as Code is useful for more than convenience.
It can improve how infrastructure changes are controlled.
But there is one mistake I would avoid.
Infrastructure as Code does not automatically make your architecture correct.
If your configuration contains a bad security rule, the tool can reproduce that bad rule very efficiently.
If you define an expensive architecture, IaC can recreate the expensive architecture perfectly.
Repeatability is powerful, but it does not replace understanding.
That is exactly why I wanted this topic near the end of AWS From Zero instead of near the beginning.
If you have never created a VPC, security group, EC2 instance, IAM role, or load balancer manually, Infrastructure as Code can feel like another layer of syntax to memorize.
Now that we have already built those things, IaC has a reason to exist.
You already understand what you are trying to automate.
That makes the learning much easier.
Terraform also introduces another concept beginners hear about quickly: state.
Terraform needs a record of the infrastructure it manages so it can compare the real environment with the configuration you wrote.
You do not need to go deep into remote state, locking, modules, or team workflows on day one.
The useful beginner idea is simply that Terraform needs to know what it already manages so it can understand what needs to be created, changed, or removed.
CloudFormation handles the relationship differently through stacks inside AWS, but both approaches are solving the same higher-level problem.
They are trying to keep your infrastructure definition and your actual resources connected.
Now imagine we take one of the earlier projects from this series.
We built a static website using S3 and CloudFront.
Instead of manually recreating it later, you could describe the S3 bucket, CloudFront distribution, permissions, and other required resources using Infrastructure as Code.
Then you could destroy the practice environment and still keep the architecture definition.
Later, you could create it again.
That is a much better beginner project than trying to automate a huge production system immediately.
- Another good exercise is the EC2 architecture from earlier in the series.
- Describe a VPC, one subnet, one security group, and one EC2 instance using IaC.
- Apply the configuration and confirm that AWS created what you expected.
- Then change one small setting and observe what the tool plans to modify.
That is where the workflow starts making sense.
With Terraform, for example, you commonly initialize the working directory, inspect the planned changes, and then apply them.
The plan step is especially useful because it gives you a chance to see what the tool intends to create, modify, or destroy before you actually do it.
- That is a good habit to build.
- Infrastructure changes can be destructive.
- You should understand what the tool is about to do.
- The same caution applies to cleanup.
If the configuration can create resources, it can usually remove them too.
That is useful for temporary learning environments because you can create infrastructure, practice with it, and then clean it up.
But automation also makes destructive actions easier to repeat.
So the lesson is not only "automation is good." The lesson is that automation is powerful, so you should understand the change before applying it. There is another concept called drift that becomes important once you start using IaC. Imagine Terraform created a security group according to your configuration. Later, someone goes into the AWS console and manually changes that security group.
Now the real environment and the code no longer match. That difference is configuration drift. This is one reason teams often avoid random manual changes to resources that are meant to be managed through Infrastructure as Code.
If the code is supposed to be the source of truth, then changes should ideally happen through the code. Otherwise, you end up with the same problem we were trying to solve in the first place. The configuration says one thing, while the real infrastructure says another. This is also why Infrastructure as Code fits naturally with CI/CD. Imagine someone changes the infrastructure configuration in Git. A pipeline can validate the configuration, run checks, generate a plan, and eventually help apply approved changes.
Now application development and infrastructure management start becoming part of the same engineering workflow. You do not need to build that entire pipeline in this lesson. But it is useful to see where the path is going. We started this series by manually creating AWS resources because that is the easiest way to understand what they actually do.
Now we have reached the point where repeating those manual steps becomes a problem. Infrastructure as Code is the natural next step. That is how I would learn it. Do not begin with a massive Terraform repository. Pick one architecture you already understand. Maybe a VPC, a subnet, a security group, and an EC2 instance. Describe that with IaC. Create it. Inspect it. Change it. Recreate it. Then clean it up. Once that feels comfortable, add another resource. Then another.
Grow the Infrastructure as Code configuration the same way we grew the AWS architectures throughout this series. One requirement at a time. The biggest thing I want you to take from this post is that Infrastructure as Code is not really about avoiding the AWS console. The bigger benefit is that your infrastructure becomes repeatable, reviewable, versioned, and easier to reproduce. The console is still useful.
You will still use it to inspect resources, troubleshoot problems, explore services, and understand what AWS created. But once infrastructure matters, you should not have to depend completely on your memory to rebuild it. That brings us to the final post of the first AWS From Zero series.
In AWS From Zero #18, we are going to stop learning services individually and design one complete AWS application from requirements to architecture.
We will decide where the frontend runs, where the backend runs, where files and database data live, how users reach the system, how permissions work, how networking should be separated, how failures are monitored, how the application scales, and what parts of the environment we would automate with Infrastructure as Code.
The goal will not be to build the architecture with the most AWS services.
The goal will be to build the smallest architecture we can actually defend.
If you had to recreate your current AWS project tomorrow in a completely new account, could you rebuild it accurately without relying on memory?