← More Blogs

From Chaos to Click-Deploy: How to Fully Automate a Complex 3-Tier Windows Web Application on AWS

Published on November 23, 2025

Automating a modern cloud-native application is usually straightforward—containerization, CI/CD pipelines, and IaC make it easy. But automating a legacy, monolithic 3-tier Windows application—written in .NET, hosted on IIS, dependent on Python and Java side-components, and requiring a labyrinth of configuration files—is an entirely different challenge.

This blog is the story of how I took a poorly architected, non-cloud-native enterprise product and turned it into a fully automated, zero-touch deployment on AWS. The final solution allowed us to provision infra + install the complete app stack—from scratch—in under an hour… fully automated with Terraform, Packer, PowerShell, and AWS-native services.

AWS architecture diagram for the automated Windows web app deployment.

1. Application Overview: A Monolithic Product on the Cloud

The application was:

This product had minimal documentation, no support for infrastructure automation, and no concept of cloud architecture (HA, scalability, secure config, etc.). But the business needed it on AWS—and they needed full automation.

2. AWS Architecture: Full Stack Infrastructure

At the infrastructure layer, we deployed:

Core AWS Services

Constraints

This created an environment where automation was the only practical way to manage complexity.

3. Infrastructure as Code: Terraform Everywhere

The entire infra stack was built using:

For a deeper dive into how we leverage these tools for complex, multi-environment setups, you can read our detailed post on the ideal pattern for multi-environment cloud infrastructure using Terraform and Terragrunt.

Terraform handled:

Terraform dependencies were carefully designed so EC2s are created last, ensuring:

4. Reverse Engineering the App

Since the product team did not provide an automated installer, we had to manually install the application in a dev environment, observe every step, and reverse-engineer:

  1. Required software
  2. Folder structures
  3. IIS website + app pool configurations
  4. Python/Java package dependencies
  5. Application execution model
  6. Hard-coded configuration files
  7. Security hardening requirements (IIS + OS)
  8. Inter-service dependencies
  9. All environment-specific values (DB endpoints, Redis, IP addresses, etc.)

This process created a detailed understanding of:

5. Building Golden AMIs using Packer

To achieve repeatable installs, we created three custom AMIs (one for each EC2 role).

Using a Packer pipeline, each AMI included:

Base OS

Installed Software

AWS CLI Installed

App Code

We packaged the product team’s code into the AMI wherever it was environment-agnostic.

These AMIs were then replicated across Dev, Test, Prod AWS accounts.

6. Runtime Configuration via EC2 User Data

Once Terraform launched the EC2, user data executed a second automation phase using PowerShell.

This script:

Fetched Environment-Specific Values

Via AWS CLI:

Generated Configuration Files

The app required many config files with dynamic values—so user data:

Warm-Up Timing

Some EC2s required 10–20 minutes to fully initialize IIS, app pools, and supporting services. We tuned launch templates so:

This ensured stable boots with zero manual intervention.

7. Orchestration & End-to-End Automation

The full orchestration looked like this:

  1. User triggers Terraform
  2. Terraform creates infra in dependency order
    • VPC → RDS → Redis → Secrets → ALB → IAM → ASG
  3. ASG launches EC2 instances
    • Using custom Packer AMIs
  4. EC2 user data runs PowerShell
    • Installs dynamic config
    • Pulls AWS secrets
    • Generates runtime files
  5. ALB begins health checks
  6. App is live

Total time:

This was a zero-touch, fully automated deployment.

The only manual item:

Everything else—infra, app install, config, hardening, wiring, and launch—was 100% automated.

8. Development-Time Challenges

1. No SSH on Windows

We initially couldn’t RDP (blocked by security), so we used:

2. Multiple services on one EC2

Each component used different ports, so ALB target groups had:

3. Inter-instance communication

We had to allow EC2-to-EC2 traffic securely and configure IP addresses dynamically.

4. IIS Configuration Automation

Some IIS components could be exported/imported; others required PowerShell-based creation during AMI building.

9. Final Outcome

The automation achieved:

A previously manual, error-prone, multi-day installation turned into a single command deployment with:

Closing Thoughts

This project was a powerful example of how legacy monolithic applications can still be fully automated on cloud platforms—even when they lack cloud-friendly design.

By combining:

…we transformed a non-cloud-native enterprise product into a fully automated, zero-touch, cloud-hosted platform.

If you’re working with legacy Windows applications on AWS, automation is possible—you just need the right combination of tools, creativity, and reverse engineering.