This topic guides you through using Terraform to build a Flask web application on an Alibaba Cloud Elastic Compute Service (ECS) instance.
This topic consists of two main parts:
Automating the creation of an ECS instance and its dependent cloud services, such as networks and security groups, on Alibaba Cloud using Terraform
Automating the deployment of a Flask web service on the ECS instance using Terraform
The sample code in this tutorial supports one-click execution. You can run the code directly. Run with one click
Preparations
This topic uses Alibaba Cloud Shell as the Terraform runtime environment. Before you begin, prepare the following:
RAM user (optional): If you already have a RAM user with permissions to access ECS and VPC, you can skip this step. Visit the RAM Users page to create a new user for operating Terraform, such as
terraform-starter, and grant this user ECS and VPC permissions:AliyunECSFullAccessandAliyunVPCFullAccess.Launch Cloud Shell: Log on to Alibaba Cloud with your RAM user and launch Alibaba Cloud Shell. Alibaba Cloud Shell is a virtual machine created by Alibaba Cloud with Terraform pre-installed. It automatically configures the access credentials related to this RAM user during startup, so you can run Terraform directly in Cloud Shell without any configuration.
Note: Alibaba Cloud Shell will be considered terminated after 30 minutes of inactivity or when all session windows are closed. The virtual machine will be destroyed 15 minutes after termination. When you launch Cloud Shell again, a new virtual machine will be created for you. Therefore, for data security and persistence, it is strongly recommended that you create and attach a file storage disk when you first launch Cloud Shell. For more information, see Limits.
Creating a working directory
Create a new directory in Cloud Shell, such as tf-quickstart.
mkdir tf-quickstart && cd tf-quickstartWriting Flask web application code
This topic will build a Python Flask application and use a single file to describe the content to be displayed on the web.
Create a file named app.py:
touch app.pyAdd the following content:
# coding=utf-8
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Hello, Alibaba Cloud!'.decode('utf-8').encode('utf-8')
app.run(host='0.0.0.0')Writing Terraform code
Create a Terraform file main.tf in this directory to define and describe the ECS instance and other dependent Alibaba Cloud resources:
touch main.tfDefining the provider
Add the Provider configuration to the main.tf file to describe which region to create Alibaba Cloud resources in:
variable "region" {
default = "cn-shanghai"
}
provider "alicloud" {
region = var.region
}The sample code sets the Alibaba Cloud region to Shanghai (cn-shanghai). You can change it to other regions as needed.
Defining the VPC network environment
Add the following Terraform resources to the main.tf file to create a VPC and a vSwitch subnet:
alicloud_vpc: Creates a VPC network
alicloud_vswitch: Creates a vSwitch subnet
alicloud_zones: Dynamically queries zones where specific instance types can be created
variable "instance_name" {
default = "deploying-flask-web-server"
}
variable "instance_type" {
default = "ecs.n1.tiny"
}
data "alicloud_zones" "default" {
available_disk_category = "cloud_efficiency"
available_resource_creation = "VSwitch"
available_instance_type = var.instance_type
}
resource "alicloud_vpc" "vpc" {
vpc_name = var.instance_name
cidr_block = "172.16.0.0/12"
}
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = "172.16.0.0/21"
zone_id = data.alicloud_zones.default.zones.0.id
}In the sample code, the VPC name is declared as deploying-flask-web-server through variable reference, and the vSwitch zone is obtained by dynamically querying one of the available zones.
Defining security groups
Add the following Terraform resources to the main.tf file to create security groups and security group rules:
alicloud_security_group: Creates a security group
alicloud_security_group_rule: Creates security group rules to open access ports for the Flask web application
resource "alicloud_security_group" "default" {
security_group_name = var.instance_name
vpc_id = alicloud_vpc.vpc.id
}
resource "alicloud_security_group_rule" "allow_tcp_22" {
type = "ingress"
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "22/22"
priority = 1
security_group_id = alicloud_security_group.default.id
cidr_ip = "0.0.0.0/0"
}
resource "alicloud_security_group_rule" "allow_tcp_5000" {
type = "ingress"
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "5000/5000"
priority = 1
security_group_id = alicloud_security_group.default.id
cidr_ip = "0.0.0.0/0"
}The sample code declares the creation of a security group named deploying-flask-web-server in the VPC network, and declares two access ports: 22 and 5000. Port 22 is used for you to connect to the ECS instance via SSH to deploy the Flask application, and port 5000 is the default access port for the Flask application.
Defining the ECS instance
Add the alicloud_instance resource to your main.tf file to create an ECS instance.
variable "image_id" {
default = "ubuntu_18_04_64_20G_alibase_20190624.vhd"
}
variable "internet_bandwidth" {
default = "10"
}
variable "password" {
default = "Test@12345"
}
resource "alicloud_instance" "instance" {
availability_zone = data.alicloud_zones.default.zones.0.id
security_groups = alicloud_security_group.default.*.id
password = var.password
instance_type = var.instance_type
system_disk_category = "cloud_efficiency"
image_id = var.image_id
instance_name = var.instance_name
vswitch_id = alicloud_vswitch.vsw.id
internet_max_bandwidth_out = var.internet_bandwidth
}
output "flask_url" {
value = format("http://%v:5000", alicloud_instance.instance.public_ip)
}The sample code selects a minimal shared instance family n1 ecs.n1.tiny, chooses Ubuntu 18 as the operating system, and allocates a public IP address to the instance by setting the internet_bandwidth parameter. Additionally, it outputs the access URL for the Flask application.
Note: This is only a development server with a simple logon password. Do not use it for production deployments.
Defining the Flask web application
In the main.tf file, add a null_resource resource to upload the app.py file to the /tmp directory of the ECS instance and execute it automatically:
# deploy flask
resource "null_resource" "deploy" {
triggers = {
script_hash = filesha256("app.py")
}
# Upload file
provisioner "file" {
connection {
type = "ssh"
user = "root"
password = var.password
host = alicloud_instance.instance.public_ip
}
source = "app.py"
destination = "/tmp/app.py"
}
# Deploy
provisioner "remote-exec" {
connection {
type = "ssh"
user = "root"
password = var.password
host = alicloud_instance.instance.public_ip
}
inline = [
# Install Flask
"pip install flask",
# Stop Flask before deployment (running on port 5000)
"nohup python /tmp/app.py &>/tmp/output.log &",
"sleep 2"
]
}
}Running Terraform commands
After completing the Terraform code, you can run Terraform commands to automatically create the ECS instance and set up the Flask application.
Switching Terraform versions
Because the default Terraform version in Cloud Shell is relatively low, which might cause some features to not work properly, it is recommended to switch to a higher version. You can use the command tfenv list to view the built-in Terraform versions in Cloud Shell, and use the command tfenv use <version number> to change the default version. This topic uses version 1.3.7 as an example:
tfenv use 1.3.7
Initializing Terraform
Run terraform init to build the .terraform directory and add the Alibaba Cloud plugin:
terraform init
Previewing Terraform code
(Optional) You can preview the Terraform code you have built. Run terraform plan to:
Verify that the syntax of the Terraform code in
main.tfis correctDisplay a preview of the resources that the current Terraform code will create
terraform plan
The preview shows that a total of 7 resources will be created.
Executing Terraform code
After confirming the preview results, run terraform apply to automatically create the 7 resources.
terraform applyWhen prompted, enter yes to allow Terraform to create all defined resources.

