Skip to content

fix(cvm): [124249242] tencentcloud_instance support modify placement_group_id #3383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/3383.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/tencentcloud_instance: support modify `placement_group_id`
```
141 changes: 138 additions & 3 deletions tencentcloud/services/cvm/resource_tc_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,14 @@ func ResourceTencentCloudInstance() *schema.Resource {
"placement_group_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: "The ID of a placement group.",
},
"force_replace_placement_group_id": {
Type: schema.TypeBool,
Optional: true,
RequiredWith: []string{"placement_group_id"},
Description: "Whether to force the instance host to be replaced. Value range: true: Allows the instance to change the host and restart the instance. Local disk machines do not support specifying this parameter; false: Does not allow the instance to change the host and only join the placement group on the current host. This may cause the placement group to fail to change. Only useful for change `placement_group_id`, Default is false.",
},
// payment
"instance_charge_type": {
Type: schema.TypeString,
Expand Down Expand Up @@ -576,8 +581,18 @@ func resourceTencentCloudInstanceCreate(d *schema.ResourceData, meta interface{}
}
}

if v, ok := d.GetOk("placement_group_id"); ok {
request.DisasterRecoverGroupIds = []*string{helper.String(v.(string))}
var (
rpgFlag bool
)

if v, ok := d.GetOkExists("force_replace_placement_group_id"); ok {
rpgFlag = v.(bool)
}

if !rpgFlag {
if v, ok := d.GetOk("placement_group_id"); ok {
request.DisasterRecoverGroupIds = []*string{helper.String(v.(string))}
}
}

// network
Expand Down Expand Up @@ -835,6 +850,57 @@ func resourceTencentCloudInstanceCreate(d *schema.ResourceData, meta interface{}
return err
}

// set placement group id
if rpgFlag {
if v, ok := d.GetOk("placement_group_id"); ok && v != "" {
request := cvm.NewModifyInstancesDisasterRecoverGroupRequest()
request.InstanceIds = helper.Strings([]string{instanceId})
request.DisasterRecoverGroupId = helper.String(v.(string))
request.Force = helper.Bool(rpgFlag)
err = resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError {
result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseCvmClient().ModifyInstancesDisasterRecoverGroup(request)
if e != nil {
return tccommon.RetryError(e)
} else {
log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString())
}

return nil
})

if err != nil {
return err
}

// wait
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
instance, errRet := cvmService.DescribeInstanceById(ctx, instanceId)
if errRet != nil {
return tccommon.RetryError(errRet, tccommon.InternalError)
}

if instance != nil && *instance.InstanceState == CVM_STATUS_LAUNCH_FAILED {
//LatestOperationCodeMode
if instance.LatestOperationErrorMsg != nil {
return resource.NonRetryableError(fmt.Errorf("cvm instance %s launch failed. Error msg: %s.\n", *instance.InstanceId, *instance.LatestOperationErrorMsg))
}

return resource.NonRetryableError(fmt.Errorf("cvm instance %s launch failed, this resource will not be stored to tfstate and will auto removed\n.", *instance.InstanceId))
}

if instance != nil && *instance.InstanceState == CVM_STATUS_RUNNING {
return nil
}

return resource.RetryableError(fmt.Errorf("cvm instance status is %s, retry...", *instance.InstanceState))
})

if err != nil {
return err
}
}
}

// Wait for the tags attached to the vm since tags attachment it's async while vm creation.
if tags := helper.GetTags(d, "tags"); len(tags) > 0 {
tcClient := meta.(tccommon.ProviderMeta).GetAPIV3Conn()
Expand Down Expand Up @@ -988,6 +1054,10 @@ func resourceTencentCloudInstanceRead(d *schema.ResourceData, meta interface{})
_ = d.Set("uuid", instance.Uuid)
}

if instance.DisasterRecoverGroupId != nil {
_ = d.Set("placement_group_id", instance.DisasterRecoverGroupId)
}

if *instance.InstanceChargeType == CVM_CHARGE_TYPE_CDHPAID {
_ = d.Set("cdh_instance_type", instance.InstanceType)
}
Expand Down Expand Up @@ -2027,6 +2097,7 @@ func resourceTencentCloudInstanceUpdate(d *schema.ResourceData, meta interface{}
return err
}
}

if d.HasChange("user_data_raw") {
userDataRaw := d.Get("user_data_raw").(string)
userData := base64.StdEncoding.EncodeToString([]byte(userDataRaw))
Expand All @@ -2040,6 +2111,70 @@ func resourceTencentCloudInstanceUpdate(d *schema.ResourceData, meta interface{}
return err
}
}

if d.HasChange("placement_group_id") || d.HasChange("force_replace_placement_group_id") {
oldPGI, newPGI := d.GetChange("placement_group_id")
oldPGIStr := oldPGI.(string)
newPGIStr := newPGI.(string)
if newPGIStr == "" {
// wait cvm support delete DisasterRecoverGroupId
return fmt.Errorf("Deleting `placement_group_id` is not currently supported.")
} else {
if oldPGIStr == newPGIStr {
return fmt.Errorf("It is not possible to change only `force_replace_placement_group_id`, it needs to be modified together with `placement_group_id`.")
}

request := cvm.NewModifyInstancesDisasterRecoverGroupRequest()
if v, ok := d.GetOkExists("force_replace_placement_group_id"); ok {
request.Force = helper.Bool(v.(bool))
}

request.InstanceIds = helper.Strings([]string{instanceId})
request.DisasterRecoverGroupId = helper.String(newPGIStr)
err = resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError {
result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseCvmClient().ModifyInstancesDisasterRecoverGroup(request)
if e != nil {
return tccommon.RetryError(e)
} else {
log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString())
}

return nil
})

if err != nil {
return err
}

// wait
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
instance, errRet := cvmService.DescribeInstanceById(ctx, instanceId)
if errRet != nil {
return tccommon.RetryError(errRet, tccommon.InternalError)
}

if instance != nil && *instance.InstanceState == CVM_STATUS_LAUNCH_FAILED {
//LatestOperationCodeMode
if instance.LatestOperationErrorMsg != nil {
return resource.NonRetryableError(fmt.Errorf("cvm instance %s launch failed. Error msg: %s.\n", *instance.InstanceId, *instance.LatestOperationErrorMsg))
}

return resource.NonRetryableError(fmt.Errorf("cvm instance %s launch failed, this resource will not be stored to tfstate and will auto removed\n.", *instance.InstanceId))
}

if instance != nil && *instance.InstanceState == CVM_STATUS_RUNNING {
return nil
}

return resource.RetryableError(fmt.Errorf("cvm instance status is %s, retry...", *instance.InstanceState))
})

if err != nil {
return err
}
}
}

d.Partial(false)

return resourceTencentCloudInstanceRead(d, meta)
Expand Down
34 changes: 33 additions & 1 deletion tencentcloud/services/cvm/resource_tc_instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ Provides a CVM instance resource.

~> **NOTE:** You can launch an CVM instance for a VPC network via specifying parameter `vpc_id`. One instance can only belong to one VPC.

~> **NOTE:** At present, 'PREPAID' instance cannot be deleted directly and must wait it to be outdated and released automatically.
~> **NOTE:** At present, `PREPAID` instance cannot be deleted directly and must wait it to be outdated and released automatically.

~> **NOTE:** Currently, the `placement_group_id` field only supports setting and modification, but not deletion.

Example Usage

Expand Down Expand Up @@ -200,6 +202,36 @@ resource "tencentcloud_instance" "example" {
}
```

