All Products
Search
Document Center

Resource Orchestration Service:Deploy NGINX on an ECS instance by using Terraform

Last Updated:May 31, 2023

This topic describes how to deploy NGINX on an Elastic Compute Service (ECS) instance by using a Terraform template.

Prerequisites

You are familiar with the syntax and the structure of ROS templates. For more information, see Getting started with templates.

Sample scenario

Create an ECS instance of the virtual private cloud (VPC) network type in the Alibaba Cloud Management Console to deploy NGINX.

2023-05-31_16-34-18..png

Usage notes

You can view the resource types of the preceding resources to obtain the details of each resource property. For more information, see View resource types.

Each resource type topic provides information about the required and optional resource properties. You must declare the required resource properties of the resource type that you want to use in the Resources section of a Terraform template.

Create a template

To find the resource types that your business requires, see List of resource types by service.

In this example, the following resource types are used: alicloud_vpc, alicloud_instance, alicloud_vswitch, alicloud_security_group, and alicloud_security_group_rule. NGINX is deployed at the same time the ECS instance is created.

Define resources and resource dependencies in the template

Define basic network resources

You can define the following basic network resources: vpc, vsw, and security_group.

  • Use alicloud_vpc.vpc.id to query the output ID of alicloud_vpc.

  • Use var.*** to query the value of a custom variable that is defined in the variable field. For example, you can use var.zone_id to query the value of zone_id that is defined in the variable field.

resource "alicloud_vpc" "vpc" {
  cidr_block = "10.1.0.0/21"
}
resource "alicloud_vswitch" "vsw" {
  vpc_id     = alicloud_vpc.vpc.id
  cidr_block = "172.16.0.0/21"
  zone_id    = var.zone_id
}
resource "alicloud_security_group" "security_group" {
  name       = "new-group"
  vpc_id     = alicloud_vpc.vpc.id
}

Define security group rules

You can define the following security group rules: allow_ssh, allow_web, and allow_egress.

Use alicloud_security_group.security_group.id to query the output ID of security_group.

# Ingress port 1 of the security group
resource "alicloud_security_group_rule" "allow_ssh" {
  security_group_id = alicloud_security_group.security_group.id
  type              = "ingress"
  cidr_ip           = "0.0.0.0/0"
  policy            = "accept"
  ip_protocol				= "tcp"
  port_range				= "22/22"
  priority					= 1
}

# Ingress port 2 of the security group
resource "alicloud_security_group_rule" "allow_web" {
  security_group_id = alicloud_security_group.security_group.id
  type 							= "ingress"
  cidr_ip						= "0.0.0.0/0"
  policy 						= "accept"
  ip_protocol				= "tcp"
  port_range				= "80/443"
  priority					= 1
}

# The egress port of the security group
resource "alicloud_security_group_rule" "allow_egress" {
  security_group_id = alicloud_security_group.security_group.id
  type 							= "egress"
  cidr_ip						= "0.0.0.0/0"
  policy 						= "accept"
  ip_protocol				= "tcp"
  port_range				= "1/65535"
  priority					= 1
}

Define an ECS instance

You can define an ECS instance in the template.

  • Use var.*** to query the value of a custom variable that is defined in the variable field. For example, you can use var.instance_type to query the value of instance_type that is defined in the variable field.

  • Use local.*** to query the value of a local variable that is defined in the locals field. For example, you can use local.new_host_name to query the value of new_host_name that is defined in the locals field.

  • Use ${path.cwd} in user_data to query the current working directory.

  • The user_data.sh file provides the initialization script that must be executed on the ECS instance. Sample initialization script:

#!/bin/bash -v
# Mount the disk to /disk1.
cat >> /root/InitDataDisk.sh << "EOF"
#!/bin/bash
echo "p
n
p
w
" |  fdisk -u /dev/vdb
EOF
/bin/bash /root/InitDataDisk.sh
rm -f /root/InitDataDisk.sh
mkfs -t ext4 /dev/vdb1
cp /etc/fstab /etc/fstab.bak
mkdir /disk1
echo `blkid /dev/vdb1 | awk '{print $2}' | sed 's/\\\"//g'` /disk1 ext4 defaults 0 0 >> /etc/fstab
mount -a
# Configure the installation script.
yum install -y nginx
# Configure the startup script.
/usr/sbin/nginx

The following code provides a sample template that defines an ECS instance:

