alicloud/resource_alicloud_instance.go (2,479 lines of code) (raw):
package alicloud
import (
"fmt"
"log"
"regexp"
"strings"
"time"
"github.com/denverdino/aliyungo/common"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"encoding/base64"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
"github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
func resourceAliCloudInstance() *schema.Resource {
return &schema.Resource{
Create: resourceAliCloudInstanceCreate,
Read: resourceAliCloudInstanceRead,
Update: resourceAliCloudInstanceUpdate,
Delete: resourceAliCloudInstanceDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Update: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(20 * time.Minute),
},
Schema: map[string]*schema.Schema{
"availability_zone": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"image_id": {
Type: schema.TypeString,
AtLeastOneOf: []string{"image_id", "launch_template_id", "launch_template_name"},
Optional: true,
Computed: true,
},
"instance_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: StringMatch(regexp.MustCompile(`^ecs\..*`), "prefix must be 'ecs.'"),
AtLeastOneOf: []string{"instance_type", "launch_template_id", "launch_template_name"},
Computed: true,
},
"credit_specification": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: StringInSlice([]string{
string(CreditSpecificationStandard),
string(CreditSpecificationUnlimited),
}, false),
},
"security_groups": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
Optional: true,
AtLeastOneOf: []string{"security_groups", "launch_template_id", "launch_template_name"},
},
"allocate_public_ip": {
Type: schema.TypeBool,
Optional: true,
Deprecated: "Field 'allocate_public_ip' has been deprecated from provider version 1.6.1. Setting 'internet_max_bandwidth_out' larger than 0 will allocate public ip for instance.",
},
"instance_name": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: StringLenBetween(2, 128),
Computed: true,
},
"resource_group_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: StringLenBetween(2, 256),
Computed: true,
},
"internet_charge_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: StringInSlice([]string{"PayByBandwidth", "PayByTraffic"}, false),
DiffSuppressFunc: ecsInternetDiffSuppressFunc,
Computed: true,
},
"internet_max_bandwidth_in": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
DiffSuppressFunc: ecsInternetDiffSuppressFunc,
Deprecated: "The attribute is invalid and no any affect for the instance. So it has been deprecated since version v1.121.2.",
},
"internet_max_bandwidth_out": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"host_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"password": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
},
"kms_encrypted_password": {
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: kmsDiffSuppressFunc,
},
"kms_encryption_context": {
Type: schema.TypeMap,
Optional: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return d.Get("kms_encrypted_password") == ""
},
Elem: schema.TypeString,
},
"password_inherit": {
Type: schema.TypeBool,
Optional: true,
},
"io_optimized": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Deprecated: "Attribute io_optimized has been deprecated on instance resource. All the launched alicloud instances will be IO optimized. Suggest to remove it from your template.",
Removed: "Attribute 'io_optimized' has been removed from provider version 1.213.1.",
},
"is_outdated": {
Type: schema.TypeBool,
Optional: true,
},
"system_disk_category": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
ValidateFunc: StringInSlice([]string{"all", "cloud", "ephemeral_ssd", "cloud_essd", "cloud_efficiency", "cloud_ssd", "local_disk", "cloud_auto", "cloud_essd_entry"}, false),
},
"system_disk_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: StringLenBetween(2, 128),
},
"system_disk_description": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: StringLenBetween(2, 256),
},
"system_disk_size": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"system_disk_performance_level": {
Type: schema.TypeString,
Optional: true,
Computed: true,
DiffSuppressFunc: ecsSystemDiskPerformanceLevelSuppressFunc,
ValidateFunc: StringInSlice([]string{"PL0", "PL1", "PL2", "PL3"}, false),
},
"system_disk_auto_snapshot_policy_id": {
Type: schema.TypeString,
Optional: true,
},
"system_disk_storage_cluster_id": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
},
"system_disk_encrypted": {
Type: schema.TypeBool,
ForceNew: true,
Optional: true,
Computed: true,
},
"system_disk_kms_key_id": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
Computed: true,
},
"system_disk_encrypt_algorithm": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
},
"system_disk_id": {
Type: schema.TypeString,
Computed: true,
},
"network_interface_traffic_mode": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
ValidateFunc: StringInSlice([]string{"Standard", "HighPerformance"}, false),
},
"network_card_index": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"queue_pair_number": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"data_disks": {
Type: schema.TypeList,
Optional: true,
MinItems: 1,
MaxItems: 16,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: StringLenBetween(2, 128),
},
"size": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"category": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: StringInSlice([]string{"all", "cloud", "ephemeral_ssd", "cloud_essd", "cloud_efficiency", "cloud_ssd", "local_disk", "cloud_auto", "cloud_essd_entry"}, false),
Default: DiskCloudEfficiency,
ForceNew: true,
},
"encrypted": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
},
"kms_key_id": {
Type: schema.TypeString,
Optional: true,
},
"snapshot_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"auto_snapshot_policy_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"delete_with_instance": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
Default: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: StringLenBetween(2, 256),
},
"performance_level": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: StringInSlice([]string{"PL0", "PL1", "PL2", "PL3"}, false),
},
"device": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
},
},
"network_interfaces": {
Type: schema.TypeList,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"network_interface_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"vswitch_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"network_interface_traffic_mode": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
ValidateFunc: StringInSlice([]string{"Standard", "HighPerformance"}, false),
},
"network_card_index": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Computed: true,
},
"queue_pair_number": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Computed: true,
},
"security_group_ids": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
//subnet_id and vswitch_id both exists, cause compatible old version, and aws habit.
"subnet_id": {
Type: schema.TypeString,
Optional: true,
Computed: true, //add this schema cause subnet_id not used enter parameter, will different, so will be ForceNew
Removed: "Field 'subnet_id' has been removed from version 1.210.0",
},
"vpc_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"vswitch_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"private_ip": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"instance_charge_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: StringInSlice([]string{string(common.PrePaid), string(common.PostPaid)}, false),
},
"period": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validation.Any(
IntBetween(1, 9),
IntInSlice([]int{12, 24, 36, 48, 60})),
DiffSuppressFunc: PostPaidDiffSuppressFunc,
},
"period_unit": {
Type: schema.TypeString,
Optional: true,
Default: Month,
ValidateFunc: StringInSlice([]string{"Week", "Month"}, false),
DiffSuppressFunc: PostPaidDiffSuppressFunc,
},
"renewal_status": {
Type: schema.TypeString,
Optional: true,
Default: RenewNormal,
ValidateFunc: StringInSlice([]string{
string(RenewAutoRenewal),
string(RenewNormal),
string(RenewNotRenewal)}, false),
DiffSuppressFunc: PostPaidDiffSuppressFunc,
},
"auto_renew_period": {
Type: schema.TypeInt,
Optional: true,
Default: 1,
ValidateFunc: IntInSlice([]int{1, 2, 3, 6, 12}),
DiffSuppressFunc: ecsNotAutoRenewDiffSuppressFunc,
},
"include_data_disks": {
Type: schema.TypeBool,
Optional: true,
Default: true,
DiffSuppressFunc: PostPaidDiffSuppressFunc,
},
"dry_run": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"public_ip": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: StringInSlice([]string{"Running", "Stopped"}, false),
Computed: true,
},
"user_data": {
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if (d.Get("user_data") != "" && new == "") && (d.Get("launch_template_name").(string) != "" ||
d.Get("launch_template_id").(string) != "") {
return true
}
return false
},
},
"role_name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
DiffSuppressFunc: vpcTypeResourceDiffSuppressFunc,
},
"key_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"spot_strategy": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: StringInSlice([]string{"NoSpot", "SpotAsPriceGo", "SpotWithPriceLimit"}, false),
DiffSuppressFunc: ecsSpotStrategyDiffSuppressFunc,
},
"spot_price_limit": {
Type: schema.TypeFloat,
Optional: true,
ForceNew: true,
Computed: true,
DiffSuppressFunc: ecsSpotPriceLimitDiffSuppressFunc,
},
"deletion_protection": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"force_delete": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: descriptions["A behavior mark used to delete 'PrePaid' ECS instance forcibly."],
DiffSuppressFunc: PostPaidDiffSuppressFunc,
},
"security_enhancement_strategy": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
ValidateFunc: StringInSlice([]string{
string(ActiveSecurityEnhancementStrategy),
string(DeactiveSecurityEnhancementStrategy),
}, false),
},
"tags": tagsSchemaWithIgnore(),
"volume_tags": tagsSchemaComputed(),
"auto_release_time": {
Type: schema.TypeString,
Optional: true,
//ValidateFunc: ValidateRFC3339TimeString(true),
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if (d.Get("auto_release_time") != "" && new == "") && (d.Get("launch_template_name").(string) != "" ||
d.Get("launch_template_id").(string) != "") {
return true
}
if d.Get("instance_charge_type").(string) == "PrePaid" {
return true
}
return old != "" && new != "" && strings.HasPrefix(new, strings.Trim(old, ":00Z"))
},
},
"hpc_cluster_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"secondary_private_ips": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Computed: true,
ConflictsWith: []string{"secondary_private_ip_address_count"},
},
"secondary_private_ip_address_count": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ConflictsWith: []string{"secondary_private_ips"},
},
"deployment_set_id": {
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if (d.Get("deployment_set_id") != "" && new == "") && (d.Get("launch_template_name").(string) != "" ||
d.Get("launch_template_id").(string) != "") {
return true
}
return false
},
},
"deployment_set_group_no": {
Type: schema.TypeString,
Computed: true,
},
"operator_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: StringInSlice([]string{"upgrade", "downgrade"}, false),
},
"stopped_mode": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ValidateFunc: StringInSlice([]string{"StopCharging", "KeepCharging", "Not-applicable"}, false),
},
"maintenance_time": {
Type: schema.TypeSet,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"start_time": {
Type: schema.TypeString,
Optional: true,
},
"end_time": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"maintenance_action": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: StringInSlice([]string{"Stop", "AutoRecover", "AutoRedeploy"}, false),
},
"maintenance_notify": {
Type: schema.TypeBool,
Optional: true,
},
"spot_duration": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: IntBetween(0, 6),
},
"http_tokens": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: StringInSlice([]string{"optional", "required"}, false),
},
"http_endpoint": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: StringInSlice([]string{"enabled", "disabled"}, false),
},
"http_put_response_hop_limit": {
Type: schema.TypeInt,
ForceNew: true,
Optional: true,
Computed: true,
ValidateFunc: IntBetween(1, 64),
},
"ipv6_address_count": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: IntBetween(1, 10),
ConflictsWith: []string{"ipv6_addresses"},
},
"ipv6_addresses": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
MaxItems: 10,
Elem: &schema.Schema{Type: schema.TypeString},
ConflictsWith: []string{"ipv6_address_count"},
},
"network_interface_id": {
Type: schema.TypeString,
Computed: true,
},
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"memory": {
Type: schema.TypeInt,
Computed: true,
},
"os_name": {
Type: schema.TypeString,
Computed: true,
},
"os_type": {
Type: schema.TypeString,
Computed: true,
},
"primary_ip_address": {
Type: schema.TypeString,
Computed: true,
},
"dedicated_host_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"spot_strategy", "spot_price_limit"},
},
"launch_template_name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"launch_template_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"launch_template_version": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"enable_jumbo_frame": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"create_time": {
Type: schema.TypeString,
Computed: true,
},
"start_time": {
Type: schema.TypeString,
Computed: true,
},
"expired_time": {
Type: schema.TypeString,
Computed: true,
},
"image_options": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"login_as_non_root": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},
},
},
},
},
}
}
func resourceAliCloudInstanceCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
var response map[string]interface{}
action := "RunInstances"
request := make(map[string]interface{})
var err error
request["RegionId"] = client.RegionId
request["ClientToken"] = buildClientToken(action)
if v, ok := d.GetOk("instance_type"); ok {
request["InstanceType"] = v
}
if v, ok := d.GetOk("image_id"); ok {
request["ImageId"] = v
}
if v, ok := d.GetOk("availability_zone"); ok {
request["ZoneId"] = v
}
if v, ok := d.GetOk("system_disk_name"); ok {
request["SystemDisk.DiskName"] = v
}
if v, ok := d.GetOk("system_disk_description"); ok {
request["SystemDisk.Description"] = v
}
if v, ok := d.GetOk("system_disk_performance_level"); ok {
request["SystemDisk.PerformanceLevel"] = v
}
if v, ok := d.GetOk("system_disk_category"); ok {
request["SystemDisk.Category"] = v
}
if v, ok := d.GetOk("system_disk_size"); ok {
request["SystemDisk.Size"] = v
}
if v, ok := d.GetOk("system_disk_auto_snapshot_policy_id"); ok {
request["SystemDisk.AutoSnapshotPolicyId"] = v
}
if v, ok := d.GetOk("system_disk_storage_cluster_id"); ok {
request["SystemDisk.StorageClusterId"] = v
}
if v, ok := d.GetOkExists("system_disk_encrypted"); ok {
request["SystemDisk.Encrypted"] = v
}
if v, ok := d.GetOk("system_disk_kms_key_id"); ok {
request["SystemDisk.KMSKeyId"] = v
}
if v, ok := d.GetOk("system_disk_encrypt_algorithm"); ok {
request["SystemDisk.EncryptAlgorithm"] = v
}
if v, ok := d.GetOk("instance_name"); ok {
request["InstanceName"] = v
}
if v, ok := d.GetOk("credit_specification"); ok {
request["CreditSpecification"] = v
}
if v, ok := d.GetOk("resource_group_id"); ok {
request["ResourceGroupId"] = v
}
if v, ok := d.GetOk("description"); ok {
request["Description"] = v
}
if v, ok := d.GetOk("launch_template_name"); ok {
request["LaunchTemplateName"] = v
}
if v, ok := d.GetOk("launch_template_id"); ok {
request["LaunchTemplateId"] = v
}
if v, ok := d.GetOk("launch_template_version"); ok {
request["LaunchTemplateVersion"] = v
}
if v, ok := d.GetOk("internet_charge_type"); ok {
request["InternetChargeType"] = v
}
if v, ok := d.GetOk("internet_max_bandwidth_out"); ok {
request["InternetMaxBandwidthOut"] = v
}
if v, ok := d.GetOk("internet_max_bandwidth_in"); ok {
request["InternetMaxBandwidthIn"] = v
}
if v, ok := d.GetOk("host_name"); ok {
request["HostName"] = v
}
if v, ok := d.GetOk("password"); ok {
request["Password"] = v
}
if v, ok := d.GetOk("kms_encrypted_password"); ok {
kmsService := KmsService{client}
decryptResp, err := kmsService.Decrypt(v.(string), d.Get("kms_encryption_context").(map[string]interface{}))
if err != nil {
return WrapError(err)
}
request["Password"] = decryptResp
}
if v, ok := d.GetOkExists("password_inherit"); ok {
request["PasswordInherit"] = v
}
vswitchValue := d.Get("vswitch_id")
if vswitchValue == "" {
vswitchValue = d.Get("subnet_id")
}
if v, ok := d.GetOk("instance_charge_type"); ok {
request["InstanceChargeType"] = v
}
if request["InstanceChargeType"] == string(PrePaid) {
if v, ok := d.GetOk("period"); ok {
request["Period"] = v
}
if v, ok := d.GetOk("period_unit"); ok {
request["PeriodUnit"] = v
}
if v, ok := d.GetOk("renewal_status"); ok && v.(string) == "AutoRenewal" {
request["AutoRenew"] = true
}
if v, ok := d.GetOk("auto_renew_period"); ok {
request["AutoRenewPeriod"] = v
}
} else {
if v, ok := d.GetOk("spot_strategy"); ok {
request["SpotStrategy"] = v
}
if v, ok := d.GetOk("spot_price_limit"); ok {
request["SpotPriceLimit"] = v
}
}
if v, ok := d.GetOk("user_data"); ok {
_, base64DecodeError := base64.StdEncoding.DecodeString(v.(string))
if base64DecodeError == nil {
request["UserData"] = v
} else {
request["UserData"] = base64.StdEncoding.EncodeToString([]byte(v.(string)))
}
}
if v, ok := d.GetOk("role_name"); ok {
request["RamRoleName"] = v
}
if v, ok := d.GetOk("key_name"); ok {
request["KeyPairName"] = v
}
if v, ok := d.GetOk("security_enhancement_strategy"); ok {
request["SecurityEnhancementStrategy"] = v
}
if v, ok := d.GetOk("auto_release_time"); ok && v.(string) != "" {
request["AutoReleaseTime"] = v
}
if v, ok := d.GetOkExists("dry_run"); ok {
request["DryRun"] = v
}
if v, ok := d.GetOkExists("deletion_protection"); ok {
request["DeletionProtection"] = v
}
if v, ok := d.GetOk("tags"); ok {
count := 1
for key, value := range v.(map[string]interface{}) {
request[fmt.Sprintf("Tag.%d.Key", count)] = key
request[fmt.Sprintf("Tag.%d.Value", count)] = value
count++
}
}
if v, ok := d.GetOk("data_disks"); ok {
disksMaps := make([]map[string]interface{}, 0)
disks := v.([]interface{})
for _, rew := range disks {
disksMap := make(map[string]interface{})
item := rew.(map[string]interface{})
disksMap["DeleteWithInstance"] = item["delete_with_instance"].(bool)
disksMap["Encrypted"] = item["encrypted"].(bool)
disksMap["Size"] = item["size"].(int)
if category, ok := item["category"].(string); ok && category != "" {
disksMap["Category"] = category
}
if name, ok := item["name"].(string); ok && name != "" {
disksMap["DiskName"] = name
}
if kmsKeyId, ok := item["kms_key_id"].(string); ok && kmsKeyId != "" {
disksMap["KMSKeyId"] = kmsKeyId
}
if snapshotId, ok := item["snapshot_id"].(string); ok && snapshotId != "" {
disksMap["SnapshotId"] = snapshotId
}
if description, ok := item["description"].(string); ok && description != "" {
disksMap["Description"] = description
}
if autoSnapshotPolicyId, ok := item["auto_snapshot_policy_id"].(string); ok && autoSnapshotPolicyId != "" {
disksMap["AutoSnapshotPolicyId"] = autoSnapshotPolicyId
}
if device, ok := item["device"].(string); ok && device != "" {
disksMap["Device"] = device
}
if performanceLevel, ok := item["performance_level"].(string); ok && performanceLevel != "" && disksMap["Category"] == string(DiskCloudESSD) {
disksMap["PerformanceLevel"] = performanceLevel
}
if disksMap["Category"] == string(DiskEphemeralSSD) {
disksMap["DeleteWithInstance"] = ""
}
disksMaps = append(disksMaps, disksMap)
}
request["DataDisk"] = disksMaps
}
networkInterfacesMaps := make([]map[string]interface{}, 0)
_, networkInterfaceTrafficModeOk := d.GetOk("network_interface_traffic_mode")
_, networkCardIndexOk := d.GetOkExists("network_card_index")
_, queuePairNumberOk := d.GetOkExists("queue_pair_number")
if networkInterfaceTrafficModeOk || networkCardIndexOk || queuePairNumberOk {
primaryNetworkInterfacesMap := make(map[string]interface{})
primaryNetworkInterfacesMap["InstanceType"] = "Primary"
if v, ok := d.GetOk("security_groups"); ok {
// At present, the classic network instance does not support multi sg in runInstances
sgs := expandStringList(v.(*schema.Set).List())
if d.Get("vswitch_id").(string) == "" && len(sgs) > 0 {
primaryNetworkInterfacesMap["SecurityGroupId"] = sgs[0]
} else {
primaryNetworkInterfacesMap["SecurityGroupIds"] = sgs
}
}
if vswitchValue != "" {
primaryNetworkInterfacesMap["VSwitchId"] = vswitchValue
if v, ok := d.GetOk("private_ip"); ok {
primaryNetworkInterfacesMap["PrimaryIpAddress"] = v
}
}
if v, ok := d.GetOk("ipv6_addresses"); ok {
primaryNetworkInterfacesMap["Ipv6Address"] = v.(*schema.Set).List()
}
if v, ok := d.GetOkExists("ipv6_address_count"); ok {
primaryNetworkInterfacesMap["Ipv6AddressCount"] = v
}
if v, ok := d.GetOk("network_interface_traffic_mode"); ok {
primaryNetworkInterfacesMap["NetworkInterfaceTrafficMode"] = v
}
if v, ok := d.GetOkExists("network_card_index"); ok {
primaryNetworkInterfacesMap["NetworkCardIndex"] = v
}
if v, ok := d.GetOkExists("queue_pair_number"); ok {
primaryNetworkInterfacesMap["QueuePairNumber"] = v
}
networkInterfacesMaps = append(networkInterfacesMaps, primaryNetworkInterfacesMap)
} else {
if vswitchValue != "" {
request["VSwitchId"] = vswitchValue
if v, ok := d.GetOk("private_ip"); ok {
request["PrivateIpAddress"] = v
}
}
if v, ok := d.GetOk("security_groups"); ok {
// At present, the classic network instance does not support multi sg in runInstances
sgs := expandStringList(v.(*schema.Set).List())
if d.Get("vswitch_id").(string) == "" && len(sgs) > 0 {
request["SecurityGroupId"] = sgs[0]
} else {
request["SecurityGroupIds"] = sgs
}
}
if v, ok := d.GetOk("ipv6_addresses"); ok {
request["Ipv6Address"] = v.(*schema.Set).List()
}
if v, ok := d.GetOkExists("ipv6_address_count"); ok {
request["Ipv6AddressCount"] = v
}
}
if v, ok := d.GetOk("network_interfaces"); ok {
for _, networkInterfaces := range v.([]interface{}) {
secondaryNetworkInterfacesMap := make(map[string]interface{})
secondaryNetworkInterfacesArg := networkInterfaces.(map[string]interface{})
if networkInterfaceId, ok := secondaryNetworkInterfacesArg["network_interface_id"]; ok && fmt.Sprint(networkInterfaceId) != "" {
secondaryNetworkInterfacesMap["NetworkInterfaceId"] = networkInterfaceId
} else {
secondaryNetworkInterfacesMap["InstanceType"] = "Secondary"
if vSwitchId, ok := secondaryNetworkInterfacesArg["vswitch_id"]; ok {
secondaryNetworkInterfacesMap["VSwitchId"] = vSwitchId
}
if networkInterfaceTrafficMode, ok := secondaryNetworkInterfacesArg["network_interface_traffic_mode"]; ok {
secondaryNetworkInterfacesMap["NetworkInterfaceTrafficMode"] = networkInterfaceTrafficMode
}
isSupported, err := ecsService.isSupportedNetworkCardIndex(fmt.Sprint(request["InstanceType"]))
if err != nil {
return WrapError(err)
}
if networkCardIndex, ok := secondaryNetworkInterfacesArg["network_card_index"]; ok && isSupported {
secondaryNetworkInterfacesMap["NetworkCardIndex"] = networkCardIndex
}
if queuePairNumber, ok := secondaryNetworkInterfacesArg["queue_pair_number"]; ok && fmt.Sprint(queuePairNumber) != "0" {
secondaryNetworkInterfacesMap["QueuePairNumber"] = queuePairNumber
}
if securityGroupIds, ok := secondaryNetworkInterfacesArg["security_group_ids"]; ok {
secondaryNetworkInterfacesMap["SecurityGroupIds"] = securityGroupIds
}
}
networkInterfacesMaps = append(networkInterfacesMaps, secondaryNetworkInterfacesMap)
}
}
if len(networkInterfacesMaps) > 0 {
request["NetworkInterface"] = networkInterfacesMaps
}
networkOptionsMap := make(map[string]interface{})
if v, ok := d.GetOkExists("enable_jumbo_frame"); ok {
networkOptionsMap["EnableJumboFrame"] = v
}
if len(networkOptionsMap) > 0 {
request["NetworkOptions"] = networkOptionsMap
}
if v, ok := d.GetOk("hpc_cluster_id"); ok {
request["HpcClusterId"] = v
}
if v, ok := d.GetOk("deployment_set_id"); ok {
request["DeploymentSetId"] = v
}
if v, ok := d.GetOk("http_tokens"); ok {
request["HttpTokens"] = v
}
if v, ok := d.GetOk("http_endpoint"); ok {
request["HttpEndpoint"] = v
}
if v, ok := d.GetOk("http_put_response_hop_limit"); ok {
request["HttpPutResponseHopLimit"] = v
}
if d.Get("is_outdated").(bool) {
request["IoOptimized"] = "none"
}
if v, ok := d.GetOkExists("spot_duration"); ok {
request["SpotDuration"] = v
}
if v, ok := d.GetOk("dedicated_host_id"); ok {
request["DedicatedHostId"] = v
}
if v, ok := d.GetOk("image_options"); ok {
for _, raw := range v.(*schema.Set).List() {
imageOptionsArg := raw.(map[string]interface{})
if v, ok := imageOptionsArg["login_as_non_root"]; ok {
request["ImageOptions.LoginAsNonRoot"] = v
}
}
}
wait := incrementalWait(1*time.Second, 1*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) || IsExpectedErrors(err, []string{"IncorrectVSwitchStatus"}) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, "alicloud_instance", action, AlibabaCloudSdkGoERROR)
}
d.SetId(fmt.Sprint(response["InstanceIdSets"].(map[string]interface{})["InstanceIdSet"].([]interface{})[0]))
stateConf := BuildStateConf([]string{"Pending", "Starting", "Stopped"}, []string{"Running"}, d.Timeout(schema.TimeoutCreate), 10*time.Second, ecsService.InstanceStateRefreshFunc(d.Id(), []string{"Stopping"}))
if _, err := stateConf.WaitForState(); err != nil {
return WrapErrorf(err, IdMsg, d.Id())
}
return resourceAliCloudInstanceUpdate(d, meta)
}
func resourceAliCloudInstanceRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
if !d.IsNewResource() && NotFoundError(err) {
log.Printf("[DEBUG] Resource alicloud_instance ecsService.DescribeInstance Failed!!! %s", err)
d.SetId("")
return nil
}
return WrapError(err)
}
disk, err := ecsService.DescribeEcsSystemDisk(d.Id())
if err != nil {
// if old resource happenes an not found error, there may system has been detached
if !d.IsNewResource() && NotFoundError(err) {
log.Printf("[WARNING] describing instance %s system disk failed. Error: %v", d.Id(), err)
} else {
return WrapError(err)
}
} else {
d.Set("system_disk_category", disk["Category"])
d.Set("system_disk_name", disk["DiskName"])
d.Set("system_disk_description", disk["Description"])
d.Set("system_disk_size", disk["Size"])
d.Set("system_disk_auto_snapshot_policy_id", disk["AutoSnapshotPolicyId"])
d.Set("system_disk_storage_cluster_id", disk["StorageClusterId"])
d.Set("system_disk_encrypted", disk["Encrypted"])
d.Set("system_disk_kms_key_id", disk["KMSKeyId"])
d.Set("system_disk_id", disk["DiskId"])
d.Set("system_disk_performance_level", disk["PerformanceLevel"])
if v, ok := disk["Tags"].(map[string]interface{}); ok {
d.Set("volume_tags", tagsToMap(v["Tag"]))
}
}
d.Set("instance_name", instance.InstanceName)
d.Set("resource_group_id", instance.ResourceGroupId)
d.Set("description", instance.Description)
d.Set("status", instance.Status)
d.Set("availability_zone", instance.ZoneId)
d.Set("host_name", instance.HostName)
d.Set("image_id", instance.ImageId)
d.Set("instance_type", instance.InstanceType)
d.Set("password", d.Get("password").(string))
d.Set("internet_max_bandwidth_out", instance.InternetMaxBandwidthOut)
d.Set("internet_max_bandwidth_in", instance.InternetMaxBandwidthIn)
d.Set("instance_charge_type", instance.InstanceChargeType)
d.Set("key_name", instance.KeyPairName)
d.Set("spot_strategy", instance.SpotStrategy)
d.Set("spot_price_limit", instance.SpotPriceLimit)
d.Set("internet_charge_type", instance.InternetChargeType)
d.Set("deletion_protection", instance.DeletionProtection)
d.Set("credit_specification", instance.CreditSpecification)
// There is an api bug that it returns non-RFC3339 timestamp, like "2023-07-26T23:50Z"
if _, err := time.Parse(time.RFC3339, instance.AutoReleaseTime); err != nil && strings.HasSuffix(instance.AutoReleaseTime, "Z") {
d.Set("auto_release_time", fmt.Sprintf("%s:00Z", strings.TrimSuffix(instance.AutoReleaseTime, "Z")))
} else {
d.Set("auto_release_time", instance.AutoReleaseTime)
}
d.Set("tags", ecsService.tagsToMap(instance.Tags.Tag))
d.Set("hpc_cluster_id", instance.HpcClusterId)
d.Set("deployment_set_id", instance.DeploymentSetId)
d.Set("deployment_set_group_no", instance.DeploymentSetGroupNo)
d.Set("stopped_mode", instance.StoppedMode)
if len(instance.PublicIpAddress.IpAddress) > 0 {
d.Set("public_ip", instance.PublicIpAddress.IpAddress[0])
} else {
d.Set("public_ip", "")
}
d.Set("subnet_id", instance.VpcAttributes.VSwitchId)
d.Set("vpc_id", instance.VpcAttributes.VpcId)
d.Set("vswitch_id", instance.VpcAttributes.VSwitchId)
d.Set("spot_duration", instance.SpotDuration)
d.Set("http_tokens", instance.MetadataOptions.HttpTokens)
d.Set("http_endpoint", instance.MetadataOptions.HttpEndpoint)
d.Set("http_put_response_hop_limit", instance.MetadataOptions.HttpPutResponseHopLimit)
d.Set("cpu", instance.Cpu)
d.Set("memory", instance.Memory)
d.Set("os_name", instance.OSName)
d.Set("os_type", instance.OSType)
d.Set("dedicated_host_id", instance.DedicatedHostAttribute.DedicatedHostId)
d.Set("create_time", instance.CreationTime)
d.Set("start_time", instance.StartTime)
d.Set("expired_time", instance.ExpiredTime)
imageOptionsMaps := make([]map[string]interface{}, 0)
imageOptionsMap := make(map[string]interface{})
imageOptionsMap["login_as_non_root"] = instance.ImageOptions.LoginAsNonRoot
imageOptionsMaps = append(imageOptionsMaps, imageOptionsMap)
d.Set("image_options", imageOptionsMaps)
if len(instance.VpcAttributes.PrivateIpAddress.IpAddress) > 0 {
d.Set("private_ip", instance.VpcAttributes.PrivateIpAddress.IpAddress[0])
} else {
d.Set("private_ip", strings.Join(instance.InnerIpAddress.IpAddress, ","))
}
sgs := make([]string, 0, len(instance.SecurityGroupIds.SecurityGroupId))
for _, sg := range instance.SecurityGroupIds.SecurityGroupId {
sgs = append(sgs, sg)
}
if err := d.Set("security_groups", sgs); err != nil {
return WrapError(err)
}
dataRequest := ecs.CreateDescribeUserDataRequest()
dataRequest.RegionId = client.RegionId
dataRequest.InstanceId = d.Id()
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.DescribeUserData(dataRequest)
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), dataRequest.GetActionName(), AlibabaCloudSdkGoERROR)
}
addDebug(dataRequest.GetActionName(), raw, dataRequest.RpcRequest, dataRequest)
userDataResponse, _ := raw.(*ecs.DescribeUserDataResponse)
if userDataResponse.UserData != "" {
if v, ok := d.GetOk("user_data"); ok {
_, base64DecodeError := base64.StdEncoding.DecodeString(v.(string))
if base64DecodeError == nil {
d.Set("user_data", userDataResponse.UserData)
} else {
d.Set("user_data", userDataHashSum(userDataResponse.UserData))
}
} else {
d.Set("user_data", userDataResponse.UserData)
}
} else {
d.Set("user_data", "")
}
if len(instance.VpcAttributes.VSwitchId) > 0 && (!d.IsNewResource() || d.HasChange("role_name")) {
request := ecs.CreateDescribeInstanceRamRoleRequest()
request.RegionId = client.RegionId
request.InstanceIds = convertListToJsonString([]interface{}{d.Id()})
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.DescribeInstanceRamRole(request)
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
response, _ := raw.(*ecs.DescribeInstanceRamRoleResponse)
if len(response.InstanceRamRoleSets.InstanceRamRoleSet) >= 1 {
d.Set("role_name", response.InstanceRamRoleSets.InstanceRamRoleSet[0].RamRoleName)
}
}
if instance.InstanceChargeType == string(PrePaid) {
request := ecs.CreateDescribeInstanceAutoRenewAttributeRequest()
request.RegionId = client.RegionId
request.InstanceId = d.Id()
var raw interface{}
var err error
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
raw, err = client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.DescribeInstanceAutoRenewAttribute(request)
})
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
response, _ := raw.(*ecs.DescribeInstanceAutoRenewAttributeResponse)
periodUnit := d.Get("period_unit").(string)
if periodUnit == "" {
periodUnit = "Month"
}
if len(response.InstanceRenewAttributes.InstanceRenewAttribute) > 0 {
renew := response.InstanceRenewAttributes.InstanceRenewAttribute[0]
d.Set("renewal_status", renew.RenewalStatus)
d.Set("auto_renew_period", renew.Duration)
if renew.RenewalStatus == "AutoRenewal" {
periodUnit = renew.PeriodUnit
}
if periodUnit == "Year" {
periodUnit = "Month"
d.Set("auto_renew_period", renew.Duration*12)
}
}
//period, err := computePeriodByUnit(instance.CreationTime, instance.ExpiredTime, d.Get("period").(int), periodUnit)
//if err != nil {
// return WrapError(err)
//}
//thisPeriod := d.Get("period").(int)
//if thisPeriod != 0 && thisPeriod != period {
// d.Set("period", thisPeriod)
//} else {
// d.Set("period", period)
//}
d.Set("period_unit", periodUnit)
}
networkInterfaceId := ""
networkInterfaceMaps := make([]map[string]interface{}, 0)
for _, obj := range instance.NetworkInterfaces.NetworkInterface {
if obj.Type == "Primary" {
networkInterfaceId = obj.NetworkInterfaceId
object, err := ecsService.DescribeEcsNetworkInterface(obj.NetworkInterfaceId)
if err != nil {
return WrapError(err)
}
d.Set("primary_ip_address", obj.PrimaryIpAddress)
d.Set("network_interface_traffic_mode", object["NetworkInterfaceTrafficMode"])
d.Set("queue_pair_number", object["QueuePairNumber"])
if attachment, ok := object["Attachment"]; ok {
attachmentArg := attachment.(map[string]interface{})
if networkCardIndex, ok := attachmentArg["NetworkCardIndex"]; ok {
d.Set("network_card_index", networkCardIndex)
}
}
} else {
networkInterfaceMap := make(map[string]interface{})
networkInterfaceMap["network_interface_id"] = obj.NetworkInterfaceId
object, err := ecsService.DescribeEcsNetworkInterface(obj.NetworkInterfaceId)
if err != nil {
return WrapError(err)
}
networkInterfaceMap["vswitch_id"] = object["VSwitchId"]
networkInterfaceMap["network_interface_traffic_mode"] = object["NetworkInterfaceTrafficMode"]
networkInterfaceMap["queue_pair_number"] = object["QueuePairNumber"]
networkInterfaceMap["security_group_ids"] = object["SecurityGroupIds"].(map[string]interface{})["SecurityGroupId"]
if attachment, ok := object["Attachment"]; ok {
attachmentArg := attachment.(map[string]interface{})
if networkCardIndex, ok := attachmentArg["NetworkCardIndex"]; ok {
networkInterfaceMap["network_card_index"] = networkCardIndex
}
}
networkInterfaceMaps = append(networkInterfaceMaps, networkInterfaceMap)
}
}
d.Set("network_interfaces", networkInterfaceMaps)
if len(networkInterfaceId) != 0 {
object, err := ecsService.DescribeEcsNetworkInterface(networkInterfaceId)
if err != nil {
return WrapError(err)
}
secondaryPrivateIpsSli := make([]interface{}, 0, len(object["PrivateIpSets"].(map[string]interface{})["PrivateIpSet"].([]interface{})))
for _, v := range object["PrivateIpSets"].(map[string]interface{})["PrivateIpSet"].([]interface{}) {
if !v.(map[string]interface{})["Primary"].(bool) {
secondaryPrivateIpsSli = append(secondaryPrivateIpsSli, v.(map[string]interface{})["PrivateIpAddress"])
}
}
ipv6SetList := make([]interface{}, 0)
for _, v := range object["Ipv6Sets"].(map[string]interface{})["Ipv6Set"].([]interface{}) {
ipv6Set := v.(map[string]interface{})
ipv6SetList = append(ipv6SetList, ipv6Set["Ipv6Address"])
}
d.Set("network_interface_id", networkInterfaceId)
d.Set("ipv6_addresses", ipv6SetList)
d.Set("ipv6_address_count", len(ipv6SetList))
d.Set("secondary_private_ips", secondaryPrivateIpsSli)
d.Set("secondary_private_ip_address_count", len(secondaryPrivateIpsSli))
}
maintenanceAttribute, err := ecsService.DescribeInstanceMaintenanceAttribute(d.Id())
if err != nil {
return WrapError(err)
}
if v, ok := maintenanceAttribute["MaintenanceWindows"]; ok {
maintenanceWindowsMaps := make([]map[string]interface{}, 0)
maintenanceWindowsList := v.(map[string]interface{})["MaintenanceWindow"].([]interface{})
maintenanceWindowsMap := make(map[string]interface{})
for _, maintenanceWindowsItem := range maintenanceWindowsList {
if maintenanceWindowsItemArg, ok := maintenanceWindowsItem.(map[string]interface{}); ok {
maintenanceWindowsMap["start_time"] = maintenanceWindowsItemArg["StartTime"]
maintenanceWindowsMap["end_time"] = maintenanceWindowsItemArg["EndTime"]
maintenanceWindowsMaps = append(maintenanceWindowsMaps, maintenanceWindowsMap)
}
}
d.Set("maintenance_time", maintenanceWindowsMaps)
}
if v, ok := maintenanceAttribute["ActionOnMaintenance"]; ok {
d.Set("maintenance_action", v.(map[string]interface{})["Value"])
}
d.Set("maintenance_notify", maintenanceAttribute["NotifyOnMaintenance"])
instanceAttribute, err := ecsService.DescribeInstanceAttribute(d.Id())
if err != nil {
return WrapError(err)
}
d.Set("enable_jumbo_frame", instanceAttribute.EnableJumboFrame)
return nil
}
func resourceAliCloudInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
var err error
d.Partial(true)
if !d.IsNewResource() {
if err := setTags(client, TagResourceInstance, d); err != nil {
return WrapError(err)
} else {
d.SetPartial("tags")
}
}
if !d.IsNewResource() && d.HasChange("resource_group_id") {
action := "JoinResourceGroup"
request := map[string]interface{}{
"ResourceType": "instance",
"ResourceId": d.Id(),
"RegionId": client.RegionId,
"ResourceGroupId": d.Get("resource_group_id"),
}
response, err := client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
addDebug(action, response, request)
d.SetPartial("resource_group_id")
}
if err := setVolumeTags(client, TagResourceDisk, d); err != nil {
return WrapError(err)
} else {
d.SetPartial("volume_tags")
}
if !d.IsNewResource() && !d.HasChange("vpc_id") && d.HasChange("security_groups") {
if !d.IsNewResource() || d.Get("vswitch_id").(string) == "" {
o, n := d.GetChange("security_groups")
os := o.(*schema.Set)
ns := n.(*schema.Set)
rl := expandStringList(os.Difference(ns).List())
al := expandStringList(ns.Difference(os).List())
if len(rl) > 0 {
err := ecsService.LeaveSecurityGroups(d.Id(), rl)
if err != nil {
return WrapError(err)
}
}
if len(al) > 0 {
err := ecsService.JoinSecurityGroups(d.Id(), al)
if err != nil {
return WrapError(err)
}
}
d.SetPartial("security_groups")
}
}
if !d.IsNewResource() && (d.HasChange("system_disk_size") || d.HasChange("system_disk_auto_snapshot_policy_id") || d.HasChange("system_disk_name") || d.HasChange("system_disk_description") || d.HasChange("system_disk_performance_level")) {
disk, err := ecsService.DescribeEcsSystemDisk(d.Id())
if err != nil {
return WrapError(err)
}
if d.HasChange("system_disk_performance_level") {
action := "ModifyDiskSpec"
var response map[string]interface{}
request := map[string]interface{}{
"RegionId": client.RegionId,
}
request["DiskId"] = disk["DiskId"].(string)
if d.HasChange("system_disk_performance_level") {
request["PerformanceLevel"] = d.Get("system_disk_performance_level")
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if IsExpectedErrors(err, []string{"ServiceUnavailable", "Throttling.ConcurrentLimitExceeded"}) || NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
stateConf := BuildStateConf([]string{}, []string{"Available", "In_use"}, d.Timeout(schema.TimeoutUpdate), 10*time.Second, ecsService.EcsDiskStateRefreshFunc(disk["DiskId"].(string), []string{}))
if _, err := stateConf.WaitForState(); err != nil {
return WrapErrorf(err, IdMsg, d.Id())
}
}
if d.HasChange("system_disk_size") {
instance, errDesc := ecsService.DescribeInstance(d.Id())
if errDesc != nil {
return WrapError(errDesc)
}
request := ecs.CreateResizeDiskRequest()
request.NewSize = requests.NewInteger(d.Get("system_disk_size").(int))
if instance.Status == string(Stopped) {
request.Type = "offline"
} else {
request.Type = "online"
}
request.DiskId = disk["DiskId"].(string)
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ResizeDisk(request)
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
d.SetPartial("system_disk_size")
}
if d.HasChange("system_disk_auto_snapshot_policy_id") {
action := "ApplyAutoSnapshotPolicy"
var response map[string]interface{}
request := map[string]interface{}{
"RegionId": client.RegionId,
}
request["autoSnapshotPolicyId"] = d.Get("system_disk_auto_snapshot_policy_id")
request["diskIds"] = convertListToJsonString([]interface{}{disk["DiskId"]})
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
d.SetPartial("system_disk_auto_snapshot_policy_id")
}
if d.HasChange("system_disk_name") || d.HasChange("system_disk_description") {
var response map[string]interface{}
modifyDiskAttributeReq := map[string]interface{}{
"DiskId": disk["DiskId"],
}
modifyDiskAttributeReq["DiskName"] = d.Get("system_disk_name")
modifyDiskAttributeReq["Description"] = d.Get("system_disk_description")
action := "ModifyDiskAttribute"
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, modifyDiskAttributeReq, false)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(action, response, modifyDiskAttributeReq)
return nil
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
d.SetPartial("system_disk_name")
d.SetPartial("system_disk_description")
}
}
run := false
imageUpdate, err := modifyInstanceImage(d, meta, run)
if err != nil {
return WrapError(err)
}
vpcUpdate, err := modifyVpcAttribute(d, meta, run)
if err != nil {
return WrapError(err)
}
passwordUpdate, err := modifyInstanceAttribute(d, meta)
if err != nil {
return WrapError(err)
}
if !d.IsNewResource() && d.HasChange("auto_release_time") {
request := ecs.CreateModifyInstanceAutoReleaseTimeRequest()
request.InstanceId = d.Id()
request.RegionId = client.RegionId
request.AutoReleaseTime = d.Get("auto_release_time").(string)
_, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceAutoReleaseTime(request)
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
d.SetPartial("auto_release_time")
}
typeUpdate, err := modifyInstanceType(d, meta, run)
if err != nil {
return WrapError(err)
}
if err := modifyInstanceChargeType(d, meta, false); err != nil {
return WrapError(err)
}
target, targetExist := d.GetOk("status")
statusUpdate := d.HasChange("status")
if d.IsNewResource() && targetExist && target.(string) == string(Running) {
statusUpdate = false
}
if imageUpdate || vpcUpdate || passwordUpdate || typeUpdate || statusUpdate {
run = true
instance, errDesc := ecsService.DescribeInstance(d.Id())
if errDesc != nil {
return WrapError(errDesc)
}
if statusUpdate && targetExist && target == string(Stopped) || instance.Status == string(Running) {
stopRequest := ecs.CreateStopInstanceRequest()
stopRequest.RegionId = client.RegionId
stopRequest.InstanceId = d.Id()
stopRequest.ForceStop = requests.NewBoolean(false)
if v, ok := d.GetOk("stopped_mode"); ok {
stopRequest.StoppedMode = v.(string)
}
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.StopInstance(stopRequest)
})
if err != nil {
if IsExpectedErrors(err, []string{"IncorrectInstanceStatus"}) {
time.Sleep(time.Second)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(stopRequest.GetActionName(), raw)
return nil
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), stopRequest.GetActionName(), AlibabaCloudSdkGoERROR)
}
stateConf := BuildStateConf([]string{"Pending", "Running", "Stopping"}, []string{"Stopped"}, d.Timeout(schema.TimeoutUpdate), 5*time.Second, ecsService.InstanceStateRefreshFunc(d.Id(), []string{}))
if _, err = stateConf.WaitForState(); err != nil {
return WrapErrorf(err, IdMsg, d.Id())
}
}
if _, err := modifyInstanceImage(d, meta, run); err != nil {
return WrapError(err)
}
if _, err := modifyVpcAttribute(d, meta, run); err != nil {
return WrapError(err)
}
if _, err := modifyInstanceType(d, meta, run); err != nil {
return WrapError(err)
}
if targetExist && target == string(Running) {
startRequest := ecs.CreateStartInstanceRequest()
startRequest.InstanceId = d.Id()
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.StartInstance(startRequest)
})
if err != nil {
if IsExpectedErrors(err, []string{"IncorrectInstanceStatus"}) {
time.Sleep(time.Second)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(startRequest.GetActionName(), raw)
return nil
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), startRequest.GetActionName(), AlibabaCloudSdkGoERROR)
}
// Start instance sometimes costs more than 8 minutes when os type is centos.
stateConf := &resource.StateChangeConf{
Pending: []string{"Pending", "Starting", "Stopped"},
Target: []string{"Running"},
Refresh: ecsService.InstanceStateRefreshFunc(d.Id(), []string{}),
Timeout: d.Timeout(schema.TimeoutUpdate),
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
if _, err = stateConf.WaitForState(); err != nil {
return WrapErrorf(err, IdMsg, d.Id())
}
}
if d.HasChange("status") {
d.SetPartial("status")
}
}
if err := modifyInstanceNetworkSpec(d, meta); err != nil {
return WrapError(err)
}
if d.HasChange("force_delete") {
d.SetPartial("force_delete")
}
// Only PrePaid instance can support modifying renewal attribute
if !d.IsNewResource() && d.Get("instance_charge_type").(string) == string(PrePaid) &&
(d.HasChange("renewal_status") || d.HasChange("auto_renew_period")) {
status := d.Get("renewal_status").(string)
request := ecs.CreateModifyInstanceAutoRenewAttributeRequest()
request.InstanceId = d.Id()
request.RenewalStatus = status
if status == string(RenewAutoRenewal) {
request.PeriodUnit = d.Get("period_unit").(string)
request.Duration = requests.NewInteger(d.Get("auto_renew_period").(int))
}
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceAutoRenewAttribute(request)
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
d.SetPartial("renewal_status")
d.SetPartial("auto_renew_period")
}
if d.HasChange("secondary_private_ips") {
var response map[string]interface{}
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
return WrapError(err)
}
networkInterfaceId := ""
for _, obj := range instance.NetworkInterfaces.NetworkInterface {
if obj.Type == "Primary" {
networkInterfaceId = obj.NetworkInterfaceId
break
}
}
oraw, nraw := d.GetChange("secondary_private_ips")
remove := oraw.(*schema.Set).Difference(nraw.(*schema.Set)).List()
create := nraw.(*schema.Set).Difference(oraw.(*schema.Set)).List()
if len(remove) > 0 {
action := "UnassignPrivateIpAddresses"
request := map[string]interface{}{
"RegionId": client.RegionId,
"NetworkInterfaceId": networkInterfaceId,
"ClientToken": buildClientToken(action),
}
for index, val := range remove {
request[fmt.Sprintf("PrivateIpAddress.%d", index+1)] = val
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) || IsExpectedErrors(err, []string{"OperationConflict", "Operation.Conflict"}) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
addDebug(action, response, request)
d.SetPartial("secondary_private_ips")
}
if len(create) > 0 {
action := "AssignPrivateIpAddresses"
request := map[string]interface{}{
"RegionId": client.RegionId,
"NetworkInterfaceId": networkInterfaceId,
"ClientToken": buildClientToken(action),
}
for index, val := range create {
request[fmt.Sprintf("PrivateIpAddress.%d", index+1)] = val
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) || IsExpectedErrors(err, []string{"OperationConflict", "Operation.Conflict"}) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
d.SetPartial("secondary_private_ips")
}
}
if d.HasChange("secondary_private_ip_address_count") {
var response map[string]interface{}
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
return WrapError(err)
}
// query for the Primary NetworkInterfaceId
networkInterfaceId := ""
for _, obj := range instance.NetworkInterfaces.NetworkInterface {
if obj.Type == "Primary" {
networkInterfaceId = obj.NetworkInterfaceId
break
}
}
privateIpList := expandStringList(d.Get("secondary_private_ips").(*schema.Set).List())
oldIpsCount, newIpsCount := d.GetChange("secondary_private_ip_address_count")
if oldIpsCount != nil && newIpsCount != nil && newIpsCount != len(privateIpList) {
diff := newIpsCount.(int) - oldIpsCount.(int)
if diff > 0 {
action := "AssignPrivateIpAddresses"
request := map[string]interface{}{
"RegionId": client.RegionId,
"NetworkInterfaceId": networkInterfaceId,
"ClientToken": buildClientToken(action),
"SecondaryPrivateIpAddressCount": diff,
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) || IsExpectedErrors(err, []string{"OperationConflict"}) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
d.SetPartial("secondary_private_ip_address_count")
}
if diff < 0 {
diff *= -1
action := "UnassignPrivateIpAddresses"
request := map[string]interface{}{
"RegionId": client.RegionId,
"NetworkInterfaceId": networkInterfaceId,
"ClientToken": buildClientToken(action),
}
for index, val := range privateIpList[:diff] {
request[fmt.Sprintf("PrivateIpAddress.%d", index+1)] = val
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) || IsExpectedErrors(err, []string{"OperationConflict"}) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
addDebug(action, response, request)
d.SetPartial("secondary_private_ip_address_count")
}
}
}
if !d.IsNewResource() && d.HasChange("deployment_set_id") {
action := "ModifyInstanceDeployment"
var response map[string]interface{}
request := map[string]interface{}{
"RegionId": client.RegionId,
"InstanceId": d.Id(),
"ClientToken": buildClientToken(action),
}
if v, ok := d.GetOk("deployment_set_id"); ok {
request["DeploymentSetId"] = v
}
if v := d.Get("deployment_set_id"); len(v.(string)) == 0 {
oldDeploymentSetId, _ := d.GetChange("deployment_set_id")
request["DeploymentSetId"] = oldDeploymentSetId
request["RemoveFromDeploymentSet"] = true
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
d.SetPartial("deployment_set_id")
}
if d.HasChange("maintenance_time") || d.HasChange("maintenance_action") || d.HasChange("maintenance_notify") {
var response map[string]interface{}
action := "ModifyInstanceMaintenanceAttributes"
request := map[string]interface{}{
"RegionId": client.RegionId,
"InstanceId": []string{d.Id()},
}
maintenanceWindowsMaps := make([]map[string]interface{}, 0)
for _, maintenanceWindows := range d.Get("maintenance_time").(*schema.Set).List() {
maintenanceWindowsMap := make(map[string]interface{})
maintenanceWindowsArg := maintenanceWindows.(map[string]interface{})
if v, ok := maintenanceWindowsArg["start_time"].(string); ok && v != "" {
maintenanceWindowsMap["StartTime"] = v
}
if v, ok := maintenanceWindowsArg["end_time"].(string); ok && v != "" {
maintenanceWindowsMap["EndTime"] = v
}
maintenanceWindowsMaps = append(maintenanceWindowsMaps, maintenanceWindowsMap)
}
request["MaintenanceWindow"] = maintenanceWindowsMaps
if v, ok := d.GetOk("maintenance_action"); ok {
request["ActionOnMaintenance"] = v
}
if v, ok := d.GetOkExists("maintenance_notify"); ok {
request["NotifyOnMaintenance"] = v
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
d.SetPartial("maintenance_time")
d.SetPartial("maintenance_action")
d.SetPartial("maintenance_notify")
}
if d.HasChange("http_endpoint") || d.HasChange("http_tokens") {
var response map[string]interface{}
action := "ModifyInstanceMetadataOptions"
request := map[string]interface{}{
"RegionId": client.RegionId,
"InstanceId": d.Id(),
}
if v, ok := d.GetOk("http_endpoint"); ok {
request["HttpEndpoint"] = v
} else {
request["HttpEndpoint"] = "enabled"
}
if v, ok := d.GetOk("http_tokens"); ok {
request["HttpTokens"] = v
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(client.GetRetryTimeout(d.Timeout(schema.TimeoutUpdate)), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
d.SetPartial("http_endpoint")
d.SetPartial("http_tokens")
}
if !d.IsNewResource() && d.HasChange("ipv6_addresses") {
var response map[string]interface{}
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
return WrapError(err)
}
networkInterfaceId := ""
for _, obj := range instance.NetworkInterfaces.NetworkInterface {
if obj.Type == "Primary" {
networkInterfaceId = obj.NetworkInterfaceId
break
}
}
oldIpv6Addresses, newIpv6Addresses := d.GetChange("ipv6_addresses")
removed := oldIpv6Addresses.(*schema.Set).Difference(newIpv6Addresses.(*schema.Set)).List()
added := newIpv6Addresses.(*schema.Set).Difference(oldIpv6Addresses.(*schema.Set)).List()
if len(removed) > 0 {
action := "UnassignIpv6Addresses"
unassignIpv6AddressesReq := map[string]interface{}{
"RegionId": client.RegionId,
"ClientToken": buildClientToken("UnassignIpv6Addresses"),
"NetworkInterfaceId": networkInterfaceId,
"Ipv6Address": removed,
}
wait := incrementalWait(3*time.Second, 5*time.Second)
err = resource.Retry(client.GetRetryTimeout(d.Timeout(schema.TimeoutUpdate)), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, unassignIpv6AddressesReq, true)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, unassignIpv6AddressesReq)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
}
if len(added) > 0 {
action := "AssignIpv6Addresses"
assignIpv6AddressesReq := map[string]interface{}{
"RegionId": client.RegionId,
"ClientToken": buildClientToken("AssignIpv6Addresses"),
"NetworkInterfaceId": networkInterfaceId,
"Ipv6Address": added,
}
wait := incrementalWait(3*time.Second, 5*time.Second)
err = resource.Retry(client.GetRetryTimeout(d.Timeout(schema.TimeoutUpdate)), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, assignIpv6AddressesReq, true)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, assignIpv6AddressesReq)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
}
d.SetPartial("ipv6_addresses")
}
d.Partial(false)
return resourceAliCloudInstanceRead(d, meta)
}
func resourceAliCloudInstanceDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
if d.Get("instance_charge_type").(string) == string(PrePaid) {
force := d.Get("force_delete").(bool)
if !force {
return WrapError(Error("Please convert 'PrePaid' instance to 'PostPaid' or set 'force_delete' as true before deleting 'PrePaid' instance."))
} else if err := modifyInstanceChargeType(d, meta, force); err != nil {
return WrapError(err)
}
}
stopRequest := ecs.CreateStopInstanceRequest()
stopRequest.InstanceId = d.Id()
stopRequest.ForceStop = requests.NewBoolean(true)
deleteRequest := ecs.CreateDeleteInstanceRequest()
deleteRequest.InstanceId = d.Id()
deleteRequest.Force = requests.NewBoolean(true)
wait := incrementalWait(1*time.Second, 1*time.Second)
err := resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.DeleteInstance(deleteRequest)
})
if err != nil {
if IsExpectedErrors(err, []string{"IncorrectInstanceStatus", "DependencyViolation.RouteEntry", "IncorrectInstanceStatus.Initializing"}) {
return resource.RetryableError(err)
}
if IsExpectedErrors(err, []string{Throttling, "LastTokenProcessing"}) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(deleteRequest.GetActionName(), raw)
return nil
})
if err != nil {
if IsExpectedErrors(err, EcsNotFound) {
return nil
}
return WrapErrorf(err, DefaultErrorMsg, d.Id(), deleteRequest.GetActionName(), AlibabaCloudSdkGoERROR)
}
stateConf := BuildStateConf([]string{"Pending", "Running", "Stopped", "Stopping"}, []string{}, d.Timeout(schema.TimeoutDelete), 10*time.Second, ecsService.InstanceStateRefreshFunc(d.Id(), []string{}))
if _, err = stateConf.WaitForState(); err != nil {
return WrapErrorf(err, IdMsg, d.Id())
}
return nil
}
func modifyInstanceChargeType(d *schema.ResourceData, meta interface{}, forceDelete bool) error {
if d.IsNewResource() {
d.Partial(false)
return nil
}
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
chargeType := d.Get("instance_charge_type").(string)
if d.HasChange("instance_charge_type") || forceDelete {
if forceDelete {
chargeType = string(PostPaid)
}
request := ecs.CreateModifyInstanceChargeTypeRequest()
request.InstanceIds = convertListToJsonString(append(make([]interface{}, 0, 1), d.Id()))
request.IncludeDataDisks = requests.NewBoolean(d.Get("include_data_disks").(bool))
request.AutoPay = requests.NewBoolean(true)
request.DryRun = requests.NewBoolean(d.Get("dry_run").(bool))
request.ClientToken = fmt.Sprintf("terraform-modify-instance-charge-type-%s", d.Id())
if chargeType == string(PrePaid) {
if v, ok := d.GetOk("period"); ok {
request.Period = requests.NewInteger(v.(int))
}
request.PeriodUnit = d.Get("period_unit").(string)
}
request.InstanceChargeType = chargeType
if err := resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceChargeType(request)
})
if err != nil {
if NeedRetry(err) || IsExpectedErrors(err, []string{"InternalError"}) {
time.Sleep(3 * time.Second)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
}); err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
// Wait for instance charge type has been changed
if err := resource.Retry(5*time.Minute, func() *resource.RetryError {
if instance, err := ecsService.DescribeInstance(d.Id()); err != nil {
return resource.NonRetryableError(err)
} else if instance.InstanceChargeType == chargeType {
return nil
}
return resource.RetryableError(Error("Waitting for instance %s to be %s timeout.", d.Id(), chargeType))
}); err != nil {
return WrapError(err)
}
d.SetPartial("instance_charge_type")
return nil
}
return nil
}
func modifyInstanceImage(d *schema.ResourceData, meta interface{}, run bool) (bool, error) {
if d.IsNewResource() {
d.Partial(false)
return false, nil
}
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
update := false
if d.HasChange("image_id") {
update = true
if !run {
return update, nil
}
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
return update, WrapError(err)
}
keyPairName := instance.KeyPairName
request := ecs.CreateReplaceSystemDiskRequest()
request.InstanceId = d.Id()
request.ImageId = d.Get("image_id").(string)
request.SystemDiskSize = requests.NewInteger(d.Get("system_disk_size").(int))
if v, ok := d.GetOk("system_disk_encrypted"); ok {
request.Encrypted = requests.NewBoolean(v.(bool))
}
if v, ok := d.GetOk("system_disk_encrypt_algorithm"); ok {
request.EncryptAlgorithm = v.(string)
}
if v, ok := d.GetOk("security_enhancement_strategy"); ok {
request.SecurityEnhancementStrategy = v.(string)
}
if v, ok := d.GetOk("password"); ok {
request.Password = v.(string)
}
if v, ok := d.GetOkExists("password_inherit"); ok {
request.PasswordInherit = requests.NewBoolean(v.(bool))
}
if v, ok := d.GetOk("system_disk_kms_key_id"); ok {
request.KMSKeyId = v.(string)
}
request.ClientToken = buildClientToken(request.GetActionName())
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ReplaceSystemDisk(request)
})
if err != nil {
return update, WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
response, _ := raw.(*ecs.ReplaceSystemDiskResponse)
d.Set("system_disk_id", response.DiskId)
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
// Ensure instance's image has been replaced successfully.
timeout := DefaultTimeoutMedium
for {
instance, errDesc := ecsService.DescribeInstance(d.Id())
if errDesc != nil {
return update, WrapError(errDesc)
}
disk, err := ecsService.DescribeInstanceSystemDisk(d.Id(), "", d.Get("system_disk_id").(string))
if err != nil {
return update, WrapError(err)
}
if instance.ImageId == d.Get("image_id") && disk.Size == d.Get("system_disk_size").(int) {
break
}
time.Sleep(DefaultIntervalShort * time.Second)
timeout = timeout - DefaultIntervalShort
if timeout <= 0 {
return update, WrapError(GetTimeErrorFromString(fmt.Sprintf("Replacing instance %s system disk timeout.", d.Id())))
}
}
// After updating image, it need to re-attach key pair
if keyPairName != "" {
if err := ecsService.AttachKeyPair(keyPairName, []interface{}{d.Id()}); err != nil {
return update, WrapError(err)
}
}
}
return update, nil
}
func modifyInstanceAttribute(d *schema.ResourceData, meta interface{}) (bool, error) {
if d.IsNewResource() {
d.Partial(false)
return false, nil
}
update := false
reboot := false
client := meta.(*connectivity.AliyunClient)
var response map[string]interface{}
action := "ModifyInstanceAttribute"
request := make(map[string]interface{})
var err error
request["RegionId"] = client.RegionId
request["ClientToken"] = buildClientToken(action)
request["InstanceId"] = d.Id()
if d.HasChange("instance_name") {
d.SetPartial("instance_name")
request["InstanceName"] = d.Get("instance_name").(string)
update = true
}
if d.HasChange("description") {
d.SetPartial("description")
request["Description"] = d.Get("description").(string)
update = true
}
if d.HasChange("user_data") {
d.SetPartial("user_data")
v := d.Get("user_data")
_, base64DecodeError := base64.StdEncoding.DecodeString(v.(string))
if base64DecodeError == nil {
request["UserData"] = v.(string)
} else {
request["UserData"] = base64.StdEncoding.EncodeToString([]byte(v.(string)))
}
update = true
reboot = true
}
if d.HasChange("host_name") {
d.SetPartial("host_name")
request["HostName"] = d.Get("host_name").(string)
update = true
reboot = true
}
if d.HasChange("password") || d.HasChange("kms_encrypted_password") {
if v := d.Get("password").(string); v != "" {
d.SetPartial("password")
request["Password"] = v
update = true
reboot = true
}
if v := d.Get("kms_encrypted_password").(string); v != "" {
kmsService := KmsService{meta.(*connectivity.AliyunClient)}
decryptResp, err := kmsService.Decrypt(v, d.Get("kms_encryption_context").(map[string]interface{}))
if err != nil {
return reboot, WrapError(err)
}
request["Password"] = decryptResp
d.SetPartial("kms_encrypted_password")
d.SetPartial("kms_encryption_context")
update = true
reboot = true
}
}
if d.HasChange("deletion_protection") {
d.SetPartial("deletion_protection")
request["DeletionProtection"] = requests.NewBoolean(d.Get("deletion_protection").(bool))
update = true
}
if d.HasChange("credit_specification") {
d.SetPartial("credit_specification")
request["CreditSpecification"] = d.Get("credit_specification").(string)
update = true
}
if d.HasChange("enable_jumbo_frame") {
d.SetPartial("enable_jumbo_frame")
if v, ok := d.GetOkExists("enable_jumbo_frame"); ok {
request["EnableJumboFrame"] = v
}
update = true
}
if update {
wait := incrementalWait(1*time.Minute, 1*time.Minute)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, false)
if err != nil {
if NeedRetry(err) || IsExpectedErrors(err, []string{"InvalidChargeType.ValueNotSupported"}) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return reboot, WrapErrorf(err, DefaultErrorMsg, "alicloud_instance", action, AlibabaCloudSdkGoERROR)
}
}
return reboot, nil
}
func modifyVpcAttribute(d *schema.ResourceData, meta interface{}, run bool) (bool, error) {
if d.IsNewResource() {
d.Partial(false)
return false, nil
}
update := false
request := ecs.CreateModifyInstanceVpcAttributeRequest()
request.InstanceId = d.Id()
request.VSwitchId = d.Get("vswitch_id").(string)
if d.HasChange("vswitch_id") {
update = true
if d.Get("vswitch_id").(string) == "" {
return update, WrapError(Error("Field 'vswitch_id' is required when modifying the instance VPC attribute."))
}
}
if d.HasChange("subnet_id") {
update = true
if d.Get("subnet_id").(string) == "" {
return update, WrapError(Error("Field 'subnet_id' is required when modifying the instance VPC attribute."))
}
request.VSwitchId = d.Get("subnet_id").(string)
}
if request.VSwitchId != "" && d.HasChange("private_ip") {
request.PrivateIpAddress = d.Get("private_ip").(string)
update = true
}
if d.HasChange("vpc_id") {
update = true
if v, ok := d.GetOk("vpc_id"); ok {
request.VpcId = v.(string)
}
if v, ok := d.GetOk("security_groups"); ok {
securityGroupIds := expandStringList(v.(*schema.Set).List())
if len(securityGroupIds) > 0 {
request.SecurityGroupId = &securityGroupIds
}
}
}
if !run {
return update, nil
}
if update {
client := meta.(*connectivity.AliyunClient)
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceVpcAttribute(request)
})
if err != nil {
if IsExpectedErrors(err, []string{"OperationConflict"}) {
time.Sleep(1 * time.Second)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
})
if err != nil {
return update, WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
ecsService := EcsService{client}
if err := ecsService.WaitForVpcAttributesChanged(d.Id(), request.VSwitchId, request.PrivateIpAddress); err != nil {
return update, WrapError(err)
}
d.SetPartial("vswitch_id")
d.SetPartial("subnet_id")
d.SetPartial("private_ip")
d.SetPartial("vpc_id")
d.SetPartial("security_groups")
}
return update, nil
}
func modifyInstanceType(d *schema.ResourceData, meta interface{}, run bool) (bool, error) {
if d.IsNewResource() {
d.Partial(false)
return false, nil
}
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
update := false
if d.HasChange("instance_type") {
update = true
if !run {
return update, nil
}
// Ensure instance_type is valid
//zoneId, validZones, err := ecsService.DescribeAvailableResources(d, meta, InstanceTypeResource)
//if err != nil {
// return update, WrapError(err)
//}
//if err = ecsService.InstanceTypeValidation(d.Get("instance_type").(string), zoneId, validZones); err != nil {
// return update, WrapError(err)
//}
// There should use the old instance charge type to decide API method because of instance_charge_type will be updated at last step
oldCharge, _ := d.GetChange("instance_charge_type")
if oldCharge.(string) == string(PrePaid) {
request := ecs.CreateModifyPrepayInstanceSpecRequest()
request.InstanceId = d.Id()
request.InstanceType = d.Get("instance_type").(string)
if v, ok := d.GetOk("operator_type"); ok {
request.OperatorType = v.(string)
}
err := resource.Retry(6*time.Minute, func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyPrepayInstanceSpec(request)
})
if err != nil {
if IsExpectedErrors(err, []string{Throttling}) {
time.Sleep(5 * time.Second)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
})
if err != nil {
return update, WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
} else {
//An instance that was successfully modified once cannot be modified again within 5 minutes.
request := ecs.CreateModifyInstanceSpecRequest()
request.InstanceId = d.Id()
request.InstanceType = d.Get("instance_type").(string)
request.ClientToken = buildClientToken(request.GetActionName())
err := resource.Retry(6*time.Minute, func() *resource.RetryError {
args := *request
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceSpec(&args)
})
if err != nil {
if IsExpectedErrors(err, []string{Throttling}) {
time.Sleep(10 * time.Second)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
})
if err != nil {
return update, WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
}
// Ensure instance's type has been replaced successfully.
timeout := DefaultTimeoutMedium
for {
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
return update, WrapError(err)
}
if instance.InstanceType == d.Get("instance_type").(string) {
break
}
timeout = timeout - DefaultIntervalShort
if timeout <= 0 {
return update, WrapErrorf(err, WaitTimeoutMsg, d.Id(), GetFunc(1), timeout, instance.InstanceType, d.Get("instance_type"), ProviderERROR)
}
time.Sleep(DefaultIntervalShort * time.Second)
}
d.SetPartial("instance_type")
}
return update, nil
}
func modifyInstanceNetworkSpec(d *schema.ResourceData, meta interface{}) error {
if d.IsNewResource() {
d.Partial(false)
return nil
}
allocate := false
update := false
request := ecs.CreateModifyInstanceNetworkSpecRequest()
request.InstanceId = d.Id()
request.ClientToken = buildClientToken(request.GetActionName())
if d.HasChange("internet_charge_type") {
request.NetworkChargeType = d.Get("internet_charge_type").(string)
update = true
d.SetPartial("internet_charge_type")
}
if d.HasChange("internet_max_bandwidth_out") {
o, n := d.GetChange("internet_max_bandwidth_out")
if o.(int) <= 0 && n.(int) > 0 {
allocate = true
}
request.InternetMaxBandwidthOut = requests.NewInteger(n.(int))
update = true
d.SetPartial("internet_max_bandwidth_out")
}
if d.HasChange("internet_max_bandwidth_in") {
request.InternetMaxBandwidthIn = requests.NewInteger(d.Get("internet_max_bandwidth_in").(int))
update = true
d.SetPartial("internet_max_bandwidth_in")
}
//An instance that was successfully modified once cannot be modified again within 5 minutes.
wait := incrementalWait(2*time.Second, 2*time.Second)
client := meta.(*connectivity.AliyunClient)
if update {
if err := resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceNetworkSpec(request)
})
if err != nil {
if IsExpectedErrors(err, []string{Throttling, "LastOrderProcessing", "LastRequestProcessing", "LastTokenProcessing"}) {
wait()
return resource.RetryableError(err)
}
if IsExpectedErrors(err, []string{"InternalError"}) {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
}); err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
ecsService := EcsService{client: client}
deadline := time.Now().Add(DefaultTimeout * time.Second)
for {
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
return WrapError(err)
}
if instance.InternetMaxBandwidthOut == d.Get("internet_max_bandwidth_out").(int) &&
instance.InternetChargeType == d.Get("internet_charge_type").(string) {
break
}
if time.Now().After(deadline) {
return WrapError(Error(`wait for internet update timeout! expect internet_charge_type value %s, get %s
expect internet_max_bandwidth_out value %d, get %d,`,
d.Get("internet_charge_type").(string), instance.InternetChargeType, d.Get("internet_max_bandwidth_out").(int),
instance.InternetMaxBandwidthOut))
}
time.Sleep(1 * time.Second)
}
if allocate {
request := ecs.CreateAllocatePublicIpAddressRequest()
request.InstanceId = d.Id()
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.AllocatePublicIpAddress(request)
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
}
}
return nil
}