diff --git a/docs/resources/aom_uniagent.md b/docs/resources/aom_uniagent.md new file mode 100644 index 0000000000..6baf559362 --- /dev/null +++ b/docs/resources/aom_uniagent.md @@ -0,0 +1,118 @@ +--- +subcategory: "Application Operations Management (AOM)" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_aom_uniagent" +description: |- + Manages an AOM UniAgent resource within HuaweiCloud. +--- + +# huaweicloud_aom_uniagent + +Manages an AOM UniAgent resource within HuaweiCloud. + +-> Destroying resource does not uninstall the UniAgent. + +## Example Usage + +### Install UniAgent for a host + +```hcl +variable "installer_agent_id" {} +variable "inner_ip" {} +variable "account" {} +variable "password" {} + +resource "huaweicloud_aom_uniagent" "test" { + installer_agent_id = var.installer_agent_id + version = "1.1.6" + public_net_flag = false + proxy_region_id = 0 + inner_ip = var.inner_ip + port = 22 + account = var.account + password = var.password + os_type = "LINUX" +} +``` + +### Reinstall UniAgent for a host + +```hcl +variable "installer_agent_id" {} +variable "inner_ip" {} +variable "account" {} +variable "password" {} +variable "agent_id" {} + +resource "huaweicloud_aom_uniagent" "test" { + installer_agent_id = var.installer_agent_id + version = "1.1.6" + public_net_flag = false + proxy_region_id = 0 + agent_id = var.agent_id + inner_ip = var.inner_ip + port = 22 + account = var.account + password = var.password + os_type = "LINUX" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional, String, ForceNew) Specifies the region in which to create the resource. + If omitted, the provider-level region will be used. Changing this creates a new resource. + +* `installer_agent_id` - (Required, String, NonUpdatable) Specifies the installer agent ID. + +* `version` - (Required, String) Specifies the UniAgent version to be installed. + + -> When updating the `version`, `agent_id` must be specified. + +* `public_net_flag` - (Required, Bool, NonUpdatable) Specifies the whether to use public network access. + +* `proxy_region_id` - (Required, Int, NonUpdatable) Specifies the proxy region ID. + + Specifies it as **0** to use the direct access. + + Specifies it as specific proxy region ID to use the proxy access. + +* `inner_ip` - (Required, String, NonUpdatable) Specifies the IP of the host where the UniAgent will be installed. + +* `port` - (Required, String, NonUpdatable) Specifies the login port of the host where the UniAgent will be installed. + +* `account` - (Required, String, NonUpdatable) Specifies the login account of the host where the UniAgent will be + installed. + +* `password` - (Required, String, NonUpdatable) Specifies the login password of the host where the UniAgent will be + installed. + +* `os_type` - (Required, String, NonUpdatable) Specifies the OS type of the host where the UniAgent will be installed. + +* `agent_id` - (Optional, String) Specifies the agent ID of the host where the UniAgent will be installed. + + -> If you are reinstalling the UniAgent or updating the UniAgent version, `agent_id` must specified. + +* `vpc_id` - (Optional, String, NonUpdatable) Specifies the VPC ID of the host where the UniAgent will be installed. + +* `coc_cmdb_id` - (Optional, String, NonUpdatable) Specifies the COC CMDB ID of the host where the UniAgent will be + installed. + +* `icagent_install_flag` - (Optional, Bool, NonUpdatable) Specifies whether to install ICAgent. Defaults to **false**. + +* `icagent_install_version` - (Optional, String, NonUpdatable) Specifies the ICAgent version to be installed. + If it's not specified and `icagent_install_flag` is **true**, install the latest version by default. + +* `access_key` - (Optional, String, NonUpdatable) Specifies the access key of the IAM account where the host ICAgent is + not installed. + +* `secret_key` - (Optional, String, NonUpdatable) Specifies the secret key of the IAM account where the host ICAgent is + not installed. + + -> AK/SK is required only when installing the older version of ICAgent. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The resource ID in UUID format. diff --git a/huaweicloud/config/logger.go b/huaweicloud/config/logger.go index 1ee46d2e44..acd0c8c8eb 100644 --- a/huaweicloud/config/logger.go +++ b/huaweicloud/config/logger.go @@ -292,6 +292,6 @@ func isSecurityFields(field string) bool { // request JSON body securityFields := []string{"adminpass", "encrypted_user_data", "nonce", "email", "phone", "phone_number", "phone_num", "sip_number", "signature", "user_passwd", "auth", "cert_content", "private_key", "trusted_root_ca", "sk", "src_sk", - "dst_sk"} + "dst_sk", "domain_sk"} return utils.StrSliceContains(securityFields, checkField) } diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index a8fcbe3305..e67b70c2a6 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -1398,6 +1398,7 @@ func Provider() *schema.Provider { "huaweicloud_access_analyzer_archive_rule": accessanalyzer.ResourceArchiveRule(), "huaweicloud_access_analyzer_archive_rule_apply": accessanalyzer.ResourceArchiveRuleApply(), + "huaweicloud_aom_uniagent": aom.ResourceUniAgent(), "huaweicloud_aom_alarm_rule": aom.ResourceAlarmRule(), "huaweicloud_aomv4_alarm_rule": aom.ResourceAlarmRuleV4(), "huaweicloud_aom_event_alarm_rule": aom.ResourceEventAlarmRule(), diff --git a/huaweicloud/services/acceptance/acceptance.go b/huaweicloud/services/acceptance/acceptance.go index d712972c94..79017f727b 100644 --- a/huaweicloud/services/acceptance/acceptance.go +++ b/huaweicloud/services/acceptance/acceptance.go @@ -358,6 +358,7 @@ var ( // The CMDB sub-application ID of AOM service HW_AOM_SUB_APPLICATION_ID = os.Getenv("HW_AOM_SUB_APPLICATION_ID") HW_AOM_MULTI_ACCOUNT_AGGREGATION_RULE_ENABLE = os.Getenv("HW_AOM_MULTI_ACCOUNT_AGGREGATION_RULE_ENABLE") + HW_AOM_UNIAGENT_AGENT_ID = os.Getenv("HW_AOM_UNIAGENT_AGENT_ID") // the ID of ECS instance which has installed uniagent HW_COC_INSTANCE_ID = os.Getenv("HW_COC_INSTANCE_ID") @@ -2151,6 +2152,13 @@ func TestAccPreCheckMultiAccountAggregationRuleEnable(t *testing.T) { } } +// lintignore:AT003 +func TestAccPreCheckAOMUniAgentAgentID(t *testing.T) { + if HW_AOM_UNIAGENT_AGENT_ID == "" { + t.Skip("HW_AOM_UNIAGENT_AGENT_ID must be set for the acceptance test") + } +} + // lintignore:AT003 func TestAccPreCheckCocInstanceID(t *testing.T) { if HW_COC_INSTANCE_ID == "" { diff --git a/huaweicloud/services/acceptance/aom/resource_huaweicloud_aom_uniagent_test.go b/huaweicloud/services/acceptance/aom/resource_huaweicloud_aom_uniagent_test.go new file mode 100644 index 0000000000..0f1d2397e6 --- /dev/null +++ b/huaweicloud/services/acceptance/aom/resource_huaweicloud_aom_uniagent_test.go @@ -0,0 +1,95 @@ +package aom + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance/common" +) + +func TestAccUniAgent_basic(t *testing.T) { + rName := acceptance.RandomAccResourceName() + resourceName := "huaweicloud_aom_uniagent.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + acceptance.TestAccPreCheckAOMUniAgentAgentID(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testUniAgent_basic(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "version", "1.1.6"), + ), + }, + { + Config: testUniAgent_update(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "version", "1.1.5"), + ), + }, + }, + }) +} + +func testUniAgent_ECS_base(name string) string { + return fmt.Sprintf(` +%[1]s + +resource "huaweicloud_compute_instance" "test" { + name = "%[2]s" + image_id = data.huaweicloud_images_image.test.id + flavor_id = data.huaweicloud_compute_flavors.test.ids[0] + availability_zone = data.huaweicloud_availability_zones.test.names[0] + admin_pass = "Terraform@123" + delete_disks_on_termination = true + security_group_ids = [huaweicloud_networking_secgroup.test.id] + + network { + uuid = huaweicloud_vpc_subnet.test.id + } +}`, common.TestBaseComputeResources(name), name) +} + +func testUniAgent_basic(name string) string { + return fmt.Sprintf(` +%[1]s + +resource "huaweicloud_aom_uniagent" "test" { + installer_agent_id = "%[2]s" + version = "1.1.6" + public_net_flag = false + proxy_region_id = 0 + inner_ip = huaweicloud_compute_instance.test.access_ip_v4 + port = 22 + account = "root" + password = "Terraform@123" + os_type = "LINUX" +}`, testUniAgent_ECS_base(name), acceptance.HW_AOM_UNIAGENT_AGENT_ID) +} + +func testUniAgent_update(name string) string { + return fmt.Sprintf(` +%[1]s + +# enter installer_agent_id to agent_id, just for test +# when update version, API do not check for agent_id, and response will be success, but actually do nothing +resource "huaweicloud_aom_uniagent" "test" { + installer_agent_id = "%[2]s" + version = "1.1.5" + public_net_flag = false + proxy_region_id = 0 + agent_id = "%[2]s" + inner_ip = huaweicloud_compute_instance.test.access_ip_v4 + port = 22 + account = "root" + password = "Terraform@123" + os_type = "LINUX" +}`, testUniAgent_ECS_base(name), acceptance.HW_AOM_UNIAGENT_AGENT_ID) +} diff --git a/huaweicloud/services/aom/resource_huaweicloud_aom_uniagent.go b/huaweicloud/services/aom/resource_huaweicloud_aom_uniagent.go new file mode 100644 index 0000000000..db79bf6b28 --- /dev/null +++ b/huaweicloud/services/aom/resource_huaweicloud_aom_uniagent.go @@ -0,0 +1,289 @@ +package aom + +import ( + "context" + "errors" + "strings" + + "github.com/hashicorp/go-uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/chnsz/golangsdk" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +var nonUpdatableParams = []string{ + "installer_agent_id", "public_net_flag", "proxy_region_id", + "inner_ip", "port", "account", "password", "os_type", "vpc_id", "coc_cmdb_id", + "icagent_install_flag", "icagent_install_version", "access_key", "secret_key", +} + +// @API AOM POST /v1/{project_id}/uniagent-console/mainview/batch-import +// @API AOM POST /v1/{project_id}/uniagent-console/upgrade/batch-upgrade +func ResourceUniAgent() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceUniAgentCreate, + ReadContext: resourceUniAgentRead, + UpdateContext: resourceUniAgentUpdate, + DeleteContext: resourceUniAgentDelete, + + CustomizeDiff: customdiff.All( + config.FlexibleForceNew(nonUpdatableParams), + func(_ context.Context, d *schema.ResourceDiff, _ interface{}) error { + if oldValue, newValue := d.GetChange("version"); oldValue != newValue && oldValue != "" { + agentID := d.Get("agent_id").(string) + if agentID == "" { + return errors.New("only support to update `version` when `agent_id` is not empty") + } + } + return nil + }, + ), + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "installer_agent_id": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the installer agent ID.`, + }, + "version": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the UniAgent version to be installed.`, + }, + "public_net_flag": { + Type: schema.TypeBool, + Required: true, + Description: `Specifies the whether to use public network access.`, + }, + "proxy_region_id": { + Type: schema.TypeInt, + Required: true, + Description: `Specifies the proxy region ID.`, + }, + // host info + "inner_ip": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the IP of the host where the UniAgent will be installed.`, + }, + "port": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the login port of the host where the UniAgent will be installed.`, + }, + "account": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the login account of the host where the UniAgent will be installed.`, + }, + "password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + Description: `Specifies the login password of the host where the UniAgent will be installed.`, + }, + "os_type": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the OS type of the host where the UniAgent will be installed.`, + }, + "vpc_id": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies the VPC ID of the host where the UniAgent will be installed.`, + }, + "agent_id": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies the agent ID of the host where the UniAgent will be installed.`, + }, + "coc_cmdb_id": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies the COC CMDB ID of the host where the UniAgent will be installed.`, + }, + // icagent + "icagent_install_flag": { + Type: schema.TypeBool, + Optional: true, + Description: `Specifies whether to install ICAgent.`, + }, + "icagent_install_version": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies the ICAgent version to be installed.`, + }, + "access_key": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"secret_key"}, + Description: `Specifies the access key of the IAM account where the host ICAgent is not installed.`, + }, + "secret_key": { + Type: schema.TypeString, + Sensitive: true, + Optional: true, + RequiredWith: []string{"access_key"}, + Description: `Specifies the secret key of the IAM account where the host ICAgent is not installed.`, + }, + }, + } +} + +func resourceUniAgentCreate(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + client, err := cfg.NewServiceClient("aom", region) + if err != nil { + return diag.Errorf("error creating AOM client: %s", err) + } + + createHttpUrl := "v1/{project_id}/uniagent-console/mainview/batch-import" + createPath := client.Endpoint + createHttpUrl + createPath = strings.ReplaceAll(createPath, "{project_id}", client.ProjectID) + createOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + MoreHeaders: map[string]string{ + "region": region, + }, + JSONBody: utils.RemoveNil(buildCreateUniAgentBodyParams(d)), + } + + createResp, err := client.Request("POST", createPath, &createOpt) + if err != nil { + return diag.Errorf("error creating AOM UniAgent: %s", err) + } + createRespBody, err := utils.FlattenResponse(createResp) + if err != nil { + return diag.Errorf("error flattening response: %s", err) + } + + if !utils.PathSearch("state", createRespBody, false).(bool) { + return diag.Errorf("error creating AOM UniAgent: %v", createRespBody) + } + + id, err := uuid.GenerateUUID() + if err != nil { + return diag.Errorf("error generating UUID") + } + d.SetId(id) + + return nil +} + +func buildCreateUniAgentBodyParams(d *schema.ResourceData) map[string]interface{} { + bodyParams := map[string]interface{}{ + "installer_agent_id": d.Get("installer_agent_id"), + "version": d.Get("version"), + "public_net_flag": d.Get("public_net_flag"), + "proxy_region_id": d.Get("proxy_region_id"), + "agent_import_param_list": buildCreateUniAgentBodyParamsAgentImportParamList(d), + "icagent_install_flag": utils.ValueIgnoreEmpty(d.Get("icagent_install_flag")), + "plugin_install_base_param": buildCreateUniAgentBodyParamsPluginInstallBaseParam(d), + } + + return bodyParams +} + +func buildCreateUniAgentBodyParamsAgentImportParamList(d *schema.ResourceData) []map[string]interface{} { + bodyParams := map[string]interface{}{ + "inner_ip": d.Get("inner_ip"), + "port": d.Get("port"), + "account": d.Get("account"), + "password": d.Get("password"), + "os_type": d.Get("os_type"), + "agent_id": utils.ValueIgnoreEmpty(d.Get("agent_id")), + "vpc_id": utils.ValueIgnoreEmpty(d.Get("vpc_id")), + "coc_cmdb_id": utils.ValueIgnoreEmpty(d.Get("coc_cmdb_id")), + } + + return []map[string]interface{}{bodyParams} +} + +func buildCreateUniAgentBodyParamsPluginInstallBaseParam(d *schema.ResourceData) interface{} { + bodyParams := utils.RemoveNil(map[string]interface{}{ + "install_version": utils.ValueIgnoreEmpty(d.Get("icagent_install_version")), + "domain_ak": utils.ValueIgnoreEmpty(d.Get("access_key")), + "domain_sk": utils.ValueIgnoreEmpty(d.Get("secret_key")), + }) + + return bodyParams +} + +func resourceUniAgentRead(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + return nil +} + +func resourceUniAgentUpdate(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + client, err := cfg.NewServiceClient("aom", region) + if err != nil { + return diag.Errorf("error creating AOM client: %s", err) + } + + if d.HasChange("version") { + updateHttpUrl := "v1/{project_id}/uniagent-console/upgrade/batch-upgrade" + updatePath := client.Endpoint + updateHttpUrl + updatePath = strings.ReplaceAll(updatePath, "{project_id}", client.ProjectID) + updateOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + JSONBody: buildUpdateUniAgentBodyParams(d), + } + + updateResp, err := client.Request("POST", updatePath, &updateOpt) + if err != nil { + return diag.Errorf("error updating UniAgent: %s", err) + } + updateRespBody, err := utils.FlattenResponse(updateResp) + if err != nil { + return diag.Errorf("error flattening response: %s", err) + } + + if !utils.PathSearch("state", updateRespBody, false).(bool) { + return diag.Errorf("error updating UniAgent: %v", updateResp) + } + } + + return nil +} + +func buildUpdateUniAgentBodyParams(d *schema.ResourceData) map[string]interface{} { + bodyParams := map[string]interface{}{ + "version": d.Get("version"), + "agent_list": buildCUpdateUniAgentBodyParamsAgentList(d), + } + + return bodyParams +} + +func buildCUpdateUniAgentBodyParamsAgentList(d *schema.ResourceData) []map[string]interface{} { + bodyParams := map[string]interface{}{ + "inner_ip": d.Get("inner_ip"), + "agent_id": d.Get("agent_id"), + } + + return []map[string]interface{}{bodyParams} +} + +func resourceUniAgentDelete(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + errorMsg := "Deleting AOM UniAgent resource is not supported. The UniAgent resource is only removed from the state," + + " the UniAgent remains in the host." + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: errorMsg, + }, + } +}