resource "alicloud_instance" "instance" {
      availability_zone					 = var.zone_id
      security_groups 					 = [alicloud_security_group.security_group.id]
      # series III
      host_name 								 = local.new_host_name
      instance_type              = var.instance_type
      system_disk_size           = 500
      system_disk_category       = "cloud_essd"
      image_id                   = "centos_7_9_x64_20G_alibase_20210318.vhd"
      vswitch_id                 = alicloud_vswitch.vsw.id
      password                   = var.instance_password
      internet_charge_type 			 = "PayByTraffic"
      internet_max_bandwidth_out = 30
      instance_charge_type 			 = var.pay_type
      period 										 = var.pay_period
      period_unit 							 = var.pay_period_unit
      user_data									 = file("${path.cwd}/user-data.sh")
      data_disks {
        size        						 = 100
        category  						   = "cloud_essd"
      }
    }

Complete sample template

variable "pay_type" {
	type                       = string
}
variable "pay_period_unit" {
	type                       = string
}
variable "pay_period" {
	type                       = number
}
variable "zone_id" {
	type                       = string
}
variable "vpc_cidr_block" {
	type                       = string
}
variable "vswitch_cidr_block" {
	type                       = string
} 
variable "instance_type" {
	type                       = string
}
variable "system_disk_category" {
	type                       = string
}
variable "system_disk_size" {
	type                       = number
}
variable "data_disk_category" {
	type                       = string
}
variable "data_disk_size" {
	type                       = number
} 
variable "instance_password" {
	type                       = string
}
# The default resource name
locals {
  production_name            = "nginx"
  new_scg_name 							 = "sg-for-${local.production_name}"
  new_host_name						   = "app-for-${local.production_name}"
}

resource "alicloud_vpc" "vpc" {
  cidr_block 								 = var.vpc_cidr_block
}

resource "alicloud_vswitch" "vsw" {
  vpc_id     								 = alicloud_vpc.vpc.id
  cidr_block 								 = var.vswitch_cidr_block
  zone_id    								 = var.zone_id
}

// Basic security group configurations
resource "alicloud_security_group" "security_group" {
  name        							 = local.new_scg_name
  description 							 = "nginx scg"
  vpc_id 										 =  alicloud_vpc.vpc.id
}

# Ingress port 1 of the security group
resource "alicloud_security_group_rule" "allow_ssh" {
  security_group_id 				 = alicloud_security_group.security_group.id
  type										   = "ingress"
  cidr_ip										 = "0.0.0.0/0"
  policy 							 		   = "accept"
  ip_protocol					 			 = "tcp"
  port_range							   = "22/22"
  priority								 	 = 1
}

# Ingress port 2 of the security group
resource "alicloud_security_group_rule" "allow_web" {
  security_group_id 			 	 = alicloud_security_group.security_group.id
  type											 = "ingress"
  cidr_ip										 = "0.0.0.0/0"
  policy									 	 = "accept"
  ip_protocol								 = "tcp"
  port_range								 = "80/443"
  priority									 = 1
}

# The egress port of the security group
resource "alicloud_security_group_rule" "allow_egress" {
  security_group_id 				 = alicloud_security_group.security_group.id
  type 											 = "egress"
  cidr_ip										 = "0.0.0.0/0"
  policy										 = "accept"
  ip_protocol								 = "tcp"
  port_range								 = "1/65535"
  priority									 = 1
}

# Basic instance configurations
resource "alicloud_instance" "instance" {
  availability_zone          = var.zone_id
  security_groups						 = [alicloud_security_group.security_group.id]
  # series III
  host_name 								 = local.new_host_name
  instance_type              = var.instance_type
  system_disk_size           = var.system_disk_size
  system_disk_category       = var.system_disk_category
  image_id                   = "centos_7_9_x64_20G_alibase_20210318.vhd"
  vswitch_id                 = alicloud_vswitch.vsw.id
  password                   = var.instance_password
  internet_charge_type			 = "PayByTraffic"
  internet_max_bandwidth_out = 30
  instance_charge_type			 = var.pay_type
  period										 = var.pay_period
  period_unit								 = var.pay_period_unit
  user_data									 = file("${path.cwd}/user-data.sh")
  data_disks {
    size       							 = var.data_disk_size
    category 							   = var.data_disk_category
  }
}
# The returned IP address of NGINX
output "nginx_ip" {
  value										 	 = "http://${alicloud_instance.instance.public_ip}:8080"
}

Convert a Terraform template

