Skip to content

Latest commit

 

History

History
166 lines (90 loc) · 11.7 KB

File metadata and controls

166 lines (90 loc) · 11.7 KB

NextWork

Infrastructure as Code with CloudFormation

Project Link: View Project

Author: luekrit kongkamon
Email: luekrit.k@gmail.com


Image


Introducing Today's Project!

In this project, I will demonstrate how to transform a manually configured CI/CD infrastructure into a fully automated, reusable CloudFormation template, resulting in faster, more consistent, and easier-to-manage deployments at scale. I’m doing this project to learn how to:

  • Translate existing AWS infrastructure into a clean, declarative YAML template
  • Fix common IaC errors and troubleshoot deployment failures
  • Use CloudFormation to provision EC2 instances, security groups, and CodeDeploy components
  • Add and test new resources manually in the template to reflect real-world change management
  • Build the habit of writing infrastructure as code, a critical DevOps skill.

We’ll take what we’ve built across Steps #1–4 — like the EC2 instance, CodeDeploy app, and deployment group — and codify it into a portable template that anyone can reuse. No need to re-click through the console.

Key tools and concepts

Services I used were AWS CloudFormation, CodeBuild, CodeDeploy, EC2, S3, IAM, and CodeArtifact. These tools came together to help me build a fully automated CI/CD pipeline from scratch.

Key concepts I learned include how to structure infrastructure as code with CloudFormation, set up automated builds and deployments, and use EC2 tags to control where my app gets deployed. I also got hands-on with IAM roles and policies, making sure each service had just the right level of access it needed.

Another big takeaway was how powerful and flexible CloudFormation parameters are—they made it easy to reuse the same template for different projects or environments. Plus, I gained a better understanding of how CodeArtifact works for managing and pulling in app dependencies securely.

Overall, this project helped me connect the dots between cloud infrastructure, automation, and real-world deployment workflows.

Project reflection

This project took me about 6 to 7 hours to finish. The toughest part was definitely dealing with CloudFormation template errors—especially when I had to track down small issues like incorrect indentations or referencing the wrong logical IDs. It was frustrating at times, but I learned a lot from those mistakes.

The most satisfying part? Watching everything finally come together—seeing my resources spin up in CloudFormation, the app build in CodeBuild, and deploy smoothly with CodeDeploy. It felt like all the puzzle pieces clicked into place, and I got a real sense of how cloud engineers manage infrastructure and automate deployments in the real world.

This project is part six of a series of DevOps projects where I'm building a CI/CD pipeline! I'll be working on the next project, hopefully tomorrow—though I’ll be honest, Day 6 took quite a bit of time to figure out, especially troubleshooting CloudFormation template errors and making sure all the resources were properly defined.

Still, I learned a lot, and now that the foundation is solid, I’m feeling more confident heading into Day 7, where I’ll bring everything together with CodePipeline. Let’s go!


Generating a CloudFormation Template

The IaC Generator is a tool that helps turn your manually created cloud infrastructure into reusable code. It works in a three-step process where:

  1. It scans your existing AWS resources (like EC2 instances, IAM roles, or security groups)
  2. It identifies their configuration, including settings like instance types, tags, VPCs, and permissions.
  3. It automatically generates a CloudFormation template that you can save, edit, reuse, or store in version control.

This makes it easier to “codify” your infrastructure and follow DevOps best practices — no more clicking around in the console to recreate something. With the IaC Generator, you can confidently recreate your entire setup with a single command or file.

A CloudFormation template is a blueprint written in YAML or JSON that defines the AWS infrastructure you want to create. It tells AWS exactly what resources to build, how they should be configured, and how they’re connected — all in a repeatable, automated way.

Using a template means you can launch complex environments with just one file, instead of clicking through the AWS Console every time.

The resources I couldn’t add to my template were things like my custom IAM policy for CodeArtifact (e.g., codeartifact-luekrit-consumer-policy) and some CodeArtifact-specific configurations, because the IaC Generator doesn’t currently support all AWS services or custom-named resources.

The IaC Generator focuses mostly on core infrastructure like EC2, IAM roles, VPC, and S3, but more specialized services like CodeArtifact, CodePipeline, or detailed IAM policy attachments often need to be added manually.

This means that while the generator is a great starting point, I still need to understand how to write or edit templates myself to fully define everything in my CI/CD stack.

Image


Template Testing

Before testing my template, I deleted the existing CI/CD resources I had manually created, like the EC2 instance, IAM roles, CodeBuild project, and CodeDeploy setup, because I wanted to make sure that the CloudFormation template could fully recreate the infrastructure on its own, without relying on anything pre-existing.

