Multi-Cloud & IaC · Oct 2025 · 22 min read

Terraform on Alibaba Cloud: Building Repeatable Infrastructure as Code

A complete Terraform project on Alibaba Cloud — module structure, state management, CI/CD, drift detection, and the boundary where Terraform ends and configuration management begins.

Part 1 of the Alibaba Cloud Engineering Lab Series.

TL;DR

This isn't "Terraform lets you provision Alibaba Cloud" — that's true but generic. It's how to build repeatable Alibaba Cloud infrastructure with Terraform: a real multi-tier project, module structure, remote state, CI/CD, drift detection, and the boundary where Terraform's job ends and configuration management begins.

Git → Terraform code → plan → state → apply → VPC/ECS/OSS/ACK → Alibaba Cloud

Before the how, the what — three terms this guide leans on:

Every resource below is terraform validate-verified against the real aliyun/alicloud provider, not written from memory and assumed correct — the companion repo has the actual module structure, restructured from a single flat file into the modules/environments layout Section 04 describes below.


01 — Why This, and What "Repeatable" Actually Means

Alibaba Cloud is the dominant hyperscaler across China and much of Southeast Asia — a region most Azure- and AWS-trained IaC practitioners have never provisioned into. Alibaba's own Terraform documentation positions it as the IaC mechanism spanning ECS, VPC, OSS, ACK, RAM, PAI, and the rest of the platform — the same breadth Terraform covers on Azure or AWS.

                    Git
                     │
                     ▼
              Terraform Code
                     │
              terraform plan
                     │
                     ▼
              Terraform State
                     │
              terraform apply
                     │
        ┌────────────┼────────────┐
        ▼            ▼            ▼
       VPC          ECS          OSS
        │            │
        ▼            ▼
    Security       ACK
     Groups
        │
        └────────────┬────────────┘
                     ▼
                 Alibaba Cloud

The rest of this article builds toward one concrete deliverable: a production-ready web application, fully reproducible from a Terraform project, not a series of disconnected resource snippets.


02 — Provider Setup: Be Explicit About the Source

The Terraform Registry currently lists two Alibaba Cloud providers — the Alibaba-maintained aliyun/alicloud and a legacy hashicorp/alicloud listing. Ambiguity here causes real init/version problems — always pin the source explicitly, not just the provider short name:

terraform {
  required_providers {
    alicloud = {
      source  = "aliyun/alicloud" # the actively Alibaba-maintained provider
      version = "~> 1.230"
    }
  }
  required_version = ">= 1.6.0"
}

provider "alicloud" {
  region = var.region
}

Credentials via environment variables — Alibaba's documentation recommends environment-based credential configuration for local usage, never hardcoded in a provider block:

export ALICLOUD_ACCESS_KEY="..."
export ALICLOUD_SECRET_KEY="..."
export ALICLOUD_REGION="ap-southeast-1"

Production CI/CD should go further than "environment variables instead of hardcoded" — use short-lived or managed credentials where the pipeline supports them, rather than a long-lived static AccessKey committed to a secrets store.


03 — The Project: A Production-Ready Web Application

Scenario: deploy a real multi-tier web application — VPC, VSwitch, security groups, ECS, OSS, SLB, a RAM role scoped to the app, optional RDS, and outputs a reader can actually reproduce.

Network foundation:

resource "alicloud_vpc" "main" {
  vpc_name   = "vpc-prod-apac"
  cidr_block = "10.10.0.0/16"
}

resource "alicloud_vswitch" "app" {
  vswitch_name = "vsw-app-tier"
  vpc_id       = alicloud_vpc.main.id
  cidr_block   = "10.10.1.0/24"
  zone_id      = "ap-southeast-1a"
}

resource "alicloud_vswitch" "data" {
  vswitch_name = "vsw-data-tier"
  vpc_id       = alicloud_vpc.main.id
  cidr_block   = "10.10.2.0/24"
  zone_id      = "ap-southeast-1b"
}

A VSwitch is bound to a single zone at creation, unlike an Azure subnet spanning a region — plan zone placement deliberately for availability.

Security groups:

resource "alicloud_security_group" "app_sg" {
  security_group_name = "sg-app-tier"
  vpc_id = alicloud_vpc.main.id
}

resource "alicloud_security_group_rule" "allow_https" {
  type              = "ingress"
  ip_protocol       = "tcp"
  nic_type          = "intranet"
  policy            = "accept"
  port_range        = "443/443"
  priority          = 1
  security_group_id = alicloud_security_group.app_sg.id
  cidr_ip           = "0.0.0.0/0"
}

Compute:

resource "alicloud_instance" "app_server" {
  instance_name              = "ecs-app-01"
  instance_type              = "ecs.g6.large"
  image_id                   = "aliyun_3_x64_20G_alibase_20240628.vhd"
  vswitch_id                 = alicloud_vswitch.app.id
  security_groups            = [alicloud_security_group.app_sg.id]
  internet_max_bandwidth_out = 5
  system_disk_category       = "cloud_essd"

  tags = { environment = "production", managed_by = "terraform" }
}

Storage (OSS):

resource "alicloud_oss_bucket" "app_assets" {
  bucket = "app-static-assets-prod"
}

# ACL is a separate resource as of provider 1.220+ — "private" is also
# the bucket's default, but set it explicitly rather than relying on it.
resource "alicloud_oss_bucket_acl" "app_assets" {
  bucket = alicloud_oss_bucket.app_assets.bucket
  acl    = "private"
}

Load balancing:

resource "alicloud_slb_load_balancer" "app_lb" {
  load_balancer_name = "slb-app-prod"
  vswitch_id          = alicloud_vswitch.app.id
  load_balancer_spec  = "slb.s2.small"
}

resource "alicloud_slb_listener" "https" {
  load_balancer_id = alicloud_slb_load_balancer.app_lb.id
  backend_port     = 443
  frontend_port    = 443
  protocol         = "tcp"
  bandwidth        = 10
}

A RAM role scoped to what the app actually needs — read access to its own OSS bucket, nothing account-wide:

resource "alicloud_ram_role" "app_role" {
  role_name                 = "app-server-role"
  assume_role_policy_document = jsonencode({
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = { Service = ["ecs.aliyuncs.com"] }
    }]
    Version = "1"
  })
}

Data tier (optional RDS), scoped to the app-tier CIDR only:

resource "alicloud_db_instance" "primary" {
  engine           = "MySQL"
  engine_version   = "8.0"
  instance_type    = "rds.mysql.s2.large"
  instance_storage = 100
  vswitch_id       = alicloud_vswitch.data.id
  instance_name    = "rds-prod-primary"
  security_ips     = ["10.10.1.0/24"]
}

Outputs, so the deployment hands back what a consumer actually needs:

output "load_balancer_ip" {
  value = alicloud_slb_load_balancer.app_lb.address
}
output "oss_bucket_name" {
  value = alicloud_oss_bucket.app_assets.bucket
}

04 — Repository Structure, and Why It's Shaped This Way

terraform-alibaba/
├── main.tf
├── provider.tf
├── variables.tf
├── outputs.tf
├── versions.tf
├── terraform.tfvars
├── modules/
│   ├── network/
│   ├── ecs/
│   ├── security/
│   └── storage/
└── environments/
    ├── dev/
    ├── staging/
    └── production/

This is the difference between "knows Terraform syntax" and "can structure a Terraform project" — the second is what actually gets evaluated in a real engineering review.


05 — The Terraform Lifecycle

WRITE
  ↓
terraform fmt
  ↓
terraform validate
  ↓
terraform plan
  ↓
CODE REVIEW
  ↓
terraform apply
  ↓
STATE
  ↓
DRIFT DETECTION
  ↓
UPDATE

Alibaba's documentation covers init, plan, apply, and destroy, with plan previewing changes before they're applied. Two commands worth treating as non-negotiable in a professional workflow, beyond that baseline:

terraform fmt -check    # enforce consistent formatting, fail CI if not run
terraform validate      # catch syntax/config errors before a plan even runs

Skipping these two doesn't save time — it just moves the failure from a 2-second local check to a slower, more visible CI failure or a bad plan output nobody trusts.


06 — State Management

Why does Terraform need state at all? Because it has to answer one question before every apply: "what did I create previously?"

Terraform configuration
        +
Terraform state
        +
Actual Alibaba Cloud infrastructure
        ↓
Terraform determines the difference

Without state, every apply would have no way to distinguish "create this new resource" from "this resource already exists, only these two fields changed." State is the memory that makes incremental, non-destructive updates possible.

What a serious state practice covers:

terraform {
  backend "oss" {
    bucket = "terraform-state-prod-apac"
    key    = "network/terraform.tfstate"
    region = "ap-southeast-1"
  }
}

07 — Terraform vs. the Alibaba Cloud Console

TaskConsoleTerraform
One-off VMExcellentOverkill
Repeat environmentPoorExcellent
Version controlLimitedExcellent
Code reviewLimitedExcellent
Multi-environmentManualExcellent
Disaster recoveryManualStrong
Large infrastructureDifficultStrong
Learning curveLowHigher

Alibaba's own documentation draws essentially this same distinction — Terraform earns its complexity budget at the point where an environment needs to be repeatable, reviewable, or reproduced more than once. A single throwaway test VM doesn't need a Terraform module; a production environment always does.


08 — Modules: From Script to Architecture

