Getting Started with AWS and Terraform: Deploying a Windows IIS Web Server on AWS EC2 Instance using Terraform
Chinmay Tonape
Posted on January 25, 2024
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:
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
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
# Create the VPC
resource "aws_vpc" "app_vpc" {
cidr_block = var.vpc_cidr_block
enable_dns_hostnames = var.enable_dns_hostnames
}
Create internet gateway
# Create the internet gateway
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.app_vpc.id
}
Create public subnet, route table and association
# 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!)
# 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
# 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
<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 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
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.
Accessing windows server using RDP:
Cleanup
Remember to stop AWS components to avoid large bills.
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
Posted on January 25, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.