Getting Started with AWS and Terraform: Deploying a Windows IIS Web Server on AWS EC2 Instance using Terraform
Tags: aws, terraform, awscloud, upskilling
Published: 2024-01-25 03:20:02

In a previous post, we covered the process of hosting a Linux Apache web server on an AWS EC2 instance using Terraform. 
In this post, we will extend our exploration to deploying a Windows-based infrastructure, specifically an Internet Information Services (IIS) web server on an AWS EC2 Windows instance. The steps involved in this process will be analogous to our earlier post, and we'll leverage Terraform modules to organize and manage the components of our infrastructure.

## Architecture Overview
Before diving into the implementation details, let's briefly overview the architecture we'll be working with:

![Windows IIS Web Server on AWS EC2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zm9b8szrykhi8kilign.png)

## Step 1: Creating the VPC and Network Components
Our first step involves setting up the VPC and associated network components. The VPC module code will handle the creation of the VPC, Internet Gateway, public subnet, and the necessary route table and security group.

Variables used throughout the modules are mentioned in variables.tf file.
variables.tf
```terraform
variable "aws_region" {
  type        = string
  description = "AWS region to use for resources."
  default     = "us-east-1"
}

variable "aws_azs" {
  type        = string
  description = "AWS Availability Zones"
  default     = "us-east-1a"
}

variable "enable_dns_hostnames" {
  type        = bool
  description = "Enable DNS hostnames in VPC"
  default     = true
}

variable "vpc_cidr_block" {
  type        = string
  description = "Base CIDR Block for VPC"
  default     = "10.0.0.0/16"
}

variable "vpc_public_subnets_cidr_block" {
  type        = string
  description = "CIDR Block for Public Subnets in VPC"
  default     = "10.0.0.0/24"
}

variable "instance_type" {
  type        = string
  description = "Type for EC2 Instance"
  default     = "t2.micro"
}

variable "instance_key" {
  default = "MyKeyPair"
}
```
main.tf in VPC module
Create VPC
```terraform
# Create the VPC
resource "aws_vpc" "app_vpc" {
  cidr_block           = var.vpc_cidr_block
  enable_dns_hostnames = var.enable_dns_hostnames
}
```

Create internet gateway
```terraform
# Create the internet gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.app_vpc.id
}
```

Create public subnet, route table and association
```terraform
# Create the public subnet
resource "aws_subnet" "public_subnet" {
  vpc_id                  = aws_vpc.app_vpc.id
  cidr_block              = var.vpc_public_subnets_cidr_block
  map_public_ip_on_launch = true
  availability_zone       = var.aws_azs
}

# Create the route table
resource "aws_route_table" "public_rt" {
  vpc_id = aws_vpc.app_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
}

# Assign the public route table to the public subnet
resource "aws_route_table_association" "public_rt_asso" {
  subnet_id      = aws_subnet.public_subnet.id
  route_table_id = aws_route_table.public_rt.id
}

```

Create security group which allows inbound SSH and HTTP traffic and all outbound traffic. (RDP 3389 port just to connect with windows instance remotely!)
```terraform
# Create the security group
resource "aws_security_group" "sg" {
  name        = "allow_ssh_http"
  description = "Allow ssh http inbound traffic"
  vpc_id      = aws_vpc.app_vpc.id

  ingress {
    from_port        = 3389
    to_port          = 3389
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  ingress {
    description      = "HTTP from VPC"
    from_port        = 80
    to_port          = 80
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}
```

## Step 2: Launching a Windows EC2 Instance with IIS Web Service
With the foundational network components in place, we proceed to launch the EC2 instance using the Web module. This module will handle the instantiation of a Windows Server 2019 instance, configuring it to run an IIS web service, and displaying instance metadata on the website.

main.tf of WEB module
```terraform
# Get latest Amazon Windows Server 2019 Ami
data "aws_ami" "windows-2019" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["Windows_Server-2019-English-Full-Base*"]
  }
}

# Create the Linux EC2 Web server
resource "aws_instance" "web" {
  ami             = data.aws_ami.windows-2019.id
  instance_type   = var.instance_type
  key_name        = var.instance_key
  subnet_id       = var.subnet_id
  security_groups = var.security_groups

  user_data       = file("./modules/web/userdata.tpl")
}
```
I have kept the user_data script in a separate file userdata.tpl
```terraform
<powershell>
Install-WindowsFeature -name Web-Server -IncludeManagementTools

$instanceId   = (Invoke-WebRequest -Uri  http://169.254.169.254/latest/meta-data/instance-id -UseBasicParsing).content
$instanceAZ   = (Invoke-WebRequest -Uri  http://169.254.169.254/latest/meta-data/placement/availability-zone -UseBasicParsing).content
$pubHostName  = (Invoke-WebRequest -Uri  http://169.254.169.254/latest/meta-data/public-hostname -UseBasicParsing).content
$pubIPv4      = (Invoke-WebRequest -Uri  http://169.254.169.254/latest/meta-data/public-ipv4 -UseBasicParsing).content
$privHostName = (Invoke-WebRequest -Uri  http://169.254.169.254/latest/meta-data/local-hostname -UseBasicParsing).content
$privIPv4     = (Invoke-WebRequest -Uri  http://169.254.169.254/latest/meta-data/local-ipv4 -UseBasicParsing).content

New-Item -Path C:\inetpub\wwwroot\index.html -ItemType File -Force
Add-Content -Path C:\inetpub\wwwroot\index.html "<font face = "Verdana" size = "5">"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center><h1>AWS Windows VM Deployed with Terraform</h1></center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>EC2 Instance Metadata</b> </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Instance ID:</b> $instanceId </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>AWS Availablity Zone:</b> $instanceAZ </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Public Hostname:</b> $pubHostName </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Public IPv4:</b> $pubIPv4 </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Private Hostname:</b> $privHostName </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Private IPv4:</b> $privIPv4 </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "</font>"

</powershell>
```

## Steps to Run Terraform
After creating the Terraform modules, we need to execute the Terraform commands to deploy the infrastructure:

```terraform
terraform init
terraform plan
terraform apply -auto-approve
```
Once the terraform apply completed successfully it will show the public ipaddress of the Windows IIS web server as output
```terraform
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.

Outputs:

instance_id = "i-0af8c526023ab3d7d"
public_ip = "http://44.205.246.179/"
```

## Running Website
And there you go – a basic website running on an EC2 Windows instance in AWS created using Terraform.

![Windows IIS Web Server on AWS EC2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5wmzyqpei7bgofb32c5j.png)


![Windows IIS Web Server on AWS EC2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vl7lb8225pi12enoamdx.png)

Accessing windows server using RDP:

![Windows IIS Web Server on AWS EC2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iw5sar8527pqgou8qxzi.png)

## Cleanup
Remember to stop AWS components to avoid large bills.
```terraform
terraform destroy -auto-approve
```

In the upcoming module, we'll take our deployment a step further by exploring CloudWatch alarms with SNS email notifications, enhancing the monitoring capabilities of our AWS environment. Happy Coding!

## Resources
GitHub Link: https://github.com/chinmayto/terraform-aws-windows-webserver
EC2 Documentation: https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/concepts.html