After successful execution, Terraform calls Alibaba Cloud APIs to complete the automated creation of the ECS instance and the automated setup of the Flask application. You can visit the "ECS Instances" page to view instance details.
Viewing the results
After Terraform executes successfully, it outputs the access URL for the Flask application. You can click the URL directly to access it:

Cleaning up resources
After completing the operations in this topic, you can run the terraform destroy command to delete all resources defined in the code file to avoid any additional charges:
terraform destroyEnter yes to allow Terraform to delete your resources.

After successful execution, all resources have been released.
Complete example
To help you quickly experience Terraform, this topic provides complete Terraform code. The content to be displayed on the Flask web page is defined through the variable web_content, and the Flask Python code is directly embedded in the Terraform code. You can copy and run it directly.
The sample code in this tutorial supports one-click execution. You can run the code directly. Run with one click
variable "region" {
default = "cn-shanghai"
}
variable "web_content" {
default = "Hello, Alibaba Cloud!"
}
variable "instance_name" {
default = "deploying-flask-web-server"
}
variable "instance_type" {
default = "ecs.n1.tiny"
}
variable "image_id" {
default = "ubuntu_18_04_64_20G_alibase_20190624.vhd"
}
variable "internet_bandwidth" {
default = 10
}
variable "password" {
default = "Test@12345"
}
provider "alicloud" {
region = var.region
}
data "alicloud_zones" "default" {
available_disk_category = "cloud_efficiency"
available_resource_creation = "VSwitch"
available_instance_type = var.instance_type
}
resource "alicloud_vpc" "vpc" {
vpc_name = var.instance_name
cidr_block = "172.16.0.0/12"
}
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = "172.16.0.0/21"
zone_id = data.alicloud_zones.default.zones.0.id
}
resource "alicloud_security_group" "default" {
security_group_name = var.instance_name
vpc_id = alicloud_vpc.vpc.id
}
resource "alicloud_instance" "instance" {
availability_zone = data.alicloud_zones.default.zones.0.id
security_groups = alicloud_security_group.default.*.id
password = var.password
instance_type = var.instance_type
system_disk_category = "cloud_efficiency"
image_id = var.image_id
instance_name = var.instance_name
vswitch_id = alicloud_vswitch.vsw.id
internet_max_bandwidth_out = var.internet_bandwidth
}
resource "alicloud_security_group_rule" "allow_tcp_22" {
type = "ingress"
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "22/22"
priority = 1
security_group_id = alicloud_security_group.default.id
cidr_ip = "0.0.0.0/0"
}
resource "alicloud_security_group_rule" "allow_tcp_5000" {
type = "ingress"
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "5000/5000"
priority = 1
security_group_id = alicloud_security_group.default.id
cidr_ip = "0.0.0.0/0"
}
# deploy flask
resource "null_resource" "deploy" {
triggers = {
web_content = var.web_content
script_hash = sha256("${local.app_content}")
}
provisioner "remote-exec" {
connection {
type = "ssh"
user = "root"
password = var.password
host = alicloud_instance.instance.public_ip
}
inline = [
# Install Flask
"pip install flask",
# Stop Flask before deployment (running on port 5000)
"kill $(netstat -tulnp | grep \":5000\" | awk '{ print $7 }' | cut -d'/' -f1)",
# Deploy Flask
"echo \"${local.app_content}\" > /tmp/app.py",
"nohup python /tmp/app.py &>/tmp/output.log &",
"sleep 2"
]
}
}
output "flask_url" {
value = format("http://%v:5000", alicloud_instance.instance.public_ip)
}
locals {
app_content = <<EOF
# coding=utf-8
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return '${var.web_content}'.decode('utf-8').encode('utf-8')
app.run(host='0.0.0.0')
EOF
}