You can convert a Terraform template into an ROS template in the ROS console. If you use ROS, you can group template parameters and dynamically obtain the parameter settings.

  1. Log on to the ROS console.

  2. In the left-side navigation pane, choose Templates > My Templates.

  3. On the My Templates page, click Create Template.

  4. On the Modify Template Content tab, select Terraform from the drop-down list.

  5. Modify the content of a Terraform template.

    1. Create a main.tf file and enter the content of the sample Terraform template.

    2. Create a user-data.sh file and enter the content of the user_data.sh initialization script.

  6. On the Modify Template Content tab, select ROS from the drop-down list.

    The Terraform template is converted into an ROS template. Sample ROS template:

    ROSTemplateFormatVersion: '2015-09-01'
    Transform: Aliyun::Terraform-v1.2
    Workspace:
      main.tf: |-
            variable "pay_type" {
              type 										   = string
            }
            variable "pay_period_unit" {
              type											 = string
            }
            variable "pay_period" {
              type											 = number
            }
            variable "zone_id" {
              type											 = string
            }
            variable "instance_type" {
              type											 = string
            }
            variable "vpc_cidr_block" {
              type											 = string
            }
            variable "vsw_cidr_block" {
              type											 = string
            }
            variable "instance_password" {
              type											 = string
            }
            # The default resource name.
            locals {
              production_name = "nginx"
              new_vpc_name  					   = "vpc-for-${local.production_name}"
              new_vsw_name  			 		   = "vsw-for-${local.production_name}"
              new_scg_name  						 = "sg-for-${local.production_name}"
              new_host_name   					 = "app-for-${local.production_name}"
            }
    
            resource "alicloud_vpc" "vpc" {
              vpc_name    					     = local.new_vpc_name
              cidr_block 								 = var.vpc_cidr_block
            }
    
    
            resource "alicloud_vswitch" "vsw" {
              vpc_id   								   = alicloud_vpc.vpc.id
              cidr_block								 = var.vsw_cidr_block
              zone_id   							   = var.zone_id
            }
    
            // Basic security group configurations
            resource "alicloud_security_group" "security_group" {
              name      							   = local.new_scg_name
              description							   = "nginx scg"
              vpc_id									   =  alicloud_vpc.vpc.id
            }
            
            # Ingress port 1 of the security group
            resource "alicloud_security_group_rule" "allow_ssh" {
              security_group_id				   = alicloud_security_group.security_group.id
              type 											 = "ingress"
              cidr_ip										 = "0.0.0.0/0"
              policy 										 = "accept"
              ip_protocol								 = "tcp"
              port_range								 = "22/22"
              priority						 		 	 = 1
            }
            
            # Ingress port 2 of the security group
            resource "alicloud_security_group_rule" "allow_web" {
              security_group_id					 = alicloud_security_group.security_group.id
              type											 = "ingress"
              cidr_ip										 = "0.0.0.0/0"
              policy										 = "accept"
              ip_protocol								 = "tcp"
              port_range								 = "80/443"
              priority									 = 1
            }
            
            # The egress port of the security group
            resource "alicloud_security_group_rule" "allow_egress" {
              security_group_id					 = alicloud_security_group.security_group.id
              type 											 = "egress"
              cidr_ip										 = "0.0.0.0/0"
              policy										 = "accept"
              ip_protocol								 = "tcp"
              port_range								 = "1/65535"
              priority						 			 = 1
            }
            
            # Basic instance configurations
            resource "alicloud_instance" "instance" {
              availability_zone					 = var.zone_id
              security_groups						 = [alicloud_security_group.security_group.id]
              # series III
              host_name									 = local.new_host_name
              instance_type              = var.instance_type
              system_disk_size           = 500
              system_disk_category       = "cloud_essd"
              image_id                   = "centos_7_9_x64_20G_alibase_20210318.vhd"
              vswitch_id                 = alicloud_vswitch.vsw.id
              password                   = var.instance_password
              internet_charge_type 			 = "PayByTraffic"
              internet_max_bandwidth_out = 30
              instance_charge_type 			 = var.pay_type
              period 										 = var.pay_period
              period_unit								 = var.pay_period_unit
              user_data 								 = file("${path.cwd}/user-data.sh")
              data_disks {
                size       							 = 100
                category  						   = "cloud_essd"
              }
            }
            # The returned IP address of NGINX
            output "nginx_ip" {
              value											 = "http://${alicloud_instance.instance.public_ip}:8080"
            }
            
      user-data.sh: |-
        #!/bin/bash -v
        # Mount the disk to /disk1.
        cat >> /root/InitDataDisk.sh << "EOF"
        #!/bin/bash
        echo "p
        n
        p
        w
        " |  fdisk -u /dev/vdb
        EOF
        /bin/bash /root/InitDataDisk.sh
        rm -f /root/InitDataDisk.sh
        mkfs -t ext4 /dev/vdb1
        cp /etc/fstab /etc/fstab.bak
        mkdir /disk1
        echo `blkid /dev/vdb1 | awk '{print $2}' | sed 's/\\\"//g'` /disk1 ext4 defaults 0 0 >> /etc/fstab
        mount -a
        # Configure the installation script.
        yum install -y nginx
        # Configure the startup script.
        /usr/sbin/nginx
    

Add parameter groups and dynamically obtain parameter settings

The preceding template includes the resources and resource dependencies that are required by your business. In the template, the value of system_disk_category is static. If you want to create and deploy a stack by using this template in another region, you must modify the template content and change the resource properties.