This clean slate approach helped me confirm that the template was complete and accurate, and that it could be used in a real-world scenario to provision everything from scratch in a consistent and automated way.

I tested my template by deploying it through AWS CloudFormation to recreate all the CI/CD resources I had previously built manually. The result of my first test was a failure — several resources, especially IAM managed policies (e.g., CodeArtifact consumer policy, CodeBuild base policy, CloudWatch logs policy), could not be created.

This likely happened because some of the resource types or permissions were either not supported by the IaC Generator, had dependencies that weren’t captured, or required manual adjustments before being deployed as part of a stack. Since those policies failed, CloudFormation canceled the rest of the stack creation.

Image


DependsOn

To fix the error I was running into, I added the DependsOn attribute so that CloudFormation would wait to create the policy until the IAM role it depends on was ready. Without it, CloudFormation tried to create the policy too early—before the role existed—which caused the deployment to fail. By adding DependsOn, I was telling CloudFormation, “Hey, make sure the IAM role is created first before you attach this policy.” That made the deployment go much smoother.

The DependsOn line was added to four different parts of my template to make sure policies didn’t try to attach to IAM roles before those roles were created.

For the CodeArtifact policy, to wait for the EC2 and CodeBuild roles

For the CodeBuild Base policy, to wait for the CodeBuild role

For the CloudWatch Logs policy, ensure the logging permissions are attached after the role

For the CodeConnections policy, to avoid race conditions with the CodeBuild role

Adding DependsOn helped prevent deployment errors and made the stack creation more stable.

Image


Circular Dependencies

I gave my CloudFormation template another test! However, this time, I encountered a new error regarding unresolved resource dependencies. Specifically, CloudFormation couldn't find IAMRolecodebuildluekritdevopscicdserviceroleap or the placeholder YOUR IAM ROLE's NAME IN THE CLOUDFORMATION TEMPLATE.

This means I either mistyped the IAM role name or forgot to replace the placeholder with the correct logical resource name defined in the template. I'll need to go back, double-check the actual IAM role's logical ID, and make sure it matches exactly in all the DependsOn references.

To resolve this error, I deleted the five lines referencing non-existent IAM Managed Policy resources under ManagedPolicyArns.

These lines were causing unresolved dependencies because the logical IDs (like IAMManagedPolicy00policycodeartifactnextworkconsumerpolicy00wJpTJ) didn’t exist in my updated template. Removing them ensured that my CloudFormation stack could launch without trying to reference resources that aren't defined anymore.

Image


Manual Additions

In this project extension, I added two key resources to bring my infrastructure closer to a complete CI/CD pipeline:

  1. A CodeBuild Project – This takes care of building my web application. It pulls the latest code directly from my GitHub repo (Luekrit/web-project-nextwork-), runs the build process using a buildspec.yml, and uploads the final build output to an S3 bucket. It's the heart of my automated build stage.

  2. A CodeDeploy Deployment Group – This handles deploying the built application to EC2 instances. I used simple EC2 tag filtering (role = webserver) to target the right instances, and set it to deploy everything at once using the CodeDeployDefault.AllAtOnce config.

By adding these two, I’ve laid the foundation for automated builds and deployments, making my workflow faster, more consistent, and DevOps-ready.

I also had to make sure the references were consistent in this template, so I edited the CodeBuild project definition to replace all placeholder values with the actual resource names from my environment.

Specifically, I updated:

<CODEBUILD_SERVICE_ROLE_ID> to match the real IAM role used by CodeBuild

<S3_BUCKET_ID> with the actual S3 bucket that stores build artifacts

I also ensured that parameter references like GitHubRepoOwner and GitHubRepo were properly used so the project links to my GitHub repo dynamically.

These edits helped avoid errors during deployment and made the template cleaner, more accurate, and easier to reuse.

I also introduced Parameters, which are like fill-in-the-blanks for my CloudFormation template. Instead of hardcoding values like my GitHub username or repo name, I can just define them once as parameters and plug in whatever I need when I launch the stack.

For example, I added a parameter for the GitHub repo owner (Luekrit) and another for the repo name (web-project-nextwork-). This makes the template way more flexible — I don’t have to rewrite anything if I reuse it for a different project or someone else wants to use it with their repo. It's a small change that makes the whole setup more user-friendly and future-proof.

Image


Success!

I checked the "Resources" tab in the CloudFormation console to make sure everything was created successfully. It showed me a clear list of all the resources that were spun up by the stack, along with their names, types, and statuses like CREATE_COMPLETE. It’s a super helpful way to confirm that all the moving parts (like CodeArtifact, EC2, S3, etc.) were deployed just the way I planned.

Image