Create CVM instance with placement_group_id

```hcl
resource "tencentcloud_instance" "example" {
instance_name = "tf-example"
availability_zone = "ap-guangzhou-6"
image_id = "img-eb30mz89"
instance_type = "S5.MEDIUM4"
system_disk_size = 50
system_disk_name = "sys_disk_1"
hostname = "user"
project_id = 0
vpc_id = "vpc-i5yyodl9"
subnet_id = "subnet-hhi88a58"
placement_group_id = "ps-ejt4brtz"
force_replace_placement_group_id = false

data_disks {
data_disk_type = "CLOUD_HSSD"
data_disk_size = 100
encrypt = false
data_disk_name = "data_disk_1"
}

tags = {
tagKey = "tagValue"
}
}
```

Import

CVM instance can be imported using the id, e.g.
Expand Down
37 changes: 35 additions & 2 deletions website/docs/r/instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ Provides a CVM instance resource.

~> **NOTE:** You can launch an CVM instance for a VPC network via specifying parameter `vpc_id`. One instance can only belong to one VPC.

~> **NOTE:** At present, 'PREPAID' instance cannot be deleted directly and must wait it to be outdated and released automatically.
~> **NOTE:** At present, `PREPAID` instance cannot be deleted directly and must wait it to be outdated and released automatically.

