All Products
Search
Document Center

Terraform:Getting started with Terraform

Last Updated:Oct 15, 2025

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

Note

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:

  1. 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: AliyunECSFullAccess and AliyunVPCFullAccess.

  2. 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.

Important

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-quickstart

Writing 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.py

Add 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.tf

Defining 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:

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:

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.

Important

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

截屏2024-09-05 20.32.13.png

Initializing Terraform

Run terraform init to build the .terraform directory and add the Alibaba Cloud plugin:

terraform init

截屏2024-09-05 20.38.27.png

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.tf is correct

  • Display a preview of the resources that the current Terraform code will create

terraform plan

截屏2024-09-13 17.36.18.png

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 apply

When prompted, enter yes to allow Terraform to create all defined resources.

截屏2024-09-06 09.24.43.png

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:

截屏2024-09-06 09.38.47.png

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 destroy

Enter yes to allow Terraform to delete your resources.

截屏2024-09-05 21.06.39.png

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.

Note

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
}