module "network" {
  source      = "./modules/network"
  vpc_cidr    = var.vpc_cidr
  environment = var.environment
}

Resource → Module → Environment is the progression that turns Terraform from a scripting tool into reusable infrastructure architecture. The Registry already has community modules worth knowing about — the alibaba/vpc/alicloud module, for instance, wraps VPC, VSwitch, and route-entry resources into a single reusable call — worth evaluating before writing an equivalent module from scratch.


09 — Multi-Environment Design

                 Terraform Modules
                        │
             ┌──────────┼──────────┐
             ▼          ▼          ▼
            DEV       STAGING      PROD
             │          │          │
             ▼          ▼          ▼
           Alibaba Cloud environments

The difference between environment = "dev" and environment = "production" isn't just a variable value — it's isolation: separate state files, separate VPC CIDR ranges (so they can never accidentally peer), separate credentials with separate permission scopes, and in most orgs, a manual-approval gate on production apply that doesn't exist for dev. Environment isolation is a security and blast-radius boundary, not a naming convention.


10 — CI/CD

Developer
    │
    ▼
Git push
    │
    ▼
CI Pipeline
    │
    ├── terraform fmt
    ├── terraform validate
    ├── security scan
    └── terraform plan
             │
             ▼
        Pull Request
             │
          Approval
             │
             ▼
       terraform apply
             │
             ▼
       Alibaba Cloud

This is the step that turns isolated Terraform knowledge into an actual DevOps practice: plan output posted to the pull request for human review, apply gated behind approval, and a security scan (tfsec/checkov) catching a public 0.0.0.0/0 security-group rule or an unencrypted OSS bucket before it merges — the same gate a CI pipeline enforces on application code, applied to infrastructure code.


11 — Drift Detection

The scenario every team eventually hits: someone opens the Alibaba Cloud console and manually changes a security group rule "just to fix something quickly" — port 22 gets opened where Terraform's configuration says it should stay restricted.

Terraform configuration says:  Port 443 = allowed, Port 22 = restricted
Actual infrastructure says:    Port 22 = open
terraform plan

This single command surfaces the exact drift — Terraform reads the real infrastructure state and diffs it against configuration, and the plan output shows the manual change as something Terraform intends to revert. This is one of the strongest arguments for IaC over console-driven changes: the drift isn't just detected, it's specifically named, in a way a manual audit would take far longer to catch.


12 — What Terraform Doesn't Manage

Terraform is excellent for infrastructure lifecycle management — it is not a full configuration-management system. Alibaba's own Terraform provider documentation is explicit that Terraform manages infrastructure resources, not system-level operational tasks like installing software or managing OS updates.

Terraform
    ↓
Infrastructure
    ↓
ECS / VPC / SLB / OSS
    ↓
Ansible / cloud-init / containers
    ↓
Application configuration

Knowing this boundary is a maturity signal — a common mistake is trying to force Terraform's null_resource + remote-exec into doing configuration management that a purpose-built tool (Ansible, cloud-init, or a container image) handles far more reliably.


13 — Connecting to the AI Infrastructure Article

This isn't an isolated tutorial — it's the infrastructure layer underneath this series' AI Infrastructure article:

Terraform
    │
    ├── VPC
    ├── ECS GPU
    ├── OSS
    ├── Security
    │
    ▼
Alibaba Cloud Infrastructure
    │
    ▼
PAI
    │
    ├── DSW
    ├── DLC
    └── EAS
    │
    ▼
AI Application

Real-world scenario: deploying an AI application on Alibaba Cloud needs GPU ECS, a VPC with a private subnet, OSS for model storage, PAI, a load balancer, security groups, and a CI/CD pipeline gating all of it. Terraform establishes every piece of that infrastructure layer before PAI's DSW/DLC/EAS ever touches a workload — the two articles are one continuous engineering path, not two separate topics that happen to both mention Alibaba Cloud.


Final Takeaways

The value here isn't "learn a fourth cloud provider's resource names" — it's proving that a well-formed Terraform practice transfers: the resources differ, the naming differs, but modular structure, remote state with locking, environment isolation, CI-gated plan/apply, and drift detection are identical disciplines regardless of which cloud sits above the API.

My recommendation: treat Terraform on Alibaba Cloud exactly like Terraform anywhere else — pin the provider source explicitly, never let state live locally, gate production apply behind review, and stop at the boundary where configuration management should take over. Get that discipline right once, and it ports to the next provider almost unchanged.

GitHub Repository: terraform-alibaba-cloud-lab — the full modular project: network/security/ecs/storage modules composed into a production environment, ready to run.

Reviewed against current Alibaba Cloud Terraform documentation and the Terraform Registry as of September 2026.

Terraform · Alibaba Cloud · Multi-Cloud IaC · State Management · Modules · CI/CD · Drift Detection