In this case, you can add the Parameters section to the template to improve the flexibility and reusability of the template.

Add parameter groups

You can use Metadata to group the parameters that are defined in the Parameters section and define a label for each group. For more information, see Metadata.

You can group parameters based on the resources that correspond to the parameters. The following table uses the preceding template as an example to describe how to group parameters.

Group

Resource

Parameter

Basic network configurations

vpc, vsw, and security_group

zone_id, vpc_cidr_block, and vswitch_cidr_block

ECS instance configurations

instance

instance_type, system_disk_category, system_disk_size, data_disk_category, data_disk_size, and instance_password

On the Terraform tab, create a .metadata file and enter the following content:

{
  "ALIYUN::ROS::Interface": {
    "ParameterGroups": [
      {
        "Parameters": [
          "pay_type",
          "pay_period_unit",
          "pay_period"
        ],
        "Label": {
          "default": {
            "en": "Payment mode Configuration",
             
          }
        }
      },
      {
        "Parameters": [
          "zone_id"
        ],
        "Label": {
          "default": {
             
            "en": "Zone Configuration"
          }
        }
      },
      {
        "Parameters": [
          "vpc_cidr_block",
          "vswitch_cidr_block"
        ],
        "Label": {
          "default": {
             
            "en": "Choose existing Infrastructure Configuration"
          }
        }
      },
      {
        "Parameters": [
          "instance_type",
          "system_disk_category",
          "system_disk_size",
          "data_disk_category",
          "data_disk_size",
          "instance_password"
        ],
        "Label": {
          "default": {
            "en": "Instance",
             
          }
        }
      }
    ]
  }
}

Dynamically obtain parameter settings

The following section uses the instance parameter as an example to describe how to dynamically obtain parameter settings. If you want to configure filter conditions for the instance parameter in the ROS console and dynamically obtain the setting of the parameter based on different filter conditions, you can use the ALIYUN::ECS::Instance resource type that corresponds to the instance parameter to query the value of AssociationProperty that is supported by the instance parameter in the "AssociationProperty and AssociationPropertyMetadata" topic of the ROS documentation. Then, you can query the value of AssociationPropertyMetadata that is supported by the instance parameter when ZoneId is used as the filter condition of AssociationProperty. In this example, the value of AssociationProperty that is supported by the instance parameter is ALIYUN::ECS::Instance::InstanceType. For more information, see AssociationProperty and AssociationPropertyMetadata.

On the Terraform tab, enter the following content in the main.tf file:

variable "pay_type" {
  type 				= string
  default     = "PostPaid"
  description = <<EOT
  {
    "Label": {
      "en": "ECS Instance Charge Type",
       
    },
    "AllowedValues": [
      "PostPaid",
      "PrePaid"
    ],
    "AssociationProperty": "ChargeType",
    "AssociationPropertyMetadata": {
      "LocaleKey": "InstanceChargeType"
    }
  }
  EOT
}
variable "pay_period_unit" {
  type 				= string
  default     = "Month"
  description = <<EOT
  {
    "Label": {
      "en": "Pay Period Unit",
       
    },
    "AllowedValues": [
      "Month",
      "Year"
    ],
    "AssociationProperty": "PayPeriodUnit",
    "AssociationPropertyMetadata": {
      "Visible": {
        "Condition": {
          "Fn::Not": {
            "Fn::Equals": [
              "$${pay_type}",
              "PostPaid"
            ]
          }
        }
      }
    }
  }
  EOT
}
variable "pay_period" {
  type				 = number
  default     = 1
  description = <<EOT
  {
    "Label": {
      "en": "Period",
       
    },
    "AllowedValues": [
      1,
      2,
      3,
      4,
      5,
      6,
      7,
      8,
      9
    ],
    "AssociationProperty": "PayPeriod",
    "AssociationPropertyMetadata": {
      "Visible": {
        "Condition": {
          "Fn::Or": [
            {
              "Fn::Equals": [
                "$${pay_type}",
                "PrePaid"
              ]
            },
            {
              "Fn::Equals": [
                "$${pay_type}",
                "undefined"
              ]
            }
          ]
        }
      }
    }
  }
  EOT
}
variable "zone_id" {
  type			  = string
  description = <<EOT
  {
    "AssociationProperty": "ALIYUN::ECS::Instance:ZoneId",
    "Description": {
        </font></b>",
      "en": "Availability Zone ID.<br><b>notex: <font color='blue'>before selecting, please confirm that the Availability Zone supports the specification of creating ECS resources, which is recommended to be different from other VSwitch Availability Zone.</font></b>"
    },
    "Label": {
       
      "en": "VSwitch Availability Zone"
    }
  }
  EOT
}
variable "vpc_cidr_block" {
  type			  = string
  default     = "192.168.0.0/16"
  description = <<EOT
  {
    "Label": {
       
      "en": "VPC CIDR Block"
    },
    "Description": {
       </font>",
      "en": "New proprietary network IP address segment range, recommended use of the following IP address segments<br><font color='green'>[10.0.0.0/8]</font><br><font color='green'>[172.16.0.0/12]</font><br><font color='green'>[192.168.0.0/16].</font>"
    }
  }
  EOT
}
variable "vswitch_cidr_block" {
  type			  = string
  default     = "192.168.0.0/24"
  description = <<EOT
  {
    "Description": {
       
      "en": "Must be a sub-network segment of the proprietary network and is not occupied by other VSwitches."
    },
    "Label": {
       
      "en": "VSwitch CIDR Block"
    }
  }
  EOT
} 
variable "instance_type" {
  type			  = string
  description = <<EOT
  {
    "Label": {
       
      "en": "Instance Type"
    },
    "AssociationProperty": "ALIYUN::ECS::Instance::InstanceType",
    "AssociationPropertyMetadata": {
      "InstanceChargeType": "$${pay_type}",
      "ZoneId": "$${zone_id}"
    }
  }
  EOT
}
variable "system_disk_category" {
  type			  = string
  description = <<EOT
  {
    "Description": {
      "en": "<font color='blue'><b>Optional values:</b></font><br>[cloud_efficiency: <font color='green'>Efficient Cloud Disk</font>]<br>[cloud_ssd: <font color='green'>SSD Cloud Disk</font>]<br>[cloud_essd: <font color='green'>ESSD Cloud Disk</font>]<br>[cloud: <font color='green'>Cloud Disk</font>]<br>[ephemeral_ssd: <font color='green'>Local SSD Cloud Disk</font>]",
       
    },
    "AssociationProperty": "ALIYUN::ECS::Disk::SystemDiskCategory",
    "AssociationPropertyMetadata": {
      "ZoneId": "$${zone_id}",
      "InstanceType": "$${instance_type}"
    },
    "Label": {
      "en": "System Disk Type",
       
    }
  }
  EOT
}
variable "system_disk_size" {
  type			  = number
  default     = 40
  description = <<EOT
  {
    "Description": {
       
      "en": "System disk size, range of values: 40~500, units: GB."
    },
    "Label": {
       
      "en": "System Disk Space"
    }
  }
  EOT
}
variable "data_disk_category" {
  type			  = string
  description = <<EOT
  {
    "Label": {
       
      "en": "Data Disk Type"
    },
    "Description": {
       
      "en": "<font color='blue'><b>Optional values:</b></font><br>[cloud_efficiency: <font color='green'>Efficient Cloud Disk</font>]<br>[cloud_ssd: <font color='green'>SSD Cloud Disk</font>]<br>[cloud_essd: <font color='green'>ESSD Cloud Disk</font>]<br>[cloud: <font color='green'>Cloud Disk</font>]"
    },      
    "AssociationProperty": "ALIYUN::ECS::Disk::DataDiskCategory",
    "AssociationPropertyMetadata": {
      "ZoneId": "$${zone_id}",
      "InstanceType": "$${instance_type}"
    }
  }
  EOT
}
variable "data_disk_size" {
  type			  = number
  default     = 100
  description = <<EOT
  {
    "Description": {
        
      "en": "ECS Instance disk size, range of values: 20~32768, units: GB"
    },
    "MaxValue": 32768,
    "MinValue": 20,
    "Label": {
       
      "en": "Data Disk Space"
    }
  }
  EOT
} 
variable "instance_password" {
  type			  = string
  sensitive   = true
  description = <<EOT
  {
    "Description": {
      "en": "Server login password, Length 8~30, must contain three(Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;<>,.?/ Special symbol in).",
       
    },
    "Label": {
      "en": "Instance Password",
       
    },
    "ConstraintDescription": {
      "en": "Length 8~30, must contain three(Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;<>,.?/ Special symbol in).",
       
    },
    "AssociationProperty": "ALIYUN::ECS::Instance::Password",
    "AllowedPattern": "^[a-zA-Z0-9-\\(\\)\\`\\~\\!\\@\\#\\$\\%\\^\\&\\*\\_\\-\\+\\=\\|\\{\\}\\[\\]\\:\\;\\<\\>\\,\\.\\?\\/]*$",
    "MinLength": 8,
    "MaxLength": 30
  }
  EOT
}

Add template parameter dependencies and constraints

The preceding template defines an ECS instance. You need to add constraints for the following parameters to the template: zone_id, instance_type, vswitch_id, system_disk_category, system_disk_size, data_disk_category, and data_disk_size. The preceding parameters are related to the alicloud_instance parameter.

