How do I deploy an app using Terraform and AWS?
A step-by-step tutorial on how to deploy a Flask application by using Terraform on Amazon EC2, from setting up the environment to deployment and verification.
Feb 26, 2024 • 10 Minute Read
Welcome to this tutorial focused on deploying Flask applications using Terraform on Amazon EC2! Before diving into the specifics, let's set the stage with some basic concepts you will need to know.
Important concepts
Amazon Web Services (AWS)
AWS is a comprehensive cloud platform offering services like computing power, storage, and more. Within AWS, Elastic Compute Cloud (EC2) is particularly relevant for us, as it provides scalable virtual servers
EC2 and Load Balancing
EC2 instances are virtual servers in AWS's environment. Load balancing across these instances helps distribute incoming application traffic, ensuring reliability and efficiency.
Terraform's Role in Modern Cloud Deployments
Terraform, an open-source tool by HashiCorp, is a key player in the world of infrastructure as code (IaC). Unlike traditional manual infrastructure management, Terraform allows you to define and provision your cloud infrastructure using simple, declarative code. This approach brings numerous benefits:
- Consistency and Reproducibility: Terraform ensures that infrastructure deployment is consistent and repeatable across different environments, reducing errors and discrepancies.
- Version Control: Infrastructure as code means your cloud setup can be version-controlled, similar to how you manage source code.
- Automation and Efficiency: Terraform automates the process of infrastructure provisioning, significantly reducing the time and effort required to manage cloud resources.
- Scalability: With Terraform, scaling infrastructure up or down becomes much simpler, as changes can be made in the code and reapplied.
Understanding Terraform and AWS Modules
Terraform modules are the cornerstone of building reusable, maintainable, and scalable infrastructure. Think of a Terraform module as a container for multiple resources that are used together. A module can include resources like virtual networks, compute instances, or higher-level components such as entire web apps.
Modules allow you to encapsulate a set of resources and configurations in a convenient package. They can be reused across different projects or shared with the community. This not only speeds up the development process but also ensures consistency across your infrastructure.
Overview of AWS Official Modules
VPC (Virtual Private Cloud): This module sets up a network environment in AWS. It's a fundamental building block for your cloud infrastructure, isolating your resources in a virtual network, tailored to your requirements.
Security Group: Acts as a virtual firewall for your EC2 instances to control inbound and outbound traffic. Security groups are crucial for ensuring the secure access to your instances.
ALB (Application Load Balancer): This module helps in distributing incoming application traffic across multiple targets, such as EC2 instances, in multiple Availability Zones. This increases the availability of your application.
EC2 Instance: This module allows the provisioning of EC2 instances (virtual servers in AWS). It is highly configurable, letting you specify the size, type, and other key parameters of the instances based on your application needs.
By combining these modules, you can build a robust and scalable cloud infrastructure. Our journey begins with setting up these foundational elements using Terraform, preparing us for deploying Flask applications.
Setting Up the AWS Environment with Terraform
In this section, we'll walk through the process of setting up a basic AWS environment using Terraform. This includes creating a Virtual Private Cloud (VPC) and setting up Security Groups for EC2 instances and the Application Load Balancer (ALB). For organization, we'll use different files for each part of our setup.
This is the main Terraform configuration file where we define our provider and the required version.
main.tf
provider "aws" {
region = "us-west-2" # Change to your preferred AWS region
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
vpc.tf
Here, we'll create a VPC using the terraform-aws-modules/vpc/aws module. This module sets up a VPC with all the necessary configurations.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.5.1" # Check for the latest version
name = "MyVPC"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = false
enable_vpn_gateway = false
map_public_ip_on_launch = true
enable_dns_hostnames = true
tags = {
"Environment" = "Development"
}
}
It is important to remark here we could use Terraform functions, variables and locals to make this configuration more dynamic; but we stated it this way for brevity.
security_groups.tf
Next, we'll set up Security Groups using the terraform-aws-modules/security-group/aws module. We'll create one for EC2 instances and another for the ALB.
module "ec2_security_group" {
source = "terraform-aws-modules/security-group/aws"
version = "5.5.1" # Check for the latest version
name = "ec2-security-group"
description = "Security group for EC2 instances"
vpc_id = module.vpc.vpc_id
ingress_cidr_blocks = ["10.0.0.0/16"] # Adjust as necessary
ingress_rules = ["all-all"]
egress_rules = ["all-all"]
}
module "alb_security_group" {
source = "terraform-aws-modules/security-group/aws"
version = "5.5.1" # Check for the latest version
name = "alb-security-group"
description = "Security group for ALB"
vpc_id = module.vpc.vpc_id
ingress_cidr_blocks = ["0.0.0.0/0"] # Adjust as necessary for your use case
ingress_rules = ["http-80-tcp", "https-443-tcp"]
egress_rules = ["all-all"]
}
In `security_groups.tf`, we've defined two modules: `ec2_security_group` for our EC2 instances and `alb_security_group` for the ALB. The `ec2_security_group` allows all traffic from the VPC, but you should adjust this to suit your security needs. The `alb_security_group` is set up to allow HTTP and HTTPS traffic.
This setup creates a basic AWS environment with a VPC and necessary security groups. In the next section, we'll delve into deploying Flask applications within this environment. Remember, it's crucial to review and understand the security implications of the configurations you apply, especially when opening ports or allowing traffic.
Setting Up the Application Load Balancer
The Application Load Balancer (ALB) is an AWS service designed to distribute incoming application traffic across multiple targets, such as EC2 instances, in multiple Availability Zones. This increases the availability and scalability of your application. Key components of an ALB include:
Target Group: A group of targets (e.g., EC2 instances) that ALB routes requests to.
Target Group Attachment: Refers to the process of attaching EC2 instances or other resources to a target group.
Forwarding Rule: A rule that defines how requests should be routed to the target groups based on criteria like URL path or hostname.
Domain: In AWS, integrating ALB with Route 53 involves pointing a domain name to the ALB to make your application accessible via a human-readable URL.
alb.tf
This file will include our ALB configuration using the terraform-aws-modules/alb/aws module.
module "alb" {
source = "terraform-aws-modules/alb/aws"
version = "9.4.1" # Ensure to check for the latest version
name = "my-alb"
load_balancer_type = "application"
vpc_id = module.vpc.vpc_id
subnets = module.vpc.public_subnets
security_groups = module.alb_security_group.security_group_id
target_groups = {
default = {
name_prefix = "default-"
backend_protocol = "HTTP"
backend_port = 80
target_type = "instance"
health_check = {
enabled = true
interval = 30
path = "/"
port = 5000
healthy_threshold = 3
unhealthy_threshold = 3
timeout = 5
matcher = "200"
}
}
}
http_tcp_listeners = [{
port = 80
protocol = "HTTP"
default_action = {
type = "forward"
target_group_name = "default"
}
}]
tags = {
Environment = "Development"
}
}
resource "aws_lb_target_group_attachment" "example" {
count = var.instance_count
target_group_arn = module.alb.target_groups["default"].arn
target_id = module.aws_instance[count.index].id
}
Setting Up the EC2 Instances
For web applications, EC2 instances provide the necessary computing power. Depending on the application's requirements, you can select different instance types which offer various combinations of CPU, memory, storage, and networking capacity.
To deploy EC2 instances that will run our Python Flask applications, we'll use the terraform-aws-modules/ec2-instance module. This module simplifies the process of launching and managing EC2 instances.
ec2_instances.tf
This Terraform configuration will create two EC2 instances suitable for running Flask applications.
module "ec2_instances" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "5.6.0" # Check for the latest version
name = "flask-app-instance"
instance_count = 2
ami = "ami-12345678" # Replace with the correct AMI for your region and OS
instance_type = "t2.micro" # Adjust the instance type based on your needs
subnet_id = module.vpc.public_subnets[0]
vpc_security_group_ids = [module.ec2_security_group.security_group_id]
tags = {
"Name" = "FlaskAppInstance"
"Environment" = "Development"
}
}
In this configuration, replace ami-12345678 with the appropriate AMI ID for your region and desired OS. The instances are configured to be part of the first subnet in our VPC and to use the security group associated with our ALB's default target group. We could distribute better with the use of Terraform Functions.
To configure the EC2 instances to run Flask applications, you would typically use a provisioning tool or user data scripts. For simplicity, here's an example of user data that installs Python and Flask:
user_data.sh
#!/bin/bash
sudo apt update
sudo apt install python3 python3-pip -y
pip3 install Flask
# Flask application setup here (e.g., cloning a repo or copying files)
To integrate this script with our Terraform configuration, we add it to the EC2 module:
module "ec2_instances" {
# ... (previous configuration)
user_data = file("${path.module}/user_data.sh")
}
This script will run when each EC2 instance is first launched, setting up the environment for running a Flask application. You'll need to customize this script based on your specific application setup, such as cloning your application code from a repository or copying files from a storage service.
Deploying Everything and Verifying the Setup
To deploy your entire AWS infrastructure including the VPC, security groups, EC2 instances, and the ALB, follow these steps:
Prepare Your Terraform Configuration: Ensure all your Terraform configuration files (vpc.tf, security_groups.tf, ec2_instances.tf, alb.tf, etc.) are complete and correct.
Initialize Terraform: Navigate to your project directory in the terminal and run:
terraform init
This command initializes Terraform, installs the required providers, and prepares the environment for deployment.
Execute the following command for deployment:
terraform apply
Terraform will display the planned changes. Review them and type yes to proceed. This command will create all the resources defined in your Terraform files.
Verify the Setup:
Check the AWS Management Console to ensure that all resources (VPC, EC2 instances, ALB, etc.) are correctly set up.
Verify that the EC2 instances are running and can communicate with the ALB.
Ensure the security groups are correctly configured, allowing the necessary traffic.
Accessing the Flask Web App
Since we have not integrated Route 53 in our current setup, you will access the Flask web application through the public DNS name of the Application Load Balancer (ALB), rather than a Route 53 domain.
Find the ALB DNS Name:
Go to the AWS Management Console.
Navigate to the EC2 Dashboard and select "Load Balancers" from the sidebar.
Find your ALB and copy its DNS name. It will look something like my-alb-1234567890.region.alb.amazonaws.com.
Access the Web Application:
Open a web browser and enter the ALB DNS name.
If everything is configured correctly, you should see your Flask application's landing page.
Remember, the Flask application needs to be running on the EC2 instances, listening on the port you have configured (default is 5000). Also, ensure your Flask application is configured to run on 0.0.0.0 instead of 127.0.0.1 to allow external access.
This completes the process of deploying and accessing a Flask web application in AWS using Terraform. You've set up a scalable, flexible infrastructure that can be adapted to a variety of web applications.
Destroying Resources (Optional):
If you wish to remove all resources created by Terraform, run:
terraform destroy
This command is useful for cleaning up after testing or development to avoid unnecessary AWS charges.
Conclusion
Throughout this tutorial, we've navigated the process of deploying a Flask web application in AWS using Terraform. Here's a quick recap:
Introduction to Terraform and AWS Modules: We started by understanding the role of Terraform in managing AWS resources as code, emphasizing its efficiency and scalability.
Setting Up the AWS Environment: We created a VPC and configured security groups using Terraform modules, laying the groundwork for our application infrastructure.
Deploying the Application Load Balancer: The ALB was set up to distribute incoming traffic across multiple EC2 instances, ensuring high availability and load balancing.
Launching EC2 Instances: We deployed EC2 instances, configured to host our Flask applications, using the terraform-aws-modules/ec2-instance module.
Configuring the Network: The network was configured to allow secure communication between the EC2 instances and the ALB, with specific rules for web traffic and SSH access.
Accessing the Web Application: Finally, we accessed the deployed Flask application through the ALB's public DNS name.
What’s Beyond?
This journey into deploying a Flask application using Terraform and AWS services is just the beginning. I encourage you to explore further:
Experiment with different AWS services and Terraform modules.
Get familiar with workspaces and Terraform functions and expressions to modularise further your configuration.
Scale your application by adding more EC2 instances or exploring other AWS computing services like ECS or EKS.
Automate your deployment process using CI/CD pipelines integrating with Terraform.
Enjoyed this article? Check out Axel Sirota's IT Ops Session "Terraform - Deploying a Full Application with Load Balancing, or one of his many other courses on the Pluralsight platform. We also recommend checking out our learning path for the HashiCorp Certified: Terraform Associate, which teaches you everything you need to know to get started with Terraform, as well as helps you prepare for certification.