~> **NOTE:** Currently, the `placement_group_id` field only supports setting and modification, but not deletion.

## Example Usage

Expand Down Expand Up @@ -211,6 +213,36 @@ resource "tencentcloud_instance" "example" {
}
```

### Create CVM instance with placement_group_id

```hcl
resource "tencentcloud_instance" "example" {
instance_name = "tf-example"
availability_zone = "ap-guangzhou-6"
image_id = "img-eb30mz89"
instance_type = "S5.MEDIUM4"
system_disk_size = 50
system_disk_name = "sys_disk_1"
hostname = "user"
project_id = 0
vpc_id = "vpc-i5yyodl9"
subnet_id = "subnet-hhi88a58"
placement_group_id = "ps-ejt4brtz"
force_replace_placement_group_id = false

data_disks {
data_disk_type = "CLOUD_HSSD"
data_disk_size = 100
encrypt = false
data_disk_name = "data_disk_1"
}

tags = {
tagKey = "tagValue"
}
}
```

## Argument Reference

The following arguments are supported:
Expand All @@ -229,6 +261,7 @@ The following arguments are supported:
* `disable_monitor_service` - (Optional, Bool) Disable enhance service for monitor, it is enabled by default. When this options is set, monitor agent won't be installed. Modifications may lead to the reinstallation of the instance's operating system.
* `disable_security_service` - (Optional, Bool) Disable enhance service for security, it is enabled by default. When this options is set, security agent won't be installed. Modifications may lead to the reinstallation of the instance's operating system.
* `force_delete` - (Optional, Bool) Indicate whether to force delete the instance. Default is `false`. If set true, the instance will be permanently deleted instead of being moved into the recycle bin. Note: only works for `PREPAID` instance.
* `force_replace_placement_group_id` - (Optional, Bool) Whether to force the instance host to be replaced. Value range: true: Allows the instance to change the host and restart the instance. Local disk machines do not support specifying this parameter; false: Does not allow the instance to change the host and only join the placement group on the current host. This may cause the placement group to fail to change. Only useful for change `placement_group_id`, Default is false.
* `hostname` - (Optional, String) The hostname of the instance. Windows instance: The name should be a combination of 2 to 15 characters comprised of letters (case insensitive), numbers, and hyphens (-). Period (.) is not supported, and the name cannot be a string of pure numbers. Other types (such as Linux) of instances: The name should be a combination of 2 to 60 characters, supporting multiple periods (.). The piece between two periods is composed of letters (case insensitive), numbers, and hyphens (-). Modifications may lead to the reinstallation of the instance's operating system.
* `hpc_cluster_id` - (Optional, String, ForceNew) High-performance computing cluster ID. If the instance created is a high-performance computing instance, you need to specify the cluster in which the instance is placed, otherwise it cannot be specified.
* `instance_charge_type_prepaid_period` - (Optional, Int) The tenancy (time unit is month) of the prepaid instance, NOTE: it only works when instance_charge_type is set to `PREPAID`. Valid values are `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `24`, `36`, `48`, `60`.
Expand All @@ -244,7 +277,7 @@ The following arguments are supported:
* `key_name` - (Optional, String, **Deprecated**) Please use `key_ids` instead. The key pair to use for the instance, it looks like `skey-16jig7tx`. Modifications may lead to the reinstallation of the instance's operating system.
* `orderly_security_groups` - (Optional, List: [`String`]) A list of orderly security group IDs to associate with.
* `password` - (Optional, String) Password for the instance. In order for the new password to take effect, the instance will be restarted after the password change. Modifications may lead to the reinstallation of the instance's operating system.
* `placement_group_id` - (Optional, String, ForceNew) The ID of a placement group.
* `placement_group_id` - (Optional, String) The ID of a placement group.
* `private_ip` - (Optional, String) The private IP to be assigned to this instance, must be in the provided subnet and available.
* `project_id` - (Optional, Int) The project the instance belongs to, default to 0.
* `running_flag` - (Optional, Bool) Set instance to running or stop. Default value is true, the instance will shutdown when this flag is false.
Expand Down
Loading