In the ResourcesForParameterConstraints field, you need to only define the ALIYUN::ECS::Instance resource type (alicloud_instance) and associate the resource type with image_id, instance_type, and zone_id to add constraints.

{
  "ALIYUN::ROS::Interface": {
    "ResourcesForParameterConstraints": {
      "instance": {
        "Type": "ALIYUN::ECS::Instance",
        "Properties": {
          "InstanceType": {
            "Ref": "instance_type"
          },
          "ImageId": "centos_7_9_x64_20G_alibase_20210318.vhd",
          "VSwitchId": {
            "Ref": "vswitch_id"
          },
          "ZoneId": {
            "Ref": "zone_id"
          },
          "SystemDiskCategory": {
            "Ref": "system_disk_category"
          },
          "SystemDiskSize": {
            "Ref": "system_disk_size"
          },
          "DataDiskCategory": {
            "Ref": "data_disk_category"
          },
          "DataDiskSize": {
            "Ref": "data_disk_size"
          }
        }
      }
    },
    "ParameterGroups": [
      {
        "Parameters": [
          "pay_type",
          "pay_period_unit",
          "pay_period"
        ],
        "Label": {
          "default": {
            "en": "Payment mode Configuration",
             
          }
        }
      },
      {
        "Parameters": [
          "zone_id"
        ],
        "Label": {
          "default": {
             
            "en": "Zone Configuration"
          }
        }
      },
      {
        "Parameters": [
          "vpc_cidr_block",
          "vswitch_cidr_block"
        ],
        "Label": {
          "default": {
             
            "en": "Choose existing Infrastructure Configuration"
          }
        }
      },
      {
        "Parameters": [
          "instance_type",
          "system_disk_category",
          "system_disk_size",
          "data_disk_category",
          "data_disk_size",
          "instance_password"
        ],
        "Label": {
          "default": {
            "en": "Instance",
             
          }
        }
      }
    ]
  }
}

Complete sample template

ROSTemplateFormatVersion: '2015-09-01'
Transform: Aliyun::Terraform-v1.2
Workspace:
  .metadata: |-
    {
      "ALIYUN::ROS::Interface": {
        "ResourcesForParameterConstraints": {
          "instance": {
            "Type": "ALIYUN::ECS::Instance",
            "Properties": {
              "InstanceType": {
                "Ref": "instance_type"
              },
              "ImageId": "centos_7_9_x64_20G_alibase_20210318.vhd",
              "VSwitchId": {
                "Ref": "vswitch_id"
              },
              "ZoneId": {
                "Ref": "zone_id"
              },
              "SystemDiskCategory": {
                "Ref": "system_disk_category"
              },
              "SystemDiskSize": {
                "Ref": "system_disk_size"
              },
              "DataDiskCategory": {
                "Ref": "data_disk_category"
              },
              "DataDiskSize": {
                "Ref": "data_disk_size"
              }
            }
          }
        },
        "ParameterGroups": [
          {
            "Parameters": [
              "pay_type",
              "pay_period_unit",
              "pay_period"
            ],
            "Label": {
              "default": {
                "en": "Payment mode Configuration",
                 
              }
            }
          },
          {
            "Parameters": [
              "zone_id"
            ],
            "Label": {
              "default": {
                 
                "en": "Zone Configuration"
              }
            }
          },
          {
            "Parameters": [
              "vpc_cidr_block",
              "vswitch_cidr_block"
            ],
            "Label": {
              "default": {
                 
                "en": "Choose existing Infrastructure Configuration"
              }
            }
          },
          {
            "Parameters": [
              "instance_type",
              "system_disk_category",
              "system_disk_size",
              "data_disk_category",
              "data_disk_size",
              "instance_password"
            ],
            "Label": {
              "default": {
                "en": "Instance",
                 
              }
            }
          }
        ]
      }
    }
  main.tf: |-
    variable "pay_type" {
      type			  = string
      default     = "PostPaid"
      description = <<EOT
      {
        "Label": {
          "en": "ECS Instance Charge Type",
           
        },
        "AllowedValues": [
          "PostPaid",
          "PrePaid"
        ],
        "AssociationProperty": "ChargeType",
        "AssociationPropertyMetadata": {
          "LocaleKey": "InstanceChargeType"
        }
      }
      EOT
    }
    variable "pay_period_unit" {
      type			  = string
      default     = "Month"
      description = <<EOT
      {
        "Label": {
          "en": "Pay Period Unit",
           
        },
        "AllowedValues": [
          "Month",
          "Year"
        ],
        "AssociationProperty": "PayPeriodUnit",
        "AssociationPropertyMetadata": {
          "Visible": {
            "Condition": {
              "Fn::Not": {
                "Fn::Equals": [
                  "$${pay_type}",
                  "PostPaid"
                ]
              }
            }
          }
        }
      }
      EOT
    }
    variable "pay_period" {
      type			  = number
      default     = 1
      description = <<EOT
      {
        "Label": {
          "en": "Period",
           
        },
        "AllowedValues": [
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9
        ],
        "AssociationProperty": "PayPeriod",
        "AssociationPropertyMetadata": {
          "Visible": {
            "Condition": {
              "Fn::Or": [
                {
                  "Fn::Equals": [
                    "$${pay_type}",
                    "PrePaid"
                  ]
                },
                {
                  "Fn::Equals": [
                    "$${pay_type}",
                    "undefined"
                  ]
                }
              ]
            }
          }
        }
      }
      EOT
    }
    variable "zone_id" {
      type			  = string
      description = <<EOT
      {
        "AssociationProperty": "ALIYUN::ECS::Instance:ZoneId",
        "Description": {
            </font></b>",
          "en": "Availability Zone ID.<br><b>notex: <font color='blue'>before selecting, please confirm that the Availability Zone supports the specification of creating ECS resources, which is recommended to be different from other VSwitch Availability Zone.</font></b>"
        },
        "Label": {
           
          "en": "VSwitch Availability Zone"
        }
      }
      EOT
    }
    variable "vpc_cidr_block" {
      type			  = string
      default     = "192.168.0.0/16"
      description = <<EOT
      {
        "Label": {
           
          "en": "VPC CIDR Block"
        },
        "Description": {
           </font>",
          "en": "New proprietary network IP address segment range, recommended use of the following IP address segments<br><font color='green'>[10.0.0.0/8]</font><br><font color='green'>[172.16.0.0/12]</font><br><font color='green'>[192.168.0.0/16].</font>"
        }
      }
      EOT
    }
    variable "vswitch_cidr_block" {
      type			  = string
      default     = "192.168.0.0/24"
      description = <<EOT
      {
        "Description": {
           
          "en": "Must be a sub-network segment of the proprietary network and is not occupied by other VSwitches."
        },
        "Label": {
           
          "en": "VSwitch CIDR Block"
        }
      }
      EOT
    } 
    variable "instance_type" {
      type			  = string
      description = <<EOT
      {
        "Label": {
           
          "en": "Instance Type"
        },
        "AssociationProperty": "ALIYUN::ECS::Instance::InstanceType",
        "AssociationPropertyMetadata": {
          "InstanceChargeType": "$${pay_type}",
          "ZoneId": "$${zone_id}"
        }
      }
      EOT
    }
    variable "system_disk_category" {
      type			  = string
      description = <<EOT
      {
        "Description": {
          "en": "<font color='blue'><b>Optional values:</b></font><br>[cloud_efficiency: <font color='green'>Efficient Cloud Disk</font>]<br>[cloud_ssd: <font color='green'>SSD Cloud Disk</font>]<br>[cloud_essd: <font color='green'>ESSD Cloud Disk</font>]<br>[cloud: <font color='green'>Cloud Disk</font>]<br>[ephemeral_ssd: <font color='green'>Local SSD Cloud Disk</font>]",
           
        },
        "AssociationProperty": "ALIYUN::ECS::Disk::SystemDiskCategory",
        "AssociationPropertyMetadata": {
          "ZoneId": "$${zone_id}",
          "InstanceType": "$${instance_type}"
        },
        "Label": {
          "en": "System Disk Type",
           
        }
      }
      EOT
    }
    variable "system_disk_size" {
      type			  = number
      default     = 40
      description = <<EOT
      {
        "Description": {
           
          "en": "System disk size, range of values: 40~500, units: GB."
        },
        "Label": {
           
          "en": "System Disk Space"
        }
      }
      EOT
    }
    variable "data_disk_category" {
      type			  = string
      description = <<EOT
      {
        "Label": {
           
          "en": "Data Disk Type"
        },
        "Description": {
           
          "en": "<font color='blue'><b>Optional values:</b></font><br>[cloud_efficiency: <font color='green'>Efficient Cloud Disk</font>]<br>[cloud_ssd: <font color='green'>SSD Cloud Disk</font>]<br>[cloud_essd: <font color='green'>ESSD Cloud Disk</font>]<br>[cloud: <font color='green'>Cloud Disk</font>]"
        },      
        "AssociationProperty": "ALIYUN::ECS::Disk::DataDiskCategory",
        "AssociationPropertyMetadata": {
          "ZoneId": "$${zone_id}",
          "InstanceType": "$${instance_type}"
        }
      }
      EOT
    }
    variable "data_disk_size" {
      type			  = number
      default     = 100
      description = <<EOT
      {
        "Description": {
            
          "en": "ECS Instance disk size, range of values: 20~32768, units: GB."
        },
        "MaxValue": 32768,
        "MinValue": 20,
        "Label": {
           
          "en": "Data Disk Space"
        }
      }
      EOT
    } 
    variable "instance_password" {
      type			  = string
      sensitive   = true
      description = <<EOT
      {
        "Description": {
          "en": "Server login password, Length 8~30, must contain three(Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;<>,.?/ Special symbol in).",
           
        },
        "Label": {
          "en": "Instance Password",
           
        },
        "ConstraintDescription": {
          "en": "Length 8~30, must contain three(Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;<>,.?/ Special symbol in).",
           
        },
        "AssociationProperty": "ALIYUN::ECS::Instance::Password",
        "AllowedPattern": "^[a-zA-Z0-9-\\(\\)\\`\\~\\!\\@\\#\\$\\%\\^\\&\\*\\_\\-\\+\\=\\|\\{\\}\\[\\]\\:\\;\\<\\>\\,\\.\\?\\/]*$",
        "MinLength": 8,
        "MaxLength": 30
      }
      EOT
    }
    # The default resource names. 
    locals {
      production_name						 = "nginx"
      new_scg_name							 = "sg-for-${local.production_name}"
      new_host_name							 = "app-for-${local.production_name}"
    }

    resource "alicloud_vpc" "vpc" {
      cidr_block								 = var.vpc_cidr_block
    }

    resource "alicloud_vswitch" "vsw" {
      vpc_id  								   = alicloud_vpc.vpc.id
      cidr_block								 = var.vswitch_cidr_block
      zone_id  								   = var.zone_id
    }

    // The basic information about security groups.
    resource "alicloud_security_group" "security_group" {
      name      							   = local.new_scg_name
      description								 = "nginx scg"
      vpc_id										 =  alicloud_vpc.vpc.id
    }

    # Ingress port 1 of the security group
    resource "alicloud_security_group_rule" "allow_ssh" {
      security_group_id					 = alicloud_security_group.security_group.id
      type											 = "ingress"
      cidr_ip										 = "0.0.0.0/0"
      policy										 = "accept"
      ip_protocol								 = "tcp"
      port_range								 = "22/22"
      priority									 = 1
    }

    # Ingress port 2 of the security group
    resource "alicloud_security_group_rule" "allow_web" {
      security_group_id					 = alicloud_security_group.security_group.id
      type											 = "ingress"
      cidr_ip										 = "0.0.0.0/0"
      policy										 = "accept"
      ip_protocol								 = "tcp"
      port_range								 = "80/443"
      priority									 = 1
    }

    # The egress port of the security group
    resource "alicloud_security_group_rule" "allow_egress" {
      security_group_id					 = alicloud_security_group.security_group.id
      type											 = "egress"
      cidr_ip										 = "0.0.0.0/0"
      policy										 = "accept"
      ip_protocol								 = "tcp"
      port_range								 = "1/65535"
      priority									 = 1
    }

    # Basic instance configurations
    resource "alicloud_instance" "instance" {
      availability_zone					 = var.zone_id
      security_groups						 = [alicloud_security_group.security_group.id]
      # series III
      host_name									 = local.new_host_name
      instance_type              = var.instance_type
      system_disk_size           = var.system_disk_size
      system_disk_category       = var.system_disk_category
      image_id                   = "centos_7_9_x64_20G_alibase_20210318.vhd"
      vswitch_id                 = alicloud_vswitch.vsw.id
      password                   = var.instance_password
      internet_charge_type			 = "PayByTraffic"
      internet_max_bandwidth_out = 30
      instance_charge_type			 = var.pay_type
      period										 = var.pay_period
      period_unit								 = var.pay_period_unit
      user_data									 = file("${path.cwd}/user-data.sh")
      data_disks {
        size     							   = var.data_disk_size
        category						     = var.data_disk_category
      }
    }
    # The returned IP address of NGINX
    output "nginx_ip" {
      value											 = "http://${alicloud_instance.instance.public_ip}:8080"
    }
  user-data.sh: |-
    #!/bin/bash -v
    # Mount the disk to /disk1.
    cat >> /root/InitDataDisk.sh << "EOF"
    #!/bin/bash
    echo "p
    n
    p
    w
    " |  fdisk -u /dev/vdb
    EOF
    /bin/bash /root/InitDataDisk.sh
    rm -f /root/InitDataDisk.sh
    mkfs -t ext4 /dev/vdb1
    cp /etc/fstab /etc/fstab.bak
    mkdir /disk1
    echo `blkid /dev/vdb1 | awk '{print $2}' | sed 's/\\\"//g'` /disk1 ext4 defaults 0 0 >> /etc/fstab
    mount -a
    # Configure the installation script.
    yum install -y nginx
    # Configure the startup script.
    /usr/sbin/nginx